Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NativeScript/runtime/Interop.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,11 @@ class Interop {
static id CallInitializer(v8::Local<v8::Context> context, const MethodMeta* methodMeta, id target, Class clazz, V8Args& args);
static v8::Local<v8::Value> CallFunction(ObjCMethodCall& methodCall);
static v8::Local<v8::Value> CallFunction(CMethodCall& methodCall);
static v8::Local<v8::Value> GetResultByType(v8::Local<v8::Context> context, BaseDataWrapper* typeWrapper, BaseCall* call, std::shared_ptr<v8::Persistent<v8::Value>> parentStruct = nullptr);
static v8::Local<v8::Value> GetResult(v8::Local<v8::Context> context, const TypeEncoding* typeEncoding, BaseCall* call, bool marshalToPrimitive, std::shared_ptr<v8::Persistent<v8::Value>> parentStruct = nullptr, bool isStructMember = false, bool ownsReturnedObject = false, bool returnsUnmanaged = false, bool isInitializer = false);
static void SetStructPropertyValue(v8::Local<v8::Context> context, StructWrapper* wrapper, StructField field, v8::Local<v8::Value> value);
static void InitializeStruct(v8::Local<v8::Context> context, void* destBuffer, std::vector<StructField> fields, v8::Local<v8::Value> inititalizer);
static void WriteTypeValue(v8::Local<v8::Context> context, BaseDataWrapper* typeWrapper, void* dest, v8::Local<v8::Value> arg);
static void WriteValue(v8::Local<v8::Context> context, const TypeEncoding* typeEncoding, void* dest, v8::Local<v8::Value> arg);
static id ToObject(v8::Local<v8::Context> context, v8::Local<v8::Value> arg);
static v8::Local<v8::Value> GetPrimitiveReturnType(v8::Local<v8::Context> context, BinaryTypeEncodingType type, BaseCall* call);
Expand Down
61 changes: 56 additions & 5 deletions NativeScript/runtime/Interop.mm
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,44 @@ inline bool isBool() {
IsOfType _isObject = UNDEFINED;
};

void Interop::WriteTypeValue(Local<Context> context, BaseDataWrapper* typeWrapper, void* dest, Local<Value> arg) {
Isolate* isolate = context->GetIsolate();
ValueCache argHelper(arg);
bool isEmptyOrUndefined = arg.IsEmpty() || arg->IsNullOrUndefined();
bool success = false;

if (typeWrapper->Type() == WrapperType::StructType) {
if (isEmptyOrUndefined) {
StructTypeWrapper* structTypeWrapper = static_cast<StructTypeWrapper*>(typeWrapper);
StructInfo structInfo = structTypeWrapper->StructInfo();

memset(dest, 0, structInfo.FFIType()->size);
success = true;
} else if (argHelper.isObject()) {
BaseDataWrapper* wrapper = tns::GetValue(isolate, arg);
if (wrapper != nullptr) {
if (wrapper->Type() == WrapperType::Struct) {
StructWrapper* structWrapper = static_cast<StructWrapper*>(wrapper);
void* buffer = structWrapper->Data();
size_t size = structWrapper->StructInfo().FFIType()->size;
memcpy(dest, buffer, size);
success = true;
}
} else {
// Create the structure using the struct initializer syntax
StructTypeWrapper* structTypeWrapper = static_cast<StructTypeWrapper*>(typeWrapper);
StructInfo structInfo = structTypeWrapper->StructInfo();
Interop::InitializeStruct(context, dest, structInfo.Fields(), arg.As<Object>());
success = true;
}
}
}

if (!success) {
tns::Assert(false, isolate);
}
}

void Interop::WriteValue(Local<Context> context, const TypeEncoding* typeEncoding, void* dest, Local<Value> arg) {
Isolate* isolate = context->GetIsolate();
ExecuteWriteValueDebugValidationsIfInDebug(context, typeEncoding, dest, arg);
Expand Down Expand Up @@ -806,6 +844,21 @@ inline bool isBool() {
*static_cast<T*>((void*)((uint8_t*)destBuffer + position)) = result;
}

Local<Value> Interop::GetResultByType(Local<Context> context, BaseDataWrapper* typeWrapper, BaseCall* call, std::shared_ptr<Persistent<Value>> parentStruct) {
Isolate* isolate = context->GetIsolate();

if (typeWrapper->Type() == WrapperType::StructType) {
StructTypeWrapper* structTypeWrapper = static_cast<StructTypeWrapper*>(typeWrapper);
StructInfo structInfo = structTypeWrapper->StructInfo();

void* result = call->ResultBuffer();
Local<Value> value = Interop::StructToValue(context, result, structInfo, parentStruct);
return value;
}

return Null(isolate);
}

Local<Value> Interop::GetResult(Local<Context> context, const TypeEncoding* typeEncoding, BaseCall* call, bool marshalToPrimitive, std::shared_ptr<Persistent<Value>> parentStruct, bool isStructMember, bool ownsReturnedObject, bool returnsUnmanaged, bool isInitializer) {
Isolate* isolate = context->GetIsolate();

Expand Down Expand Up @@ -1031,18 +1084,16 @@ inline bool isBool() {
}

const TypeEncoding* innerType = typeEncoding->details.pointer.getInnerType();
Local<Value> pointer = Pointer::NewInstance(context, result);

if (innerType->type == BinaryTypeEncodingType::VoidEncoding) {
Local<Value> instance = Pointer::NewInstance(context, result);
return instance;
return pointer;
}

BaseCall c(result);
Local<Value> value = Interop::GetResult(context, innerType, &c, true);
Local<Value> type = Interop::GetInteropType(context, innerType->type);

std::vector<Local<Value>> args;
args.push_back(value);
args.push_back(pointer);
if (!type.IsEmpty()) {
args.insert(args.begin(), type);
}
Expand Down
86 changes: 65 additions & 21 deletions NativeScript/runtime/Reference.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,20 @@ void Reference::IndexedPropertyGetCallback(uint32_t index, const PropertyCallbac
Local<Object> thiz = info.This();
Local<Context> context = isolate->GetCurrentContext();

DataPair pair = Reference::GetTypeEncodingDataPair(thiz);
DataPair pair = Reference::GetDataPair(thiz);
const TypeEncoding* typeEncoding = pair.typeEncoding_;
size_t size = pair.size_;
void* data = pair.data_;

void* ptr = (uint8_t*)data + index * size;
BaseCall call((uint8_t*)ptr);
Local<Value> result = Interop::GetResult(context, typeEncoding, &call, false);

Local<Value> result;
if (typeEncoding != nullptr) {
result = Interop::GetResult(context, typeEncoding, &call, false);
} else {
result = Interop::GetResultByType(context, pair.typeWrapper_, &call);
}
info.GetReturnValue().Set(result);
}

Expand All @@ -115,13 +121,17 @@ void Reference::IndexedPropertySetCallback(uint32_t index, Local<Value> value, c
Local<Context> context = isolate->GetCurrentContext();
Local<Object> thiz = info.This();

DataPair pair = Reference::GetTypeEncodingDataPair(thiz);
DataPair pair = Reference::GetDataPair(thiz);
const TypeEncoding* typeEncoding = pair.typeEncoding_;
size_t size = pair.size_;
void* data = pair.data_;

void* ptr = (uint8_t*)data + index * size;
Interop::WriteValue(context, typeEncoding, ptr, value);

if (typeEncoding != nullptr) {
Interop::WriteValue(context, typeEncoding, ptr, value);
} else {
Interop::WriteTypeValue(context, pair.typeWrapper_, ptr, value);
}
}

void Reference::GetValueCallback(Local<v8::Name> name, const PropertyCallbackInfo<Value>& info) {
Expand Down Expand Up @@ -186,11 +196,17 @@ Local<Value> Reference::GetReferredValue(Local<Context> context, Local<Value> va
}

BaseDataWrapper* typeWrapper = wrapper->TypeWrapper();
if (typeWrapper != nullptr && typeWrapper->Type() == WrapperType::Primitive && baseWrapper != nullptr && baseWrapper->Type() == WrapperType::Pointer) {
Reference::DataPair pair = GetTypeEncodingDataPair(value.As<Object>());
if (pair.data_ != nullptr && pair.typeEncoding_ != nullptr) {
if (typeWrapper != nullptr && Reference::IsSupportedType(typeWrapper->Type()) && baseWrapper != nullptr && baseWrapper->Type() == WrapperType::Pointer) {
Reference::DataPair pair = Reference::GetDataPair(value.As<Object>());
if (pair.data_ != nullptr) {
BaseCall call((uint8_t*)pair.data_);
Local<Value> result = Interop::GetResult(context, pair.typeEncoding_, &call, false);
Local<Value> result;

if (pair.typeEncoding_ != nullptr) {
result = Interop::GetResult(context, pair.typeEncoding_, &call, false);
} else {
result = Interop::GetResultByType(context, typeWrapper, &call);
}
return result;
}
}
Expand All @@ -203,7 +219,6 @@ void* Reference::GetWrappedPointer(Local<Context> context, Local<Value> referenc
return nullptr;
}


Isolate* isolate = context->GetIsolate();
BaseDataWrapper* wrapper = tns::GetValue(isolate, reference);
tns::Assert(wrapper != nullptr && wrapper->Type() == WrapperType::Reference, isolate);
Expand Down Expand Up @@ -313,7 +328,7 @@ void Reference::RegisterToStringMethod(Local<Context> context, Local<Object> pro
tns::Assert(success, isolate);
}

Reference::DataPair Reference::GetTypeEncodingDataPair(Local<Object> obj) {
Reference::DataPair Reference::GetDataPair(Local<Object> obj) {
Local<Context> context;
bool success = obj->GetCreationContext().ToLocal(&context);
tns::Assert(success);
Expand All @@ -327,33 +342,62 @@ Reference::DataPair Reference::GetTypeEncodingDataPair(Local<Object> obj) {
// TODO: Missing type when creating the Reference instance
tns::Assert(false, isolate);
}

if (typeWrapper->Type() != WrapperType::Primitive) {
// TODO: Currently only PrimitiveDataWrappers are supported as type parameters
// Objective C class classes and structures should also be handled

size_t size = 0;
const TypeEncoding* typeEncoding = nullptr;
bool isUnknownType = false;

if (Reference::IsSupportedType(typeWrapper->Type())) {
switch(typeWrapper->Type()) {
case WrapperType::Primitive: {
PrimitiveDataWrapper* primitiveWrapper = static_cast<PrimitiveDataWrapper*>(typeWrapper);

size = primitiveWrapper->Size();
typeEncoding = primitiveWrapper->TypeEncoding();
break;
}
case WrapperType::StructType: {
StructTypeWrapper* structTypeWrapper = static_cast<StructTypeWrapper*>(refWrapper->TypeWrapper());
StructInfo structInfo = structTypeWrapper->StructInfo();

size = structInfo.FFIType()->size;
break;
}
default: {
isUnknownType = true;
break;
}
}
} else {
isUnknownType = true;
}

if (isUnknownType) {
// TODO: Currently only PrimitiveDataWrappers and Structs are supported as type parameters
// Objective C class classes should also be handled
tns::Assert(false, isolate);
}

PrimitiveDataWrapper* primitiveWrapper = static_cast<PrimitiveDataWrapper*>(typeWrapper);

Local<Value> value = refWrapper->Value()->Get(isolate);
BaseDataWrapper* wrappedValue = tns::GetValue(isolate, value);
if (wrappedValue != nullptr && wrappedValue->Type() == WrapperType::Pointer) {
const TypeEncoding* typeEncoding = primitiveWrapper->TypeEncoding();
PointerWrapper* pw = static_cast<PointerWrapper*>(wrappedValue);
void* data = pw->Data();

DataPair pair(typeEncoding, data, primitiveWrapper->Size());
DataPair pair(typeWrapper, typeEncoding, data, size);
return pair;
}

if (refWrapper->Encoding() != nullptr && refWrapper->Data() != nullptr) {
DataPair pair(refWrapper->Encoding(), refWrapper->Data(), primitiveWrapper->Size());
DataPair pair(typeWrapper, refWrapper->Encoding(), refWrapper->Data(), size);
return pair;
}

tns::Assert(false, isolate);
return DataPair(nullptr, nullptr, 0);
return DataPair(typeWrapper, nullptr, nullptr, 0);
}

bool Reference::IsSupportedType(WrapperType type) {
return type == WrapperType::Primitive || type == WrapperType::StructType;
}
}
6 changes: 4 additions & 2 deletions NativeScript/runtime/Reference.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ class Reference {
static void* GetWrappedPointer(v8::Local<v8::Context> context, v8::Local<v8::Value> reference, const TypeEncoding* typeEncoding);
private:
struct DataPair {
DataPair(const TypeEncoding* typeEncoding, void* data, size_t size): typeEncoding_(typeEncoding), data_(data), size_(size) {
DataPair(BaseDataWrapper* typeWrapper, const TypeEncoding* typeEncoding, void* data, size_t size): typeWrapper_(typeWrapper), typeEncoding_(typeEncoding), data_(data), size_(size) {
}

BaseDataWrapper* typeWrapper_;
const TypeEncoding* typeEncoding_;
void* data_;
size_t size_;
Expand All @@ -30,7 +31,8 @@ class Reference {
static void GetValueCallback(v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info);
static void SetValueCallback(v8::Local<v8::Name> name, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<void>& info);
static void RegisterToStringMethod(v8::Local<v8::Context> context, v8::Local<v8::Object> prototype);
static DataPair GetTypeEncodingDataPair(v8::Local<v8::Object> obj);
static DataPair GetDataPair(v8::Local<v8::Object> obj);
static bool IsSupportedType(WrapperType type);
};

}
Expand Down
51 changes: 51 additions & 0 deletions TestRunner/app/tests/Marshalling/ReferenceTests.js
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,57 @@ describe(module.id, function () {
interop.free(ptr);
});

it("Struct reference with value", function () {
const value = new TNSSimpleStruct({x: 1, y: 2});
const ref = new interop.Reference(TNSSimpleStruct, value);

expect(TNSSimpleStruct.equals(ref.value, value)).toBe(true);
});

it("Struct reference with pointer and indexed values", function () {
const structs = [
new TNSSimpleStruct({x: 1, y: 2}),
new TNSSimpleStruct({x: 3, y: 4}),
new TNSSimpleStruct({x: 5, y: 6})
];
const length = structs.length;
const ptr = interop.alloc(interop.sizeof(TNSSimpleStruct) * length);

const ref = new interop.Reference(TNSSimpleStruct, ptr);
for (let i = 0; i < length; i++) {
ref[i] = structs[i];
}

// Check if values were stored into pointer
const resultRef = new interop.Reference(TNSSimpleStruct, ptr);
for (let i = 0; i < length; i++) {
expect(TNSSimpleStruct.equals(resultRef[i], structs[i])).toBe(true);
}

interop.free(ptr);
});

it("Struct reference get first value as referred value", function () {
const structs = [
new TNSSimpleStruct({x: 1, y: 2}),
new TNSSimpleStruct({x: 3, y: 4}),
new TNSSimpleStruct({x: 5, y: 6})
];
const length = structs.length;
const ptr = interop.alloc(interop.sizeof(TNSSimpleStruct) * length);

const ref = new interop.Reference(TNSSimpleStruct, ptr);
for (let i = 0; i < length; i++) {
ref[i] = structs[i];
}

// Check if values were stored into pointer
const resultRef = new interop.Reference(TNSSimpleStruct, ptr);
expect(TNSSimpleStruct.equals(resultRef.value, structs[0])).toBe(true);

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));
Expand Down