Skip to content

Commit 0a07fcb

Browse files
committed
feat: optimized 1/3 cost.
1 parent 8f1a16a commit 0a07fcb

File tree

11 files changed

+137
-75
lines changed

11 files changed

+137
-75
lines changed

dart_native/example/ios/Runner.xcodeproj/project.pbxproj

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@
159159
97C146EC1CF9000F007C117D /* Resources */,
160160
9705A1C41CF9048500538489 /* Embed Frameworks */,
161161
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
162-
3199B333F30D678225AFDF10 /* [CP] Embed Pods Frameworks */,
162+
EBECF6F1409D0440724C4F6A /* [CP] Embed Pods Frameworks */,
163163
);
164164
buildRules = (
165165
);
@@ -240,53 +240,53 @@
240240
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
241241
showEnvVarsInLog = 0;
242242
};
243-
3199B333F30D678225AFDF10 /* [CP] Embed Pods Frameworks */ = {
243+
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
244244
isa = PBXShellScriptBuildPhase;
245245
buildActionMask = 2147483647;
246246
files = (
247247
);
248248
inputPaths = (
249-
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
250-
"${PODS_ROOT}/../Flutter/Flutter.framework",
251-
"${PODS_ROOT}/../.symlinks/plugins/dart_native/ios/DartNative.framework",
252249
);
253-
name = "[CP] Embed Pods Frameworks";
250+
name = "Thin Binary";
254251
outputPaths = (
255-
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework",
256-
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DartNative.framework",
257252
);
258253
runOnlyForDeploymentPostprocessing = 0;
259254
shellPath = /bin/sh;
260-
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
261-
showEnvVarsInLog = 0;
255+
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n";
262256
};
263-
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
257+
9740EEB61CF901F6004384FC /* Run Script */ = {
264258
isa = PBXShellScriptBuildPhase;
265259
buildActionMask = 2147483647;
266260
files = (
267261
);
268262
inputPaths = (
269263
);
270-
name = "Thin Binary";
264+
name = "Run Script";
271265
outputPaths = (
272266
);
273267
runOnlyForDeploymentPostprocessing = 0;
274268
shellPath = /bin/sh;
275-
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n";
269+
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
276270
};
277-
9740EEB61CF901F6004384FC /* Run Script */ = {
271+
EBECF6F1409D0440724C4F6A /* [CP] Embed Pods Frameworks */ = {
278272
isa = PBXShellScriptBuildPhase;
279273
buildActionMask = 2147483647;
280274
files = (
281275
);
282276
inputPaths = (
277+
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
278+
"${PODS_ROOT}/../Flutter/Flutter.framework",
279+
"${PODS_ROOT}/../.symlinks/plugins/dart_native/ios/DartNative.framework",
283280
);
284-
name = "Run Script";
281+
name = "[CP] Embed Pods Frameworks";
285282
outputPaths = (
283+
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework",
284+
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DartNative.framework",
286285
);
287286
runOnlyForDeploymentPostprocessing = 0;
288287
shellPath = /bin/sh;
289-
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
288+
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
289+
showEnvVarsInLog = 0;
290290
};
291291
/* End PBXShellScriptBuildPhase section */
292292

dart_native/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,6 @@
5959
ReferencedContainer = "container:Runner.xcodeproj">
6060
</BuildableReference>
6161
</BuildableProductRunnable>
62-
<AdditionalOptions>
63-
<AdditionalOption
64-
key = "NSZombieEnabled"
65-
value = "YES"
66-
isEnabled = "YES">
67-
</AdditionalOption>
68-
</AdditionalOptions>
6962
</LaunchAction>
7063
<ProfileAction
7164
buildConfiguration = "Profile"

dart_native/ios/Classes/native_runtime.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,23 @@ native_protocol_method_types(Protocol *proto, SEL selector);
3333
DN_EXTERN Class _Nullable
3434
native_get_class(const char *className, Class superclass);
3535

36+
37+
/// Invoke Objective-C method.
38+
/// @param object instance or class object.
39+
/// @param selector selector of method.
40+
/// @param signature signature of method.
41+
/// @param queue dispatch queue for async method.
42+
/// @param args arguments passed to method.
43+
/// @param dartPort port for dart isolate.
44+
/// @param stringTypeBitmask first bit is for return value.
3645
DN_EXTERN void * _Nullable
37-
native_instance_invoke(id object, SEL selector, NSMethodSignature *signature, dispatch_queue_t queue, void * _Nonnull * _Nullable args, void (^callback)(void *), Dart_Port dartPort);
46+
native_instance_invoke(id object, SEL selector, NSMethodSignature *signature, dispatch_queue_t queue, void * _Nonnull * _Nullable args, void (^callback)(void *), Dart_Port dartPort, int64_t stringTypeBitmask);
3847

3948
DN_EXTERN void *
4049
native_block_create(char *types, void *callback, Dart_Port dartPort);
4150

4251
DN_EXTERN void *
43-
native_block_invoke(void *block, void * _Nonnull * _Nullable args, Dart_Port dartPort);
52+
native_block_invoke(void *block, void * _Nonnull * _Nullable args, Dart_Port dartPort, int64_t stringTypeBitmask);
4453

4554
DN_EXTERN const char * _Nonnull * _Nonnull
4655
native_all_type_encodings(void);

dart_native/ios/Classes/native_runtime.mm

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@
104104
}
105105

