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
27 changes: 13 additions & 14 deletions NativeScript/runtime/ArgConverter.mm
Original file line number Diff line number Diff line change
Expand Up @@ -327,8 +327,15 @@

bool hasErrorOutParameter = false;

std::vector<Local<Value>> v8Args;
v8Args.reserve(data->paramsCount_);
constexpr int kInlineArgs = 8;
Local<Value> inlineArgs[kInlineArgs];
std::vector<Local<Value>> heapArgs;
Local<Value>* argv = inlineArgs;
if (data->paramsCount_ > kInlineArgs) {
heapArgs.resize(data->paramsCount_);
argv = heapArgs.data();
}
int argc = 0;
const TypeEncoding* typeEncoding = data->typeEncoding_;
for (int i = 0; i < data->paramsCount_; i++) {
typeEncoding = typeEncoding->next();
Expand All @@ -344,11 +351,7 @@
BaseCall call(argBuffer);
Local<Value> jsWrapper = Interop::GetResult(context, typeEncoding, &call, true);

if (!jsWrapper.IsEmpty()) {
v8Args.push_back(jsWrapper);
} else {
v8Args.push_back(v8::Undefined(isolate));
}
argv[argc++] = jsWrapper.IsEmpty() ? v8::Undefined(isolate).As<Value>() : jsWrapper;
}

Local<Object> thiz = context->Global();
Expand All @@ -374,7 +377,7 @@
// Those js errors will be marshalled to NSError* and sent directly to the calling native
// code. The v8::TryCatch statement prevents the global handler to be called.
TryCatch tc(isolate);
success = callback->Call(context, thiz, (int)v8Args.size(), v8Args.data()).ToLocal(&result);
success = callback->Call(context, thiz, argc, argv).ToLocal(&result);
if (!success && tc.HasCaught()) {
// A branded escapeException follows the @throw path even here, taking
// precedence over NSError-out marshaling.
Expand All @@ -399,7 +402,7 @@
}
} else {
TryCatch tc(isolate);
success = callback->Call(context, thiz, (int)v8Args.size(), v8Args.data()).ToLocal(&result);
success = callback->Call(context, thiz, argc, argv).ToLocal(&result);
if (!success && tc.HasCaught()) {
NSException* escaped = ExtractEscapedException(context, tc.Exception());
if (escaped != nil) {
Expand Down Expand Up @@ -553,11 +556,7 @@
} else if (type == BinaryTypeEncodingType::StructDeclarationReference) {
BaseDataWrapper* baseWrapper = tns::GetValue(isolate, value);
if (baseWrapper == nullptr) {
const char* structName = typeEncoding->details.declarationReference.name.valuePtr();
const Meta* meta = ArgConverter::GetMeta(structName);
tns::Assert(meta != nullptr && meta->type() == MetaType::Struct, isolate);
const StructMeta* structMeta = static_cast<const StructMeta*>(meta);
const StructInfo& structInfo = FFICall::GetStructInfo(structMeta);
const StructInfo& structInfo = FFICall::GetStructInfo(typeEncoding);
Interop::InitializeStruct(context, retValue, structInfo.Fields(), value);
return;
} else if (baseWrapper->Type() == WrapperType::Struct) {
Expand Down
88 changes: 71 additions & 17 deletions NativeScript/runtime/FFICall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,9 @@ ffi_type* FFICall::GetArgumentType(const TypeEncoding* typeEncoding, bool isStru
type->elements[size] = nullptr;
return type;
}
case BinaryTypeEncodingType::StructDeclarationReference: {
const char* structName = typeEncoding->details.declarationReference.name.valuePtr();
const Meta* meta = ArgConverter::GetMeta(structName);
tns::Assert(meta->type() == MetaType::Struct);
const StructMeta* structMeta = static_cast<const StructMeta*>(meta);

const StructInfo& structInfo =
FFICall::GetStructInfo(structMeta, structName);
return structInfo.FFIType();
case BinaryTypeEncodingType::StructDeclarationReference:
case BinaryTypeEncodingType::AnonymousStructEncoding: {
return FFICall::GetStructInfo(typeEncoding).FFIType();
}
case BinaryTypeEncodingType::ConstantArrayEncoding: {
if (isStructMember) {
Expand All @@ -124,14 +118,6 @@ ffi_type* FFICall::GetArgumentType(const TypeEncoding* typeEncoding, bool isStru

return &ffi_type_pointer;
}
case BinaryTypeEncodingType::AnonymousStructEncoding: {
size_t count = typeEncoding->details.anonymousRecord.fieldsCount;
const TypeEncoding* fieldEncoding = typeEncoding->details.anonymousRecord.getFieldsEncodings();
const String* fieldNames = typeEncoding->details.anonymousRecord.getFieldNames();
const StructInfo& structInfo =
FFICall::GetStructInfo(count, fieldEncoding, fieldNames);
return structInfo.FFIType();
}
default: {
break;
}
Expand Down Expand Up @@ -174,6 +160,71 @@ void FFICall::DisposeFFIType(ffi_type* type, const TypeEncoding* typeEncoding) {

}

size_t FFICall::GetTypeSize(const TypeEncoding* typeEncoding,
bool isStructMember) {
switch (typeEncoding->type) {
case BinaryTypeEncodingType::StructDeclarationReference:
case BinaryTypeEncodingType::AnonymousStructEncoding: {
return FFICall::GetStructInfo(typeEncoding).FFIType()->size;
}
case BinaryTypeEncodingType::ConstantArrayEncoding: {
if (!isStructMember) {
return ffi_type_pointer.size;
}
const TypeEncoding* innerType =
typeEncoding->details.constantArray.getInnerType();
return typeEncoding->details.constantArray.size *
FFICall::GetTypeSize(innerType, true);
}
case BinaryTypeEncodingType::ExtVectorEncoding: {
const TypeEncoding* innerType =
typeEncoding->details.extVector.getInnerType();
size_t vecSize = typeEncoding->details.extVector.size *
FFICall::GetTypeSize(innerType, isStructMember);
size_t p2 = 1;
while (p2 < vecSize) {
p2 <<= 1;
}
return p2;
}
default:
// Every remaining encoding maps to a static ffi_type.
return FFICall::GetArgumentType(typeEncoding, isStructMember)->size;
}
}

const StructInfo& FFICall::GetStructInfo(const TypeEncoding* typeEncoding) {
{
SpinLock lock(structInfosByEncodingMutex_);
auto it = structInfosByEncoding_.find(typeEncoding);
if (it != structInfosByEncoding_.end()) {
return *it->second;
}
}

const StructInfo* structInfo = nullptr;
if (typeEncoding->type ==
BinaryTypeEncodingType::StructDeclarationReference) {
const char* structName =
typeEncoding->details.declarationReference.name.valuePtr();
const Meta* meta = ArgConverter::GetMeta(structName);
tns::Assert(meta != nullptr && meta->type() == MetaType::Struct);
structInfo = &FFICall::GetStructInfo(static_cast<const StructMeta*>(meta),
structName);
} else {
tns::Assert(typeEncoding->type ==
BinaryTypeEncodingType::AnonymousStructEncoding);
structInfo = &FFICall::GetStructInfo(
typeEncoding->details.anonymousRecord.fieldsCount,
typeEncoding->details.anonymousRecord.getFieldsEncodings(),
typeEncoding->details.anonymousRecord.getFieldNames());
}

SpinLock lock(structInfosByEncodingMutex_);
structInfosByEncoding_.emplace(typeEncoding, structInfo);
return *structInfo;
}

const StructInfo& FFICall::GetStructInfo(const StructMeta* structMeta,
std::string structName) {
size_t fieldsCount = structMeta->fieldsCount();
Expand Down Expand Up @@ -331,4 +382,7 @@ SpinMutex ParametrizedCall::callsCacheMutex_;
robin_hood::unordered_map<std::string, std::unique_ptr<StructInfo>>
FFICall::structInfosCache_;
SpinMutex FFICall::structInfosCacheMutex_;
robin_hood::unordered_map<const TypeEncoding*, const StructInfo*>
FFICall::structInfosByEncoding_;
SpinMutex FFICall::structInfosByEncodingMutex_;
}
13 changes: 13 additions & 0 deletions NativeScript/runtime/FFICall.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,13 @@ class FFICall : public BaseCall {
static ffi_type* GetArgumentType(const TypeEncoding* typeEncoding,
bool isStructMember = false);
static void DisposeFFIType(ffi_type* type, const TypeEncoding* typeEncoding);
// GetArgumentType(typeEncoding, isStructMember)->size, without building or
// disposing an ffi_type.
static size_t GetTypeSize(const TypeEncoding* typeEncoding,
bool isStructMember = false);
// For a StructDeclarationReference or AnonymousStructEncoding. Same
// lifetime as the overloads below.
static const StructInfo& GetStructInfo(const TypeEncoding* typeEncoding);
// The returned reference points into a process-wide cache that is never
// pruned, so it stays valid for the lifetime of the process.
static const StructInfo& GetStructInfo(const StructMeta* structMeta,
Expand All @@ -124,6 +131,12 @@ class FFICall : public BaseCall {
static robin_hood::unordered_map<std::string, std::unique_ptr<StructInfo>>
structInfosCache_;
static SpinMutex structInfosCacheMutex_;
// Keyed by encoding address: metadata encodings live for the process, and
// the runtime-built ones (Interop::CreateEncoding, ClassBuilder) are never
// freed and never describe structs.
static robin_hood::unordered_map<const TypeEncoding*, const StructInfo*>
structInfosByEncoding_;
static SpinMutex structInfosByEncodingMutex_;
void** argsArray_;
bool useDynamicBuffer_;
uint8_t staticBuffer[512];
Expand Down
5 changes: 5 additions & 0 deletions NativeScript/runtime/Interop.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ class Interop {
static v8::Local<v8::Value> GetPrimitiveReturnType(
v8::Local<v8::Context> context, BinaryTypeEncodingType type,
BaseCall* call);
// Must run before the class_replaceMethod that swizzles `selector`; returns
// the selector the original implementation is re-added under.
static SEL RegisterSwizzledSelector(SEL selector);

private:
static void ExecuteWriteValueDebugValidationsIfInDebug(
Expand Down Expand Up @@ -177,6 +180,8 @@ class Interop {
BinaryTypeEncodingType type);
static std::vector<std::string> GetAdditionalProtocols(
const TypeEncoding* typeEncoding);
// The `__NativeScript__`-prefixed twin of `selector`, or nullptr when no
// class has had `selector` swizzled.
static SEL GetSwizzledMethodSelector(SEL selector);

template <typename T>
Expand Down
Loading
Loading