diff --git a/NativeScript/NativeScript-Prefix.pch b/NativeScript/NativeScript-Prefix.pch index ec0604b8..11999277 100644 --- a/NativeScript/NativeScript-Prefix.pch +++ b/NativeScript/NativeScript-Prefix.pch @@ -1,7 +1,7 @@ #ifndef NativeScript_Prefix_pch #define NativeScript_Prefix_pch -#define NATIVESCRIPT_VERSION "8.9.0" +#define NATIVESCRIPT_VERSION "9.0.0-esm.2" #ifdef DEBUG #define SIZEOF_OFF_T 8 diff --git a/NativeScript/runtime/FFICall.h b/NativeScript/runtime/FFICall.h index c2f35549..46666405 100644 --- a/NativeScript/runtime/FFICall.h +++ b/NativeScript/runtime/FFICall.h @@ -2,117 +2,127 @@ #define FFICall_h #include + #include -#include "robin_hood.h" -#include "Metadata.h" + #include "DataWrapper.h" +#include "Metadata.h" #include "libffi.h" +#include "robin_hood.h" namespace tns { class BaseCall { -public: - BaseCall(uint8_t* buffer, size_t returnOffset = 0) - : buffer_(buffer), - returnOffset_(returnOffset) { - } + public: + BaseCall(uint8_t* buffer, size_t returnOffset = 0) + : buffer_(buffer), returnOffset_(returnOffset) {} - ~BaseCall() { - } + ~BaseCall() {} - inline void* ResultBuffer() { - return this->buffer_ + this->returnOffset_; - } + inline void* ResultBuffer() { return this->buffer_ + this->returnOffset_; } - template - inline T& GetResult() { - return *static_cast(this->ResultBuffer()); - } -protected: - uint8_t* buffer_; - size_t returnOffset_; + template + inline T& GetResult() { + return *static_cast(this->ResultBuffer()); + } + + protected: + uint8_t* buffer_; + size_t returnOffset_; }; class ParametrizedCall { -public: - ParametrizedCall(ffi_cif* cif) - : Cif(cif), - ReturnOffset(0), - StackSize(0) { - unsigned int argsCount = cif->nargs; - this->StackSize = 0; - - if (argsCount > 0) { - this->StackSize = malloc_good_size(sizeof(void* [argsCount])); - } - - this->ReturnOffset = this->StackSize; - - this->StackSize += malloc_good_size(std::max(cif->rtype->size, sizeof(ffi_arg))); - - this->ArgValueOffsets.reserve(argsCount); - for (size_t i = 0; i < argsCount; i++) { - this->ArgValueOffsets.push_back(this->StackSize); - ffi_type* argType = cif->arg_types[i]; - this->StackSize += malloc_good_size(std::max(argType->size, sizeof(ffi_arg))); - } + public: + ParametrizedCall(ffi_cif* cif) : Cif(cif), ReturnOffset(0), StackSize(0) { + unsigned int argsCount = cif->nargs; + this->StackSize = 0; + + if (argsCount > 0) { + // compute total bytes = number of pointers × size of a pointer + size_t needed = sizeof(void*) * static_cast(argsCount); + this->StackSize = malloc_good_size(needed); } - static ParametrizedCall* Get(const TypeEncoding* typeEncoding, const int initialParameterIndex, const int argsCount); + this->ReturnOffset = this->StackSize; - ffi_cif* Cif; - size_t ReturnOffset; - size_t StackSize; - std::vector ArgValueOffsets; -private: - static robin_hood::unordered_map callsCache_; -}; + this->StackSize += + malloc_good_size(std::max(cif->rtype->size, sizeof(ffi_arg))); -class FFICall: public BaseCall { -public: - FFICall(ParametrizedCall* parametrizedCall): BaseCall(nullptr) { - this->returnOffset_ = parametrizedCall->ReturnOffset; - this->useDynamicBuffer_ = parametrizedCall->StackSize > 512; - if(this->useDynamicBuffer_) { - this->buffer_ = reinterpret_cast(malloc(parametrizedCall->StackSize)); - } else { - this->buffer_ = reinterpret_cast(this->staticBuffer); - } - - this->argsArray_ = reinterpret_cast(this->buffer_); - for (size_t i = 0; i < parametrizedCall->Cif->nargs; i++) { - this->argsArray_[i] = this->buffer_ + parametrizedCall->ArgValueOffsets[i]; - } + this->ArgValueOffsets.reserve(argsCount); + for (size_t i = 0; i < argsCount; i++) { + this->ArgValueOffsets.push_back(this->StackSize); + ffi_type* argType = cif->arg_types[i]; + this->StackSize += + malloc_good_size(std::max(argType->size, sizeof(ffi_arg))); } + } - ~FFICall() { - if(this->useDynamicBuffer_) { - free(this->buffer_); - } - } + static ParametrizedCall* Get(const TypeEncoding* typeEncoding, + const int initialParameterIndex, + const int argsCount); - /** - When calling this, always make another call to DisposeFFIType with the same parameters - */ - static ffi_type* GetArgumentType(const TypeEncoding* typeEncoding, bool isStructMember = false); - static void DisposeFFIType(ffi_type* type, const TypeEncoding* typeEncoding); - static StructInfo GetStructInfo(const StructMeta* structMeta, std::string structName = ""); - static StructInfo GetStructInfo(size_t fieldsCount, const TypeEncoding* fieldEncoding, const String* fieldNames, std::string structName = ""); + ffi_cif* Cif; + size_t ReturnOffset; + size_t StackSize; + std::vector ArgValueOffsets; + + private: + static robin_hood::unordered_map + callsCache_; +}; + +class FFICall : public BaseCall { + public: + FFICall(ParametrizedCall* parametrizedCall) : BaseCall(nullptr) { + this->returnOffset_ = parametrizedCall->ReturnOffset; + this->useDynamicBuffer_ = parametrizedCall->StackSize > 512; + if (this->useDynamicBuffer_) { + this->buffer_ = + reinterpret_cast(malloc(parametrizedCall->StackSize)); + } else { + this->buffer_ = reinterpret_cast(this->staticBuffer); + } - inline void* ArgumentBuffer(unsigned index) { - return this->argsArray_[index]; + this->argsArray_ = reinterpret_cast(this->buffer_); + for (size_t i = 0; i < parametrizedCall->Cif->nargs; i++) { + this->argsArray_[i] = + this->buffer_ + parametrizedCall->ArgValueOffsets[i]; } + } - inline void** ArgsArray() { - return this->argsArray_; + ~FFICall() { + if (this->useDynamicBuffer_) { + free(this->buffer_); } -private: - static robin_hood::unordered_map structInfosCache_; - void** argsArray_; - bool useDynamicBuffer_; - uint8_t staticBuffer[512]; + } + + /** + When calling this, always make another call to DisposeFFIType with the same + parameters + */ + static ffi_type* GetArgumentType(const TypeEncoding* typeEncoding, + bool isStructMember = false); + static void DisposeFFIType(ffi_type* type, const TypeEncoding* typeEncoding); + static StructInfo GetStructInfo(const StructMeta* structMeta, + std::string structName = ""); + static StructInfo GetStructInfo(size_t fieldsCount, + const TypeEncoding* fieldEncoding, + const String* fieldNames, + std::string structName = ""); + + inline void* ArgumentBuffer(unsigned index) { + return this->argsArray_[index]; + } + + inline void** ArgsArray() { return this->argsArray_; } + + private: + static robin_hood::unordered_map structInfosCache_; + void** argsArray_; + bool useDynamicBuffer_; + uint8_t staticBuffer[512]; }; -} +} // namespace tns #endif /* FFICall_h */ diff --git a/NativeScript/runtime/Helpers.mm b/NativeScript/runtime/Helpers.mm index db93fcb2..73f5621a 100644 --- a/NativeScript/runtime/Helpers.mm +++ b/NativeScript/runtime/Helpers.mm @@ -1,961 +1,851 @@ +#include "Helpers.h" #include +#include #include -#include +#include #include -#include +#include +#include #include +#include #include -#include #include -#include -#include -#include "RuntimeConfig.h" -#include "Runtime.h" -#include "Helpers.h" #include "Caches.h" +#include "NativeScriptException.h" +#include "Runtime.h" +#include "RuntimeConfig.h" using namespace v8; namespace { - const int BUFFER_SIZE = 1024 * 1024; - char* Buffer = new char[BUFFER_SIZE]; - uint8_t* BinBuffer = new uint8_t[BUFFER_SIZE]; +const int BUFFER_SIZE = 1024 * 1024; +char* Buffer = new char[BUFFER_SIZE]; +uint8_t* BinBuffer = new uint8_t[BUFFER_SIZE]; } std::u16string tns::ToUtf16String(Isolate* isolate, const Local& value) { - std::string valueStr = tns::ToString(isolate, value); + std::string valueStr = tns::ToString(isolate, value); #pragma GCC diagnostic ignored "-Wdeprecated-declarations" - // FIXME: std::codecvt_utf8_utf16 is deprecated - std::wstring_convert, char16_t> convert; - std::u16string value16 = convert.from_bytes(valueStr); + // FIXME: std::codecvt_utf8_utf16 is deprecated + std::wstring_convert, char16_t> convert; + std::u16string value16 = convert.from_bytes(valueStr); - return value16; + return value16; } std::vector tns::ToVector(const std::string& value) { #pragma GCC diagnostic ignored "-Wdeprecated-declarations" - // FIXME: std::codecvt_utf8_utf16 is deprecated - std::wstring_convert, char16_t> convert; - std::u16string value16 = convert.from_bytes(value); + // FIXME: std::codecvt_utf8_utf16 is deprecated + std::wstring_convert, char16_t> convert; + std::u16string value16 = convert.from_bytes(value); - const uint16_t *begin = reinterpret_cast(value16.data()); - const uint16_t *end = reinterpret_cast(value16.data() + value16.size()); - std::vector vector(begin, end); - return vector; + const uint16_t* begin = reinterpret_cast(value16.data()); + const uint16_t* end = reinterpret_cast(value16.data() + value16.size()); + std::vector vector(begin, end); + return vector; } bool tns::Exists(const char* fullPath) { - struct stat statbuf; - mode_t mode = S_IFDIR | S_IFREG; - if (stat(fullPath, &statbuf) == 0) { - return (statbuf.st_mode & S_IFMT) & mode; - } + struct stat statbuf; + mode_t mode = S_IFDIR | S_IFREG; + if (stat(fullPath, &statbuf) == 0) { + return (statbuf.st_mode & S_IFMT) & mode; + } - return false; + return false; } -Local tns::ReadModule(Isolate* isolate, const std::string &filePath) { - struct stat finfo; +Local tns::ReadModule(Isolate* isolate, const std::string& filePath) { + struct stat finfo; - int file = open(filePath.c_str(), O_RDONLY); - if (file < 0) { - tns::Assert(false); - } + int file = open(filePath.c_str(), O_RDONLY); + if (file < 0) { + throw NativeScriptException(isolate, "Cannot read module " + filePath); + } - fstat(file, &finfo); - long length = finfo.st_size; + fstat(file, &finfo); + long length = finfo.st_size; - char* newBuffer = new char[length + 128]; - strcpy(newBuffer, "(function(module, exports, require, __filename, __dirname) { "); // 61 Characters - read(file, &newBuffer[61], length); - close(file); - length += 61; + char* newBuffer = new char[length + 128]; + strcpy(newBuffer, + "(function(module, exports, require, __filename, __dirname) { "); // 61 Characters + read(file, &newBuffer[61], length); + close(file); + length += 61; - // Add the closing "\n})" - newBuffer[length] = 10; - ++length; - newBuffer[length] = '}'; - ++length; - newBuffer[length] = ')'; - ++length; - newBuffer[length] = 0; + // Add the closing "\n})" + newBuffer[length] = 10; + ++length; + newBuffer[length] = '}'; + ++length; + newBuffer[length] = ')'; + ++length; + newBuffer[length] = 0; - Local str = v8::String::NewFromUtf8(isolate, newBuffer, NewStringType::kNormal, (int)length).ToLocalChecked(); - delete[] newBuffer; + Local str = + v8::String::NewFromUtf8(isolate, newBuffer, NewStringType::kNormal, (int)length) + .ToLocalChecked(); + delete[] newBuffer; - return str; + return str; } const char* tns::ReadText(const std::string& filePath, long& length, bool& isNew) { - FILE* file = fopen(filePath.c_str(), "rb"); - if (file == nullptr) { - tns::Assert(false); - } + FILE* file = fopen(filePath.c_str(), "rb"); + if (file == nullptr) { + tns::Assert(false); + } - fseek(file, 0, SEEK_END); + fseek(file, 0, SEEK_END); - length = ftell(file); - isNew = length > BUFFER_SIZE; + length = ftell(file); + isNew = length > BUFFER_SIZE; - rewind(file); + rewind(file); - if (isNew) { - char* newBuffer = new char[length]; - fread(newBuffer, 1, length, file); - fclose(file); + if (isNew) { + char* newBuffer = new char[length]; + fread(newBuffer, 1, length, file); + fclose(file); - return newBuffer; - } + return newBuffer; + } - fread(Buffer, 1, length, file); - fclose(file); + fread(Buffer, 1, length, file); + fclose(file); - return Buffer; + return Buffer; } std::string tns::ReadText(const std::string& file) { - long length; - bool isNew; - const char* content = tns::ReadText(file, length, isNew); + long length; + bool isNew; + const char* content = tns::ReadText(file, length, isNew); - std::string result(content, length); + std::string result(content, length); - if (isNew) { - delete[] content; - } + if (isNew) { + delete[] content; + } - return result; + return result; } uint8_t* tns::ReadBinary(const std::string path, long& length, bool& isNew) { - length = 0; - std::ifstream ifs(path); - if (ifs.fail()) { - return nullptr; - } - - FILE* file = fopen(path.c_str(), "rb"); - if (!file) { - return nullptr; - } - - fseek(file, 0, SEEK_END); - length = ftell(file); - rewind(file); - - isNew = length > BUFFER_SIZE; - - if (isNew) { - uint8_t* data = new uint8_t[length]; - fread(data, sizeof(uint8_t), length, file); - fclose(file); - return data; - } - - fread(BinBuffer, 1, length, file); + length = 0; + std::ifstream ifs(path); + if (ifs.fail()) { + return nullptr; + } + + FILE* file = fopen(path.c_str(), "rb"); + if (!file) { + return nullptr; + } + + fseek(file, 0, SEEK_END); + length = ftell(file); + rewind(file); + + isNew = length > BUFFER_SIZE; + + if (isNew) { + uint8_t* data = new uint8_t[length]; + fread(data, sizeof(uint8_t), length, file); fclose(file); + return data; + } + + fread(BinBuffer, 1, length, file); + fclose(file); - return BinBuffer; + return BinBuffer; } bool tns::WriteBinary(const std::string& path, const void* data, long length) { - FILE* file = fopen(path.c_str(), "wb"); - if (!file) { - return false; - } + FILE* file = fopen(path.c_str(), "wb"); + if (!file) { + return false; + } - size_t writtenBytes = fwrite(data, sizeof(uint8_t), length, file); - fclose(file); + size_t writtenBytes = fwrite(data, sizeof(uint8_t), length, file); + fclose(file); - return writtenBytes == length; + return writtenBytes == length; } -void tns::SetPrivateValue(const Local& obj, const Local& propName, const Local& value) { - Local context; - bool success = obj->GetCreationContext().ToLocal(&context); - tns::Assert(success); - Isolate* isolate = context->GetIsolate(); - Local privateKey = Private::ForApi(isolate, propName); +void tns::SetPrivateValue(const Local& obj, const Local& propName, + const Local& value) { + Local context; + bool success = obj->GetCreationContext().ToLocal(&context); + tns::Assert(success); + Isolate* isolate = context->GetIsolate(); + Local privateKey = Private::ForApi(isolate, propName); - if (!obj->SetPrivate(context, privateKey, value).To(&success) || !success) { - tns::Assert(false, isolate); - } + if (!obj->SetPrivate(context, privateKey, value).To(&success) || !success) { + tns::Assert(false, isolate); + } } Local tns::GetPrivateValue(const Local& obj, const Local& propName) { - Local context; - bool success = obj->GetCreationContext().ToLocal(&context); - tns::Assert(success); - Isolate* isolate = context->GetIsolate(); - Local privateKey = Private::ForApi(isolate, propName); + Local context; + bool success = obj->GetCreationContext().ToLocal(&context); + tns::Assert(success); + Isolate* isolate = context->GetIsolate(); + Local privateKey = Private::ForApi(isolate, propName); - Maybe hasPrivate = obj->HasPrivate(context, privateKey); + Maybe hasPrivate = obj->HasPrivate(context, privateKey); - tns::Assert(!hasPrivate.IsNothing(), isolate); + tns::Assert(!hasPrivate.IsNothing(), isolate); - if (!hasPrivate.FromMaybe(false)) { - return Local(); - } + if (!hasPrivate.FromMaybe(false)) { + return Local(); + } - v8::Locker locker(isolate); - Local result; - if (!obj->GetPrivate(context, privateKey).ToLocal(&result)) { - tns::Assert(false, isolate); - } + v8::Locker locker(isolate); + Local result; + if (!obj->GetPrivate(context, privateKey).ToLocal(&result)) { + tns::Assert(false, isolate); + } - return result; + return result; } bool tns::DeleteWrapperIfUnused(Isolate* isolate, const Local& obj, BaseDataWrapper* value) { - if (GetValue(isolate, obj) != value) { - delete value; - return true; - } - return false; + if (GetValue(isolate, obj) != value) { + delete value; + return true; + } + return false; } void tns::SetValue(Isolate* isolate, const Local& obj, BaseDataWrapper* value) { - if (obj.IsEmpty() || obj->IsNullOrUndefined()) { - return; - } + if (obj.IsEmpty() || obj->IsNullOrUndefined()) { + return; + } - Local ext = External::New(isolate, value); + Local ext = External::New(isolate, value); - if (obj->InternalFieldCount() > 0) { - obj->SetInternalField(0, ext); - } else { - tns::SetPrivateValue(obj, tns::ToV8String(isolate, "metadata"), ext); - } + if (obj->InternalFieldCount() > 0) { + obj->SetInternalField(0, ext); + } else { + tns::SetPrivateValue(obj, tns::ToV8String(isolate, "metadata"), ext); + } } tns::BaseDataWrapper* tns::GetValue(Isolate* isolate, const Local& val) { - if (val.IsEmpty() || val->IsNullOrUndefined() || !val->IsObject()) { - return nullptr; - } - - Local obj = val.As(); - if (obj->InternalFieldCount() > 0) { - Local field = obj->GetInternalField(0); - if (field.IsEmpty() || field->IsNullOrUndefined() || !field->IsExternal()) { - return nullptr; - } + if (val.IsEmpty() || val->IsNullOrUndefined() || !val->IsObject()) { + return nullptr; + } - return static_cast(field.As()->Value()); + Local obj = val.As(); + if (obj->InternalFieldCount() > 0) { + Local field = obj->GetInternalField(0); + if (field.IsEmpty() || field->IsNullOrUndefined() || !field->IsExternal()) { + return nullptr; } - Local metadataProp = tns::GetPrivateValue(obj, tns::ToV8String(isolate, "metadata")); - if (metadataProp.IsEmpty() || metadataProp->IsNullOrUndefined() || !metadataProp->IsExternal()) { - return nullptr; - } + return static_cast(field.As()->Value()); + } - return static_cast(metadataProp.As()->Value()); + Local metadataProp = tns::GetPrivateValue(obj, tns::ToV8String(isolate, "metadata")); + if (metadataProp.IsEmpty() || metadataProp->IsNullOrUndefined() || !metadataProp->IsExternal()) { + return nullptr; + } + + return static_cast(metadataProp.As()->Value()); } void tns::DeleteValue(Isolate* isolate, const Local& val) { - if (val.IsEmpty() || val->IsNullOrUndefined() || !val->IsObject()) { - return; - } + if (val.IsEmpty() || val->IsNullOrUndefined() || !val->IsObject()) { + return; + } - Local obj = val.As(); - if (obj->InternalFieldCount() > 0) { - obj->SetInternalField(0, v8::Undefined(isolate)); - return; - } + Local obj = val.As(); + if (obj->InternalFieldCount() > 0) { + obj->SetInternalField(0, v8::Undefined(isolate)); + return; + } - Local metadataKey = tns::ToV8String(isolate, "metadata"); - Local metadataProp = tns::GetPrivateValue(obj, metadataKey); - if (metadataProp.IsEmpty() || metadataProp->IsNullOrUndefined() || !metadataProp->IsExternal()) { - return; - } + Local metadataKey = tns::ToV8String(isolate, "metadata"); + Local metadataProp = tns::GetPrivateValue(obj, metadataKey); + if (metadataProp.IsEmpty() || metadataProp->IsNullOrUndefined() || !metadataProp->IsExternal()) { + return; + } - Local context; - bool success = obj->GetCreationContext().ToLocal(&context); - tns::Assert(success, isolate); - Local privateKey = Private::ForApi(isolate, metadataKey); + Local context; + bool success = obj->GetCreationContext().ToLocal(&context); + tns::Assert(success, isolate); + Local privateKey = Private::ForApi(isolate, metadataKey); - success = obj->DeletePrivate(context, privateKey).FromMaybe(false); - tns::Assert(success, isolate); + success = obj->DeletePrivate(context, privateKey).FromMaybe(false); + tns::Assert(success, isolate); } std::vector> tns::ArgsToVector(const FunctionCallbackInfo& info) { - std::vector> args; - args.reserve(info.Length()); - for (int i = 0; i < info.Length(); i++) { - args.push_back(info[i]); - } - return args; + std::vector> args; + args.reserve(info.Length()); + for (int i = 0; i < info.Length(); i++) { + args.push_back(info[i]); + } + return args; } bool tns::IsArrayOrArrayLike(Isolate* isolate, const Local& value) { - if (value->IsArray()) { - return true; - } + if (value->IsArray()) { + return true; + } - if (!value->IsObject()) { - return false; - } + if (!value->IsObject()) { + return false; + } - Local obj = value.As(); - Local context; - bool success = obj->GetCreationContext().ToLocal(&context); - tns::Assert(success, isolate); - return obj->Has(context, ToV8String(isolate, "length")).FromMaybe(false); + Local obj = value.As(); + Local context; + bool success = obj->GetCreationContext().ToLocal(&context); + tns::Assert(success, isolate); + return obj->Has(context, ToV8String(isolate, "length")).FromMaybe(false); } void* tns::TryGetBufferFromArrayBuffer(const v8::Local& value, bool& isArrayBuffer) { - isArrayBuffer = false; + isArrayBuffer = false; - if (value.IsEmpty() || (!value->IsArrayBuffer() && !value->IsArrayBufferView())) { - return nullptr; - } + if (value.IsEmpty() || (!value->IsArrayBuffer() && !value->IsArrayBufferView())) { + return nullptr; + } - Local buffer; - if (value->IsArrayBufferView()) { - isArrayBuffer = true; - buffer = value.As()->Buffer(); - } else if (value->IsArrayBuffer()) { - isArrayBuffer = true; - buffer = value.As(); - } + Local buffer; + if (value->IsArrayBufferView()) { + isArrayBuffer = true; + buffer = value.As()->Buffer(); + } else if (value->IsArrayBuffer()) { + isArrayBuffer = true; + buffer = value.As(); + } - if (buffer.IsEmpty()) { - return nullptr; - } + if (buffer.IsEmpty()) { + return nullptr; + } - void* data = buffer->GetBackingStore()->Data(); - return data; + void* data = buffer->GetBackingStore()->Data(); + return data; } struct LockAndCV { - std::mutex m; - std::condition_variable cv; + std::mutex m; + std::condition_variable cv; }; -void tns::ExecuteOnRunLoop(CFRunLoopRef queue, std::function func, bool async) { - if(!async) { - bool __block finished = false; - auto v = new LockAndCV; - std::unique_lock lock(v->m); - CFRunLoopPerformBlock(queue, kCFRunLoopCommonModes, ^(void) { - func(); - { - std::unique_lock lk(v->m); - finished = true; - } - v->cv.notify_all(); - }); - CFRunLoopWakeUp(queue); - while(!finished) { - v->cv.wait(lock); - } - delete v; - } else { - CFRunLoopPerformBlock(queue, kCFRunLoopCommonModes, ^(void) { - func(); - }); - CFRunLoopWakeUp(queue); - } - -} - -void tns::ExecuteOnDispatchQueue(dispatch_queue_t queue, std::function func, bool async) { - if (async) { - dispatch_async(queue, ^(void) { - func(); - }); - } else { - dispatch_sync(queue, ^(void) { - func(); - }); - } -} - -void tns::ExecuteOnMainThread(std::function func, bool async) { - if (async) { - dispatch_async(dispatch_get_main_queue(), ^(void) { - func(); - }); - } else { - dispatch_sync(dispatch_get_main_queue(), ^(void) { - func(); - }); - } +void tns::ExecuteOnRunLoop(CFRunLoopRef queue, std::function func, bool async) { + if (!async) { + bool __block finished = false; + auto v = new LockAndCV; + std::unique_lock lock(v->m); + CFRunLoopPerformBlock(queue, kCFRunLoopCommonModes, ^(void) { + func(); + { + std::unique_lock lk(v->m); + finished = true; + } + v->cv.notify_all(); + }); + CFRunLoopWakeUp(queue); + while (!finished) { + v->cv.wait(lock); + } + delete v; + } else { + CFRunLoopPerformBlock(queue, kCFRunLoopCommonModes, ^(void) { + func(); + }); + CFRunLoopWakeUp(queue); + } +} + +void tns::ExecuteOnDispatchQueue(dispatch_queue_t queue, std::function func, bool async) { + if (async) { + dispatch_async(queue, ^(void) { + func(); + }); + } else { + dispatch_sync(queue, ^(void) { + func(); + }); + } +} + +void tns::ExecuteOnMainThread(std::function func, bool async) { + if (async) { + dispatch_async(dispatch_get_main_queue(), ^(void) { + func(); + }); + } else { + dispatch_sync(dispatch_get_main_queue(), ^(void) { + func(); + }); + } } void tns::LogError(Isolate* isolate, TryCatch& tc) { - if (!tc.HasCaught()) { - return; - } - - Log(@"Native stack trace:"); - LogBacktrace(); - - Local stack; - Local context = isolate->GetCurrentContext(); - bool success = tc.StackTrace(context).ToLocal(&stack); - if (!success || stack.IsEmpty()) { - return; - } - - Local stackV8Str; - success = stack->ToDetailString(context).ToLocal(&stackV8Str); - if (!success || stackV8Str.IsEmpty()) { - return; - } - - std::string stackTraceStr = tns::ToString(isolate, stackV8Str); - stackTraceStr = ReplaceAll(stackTraceStr, RuntimeConfig.BaseDir, ""); - - Log(@"JavaScript error:"); - Log(@"%s", stackTraceStr.c_str()); -} - -Local tns::JsonStringifyObject(Local context, Local value, bool handleCircularReferences) { - Isolate* isolate = context->GetIsolate(); - if (value.IsEmpty()) { - return v8::String::Empty(isolate); - } - - if (handleCircularReferences) { - Local smartJSONStringifyFunction = tns::GetSmartJSONStringifyFunction(isolate); - - if (!smartJSONStringifyFunction.IsEmpty()) { - if (value->IsObject()) { - Local resultValue; - TryCatch tc(isolate); - - Local args[] = { - value->ToObject(context).ToLocalChecked() - }; - bool success = smartJSONStringifyFunction->Call(context, v8::Undefined(isolate), 1, args).ToLocal(&resultValue); - - if (success && !tc.HasCaught()) { - return resultValue->ToString(context).ToLocalChecked(); - } - } + if (!tc.HasCaught()) { + return; + } + + Log(@"Native stack trace:"); + LogBacktrace(); + + Local stack; + Local context = isolate->GetCurrentContext(); + bool success = tc.StackTrace(context).ToLocal(&stack); + if (!success || stack.IsEmpty()) { + return; + } + + Local stackV8Str; + success = stack->ToDetailString(context).ToLocal(&stackV8Str); + if (!success || stackV8Str.IsEmpty()) { + return; + } + + std::string stackTraceStr = tns::ToString(isolate, stackV8Str); + stackTraceStr = ReplaceAll(stackTraceStr, RuntimeConfig.BaseDir, ""); + + Log(@"JavaScript error:"); + Log(@"%s", stackTraceStr.c_str()); +} + +Local tns::JsonStringifyObject(Local context, Local value, + bool handleCircularReferences) { + Isolate* isolate = context->GetIsolate(); + if (value.IsEmpty()) { + return v8::String::Empty(isolate); + } + + if (handleCircularReferences) { + Local smartJSONStringifyFunction = tns::GetSmartJSONStringifyFunction(isolate); + + if (!smartJSONStringifyFunction.IsEmpty()) { + if (value->IsObject()) { + Local resultValue; + TryCatch tc(isolate); + + Local args[] = {value->ToObject(context).ToLocalChecked()}; + bool success = smartJSONStringifyFunction->Call(context, v8::Undefined(isolate), 1, args) + .ToLocal(&resultValue); + + if (success && !tc.HasCaught()) { + return resultValue->ToString(context).ToLocalChecked(); } + } } + } - Local resultString; - TryCatch tc(isolate); - bool success = v8::JSON::Stringify(context, value->ToObject(context).ToLocalChecked()).ToLocal(&resultString); + Local resultString; + TryCatch tc(isolate); + bool success = v8::JSON::Stringify(context, value->ToObject(context).ToLocalChecked()) + .ToLocal(&resultString); - if (!success && tc.HasCaught()) { - tns::LogError(isolate, tc); - return Local(); - } + if (!success && tc.HasCaught()) { + tns::LogError(isolate, tc); + return Local(); + } - return resultString; + return resultString; } Local tns::GetSmartJSONStringifyFunction(Isolate* isolate) { - std::shared_ptr caches = Caches::Get(isolate); - if (caches->SmartJSONStringifyFunc != nullptr) { - return caches->SmartJSONStringifyFunc->Get(isolate); - } - - std::string smartStringifyFunctionScript = - "(function () {\n" - " function smartStringify(object) {\n" - " const seen = [];\n" - " var replacer = function (key, value) {\n" - " if (value != null && typeof value == \"object\") {\n" - " if (seen.indexOf(value) >= 0) {\n" - " if (key) {\n" - " return \"[Circular]\";\n" - " }\n" - " return;\n" - " }\n" - " seen.push(value);\n" - " }\n" - " return value;\n" - " };\n" - " return JSON.stringify(object, replacer, 2);\n" - " }\n" - " return smartStringify;\n" - "})();"; - - Local source = tns::ToV8String(isolate, smartStringifyFunctionScript); - Local context = isolate->GetCurrentContext(); - - Local