Skip to content

Commit

Permalink
Merge branch 'main' into feat/visionos-unit-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
NathanWalker authored Jan 19, 2025
2 parents accd6aa + b73693a commit 2ba7f87
Show file tree
Hide file tree
Showing 12 changed files with 1,058 additions and 837 deletions.
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,23 @@
## [8.8.3-alpha.0](https://github.com/NativeScript/ios/compare/v8.8.2...v8.8.3-alpha.0) (2024-12-05)


### Bug Fixes

* handle gc protection in runtime run loop ([78b5e37](https://github.com/NativeScript/ios/commit/78b5e3799f1305b3eafe7d3deb60a7e56b86b230))
* possible race condition extending native class ([8b932a3](https://github.com/NativeScript/ios/commit/8b932a31fe735c69b9d72b76eb106037653764ce))



## [8.8.2](https://github.com/NativeScript/ios/compare/v8.8.1...v8.8.2) (2024-09-06)


### Bug Fixes

* ensure same mtime for js and code cache to prevent loading old code caches ([#261](https://github.com/NativeScript/ios/issues/261)) ([055b042](https://github.com/NativeScript/ios/commit/055b0427cf49e7c4cb37991c9419b899868b6bbd))
* revert visionOS changes to iOS project template ([55c5c51](https://github.com/NativeScript/ios/commit/55c5c5198f04ff2b5cbe1be6f5add92acb3ed23f))



## [8.8.1](https://github.com/NativeScript/ios/compare/v8.8.0...v8.8.1) (2024-07-10)


Expand Down
43 changes: 25 additions & 18 deletions NativeScript/runtime/ClassBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,34 @@

namespace tns {

// Moved this method in a separate .cpp file because ARC destroys the class created with objc_allocateClassPair
// when the control leaves this method scope
// Moved this method in a separate .cpp file because ARC destroys the class
// created with objc_allocateClassPair when the control leaves this method scope

Class ClassBuilder::GetExtendedClass(std::string baseClassName, std::string staticClassName) {
Class baseClass = objc_getClass(baseClassName.c_str());
std::string name = !staticClassName.empty() ? staticClassName : baseClassName + "_" + std::to_string(++ClassBuilder::classNameCounter_);
Class clazz = objc_getClass(name.c_str());
Class ClassBuilder::GetExtendedClass(std::string baseClassName,
std::string staticClassName) {
Class baseClass = objc_getClass(baseClassName.c_str());
std::string name =
!staticClassName.empty()
? staticClassName
: baseClassName + "_" +
std::to_string(++ClassBuilder::classNameCounter_);
// here we could either call objc_getClass with the name to see if the class
// already exists or we can just try allocating it, which will return nil if
// the class already exists so we try allocating it every time to avoid race
// conditions in case this method is being executed by multiple threads
Class clazz = objc_allocateClassPair(baseClass, name.c_str(), 0);

if (clazz != nil) {
int i = 1;
std::string initialName = name;
while (clazz != nil) {
name = initialName + std::to_string(i++);
clazz = objc_getClass(name.c_str());
}
if (clazz == nil) {
int i = 1;
std::string initialName = name;
while (clazz == nil) {
name = initialName + std::to_string(i++);
clazz = objc_allocateClassPair(baseClass, name.c_str(), 0);
}
}

clazz = objc_allocateClassPair(baseClass, name.c_str(), 0);

objc_registerClassPair(clazz);
return clazz;
objc_registerClassPair(clazz);
return clazz;
}

}
} // namespace tns
83 changes: 60 additions & 23 deletions NativeScript/runtime/ClassBuilder.mm
Original file line number Diff line number Diff line change
Expand Up @@ -268,19 +268,30 @@
return retain(self, @selector(retain));
}
if ([self retainCount] == 1) {
auto innerCache = isolateWrapper.GetCache();
auto it = innerCache->Instances.find(self);
if (it != innerCache->Instances.end()) {
v8::Locker locker(isolate);
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);
Local<Value> value = it->second->Get(isolate);
BaseDataWrapper* wrapper = tns::GetValue(isolate, value);
if (wrapper != nullptr && wrapper->Type() == WrapperType::ObjCObject) {
ObjCDataWrapper* objcWrapper = static_cast<ObjCDataWrapper*>(wrapper);
objcWrapper->GcProtect();
auto runtime = Runtime::GetRuntime(isolate);
auto runtimeLoop = runtime->RuntimeLoop();
void* weakSelf = (__bridge void*) self;
auto gcProtect = ^() {
auto innerCache = isolateWrapper.GetCache();
auto it = innerCache->Instances.find((id)weakSelf);
if (it != innerCache->Instances.end()) {
v8::Locker locker(isolate);
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);
Local<Value> value = it->second->Get(isolate);
BaseDataWrapper* wrapper = tns::GetValue(isolate, value);
if (wrapper != nullptr && wrapper->Type() == WrapperType::ObjCObject) {
ObjCDataWrapper* objcWrapper = static_cast<ObjCDataWrapper*>(wrapper);
objcWrapper->GcProtect();
}
}
};
if(CFRunLoopGetCurrent() != runtimeLoop) {
tns::ExecuteOnRunLoop(runtimeLoop, gcProtect);
} else {
gcProtect();
}

}

return retain(self, @selector(retain));
Expand All @@ -295,18 +306,44 @@
}

if ([self retainCount] == 2) {
auto innerCache = isolateWrapper.GetCache();
auto it = innerCache->Instances.find(self);
if (it != innerCache->Instances.end()) {
v8::Locker locker(isolate);
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);
if (it->second != nullptr) {
Local<Value> value = it->second->Get(isolate);
BaseDataWrapper* wrapper = tns::GetValue(isolate, value);
if (wrapper != nullptr && wrapper->Type() == WrapperType::ObjCObject) {
ObjCDataWrapper* objcWrapper = static_cast<ObjCDataWrapper*>(wrapper);
objcWrapper->GcUnprotect();
void* weakSelf = (__bridge void*) self;
auto gcUnprotect = ^() {


auto innerCache = isolateWrapper.GetCache();
auto it = innerCache->Instances.find((id)weakSelf);
if (it != innerCache->Instances.end()) {
v8::Locker locker(isolate);
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);
if (it->second != nullptr) {
Local<Value> value = it->second->Get(isolate);
BaseDataWrapper* wrapper = tns::GetValue(isolate, value);
if (wrapper != nullptr && wrapper->Type() == WrapperType::ObjCObject) {
ObjCDataWrapper* objcWrapper = static_cast<ObjCDataWrapper*>(wrapper);
objcWrapper->GcUnprotect();
}
}
}
};
auto runtime = Runtime::GetRuntime(isolate);
auto runtimeLoop = runtime->RuntimeLoop();
if(CFRunLoopGetCurrent() != runtimeLoop) {
tns::ExecuteOnRunLoop(runtimeLoop, gcUnprotect);
} else {
auto innerCache = isolateWrapper.GetCache();
auto it = innerCache->Instances.find(self);
if (it != innerCache->Instances.end()) {
v8::Locker locker(isolate);
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);
if (it->second != nullptr) {
Local<Value> value = it->second->Get(isolate);
BaseDataWrapper* wrapper = tns::GetValue(isolate, value);
if (wrapper != nullptr && wrapper->Type() == WrapperType::ObjCObject) {
ObjCDataWrapper* objcWrapper = static_cast<ObjCDataWrapper*>(wrapper);
objcWrapper->GcUnprotect();
}
}
}
}
Expand Down
118 changes: 68 additions & 50 deletions NativeScript/runtime/ModuleInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,57 +7,75 @@
namespace tns {

class ModuleInternal {
public:
ModuleInternal(v8::Local<v8::Context> context);
bool RunModule(v8::Isolate* isolate, std::string path);
void RunScript(v8::Isolate* isolate, std::string script);

private:
static void RequireCallback(const v8::FunctionCallbackInfo<v8::Value>& info);
v8::Local<v8::Function> GetRequireFunction(v8::Isolate* isolate, const std::string& dirName);
v8::Local<v8::Object> LoadImpl(v8::Isolate* isolate, const std::string& moduleName, const std::string& baseDir, bool& isData);
v8::Local<v8::Script> LoadScript(v8::Isolate* isolate, const std::string& path);
v8::Local<v8::String> WrapModuleContent(v8::Isolate* isolate, const std::string& path);
v8::Local<v8::Object> LoadModule(v8::Isolate* isolate, const std::string& modulePath, const std::string& cacheKey);
v8::Local<v8::Object> LoadData(v8::Isolate* isolate, const std::string& modulePath);
std::string ResolvePath(v8::Isolate* isolate, const std::string& baseDir, const std::string& moduleName);
std::string ResolvePathFromPackageJson(const std::string& packageJson, bool& error);
v8::ScriptCompiler::CachedData* LoadScriptCache(const std::string& path);
void SaveScriptCache(const v8::Local<v8::Script> script, const std::string& path);
std::string GetCacheFileName(const std::string& path);
v8::MaybeLocal<v8::Value> RunScriptString(v8::Isolate* isolate, v8::Local<v8::Context> context, const std::string script);


std::unique_ptr<v8::Persistent<v8::Function>> requireFunction_;
std::unique_ptr<v8::Persistent<v8::Function>> requireFactoryFunction_;
robin_hood::unordered_map<std::string, std::shared_ptr<v8::Persistent<v8::Object>>> loadedModules_;

struct TempModule {
public:
TempModule(ModuleInternal* module, std::string modulePath, std::string cacheKey, std::shared_ptr<v8::Persistent<v8::Object>> poModuleObj)
: module_(module), dispose_(true), modulePath_(modulePath), cacheKey_(cacheKey) {
module->loadedModules_.emplace(modulePath, poModuleObj);
module->loadedModules_.emplace(cacheKey, poModuleObj);
}

~TempModule() {
if (this->dispose_) {
this->module_->loadedModules_.erase(modulePath_);
this->module_->loadedModules_.erase(cacheKey_);
}
}

void SaveToCache() {
this->dispose_ = false;
}
private:
ModuleInternal* module_;
bool dispose_;
std::string modulePath_;
std::string cacheKey_;
};
public:
ModuleInternal(v8::Local<v8::Context> context);
bool RunModule(v8::Isolate* isolate, std::string path);
void RunScript(v8::Isolate* isolate, std::string script);

private:
static void RequireCallback(const v8::FunctionCallbackInfo<v8::Value>& info);
v8::Local<v8::Function> GetRequireFunction(v8::Isolate* isolate,
const std::string& dirName);
v8::Local<v8::Object> LoadImpl(v8::Isolate* isolate,
const std::string& moduleName,
const std::string& baseDir, bool& isData);
v8::Local<v8::Script> LoadScript(v8::Isolate* isolate,
const std::string& path);
v8::Local<v8::String> WrapModuleContent(v8::Isolate* isolate,
const std::string& path);
v8::Local<v8::Object> LoadModule(v8::Isolate* isolate,
const std::string& modulePath,
const std::string& cacheKey);
v8::Local<v8::Object> LoadData(v8::Isolate* isolate,
const std::string& modulePath);
std::string ResolvePath(v8::Isolate* isolate, const std::string& baseDir,
const std::string& moduleName);
std::string ResolvePathFromPackageJson(const std::string& packageJson,
bool& error);
v8::ScriptCompiler::CachedData* LoadScriptCache(const std::string& path);
void SaveScriptCache(const v8::Local<v8::Script> script,
const std::string& path);
std::string GetCacheFileName(const std::string& path);
v8::MaybeLocal<v8::Value> RunScriptString(v8::Isolate* isolate,
v8::Local<v8::Context> context,
const std::string script);

std::unique_ptr<v8::Persistent<v8::Function>> requireFunction_;
std::unique_ptr<v8::Persistent<v8::Function>> requireFactoryFunction_;
robin_hood::unordered_map<std::string,
std::shared_ptr<v8::Persistent<v8::Object>>>
loadedModules_;

struct TempModule {
public:
TempModule(ModuleInternal* module, std::string modulePath,
std::string cacheKey,
std::shared_ptr<v8::Persistent<v8::Object>> poModuleObj)
: module_(module),
dispose_(true),
modulePath_(modulePath),
cacheKey_(cacheKey) {
module->loadedModules_.emplace(modulePath, poModuleObj);
module->loadedModules_.emplace(cacheKey, poModuleObj);
}

~TempModule() {
if (this->dispose_) {
this->module_->loadedModules_.erase(modulePath_);
this->module_->loadedModules_.erase(cacheKey_);
}
}

void SaveToCache() { this->dispose_ = false; }

private:
ModuleInternal* module_;
bool dispose_;
std::string modulePath_;
std::string cacheKey_;
};
};

}
} // namespace tns

#endif /* ModuleInternal_h */
Loading

0 comments on commit 2ba7f87

Please sign in to comment.