Skip to content

Commit 1237ad1

Browse files
authored
feat: Support the Unmanaged type (#54) (#72)
1 parent b43660b commit 1237ad1

File tree

10 files changed

+212
-33
lines changed

10 files changed

+212
-33
lines changed

NativeScript/runtime/Caches.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ class Caches {
8686
std::unique_ptr<v8::Persistent<v8::Function>> InteropReferenceCtorFunc = std::unique_ptr<v8::Persistent<v8::Function>>(nullptr);
8787
std::unique_ptr<v8::Persistent<v8::Function>> PointerCtorFunc = std::unique_ptr<v8::Persistent<v8::Function>>(nullptr);
8888
std::unique_ptr<v8::Persistent<v8::Function>> FunctionReferenceCtorFunc = std::unique_ptr<v8::Persistent<v8::Function>>(nullptr);
89+
std::unique_ptr<v8::Persistent<v8::Function>> UnmanagedTypeCtorFunc = std::unique_ptr<v8::Persistent<v8::Function>>(nullptr);
8990
private:
9091
static std::shared_ptr<ConcurrentMap<v8::Isolate*, std::shared_ptr<Caches>>> perIsolateCaches_;
9192
v8::Isolate* isolate_;

NativeScript/runtime/DataWrapper.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ enum class WrapperType {
3333
FunctionReferenceType = 1 << 17,
3434
ExtVector = 1 << 18,
3535
Worker = 1 << 19,
36+
UnmanagedType = 1 << 20,
3637
};
3738

3839
struct V8Args {
@@ -403,6 +404,34 @@ class ObjCAllocDataWrapper: public BaseDataWrapper {
403404
Class klass_;
404405
};
405406

407+
class UnmanagedTypeWrapper: public BaseDataWrapper {
408+
public:
409+
UnmanagedTypeWrapper(uint8_t* data, const TypeEncoding* typeEncoding)
410+
: data_(data), typeEncoding_(typeEncoding), valueTaken_(false) {
411+
}
412+
413+
const WrapperType Type() {
414+
return WrapperType::UnmanagedType;
415+
}
416+
417+
uint8_t* Data() {
418+
this->valueTaken_ = true;
419+
return this->data_;
420+
}
421+
422+
const TypeEncoding* TypeEncoding() {
423+
return this->typeEncoding_;
424+
}
425+
426+
bool ValueTaken() {
427+
return this->valueTaken_;
428+
}
429+
private:
430+
uint8_t* data_;
431+
const tns::TypeEncoding* typeEncoding_;
432+
bool valueTaken_;
433+
};
434+
406435
class ObjCDataWrapper: public BaseDataWrapper {
407436
public:
408437
ObjCDataWrapper(id data, const TypeEncoding* typeEncoding = nullptr)

NativeScript/runtime/Interop.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ struct MethodCall {
2626
MetaType metaType,
2727
bool provideErrorOutParameter,
2828
bool ownsReturnedObject,
29+
bool returnsUnmanaged,
2930
bool isInitializer)
3031
: context_(context),
3132
isPrimitiveFunction_(isPrimitiveFunction),
@@ -39,6 +40,7 @@ struct MethodCall {
3940
metaType_(metaType),
4041
provideErrorOutParameter_(provideErrorOutParameter),
4142
ownsReturnedObject_(ownsReturnedObject),
43+
returnsUnmanaged_(returnsUnmanaged),
4244
isInitializer_(isInitializer) {
4345
}
4446

@@ -54,6 +56,7 @@ struct MethodCall {
5456
MetaType metaType_;
5557
bool provideErrorOutParameter_;
5658
bool ownsReturnedObject_;
59+
bool returnsUnmanaged_;
5760
bool isInitializer_;
5861
};
5962

@@ -63,7 +66,8 @@ struct CMethodCall: MethodCall {
6366
void* functionPointer,
6467
const TypeEncoding* typeEncoding,
6568
V8Args& args,
66-
bool ownsReturnedObject)
69+
bool ownsReturnedObject,
70+
bool returnsUnmanaged)
6771
: MethodCall(
6872
context,
6973
true,
@@ -77,6 +81,7 @@ struct CMethodCall: MethodCall {
7781
MetaType::Undefined,
7882
false,
7983
ownsReturnedObject,
84+
returnsUnmanaged,
8085
false) {
8186
}
8287
};
@@ -102,6 +107,7 @@ struct ObjCMethodCall: public MethodCall {
102107
meta->type(),
103108
meta->hasErrorOutParameter() && args.Length() < meta->encodings()->count - 1,
104109
meta->ownsReturnedCocoaObject(),
110+
false,
105111
meta->isInitializer()) {
106112
}
107113
};
@@ -113,7 +119,7 @@ class Interop {
113119
static id CallInitializer(v8::Local<v8::Context> context, const MethodMeta* methodMeta, id target, Class clazz, V8Args& args);
114120
static v8::Local<v8::Value> CallFunction(ObjCMethodCall& methodCall);
115121
static v8::Local<v8::Value> CallFunction(CMethodCall& methodCall);
116-
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 isInitializer = false);
122+
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);
117123
static void SetStructPropertyValue(v8::Local<v8::Context> context, StructWrapper* wrapper, StructField field, v8::Local<v8::Value> value);
118124
static void InitializeStruct(v8::Local<v8::Context> context, void* destBuffer, std::vector<StructField> fields, v8::Local<v8::Value> inititalizer);
119125
static void WriteValue(v8::Local<v8::Context> context, const TypeEncoding* typeEncoding, void* dest, v8::Local<v8::Value> arg);

NativeScript/runtime/Interop.mm

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "Pointer.h"
1717
#include "ExtVector.h"
1818
#include "SymbolIterator.h"
19+
#include "UnmanagedType.h"
1920

2021
using namespace v8;
2122

@@ -715,9 +716,16 @@
715716
*static_cast<T*>((void*)((uint8_t*)destBuffer + position)) = result;
716717
}
717718

718-
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 isInitializer) {
719+
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) {
719720
Isolate* isolate = context->GetIsolate();
720721

722+
if (returnsUnmanaged) {
723+
uint8_t* data = call->GetResult<uint8_t*>();
724+
UnmanagedTypeWrapper* wrapper = new UnmanagedTypeWrapper(data, typeEncoding);
725+
Local<Value> result = UnmanagedType::Create(context, wrapper);
726+
return result;
727+
}
728+
721729
if (typeEncoding->type == BinaryTypeEncodingType::ExtVectorEncoding) {
722730
ffi_type* ffiType = FFICall::GetArgumentType(typeEncoding, isStructMember);
723731
const TypeEncoding* innerTypeEncoding = typeEncoding->details.extVector.getInnerType();
@@ -909,7 +917,7 @@
909917
const TypeEncoding* typeEncoding = wrapper->ParametersEncoding();
910918

911919
Local<Context> context = isolate->GetCurrentContext();
912-
CMethodCall methodCall(context, functionPointer, typeEncoding, args, false);
920+
CMethodCall methodCall(context, functionPointer, typeEncoding, args, false, false);
913921
Local<Value> result = Interop::CallFunction(methodCall);
914922

915923
info.GetReturnValue().Set(result);
@@ -1406,7 +1414,16 @@
14061414
}
14071415
}
14081416

1409-
Local<Value> result = Interop::GetResult(methodCall.context_, methodCall.typeEncoding_, &call, marshalToPrimitive, nullptr, false, methodCall.ownsReturnedObject_, methodCall.isInitializer_);
1417+
Local<Value> result = Interop::GetResult(
1418+
methodCall.context_,
1419+
methodCall.typeEncoding_,
1420+
&call,
1421+
marshalToPrimitive,
1422+
nullptr,
1423+
false,
1424+
methodCall.ownsReturnedObject_,
1425+
methodCall.returnsUnmanaged_,
1426+
methodCall.isInitializer_);
14101427

14111428
return result;
14121429
}

NativeScript/runtime/MetadataBuilder.mm

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -841,7 +841,7 @@
841841
V8VectorArgs vectorArgs(localArgs);
842842
Local<Context> context = Caches::Get(isolate)->GetContext();
843843
v8::Unlocker unlocker(isolate);
844-
CMethodCall methodCall(context, item->userData_, typeEncoding, vectorArgs, item->meta_->ownsReturnedCocoaObject());
844+
CMethodCall methodCall(context, item->userData_, typeEncoding, vectorArgs, item->meta_->ownsReturnedCocoaObject(), false);
845845
Interop::CallFunction(methodCall);
846846
});
847847

@@ -851,7 +851,8 @@
851851
V8FunctionCallbackArgs args(info);
852852
const TypeEncoding* typeEncoding = item->meta_->encodings()->first();
853853
Local<Context> context = isolate->GetCurrentContext();
854-
CMethodCall methodCall(context, item->userData_, typeEncoding, args, item->meta_->ownsReturnedCocoaObject());
854+
const FunctionMeta* funcMeta = item->meta_;
855+
CMethodCall methodCall(context, item->userData_, typeEncoding, args, funcMeta->ownsReturnedCocoaObject(), funcMeta->returnsUnmanaged());
855856
Local<Value> result = Interop::CallFunction(methodCall);
856857

857858
if (typeEncoding->type != BinaryTypeEncodingType::VoidEncoding) {

NativeScript/runtime/ObjectManager.mm

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@
9797
}
9898
break;
9999
}
100+
case WrapperType::UnmanagedType: {
101+
UnmanagedTypeWrapper* unmanagedTypeWrapper = static_cast<UnmanagedTypeWrapper*>(wrapper);
102+
if (unmanagedTypeWrapper != nullptr) {
103+
delete unmanagedTypeWrapper;
104+
}
105+
break;
106+
}
100107
case WrapperType::Block: {
101108
BlockWrapper* blockWrapper = static_cast<BlockWrapper*>(wrapper);
102109
std::free(blockWrapper->Block());
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#ifndef UnmanagedType_h
2+
#define UnmanagedType_h
3+
4+
#include "Common.h"
5+
#include "DataWrapper.h"
6+
7+
namespace tns {
8+
9+
class UnmanagedType {
10+
public:
11+
static v8::Local<v8::Value> Create(v8::Local<v8::Context> context, UnmanagedTypeWrapper* wrapper);
12+
private:
13+
static void ConstructorCallback(const v8::FunctionCallbackInfo<v8::Value>& info);
14+
static void TakeUnretainedValueCallback(const v8::FunctionCallbackInfo<v8::Value>& info);
15+
static void TakeRetainedValueCallback(const v8::FunctionCallbackInfo<v8::Value>& info);
16+
static v8::Local<v8::Value> TakeValue(const v8::FunctionCallbackInfo<v8::Value>& info, bool retained);
17+
};
18+
19+
}
20+
21+
#endif /* UnmanagedType_h */
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#include "UnmanagedType.h"
2+
#include "NativeScriptException.h"
3+
#include "Caches.h"
4+
#include "Helpers.h"
5+
#include "Interop.h"
6+
7+
using namespace v8;
8+
9+
namespace tns {
10+
11+
Local<Value> UnmanagedType::Create(Local<Context> context, UnmanagedTypeWrapper* wrapper) {
12+
Isolate* isolate = context->GetIsolate();
13+
auto cache = Caches::Get(isolate);
14+
if (cache->UnmanagedTypeCtorFunc.get() == nullptr) {
15+
Local<FunctionTemplate> ctorFuncTemplate = FunctionTemplate::New(isolate, ConstructorCallback);
16+
ctorFuncTemplate->SetClassName(tns::ToV8String(isolate, "Unmanaged"));
17+
Local<ObjectTemplate> proto = ctorFuncTemplate->PrototypeTemplate();
18+
19+
Local<FunctionTemplate> takeUnretainedValueFuncTemplate = FunctionTemplate::New(isolate, UnmanagedType::TakeUnretainedValueCallback);
20+
Local<FunctionTemplate> takeRetainedValueFuncTemplate = FunctionTemplate::New(isolate, UnmanagedType::TakeRetainedValueCallback);
21+
proto->Set(tns::ToV8String(isolate, "takeUnretainedValue"), takeUnretainedValueFuncTemplate);
22+
proto->Set(tns::ToV8String(isolate, "takeRetainedValue"), takeRetainedValueFuncTemplate);
23+
24+
Local<v8::Function> ctorFunc;
25+
bool success = ctorFuncTemplate->GetFunction(context).ToLocal(&ctorFunc);
26+
tns::Assert(success, isolate);
27+
28+
cache->UnmanagedTypeCtorFunc = std::make_unique<Persistent<v8::Function>>(isolate, ctorFunc);
29+
}
30+
31+
Local<External> ext = External::New(isolate, wrapper);
32+
33+
Local<v8::Function> ctorFunc = cache->UnmanagedTypeCtorFunc->Get(isolate);
34+
Local<Value> result;
35+
Local<Value> args[] = { ext };
36+
bool success = ctorFunc->NewInstance(context, 1, args).ToLocal(&result);
37+
tns::Assert(success, isolate);
38+
39+
return result;
40+
}
41+
42+
void UnmanagedType::ConstructorCallback(const FunctionCallbackInfo<Value>& info) {
43+
Local<External> ext = info[0].As<External>();
44+
UnmanagedTypeWrapper* wrapper = static_cast<UnmanagedTypeWrapper*>(ext->Value());
45+
tns::SetValue(info.GetIsolate(), info.This(), wrapper);
46+
}
47+
48+
void UnmanagedType::TakeUnretainedValueCallback(const FunctionCallbackInfo<Value>& info) {
49+
try {
50+
info.GetReturnValue().Set(UnmanagedType::TakeValue(info, false));
51+
} catch (NativeScriptException& ex) {
52+
ex.ReThrowToV8(info.GetIsolate());
53+
}
54+
}
55+
56+
void UnmanagedType::TakeRetainedValueCallback(const FunctionCallbackInfo<Value>& info) {
57+
try {
58+
info.GetReturnValue().Set(UnmanagedType::TakeValue(info, true));
59+
} catch (NativeScriptException& ex) {
60+
ex.ReThrowToV8(info.GetIsolate());
61+
}
62+
}
63+
64+
Local<Value> UnmanagedType::TakeValue(const FunctionCallbackInfo<Value>& info, bool retained) {
65+
Isolate* isolate = info.GetIsolate();
66+
Local<Context> context = isolate->GetCurrentContext();
67+
68+
BaseDataWrapper* baseWrapper = tns::GetValue(isolate, info.This());
69+
UnmanagedTypeWrapper* wrapper = static_cast<UnmanagedTypeWrapper*>(baseWrapper);
70+
71+
if (wrapper->ValueTaken()) {
72+
throw NativeScriptException("Unmanaged value has already been consumed.");
73+
}
74+
75+
uint8_t* data = wrapper->Data();
76+
const TypeEncoding* typeEncoding = wrapper->TypeEncoding();
77+
78+
BaseCall call((uint8_t*)&data);
79+
Local<Value> result = Interop::GetResult(context, typeEncoding, &call, false);
80+
81+
if (retained) {
82+
id value = static_cast<id>((void*)data);
83+
[value release];
84+
}
85+
86+
return result;
87+
}
88+
89+
}

TestRunner/app/tests/ApiTests.js

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -584,35 +584,35 @@ describe(module.id, function () {
584584
expect(TNSMutableObjectGet() instanceof NSObject).toBe(true);
585585
});
586586

587-
// it("returns retained", function () {
588-
// expect(functionReturnsNSRetained().retainCount()).toBe(1);
589-
// expect(functionReturnsCFRetained().retainCount()).toBe(1);
590-
// expect(functionImplicitCreate().retainCount()).toBe(1);
591-
592-
// var obj = functionExplicitCreateNSObject();
593-
// expect(obj.retainCount()).toBe(2);
594-
// CFRelease(obj);
595-
596-
// expect(TNSReturnsRetained.methodReturnsNSRetained().retainCount()).toBe(1);
597-
// expect(TNSReturnsRetained.methodReturnsCFRetained().retainCount()).toBe(1);
598-
// expect(TNSReturnsRetained.newNSObjectMethod().retainCount()).toBe(1);
599-
// });
587+
it("returns retained", function () {
588+
expect(functionReturnsNSRetained().retainCount()).toBe(1);
589+
expect(functionReturnsCFRetained().retainCount()).toBe(1);
590+
expect(functionImplicitCreate().retainCount()).toBe(1);
591+
592+
var obj = functionExplicitCreateNSObject();
593+
expect(obj.retainCount()).toBe(2);
594+
CFRelease(obj);
595+
596+
expect(TNSReturnsRetained.methodReturnsNSRetained().retainCount()).toBe(1);
597+
expect(TNSReturnsRetained.methodReturnsCFRetained().retainCount()).toBe(1);
598+
expect(TNSReturnsRetained.newNSObjectMethod().retainCount()).toBe(1);
599+
});
600600

601-
// it("unmanaged", function () {
602-
// var unmanaged = functionReturnsUnmanaged();
603-
// expect('takeRetainedValue' in unmanaged).toBe(true);
604-
// expect('takeUnretainedValue' in unmanaged).toBe(true);
605-
// expect(functionReturnsUnmanaged().takeRetainedValue().retainCount()).toBe(1);
601+
it("unmanaged", function () {
602+
var unmanaged = functionReturnsUnmanaged();
603+
expect('takeRetainedValue' in unmanaged).toBe(true);
604+
expect('takeUnretainedValue' in unmanaged).toBe(true);
605+
expect(functionReturnsUnmanaged().takeRetainedValue().retainCount()).toBe(1);
606606

607-
// var value = functionReturnsUnmanaged().takeUnretainedValue();
608-
// expect(value.retainCount()).toBe(2);
609-
// CFRelease(value);
607+
var value = functionReturnsUnmanaged().takeUnretainedValue();
608+
expect(value.retainCount()).toBe(2);
609+
CFRelease(value);
610610

611-
// unmanaged.takeRetainedValue();
612-
// expect(function() {
613-
// unmanaged.takeUnretainedValue();
614-
// }).toThrow();
615-
// });
611+
unmanaged.takeRetainedValue();
612+
expect(function() {
613+
unmanaged.takeUnretainedValue();
614+
}).toThrow();
615+
});
616616

617617
it('methods can be recursively called', function() {
618618
var result = TNSTestNativeCallbacks.callRecursively(function() {

0 commit comments

Comments
 (0)