diff --git a/NativeScript/runtime/Interop.h b/NativeScript/runtime/Interop.h index 3963fc28..a2582374 100644 --- a/NativeScript/runtime/Interop.h +++ b/NativeScript/runtime/Interop.h @@ -134,6 +134,7 @@ class Interop { static void InitializeStruct(v8::Local context, void* destBuffer, std::vector fields, v8::Local inititalizer, ptrdiff_t& position); static void RegisterInteropType(v8::Local context, v8::Local types, std::string name, PrimitiveDataWrapper* wrapper, bool autoDelete = true); static void RegisterBufferFromDataFunction(v8::Local context, v8::Local interop); + static void RegisterStringFromCString(v8::Local context, v8::Local interop); static void RegisterHandleOfFunction(v8::Local context, v8::Local interop); static void RegisterAllocFunction(v8::Local context, v8::Local interop); static void RegisterFreeFunction(v8::Local context, v8::Local interop); diff --git a/NativeScript/runtime/InteropTypes.mm b/NativeScript/runtime/InteropTypes.mm index bca1e2a8..27c7579d 100644 --- a/NativeScript/runtime/InteropTypes.mm +++ b/NativeScript/runtime/InteropTypes.mm @@ -25,6 +25,7 @@ Pointer::Register(context, interop); FunctionReference::Register(context, interop); RegisterBufferFromDataFunction(context, interop); + RegisterStringFromCString(context, interop); RegisterHandleOfFunction(context, interop); RegisterAllocFunction(context, interop); RegisterFreeFunction(context, interop); @@ -140,6 +141,60 @@ tns::Assert(success, isolate); } +void Interop::RegisterStringFromCString(Local context, Local interop) { + Local func; + bool success = v8::Function::New(context, [](const FunctionCallbackInfo& info) { + Isolate* isolate = info.GetIsolate(); + tns::Assert(info.Length() >= 1 && info[0]->IsObject(), isolate); + Local arg = info[0].As(); + int stringLength = -1; + if(info.Length() >= 2 && !info[1].IsEmpty() && !info[1]->IsNullOrUndefined()) { + auto desiredLength = ToNumber(isolate, info[1]); + if (desiredLength != NAN) { + stringLength = desiredLength; + } + } + tns::Assert(arg->InternalFieldCount() > 0 && arg->GetInternalField(0)->IsExternal(), isolate); + + Local ext = arg->GetInternalField(0).As(); + BaseDataWrapper* wrapper = static_cast(ext->Value()); + tns::Assert(wrapper != nullptr); + char* data = nullptr; + switch (wrapper->Type()) { + case WrapperType::Pointer: + { + PointerWrapper* pointerWrapper = static_cast(wrapper); + data = static_cast(pointerWrapper->Data()); + } + break; + case WrapperType::Reference: + { + ReferenceWrapper* referenceWrapper = static_cast(wrapper); + if (referenceWrapper->Data() != nullptr) { + data = static_cast(referenceWrapper->Data()); + break; + } + auto wrappedValue = referenceWrapper->Value()->Get(isolate); + auto wrappedWrapper = tns::GetValue(isolate, wrappedValue); + tns::Assert(wrappedWrapper->Type() == WrapperType::Pointer); + data = static_cast((static_cast(wrappedWrapper))->Data()); + } + default: + break; + } + tns::Assert(data != nullptr); + + auto result = v8::String::NewFromUtf8(isolate, data, v8::NewStringType::kNormal, stringLength).ToLocalChecked(); + info.GetReturnValue().Set(result); + }).ToLocal(&func); + + Isolate* isolate = context->GetIsolate(); + tns::Assert(success, isolate); + + success = interop->Set(context, tns::ToV8String(isolate, "stringFromCString"), func).FromMaybe(false); + tns::Assert(success, isolate); +} + void Interop::RegisterHandleOfFunction(Local context, Local interop) { Local func; bool success = v8::Function::New(context, [](const FunctionCallbackInfo& info) { diff --git a/TestRunner/app/tests/Marshalling/ReferenceTests.js b/TestRunner/app/tests/Marshalling/ReferenceTests.js index bb899947..fe0f9316 100644 --- a/TestRunner/app/tests/Marshalling/ReferenceTests.js +++ b/TestRunner/app/tests/Marshalling/ReferenceTests.js @@ -329,6 +329,38 @@ describe(module.id, function () { expect(TNSGetOutput()).toBe(str); expect(interop.handleof(result).toNumber() == interop.handleof(ptr).toNumber()); expect(NSString.stringWithUTF8String(result).toString()).toBe(str); + interop.free(ptr); + }); + + it("interops string from CString", function () { + const str = "test"; + const ptr = interop.alloc((str.length + 1) * interop.sizeof(interop.types.uint8)); + var reference = new interop.Reference(interop.types.uint8, ptr); + for (ii in str) { + const i = parseInt(ii); + reference[i] = str.charCodeAt(i); + } + reference[str.length] = 0; + expect(interop.stringFromCString(ptr)).toBe(str); + expect(interop.stringFromCString(reference)).toBe(str); + interop.free(ptr); + }); + + it("interops string from CString with fixed length", function () { + const str = "te\0st"; + const ptr = interop.alloc((str.length + 1) * interop.sizeof(interop.types.uint8)); + var reference = new interop.Reference(interop.types.uint8, ptr); + for (ii in str) { + const i = parseInt(ii); + reference[i] = str.charCodeAt(i); + } + reference[str.length] = 0; + // no length means it will go until it finds \0 + expect(interop.stringFromCString(ptr)).toBe('te'); + expect(interop.stringFromCString(ptr, 1)).toBe('t'); + expect(interop.stringFromCString(ptr, str.length)).toBe(str); + expect(interop.stringFromCString(reference, str.length)).toBe(str); + interop.free(ptr); }); it("CString should be passed as its UTF8 encoding and returned as a reference to unsigned characters", function () {