FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Routed JS-to-NSString bridge through a UTF-16-faithful ToNSString · NativeScript/ios@d2c6d57 · GitHub

Commit d2c6d57

Browse files
Adrian Niculescu
committed
Routed JS-to-NSString bridge through a UTF-16-faithful ToNSString
Made tns::ToNSString read the V8 string's UTF-16 buffer directly and pointed the six bridge sites at it, instead of building a std::u16string and copying it into NSString at each one. That drops the extra copy and the repeated stringWithCharacters boilerplate, keeps the conversion in one place, and brings Interop back to matching vanilla. Kept the ToUtf16String rewrite as the general accessor. Added an embedded-NUL test next to the lone-surrogate ones, since the bridge now has to preserve NUL bytes too.
1 parent 8cc59bf commit d2c6d57

5 files changed

Lines changed: 29 additions & 19 deletions

File tree

‎NativeScript/runtime/ArgConverter.mm‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,7 @@
294294
} else if (value->IsString()) {
295295
if (type == BinaryTypeEncodingType::IdEncoding ||
296296
type == BinaryTypeEncodingType::InterfaceDeclarationReference) {
297-
std::u16string strValue = tns::ToUtf16String(isolate, value);
298-
id data = [NSString stringWithCharacters:(const unichar*)strValue.data() length:strValue.size()];
297+
id data = tns::ToNSString(isolate, value);
299298
// this feels wrong but follows the other CFBridgingRetain calls
300299
// and also solves a leak
301300
auto ref = CFBridgingRetain(data);

‎NativeScript/runtime/DictionaryAdapter.mm‎

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,7 @@ - (id)nextObject {
4848
bool success = array->Get(context, self->index_).ToLocal(&key);
4949
tns::Assert(success, isolate);
5050
self->index_ += 2;
51-
std::u16string keyStr = tns::ToUtf16String(isolate, key);
52-
NSString* result = [NSString stringWithCharacters:(const unichar*)keyStr.data() length:keyStr.length()];
51+
NSString* result = tns::ToNSString(isolate, key);
5352
return result;
5453
}
5554

@@ -117,8 +116,7 @@ - (id)nextObject {
117116
bool success = properties->Get(context, (uint)self->index_).ToLocal(&value);
118117
tns::Assert(success, isolate);
119118
self->index_++;
120-
std::u16string result = tns::ToUtf16String(isolate, value);
121-
return [NSString stringWithCharacters:(const unichar*)result.data() length:result.size()];
119+
return tns::ToNSString(isolate, value);
122120
}
123121

124122
return nil;
@@ -140,8 +138,7 @@ - (NSArray*)allObjects {
140138
Local<Value> value;
141139
bool success = properties->Get(context, i).ToLocal(&value);
142140
tns::Assert(success, isolate);
143-
std::u16string result = tns::ToUtf16String(isolate, value);
144-
[array addObject:[NSString stringWithCharacters:(const unichar*)result.data() length:result.size()]];
141+
[array addObject:tns::ToNSString(isolate, value)];
145142
}
146143

147144
return array;

‎NativeScript/runtime/Helpers.h‎

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@ inline NSString* ToNSString(const std::string& v) {
106106
length:v.length()
107107
encoding:NSUTF8StringEncoding] S_AUTORELEASE];
108108
}
109-
// this method is a copy of ToString to avoid needless std::string<->NSString
110-
// conversions
109+
// Reads the V8 string's native UTF-16 buffer directly so lone surrogates and
110+
// embedded NUL survive the bridge; a UTF-8 round-trip loses both.
111111
inline NSString* ToNSString(v8::Isolate* isolate,
112112
const v8::Local<v8::Value>& value) {
113113
if (value.IsEmpty()) {
@@ -119,16 +119,15 @@ inline NSString* ToNSString(v8::Isolate* isolate,
119119
return ToNSString(isolate, obj);
120120
}
121121

122-
v8::String::Utf8Value result(isolate, value);
122+
v8::String::Value result(isolate, value);
123123

124-
const char* val = *result;
124+
const uint16_t* val = *result;
125125
if (val == nullptr) {
126126
return @"";
127127
}
128128

129-
return [[[NSString alloc] initWithBytes:*result
130-
length:result.length()
131-
encoding:NSUTF8StringEncoding] S_AUTORELEASE];
129+
return [NSString stringWithCharacters:(const unichar*)val
130+
length:result.length()];
132131
}
133132
#endif
134133
std::u16string ToUtf16String(v8::Isolate* isolate,

‎NativeScript/runtime/Interop.mm‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -324,8 +324,7 @@ inline bool isBool() {
324324
} else if (argHelper.isString() &&
325325
(typeEncoding->type == BinaryTypeEncodingType::InterfaceDeclarationReference ||
326326
typeEncoding->type == BinaryTypeEncodingType::IdEncoding)) {
327-
std::u16string str = tns::ToUtf16String(isolate, arg);
328-
NSString* result = [NSString stringWithCharacters:(const unichar*)str.data() length:str.size()];
327+
NSString* result = tns::ToNSString(isolate, arg);
329328
Interop::SetValue(dest, result);
330329
} else if (Interop::IsNumbericType(typeEncoding->type) || tns::IsNumber(arg)) {
331330
double value = tns::ToNumber(isolate, arg);
@@ -687,8 +686,7 @@ inline bool isBool() {
687686
if (arg.IsEmpty() || arg->IsNullOrUndefined()) {
688687
return nil;
689688
} else if (tns::IsString(arg)) {
690-
std::u16string value = tns::ToUtf16String(isolate, arg);
691-
NSString* result = [NSString stringWithCharacters:(const unichar*)value.data() length:value.size()];
689+
NSString* result = tns::ToNSString(isolate, arg);
692690
return result;
693691
} else if (tns::IsNumber(arg)) {
694692
double value = tns::ToNumber(isolate, arg);

‎TestRunner/app/tests/ApiTests.js‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,23 @@ describe(module.id, function () {
4545
expect(codeUnit).toBe(0xDC00); // 0xFFFD (65533) after a lossy UTF-8 round-trip
4646
});
4747

48+
it("preserves an embedded NUL when bridging a JS string to NSString", function () {
49+
// U+0000 is a valid JS code unit but terminates a C string, so a bridge
50+
// that went through char* would cut "a\0b" down to "a". Faithful UTF-16
51+
// bridging keeps all three units. Read the NUL unit straight out of the
52+
// NSString's buffer so the check does not lean on a native-to-JS conversion.
53+
var withNul = "a" + String.fromCharCode(0) + "b";
54+
var ns = NSString.stringWithString(withNul);
55+
expect(ns.length).toBe(3);
56+
57+
var buffer = interop.alloc(interop.sizeof(interop.types.uint16));
58+
ns.getCharactersRange(buffer, NSMakeRange(1, 1));
59+
var codeUnit = new interop.Reference(interop.types.uint16, buffer).value;
60+
interop.free(buffer);
61+
62+
expect(codeUnit).toBe(0x0000); // a char* bridge would have stopped before this
63+
});
64+
4865
it("NSArray from native (uncached) array access", function () {
4966
const res = TNSObjCTypes.new().getNSArrayOfNSURLs();
5067
console.log(res);

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL