Skip to content
Open
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
11 changes: 3 additions & 8 deletions NativeScript/runtime/ArgConverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,14 @@ struct MethodCallbackWrapper {

class ArgConverter {
public:
static void Init(v8::Local<v8::Context> context,
v8::NamedPropertyGetterCallback structPropertyGetter,
v8::NamedPropertySetterCallbackV2 structPropertySetter);
static void Init(v8::Local<v8::Context> context);
static v8::Local<v8::Value> Invoke(v8::Local<v8::Context> context,
Class klass,
v8::Local<v8::Object> receiver,
V8Args& args, const MethodMeta* meta,
bool isMethodCallback);
// skipGCRegistration: for ObjC wrappers the object gets a strong handle and
// no finalizer; for Struct wrappers the caller must call
// ObjectManager::Register itself, exactly once.
// no finalizer.
static v8::Local<v8::Value> ConvertArgument(
v8::Local<v8::Context> context, BaseDataWrapper* wrapper,
bool skipGCRegistration = false,
Expand Down Expand Up @@ -101,9 +98,7 @@ class ArgConverter {

private:
static v8::Local<v8::Function> CreateEmptyInstanceFunction(
v8::Local<v8::Context> context,
v8::NamedPropertyGetterCallback propertyGetter = nullptr,
v8::NamedPropertySetterCallbackV2 propertySetter = nullptr);
v8::Local<v8::Context> context);
static v8::Local<v8::Object> NewEmptyInstance(
v8::Local<v8::Context> context, v8::Persistent<v8::Function>* ctorFunc);
static std::shared_ptr<v8::Persistent<v8::Value>> CreateEmptyInstance(
Expand Down
46 changes: 5 additions & 41 deletions NativeScript/runtime/ArgConverter.mm
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,11 @@

namespace tns {

void ArgConverter::Init(Local<Context> context, NamedPropertyGetterCallback structPropertyGetter,
NamedPropertySetterCallbackV2 structPropertySetter) {
void ArgConverter::Init(Local<Context> context) {
Isolate* isolate = v8::Isolate::GetCurrent();
auto cache = Caches::Get(isolate);
cache->EmptyObjCtorFunc = std::make_unique<Persistent<v8::Function>>(
isolate, ArgConverter::CreateEmptyInstanceFunction(context));
cache->EmptyStructCtorFunc = std::make_unique<Persistent<v8::Function>>(
isolate, ArgConverter::CreateEmptyInstanceFunction(context, structPropertyGetter,
structPropertySetter));
}

Local<Value> ArgConverter::Invoke(Local<Context> context, Class klass, Local<Object> receiver,
Expand Down Expand Up @@ -855,34 +851,9 @@
return Null(isolate);
}

if (wrapper->Type() == WrapperType::Struct) {
if (receiver.IsEmpty()) {
Persistent<v8::Function>* ctorFunc = Caches::Get(isolate)->EmptyStructCtorFunc.get();
tns::Assert(ctorFunc != nullptr, isolate);
receiver = NewEmptyInstance(context, ctorFunc);
// A struct object must carry exactly one finalizer registration; callers
// passing skipGCRegistration keep the handle and register it themselves.
if (!skipGCRegistration) {
ObjectManager::Register(context, receiver);
}
}

StructWrapper* structWrapper = static_cast<StructWrapper*>(wrapper);
const StructInfo& structInfo = structWrapper->StructInfo();
auto cache = Caches::Get(isolate);
Local<Object> proto;
if (cache->StructPrototypeInitializer) {
proto = cache->StructPrototypeInitializer(context, structInfo);
}
if (!proto.IsEmpty()) {
bool success = receiver->SetPrototype(context, proto).FromMaybe(false);
tns::Assert(success, isolate);
}

tns::SetValue(isolate, receiver, structWrapper);

return receiver;
}
// Struct objects come from Interop::StructToValue or a struct constructor,
// never from here: their shape and lifetime are per struct type.
tns::Assert(wrapper->Type() != WrapperType::Struct, isolate);

if (wrapper->Type() == WrapperType::ObjCAllocObject) {
ObjCAllocDataWrapper* allocDataWrapper = static_cast<ObjCAllocDataWrapper*>(wrapper);
Expand Down Expand Up @@ -1131,19 +1102,12 @@
return poValue;
}

Local<v8::Function> ArgConverter::CreateEmptyInstanceFunction(
Local<Context> context, NamedPropertyGetterCallback propertyGetter,
NamedPropertySetterCallbackV2 propertySetter) {
Local<v8::Function> ArgConverter::CreateEmptyInstanceFunction(Local<Context> context) {
Isolate* isolate = v8::Isolate::GetCurrent();
Local<FunctionTemplate> emptyInstanceCtorFuncTemplate = FunctionTemplate::New(isolate, nullptr);
Local<ObjectTemplate> instanceTemplate = emptyInstanceCtorFuncTemplate->InstanceTemplate();
instanceTemplate->SetInternalFieldCount(2);

if (propertyGetter != nullptr || propertySetter != nullptr) {
NamedPropertyHandlerConfiguration config(propertyGetter, propertySetter);
instanceTemplate->SetHandler(config);
}

instanceTemplate->SetHandler(IndexedPropertyHandlerConfiguration(IndexedPropertyGetterCallback,
IndexedPropertySetterCallback));

Expand Down
1 change: 0 additions & 1 deletion NativeScript/runtime/Caches.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ Caches::~Caches() {
this->CFunctions.clear();

this->Instances.clear();
this->StructInstances.clear();
this->PointerInstances.clear();
this->cacheBoundObjects_.clear();
}
Expand Down
30 changes: 9 additions & 21 deletions NativeScript/runtime/Caches.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
namespace tns {

struct StructInfo;
class StructWrapper;
enum class StructStorage;
struct ObjectWeakCallbackState;
class PromiseRejectionTracker;
class IsolateTracked;
Expand All @@ -35,13 +37,6 @@ struct TransparentStringEqual {
}
};

struct pair_hash {
template <class T1, class T2>
std::size_t operator()(const std::pair<T1, T2>& pair) const {
return std::hash<T1>()(pair.first) ^ std::hash<T2>()(pair.second);
}
};

class Caches {
public:
class WorkerState {
Expand Down Expand Up @@ -169,12 +164,6 @@ class Caches {

robin_hood::unordered_map<id, std::shared_ptr<v8::Persistent<v8::Value>>>
Instances;
// Root struct objects by (backing buffer, struct type). Child views created
// for nested-struct fields are never entered here.
robin_hood::unordered_map<std::pair<void*, const StructInfo*>,
std::shared_ptr<v8::Persistent<v8::Value>>,
pair_hash>
StructInstances;
robin_hood::unordered_map<const void*,
std::shared_ptr<v8::Persistent<v8::Object>>>
PointerInstances;
Expand All @@ -188,20 +177,19 @@ class Caches {
v8::Local<v8::Context>, const BaseClassMeta*, KnownUnknownClassPair,
const std::vector<std::string>&)>
ObjectCtorInitializer;
// Resolves the prototype a struct instance of the given type must carry.
// Indirected through std::function so this header stays free of
// MetadataBuilder.
std::function<v8::Local<v8::Object>(v8::Local<v8::Context>,
const StructInfo&)>
StructPrototypeInitializer;
// Builds a struct object over `data`: a copy of it (kCopy; zero-filled when
// `data` is null) or an alias kept alive by `parent` (kAlias). Indirected
// through std::function so this header stays free of MetadataBuilder.
std::function<v8::Local<v8::Object>(v8::Local<v8::Context>, const StructInfo&,
void* data, StructStorage storage,
v8::Local<v8::Object> parent)>
StructInstanceFactory;
robin_hood::unordered_map<const InterfaceMeta*,
std::vector<const MethodMeta*>>
Initializers;

std::unique_ptr<v8::Persistent<v8::Function>> EmptyObjCtorFunc =
std::unique_ptr<v8::Persistent<v8::Function>>(nullptr);
std::unique_ptr<v8::Persistent<v8::Function>> EmptyStructCtorFunc =
std::unique_ptr<v8::Persistent<v8::Function>>(nullptr);
std::unique_ptr<v8::Persistent<v8::Function>> SliceFunc =
std::unique_ptr<v8::Persistent<v8::Function>>(nullptr);
std::unique_ptr<v8::Persistent<v8::Function>> OriginalExtendsFunc =
Expand Down
40 changes: 24 additions & 16 deletions NativeScript/runtime/DataWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -314,31 +314,39 @@ class StructTypeWrapper : public BaseDataWrapper {
const struct StructInfo* structInfo_;
};

enum class StructStorage {
// The struct object owns a copy of the bytes.
kCopy,
// The struct object aliases bytes owned by whatever its parent field holds.
kAlias,
};

// Constructed in place at the start of the ArrayBuffer backing store held in
// kStorageField (followed by the bytes, for kCopy) and destroyed by the store's
// deleter, so struct objects need no finalizer. V8 may run that deleter on a
// background thread, and never runs it at process exit: the destructor must
// not touch V8 or the isolate, and nothing may depend on it running. That
// ArrayBuffer must never reach JS (copy the bytes instead): a detach or
// transfer would pull the wrapper out from under the struct object.
class StructWrapper : public StructTypeWrapper {
public:
StructWrapper(const struct StructInfo& structInfo, void* data,
std::shared_ptr<v8::Persistent<v8::Value>> parent)
: StructTypeWrapper(structInfo),
data_(data),
childCount_(0),
parent_(parent) {}
// Internal fields of a struct object after the wrapper (field 0). The parent
// field holds whatever keeps aliased bytes alive: a view's parent struct
// object, or the Pointer object a pointer-backed struct was built over. From
// kFirstViewField on, one slot per struct-typed field caches its view.
static constexpr int kParentField = 1;
static constexpr int kStorageField = 2;
static constexpr int kFirstViewField = 3;

StructWrapper(const struct StructInfo& structInfo, void* data)
: StructTypeWrapper(structInfo), data_(data) {}

const WrapperType Type() { return WrapperType::Struct; }

void* Data() const { return this->data_; }

std::shared_ptr<v8::Persistent<v8::Value>> Parent() { return this->parent_; }

void IncrementChildren() { this->childCount_++; }

void DecrementChildren() { this->childCount_--; }

int ChildCount() { return this->childCount_; }

private:
void* data_;
int childCount_;
std::shared_ptr<v8::Persistent<v8::Value>> parent_;
};

class ObjCAllocDataWrapper : public BaseDataWrapper {
Expand Down
14 changes: 8 additions & 6 deletions NativeScript/runtime/Interop.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,13 @@ class Interop {
static v8::Local<v8::Value> GetResultByType(
v8::Local<v8::Context> context, BaseDataWrapper* typeWrapper,
BaseCall* call,
std::shared_ptr<v8::Persistent<v8::Value>> parentStruct = nullptr);
v8::Local<v8::Object> parentStruct = v8::Local<v8::Object>());
// A non-empty `parentStruct` makes a struct-typed result a view into the
// parent's buffer (`call` points at the field) instead of a copy.
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,
v8::Local<v8::Object> parentStruct = v8::Local<v8::Object>(),
bool isStructMember = false, bool ownsReturnedObject = false,
bool returnsUnmanaged = false, bool isInitializer = false);
static void SetStructPropertyValue(v8::Local<v8::Context> context,
Expand Down Expand Up @@ -160,10 +162,10 @@ class Interop {
static bool isRefTypeEqual(const TypeEncoding* typeEncoding,
const char* clazz);
static v8::Local<v8::Array> ToArray(v8::Local<v8::Object> object);
static v8::Local<v8::Value> StructToValue(
v8::Local<v8::Context> context, void* result,
const StructInfo& structInfo,
std::shared_ptr<v8::Persistent<v8::Value>> parentStruct);
static v8::Local<v8::Value> StructToValue(v8::Local<v8::Context> context,
void* result,
const StructInfo& structInfo,
v8::Local<v8::Object> parentStruct);
static const TypeEncoding* CreateEncoding(BinaryTypeEncodingType type);
static v8::Local<v8::Value> HandleOf(v8::Local<v8::Context> context,
v8::Local<v8::Value> value);
Expand Down
Loading
Loading