106106
void
107-
_fillArgsToInvocation(NSMethodSignature *signature, void **args, NSInvocation *invocation, NSUInteger offset) {
107+
_fillArgsToInvocation(NSMethodSignature *signature, void **args, NSInvocation *invocation, NSUInteger offset, int64_t stringTypeBitmask, NSMutableArray<NSString *> *stringTypeBucket) {
108108
for (NSUInteger i = offset; i < signature.numberOfArguments; i++) {
109109
const char *argType = [signature getArgumentTypeAtIndex:i];
110110
NSUInteger argsIndex = i - offset;
@@ -118,21 +118,36 @@
118118
if (argType[0] == '{') {
119119
// Already put struct in pointer on Dart side.
120120
[invocation setArgument:args[argsIndex] atIndex:i];
121+
} else if (argType[0] == '@' &&
122+
(stringTypeBitmask >> argsIndex & 0x1) == 1) {
123+
const unichar *data = ((const unichar **)args)[argsIndex];
124+
// First four uint16_t is for data length.
125+
const NSUInteger dataOffset = 4;
126+
uint64_t length = data[0];
127+
for (int i = 1; i < dataOffset; i++) {
128+
length <<= 16;
129+
length |= data[i];
130+
}
131+
NSString *realArg = [NSString stringWithCharacters:data + dataOffset length:length];
132+
[stringTypeBucket addObject:realArg];
133+
free((void *)data); // Malloc data on dart side, need free here.
134+
[invocation setArgument:&realArg atIndex:i];
121135
} else {
122136
[invocation setArgument:&args[argsIndex] atIndex:i];
123137
}
124138
}
125139
}
126140

127141
void *
128-
native_instance_invoke(id object, SEL selector, NSMethodSignature *signature, dispatch_queue_t queue, void **args, void (^callback)(void *), Dart_Port dartPort) {
142+
native_instance_invoke(id object, SEL selector, NSMethodSignature *signature, dispatch_queue_t queue, void **args, void (^callback)(void *), Dart_Port dartPort, int64_t stringTypeBitmask) {
129143
if (!object || !selector || !signature) {
130144
return NULL;
131145
}
132146
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
133147
invocation.target = object;
134148
invocation.selector = selector;
135-
_fillArgsToInvocation(signature, args, invocation, 2);
149+
NSMutableArray<NSString *> *stringTypeBucket = [NSMutableArray array];
150+
_fillArgsToInvocation(signature, args, invocation, 2, stringTypeBitmask, stringTypeBucket);
136151

137152
void *(^resultBlock)() = ^() {
138153
void *result = NULL;
@@ -183,11 +198,12 @@
183198
}
184199

185200
void *
186-
native_block_invoke(void *block, void **args, Dart_Port dartPort) {
201+
native_block_invoke(void *block, void **args, Dart_Port dartPort, int64_t stringTypeBitmask) {
187202
const char *typeString = DNBlockTypeEncodeString((__bridge id)block);
188203
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:typeString];
189204
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
190-
_fillArgsToInvocation(signature, args, invocation, 1);
205+
NSMutableArray<NSString *> *stringTypeBucket = [NSMutableArray array];
206+
_fillArgsToInvocation(signature, args, invocation, 1, stringTypeBitmask, stringTypeBucket);
191207
[invocation invokeWithTarget:(__bridge id)block];
192208
void *result = NULL;
193209
const char returnType = signature.methodReturnType[0];
88 Bytes
Binary file not shown.

dart_native/lib/src/ios/common/pointer_encoding.dart

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,7 @@ dynamic storeValueToPointer(
156156
if (encoding.maybeCString) {
157157
return storeCStringToPointer(object, ptr);
158158
} else if (encoding.maybeObject) {
159-
NSString string = NSString(object);
160-
ptr.value = string.pointer;
159+
storeStringToPointer(object, ptr);
161160
}
162161
} else if (object is List && encoding.maybeObject) {
163162
ptr.value = NSArray(object).pointer;
@@ -188,6 +187,23 @@ PointerWrapper storeStructToPointer(
188187
return null;
189188
}
190189

190+
void storeStringToPointer(String object, Pointer<Pointer<Void>> ptr) {
191+
List<int> units = object.codeUnits;
192+
List<int> data = List.from(units);
193+
int length = units.length;
194+
//
195+
List<int> length64Bit = [
196+
length >> 48 & 0xffff,
197+
length >> 32 & 0xffff,
198+
length >> 16 & 0xffff,
199+
length & 0xffff,
200+
];
201+
data.insertAll(0, length64Bit);
202+
// utf16Ptr will be freed on native side.
203+
Pointer<Uint16> utf16Ptr = data.toUtf16Buffer();
204+
ptr.value = utf16Ptr.cast();
205+
}
206+
191207
dynamic storeCStringToPointer(String object, Pointer<Pointer<Void>> ptr) {
192208
Pointer<Utf8> charPtr = Utf8.toUtf8(object);
193209
PointerWrapper wrapper = PointerWrapper();

dart_native/lib/src/ios/foundation/nsstring.dart

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,30 @@ class NSMutableString extends NSString {
3838

3939
Pointer<Void> _new(dynamic value) {
4040
if (value is String) {
41-
final units = value.codeUnits;
42-
final Pointer<Uint16> charPtr = allocate<Uint16>(count: units.length + 1);
43-
final Uint16List nativeString = charPtr.asTypedList(units.length + 1);
44-
nativeString.setAll(0, units);
45-
nativeString[units.length] = 0;
46-
NSObject result = Class('NSString').perform(
41+
List<int> units = value.codeUnits;
42+
Pointer<Uint16> utf16Ptr = units.toUtf16Buffer();
43+
Pointer<Void> result = Class('NSString').perform(
4744
SEL('stringWithCharacters:length:'),
48-
args: [charPtr, units.length]);
49-
free(charPtr);
50-
return result.pointer;
45+
args: [utf16Ptr, units.length],
46+
decodeRetVal: false);
47+
free(utf16Ptr);
48+
return result;
5149
} else {
5250
throw 'Invalid param when initializing NSString.';
5351
}
5452
}
5553

54+
extension Utf16Buffer on List<int> {
55+
Pointer<Uint16> toUtf16Buffer() {
56+
final count = length + 1;
57+
final Pointer<Uint16> result = allocate<Uint16>(count: count);
58+
final Uint16List typedList = result.asTypedList(count);
59+
typedList.setAll(0, this);
60+
typedList[this.length] = 0;
61+
return result;
62+
}
63+
}
64+
5665
extension ConvertToNSString on String {
5766
NSString toNSString() => NSString(this);
5867
NSMutableString toNSMutableString() => NSMutableString(this);

dart_native/lib/src/ios/runtime/block.dart

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ class Block extends id {
137137
if (count != (args?.length ?? 0) + 2) {
138138
throw 'Args Count NOT match';
139139
}
140-
140+
int stringTypeBitmask = 0;
141141
Pointer<Pointer<Void>> argsPtrPtr = nullptr.cast();
142142
if (args != null) {
143143
argsPtrPtr = allocate<Pointer<Void>>(count: args.length);
@@ -146,11 +146,15 @@ class Block extends id {
146146
if (arg == null) {
147147
arg = nil;
148148
}
149+
if (arg is String) {
150+
stringTypeBitmask |= (0x1 << i);
151+
}
149152
storeValueToPointer(
150153
arg, argsPtrPtr.elementAt(i), typesPtrPtr.elementAt(i + 2).value);
151154
}
152155
}
153-
Pointer<Void> resultPtr = blockInvoke(pointer, argsPtrPtr, nativePort);
156+
Pointer<Void> resultPtr =
157+
blockInvoke(pointer, argsPtrPtr, nativePort, stringTypeBitmask);
154158
if (argsPtrPtr != nullptr.cast()) {
155159
free(argsPtrPtr);
156160
}

dart_native/lib/src/ios/runtime/internal/native_runtime.dart

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ typedef InvokeMethodC = Pointer<Void> Function(
5555
Pointer<Pointer<Void>> args,
5656
Pointer<Void> callback,
5757
Int64 dartPort,
58+
Uint64 stringTypeBitmask,
5859
);
5960
typedef InvokeMethodD = Pointer<Void> Function(
6061
Pointer<Void> instance,
@@ -64,6 +65,7 @@ typedef InvokeMethodD = Pointer<Void> Function(
6465
Pointer<Pointer<Void>> args,
6566
Pointer<Void> callback,
6667
int dartPort,
68+
int stringTypeBitmask,
6769
);
6870
final InvokeMethodD nativeInvokeMethod = runtimeLib
6971
.lookupFunction<InvokeMethodC, InvokeMethodD>('native_instance_invoke');
@@ -106,9 +108,17 @@ final BlockCreateD blockCreate = runtimeLib
106108
.lookupFunction<BlockCreateC, BlockCreateD>('native_block_create');
107109

108110
typedef BlockInvokeC = Pointer<Void> Function(
109-
Pointer<Void> block, Pointer<Pointer<Void>> args, Int64 dartPort);
111+
Pointer<Void> block,
112+
Pointer<Pointer<Void>> args,
113+
Int64 dartPort,
114+
Uint64 stringTypeBitmask,
115+
);
110116
typedef BlockInvokeD = Pointer<Void> Function(
111-
Pointer<Void> block, Pointer<Pointer<Void>> args, int dartPort);
117+
Pointer<Void> block,
118+
Pointer<Pointer<Void>> args,
119+
int dartPort,
120+
int stringTypeBitmask,
121+
);
112122
final BlockInvokeD blockInvoke = runtimeLib
113123
.lookupFunction<BlockInvokeC, BlockInvokeD>('native_block_invoke');
114124

dart_native/lib/src/ios/runtime/message.dart

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@ import 'package:ffi/ffi.dart';
1414
typedef void _AsyncMessageCallback(dynamic result);
1515

1616
Pointer<Void> _sendMsgToNative(
17-
Pointer<Void> target,
18-
Pointer<Void> selector,
19-
Pointer<Void> signature,
20-
Pointer<Pointer<Void>> args,
21-
DispatchQueue queue,
22-
Pointer<Void> callbackPtr) {
17+
Pointer<Void> target,
18+
Pointer<Void> selector,
19+
Pointer<Void> signature,
20+
Pointer<Pointer<Void>> args,
21+
DispatchQueue queue,
22+
Pointer<Void> callbackPtr,
23+
int stringTypeBitmask,
24+
) {
2325
Pointer<Void> result;
2426
Pointer<Void> queuePtr = queue != null ? queue.pointer : nullptr.cast();
2527
// This awful code dues to this issue: https://github.com/dart-lang/sdk/issues/39488
@@ -32,8 +34,8 @@ Pointer<Void> _sendMsgToNative(
3234
if (callbackPtr == nullptr) {
3335
callbackPtr = nullptr.cast();
3436
}
35-
result = nativeInvokeMethod(
36-
target, selector, signature, queuePtr, args, callbackPtr, nativePort);
37+
result = nativeInvokeMethod(target, selector, signature, queuePtr, args,
38+
callbackPtr, nativePort, stringTypeBitmask);
3739
return result;
3840
}
3941

@@ -87,7 +89,7 @@ dynamic _msgSend(Pointer<Void> target, SEL selector,
8789
nativeSignatureEncodingList(signaturePtr, typeEncodingsPtrPtr);
8890

8991
List<NSObjectRef> outRefArgs = [];
90-
92+
int stringTypeBitmask = 0;
9193
Pointer<Pointer<Void>> pointers;
9294
if (args != null) {
9395
pointers = allocate<Pointer<Void>>(count: argCount);
@@ -98,6 +100,9 @@ dynamic _msgSend(Pointer<Void> target, SEL selector,
98100
} else if (arg is NSObjectRef) {
99101
outRefArgs.add(arg);
100102
}
103+
if (arg is String) {
104+
stringTypeBitmask |= (0x1 << i);
105+
}
101106
Pointer<Utf8> argTypePtr =
102107
nativeTypeEncoding(typeEncodingsPtrPtr.elementAt(i + 1).value);
103108
storeValueToPointer(arg, pointers.elementAt(i), argTypePtr);
@@ -115,8 +120,8 @@ dynamic _msgSend(Pointer<Void> target, SEL selector,
115120
}
116121
}
117122

118-
Pointer<Void> resultPtr = _sendMsgToNative(
119-
target, selectorPtr, signaturePtr, pointers, onQueue, callbackPtr);
123+
Pointer<Void> resultPtr = _sendMsgToNative(target, selectorPtr, signaturePtr,
124+
pointers, onQueue, callbackPtr, stringTypeBitmask);
120125
if (pointers != null) {
121126
free(pointers);
122127
}

0 commit comments

Comments
 (0)