diff --git a/NativeScript/runtime/ModuleInternal.h b/NativeScript/runtime/ModuleInternal.h index 0b71496c..32874b1f 100644 --- a/NativeScript/runtime/ModuleInternal.h +++ b/NativeScript/runtime/ModuleInternal.h @@ -23,6 +23,11 @@ class ModuleInternal { v8::ScriptCompiler::CachedData* LoadScriptCache(const std::string& path); void SaveScriptCache(const v8::Local script, const std::string& path); std::string GetCacheFileName(const std::string& path); + + static void cacheGetterCallback(v8::Local property, const v8::PropertyCallbackInfo& info); + static void cacheSetterCallback(v8::Local property, v8::Local value, const v8::PropertyCallbackInfo& info); + static void cacheDeleterCallback(v8::Local property, const v8::PropertyCallbackInfo< v8::Boolean > &info); + static void cacheEnumeratorCallback(const v8::PropertyCallbackInfo& info); std::unique_ptr> requireFunction_; std::unique_ptr> requireFactoryFunction_; diff --git a/NativeScript/runtime/ModuleInternal.mm b/NativeScript/runtime/ModuleInternal.mm index ab5890ed..182ebff8 100644 --- a/NativeScript/runtime/ModuleInternal.mm +++ b/NativeScript/runtime/ModuleInternal.mm @@ -63,6 +63,142 @@ return success; } +void ModuleInternal::cacheGetterCallback(v8::Local property, const v8::PropertyCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + if(!property->IsString()) { + return; + } + try { + //ModuleInternal* moduleInternal = static_cast(info.Data().As()->Value()); + ModuleInternal* moduleInternal = static_cast(info.Data().As()->Get(isolate->GetCurrentContext(), 0).ToLocalChecked().As()->Value()); + std::string moduleName = tns::ToString(isolate, property); + auto it = moduleInternal->loadedModules_.find(moduleName); + if (it != moduleInternal->loadedModules_.end()) { + info.GetReturnValue().Set(it->second->Get(isolate)); + return; + } + std::string callingModuleDirName = tns::ToString(isolate, info.Data().As()->Get(isolate->GetCurrentContext(), 1).ToLocalChecked()); //tns::ToString(isolate, info[1].As()); + + NSString* fullPath; + if (moduleName.length() > 0 && moduleName[0] != '/') { + if (moduleName[0] == '.') { + fullPath = [[NSString stringWithUTF8String:callingModuleDirName.c_str()] stringByAppendingPathComponent:[NSString stringWithUTF8String:moduleName.c_str()]]; + } else if (moduleName[0] == '~') { + moduleName = moduleName.substr(2); + fullPath = [[NSString stringWithUTF8String:RuntimeConfig.ApplicationPath.c_str()] stringByAppendingPathComponent:[NSString stringWithUTF8String:moduleName.c_str()]]; + } else { + NSString* tnsModulesPath = [[NSString stringWithUTF8String:RuntimeConfig.ApplicationPath.c_str()] stringByAppendingPathComponent:@"tns_modules"]; + fullPath = [tnsModulesPath stringByAppendingPathComponent:[NSString stringWithUTF8String:moduleName.c_str()]]; + + const char* path1 = [fullPath fileSystemRepresentation]; + const char* path2 = [[fullPath stringByAppendingPathExtension:@"js"] fileSystemRepresentation]; + + if (!tns::Exists(path1) && !tns::Exists(path2)) { + fullPath = [tnsModulesPath stringByAppendingPathComponent:@"tns-core-modules"]; + fullPath = [fullPath stringByAppendingPathComponent:[NSString stringWithUTF8String:moduleName.c_str()]]; + } + } + } else { + fullPath = [NSString stringWithUTF8String:moduleName.c_str()]; + } + NSString* fileNameOnly = [fullPath lastPathComponent]; + NSString* pathOnly = [fullPath stringByDeletingLastPathComponent]; + std::string fileNameOnlyStr = [fileNameOnly UTF8String]; + std::string pathOnlyStr = [pathOnly UTF8String]; + size_t lastIndex = fileNameOnlyStr.find_last_of("."); + std::string moduleNameWithoutExtension = (lastIndex == std::string::npos) ? fileNameOnlyStr : fileNameOnlyStr.substr(0, lastIndex); + std::string cacheKey = pathOnlyStr + "*" + moduleNameWithoutExtension; + + it = moduleInternal->loadedModules_.find(cacheKey); + + if (it != moduleInternal->loadedModules_.end()) { + info.GetReturnValue().Set(it->second->Get(isolate)); + } + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(isolate); + } + +} +void ModuleInternal::cacheSetterCallback(v8::Local property, v8::Local value, const v8::PropertyCallbackInfo& info) { + // TODO: maybe implement? +} +void ModuleInternal::cacheDeleterCallback(v8::Local property, const v8::PropertyCallbackInfo< v8::Boolean > &info) { + v8::Isolate* isolate = info.GetIsolate(); + if(!property->IsString()) { + return; + } + try { + //ModuleInternal* moduleInternal = static_cast(info.Data().As()->Value()); + ModuleInternal* moduleInternal = static_cast(info.Data().As()->Get(isolate->GetCurrentContext(), 0).ToLocalChecked().As()->Value()); + std::string moduleName = tns::ToString(isolate, property); + std::string callingModuleDirName = tns::ToString(isolate, info.Data().As()->Get(isolate->GetCurrentContext(), 1).ToLocalChecked()); //tns::ToString(isolate, info[1].As()); + + NSString* fullPath; + if (moduleName.length() > 0 && moduleName[0] != '/') { + if (moduleName[0] == '.') { + fullPath = [[NSString stringWithUTF8String:callingModuleDirName.c_str()] stringByAppendingPathComponent:[NSString stringWithUTF8String:moduleName.c_str()]]; + } else if (moduleName[0] == '~') { + moduleName = moduleName.substr(2); + fullPath = [[NSString stringWithUTF8String:RuntimeConfig.ApplicationPath.c_str()] stringByAppendingPathComponent:[NSString stringWithUTF8String:moduleName.c_str()]]; + } else { + NSString* tnsModulesPath = [[NSString stringWithUTF8String:RuntimeConfig.ApplicationPath.c_str()] stringByAppendingPathComponent:@"tns_modules"]; + fullPath = [tnsModulesPath stringByAppendingPathComponent:[NSString stringWithUTF8String:moduleName.c_str()]]; + + const char* path1 = [fullPath fileSystemRepresentation]; + const char* path2 = [[fullPath stringByAppendingPathExtension:@"js"] fileSystemRepresentation]; + + if (!tns::Exists(path1) && !tns::Exists(path2)) { + fullPath = [tnsModulesPath stringByAppendingPathComponent:@"tns-core-modules"]; + fullPath = [fullPath stringByAppendingPathComponent:[NSString stringWithUTF8String:moduleName.c_str()]]; + } + } + } else { + fullPath = [NSString stringWithUTF8String:moduleName.c_str()]; + } + NSString* fileNameOnly = [fullPath lastPathComponent]; + NSString* pathOnly = [fullPath stringByDeletingLastPathComponent]; + std::string fileNameOnlyStr = [fileNameOnly UTF8String]; + std::string pathOnlyStr = [pathOnly UTF8String]; + size_t lastIndex = fileNameOnlyStr.find_last_of("."); + std::string moduleNameWithoutExtension = (lastIndex == std::string::npos) ? fileNameOnlyStr : fileNameOnlyStr.substr(0, lastIndex); + std::string cacheKey = pathOnlyStr + "*" + moduleNameWithoutExtension; + // auto str = moduleInternal->ResolvePath(isolate, [pathOnly UTF8String], [fileNameOnly UTF8String]); + auto it = moduleInternal->loadedModules_.find(cacheKey); + + if (it != moduleInternal->loadedModules_.end()) { + moduleInternal->loadedModules_.erase(it); + Local outStr; + bool success = it->second->Get(isolate)->Get(isolate->GetCurrentContext(), tns::ToV8String(isolate, "id")).ToLocal(&outStr); + if(success) { + it = moduleInternal->loadedModules_.find(tns::ToString(isolate, outStr)); + if (it != moduleInternal->loadedModules_.end()) { + moduleInternal->loadedModules_.erase(it); + } + } + info.GetReturnValue().Set(v8::Boolean::New(isolate, true)); + } + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(isolate); + } +} +void ModuleInternal::cacheEnumeratorCallback(const v8::PropertyCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + try { + ModuleInternal* moduleInternal = static_cast(info.Data().As()->Get(isolate->GetCurrentContext(), 0).ToLocalChecked().As()->Value()); + v8::Local result = v8::Array::New(isolate, (int)moduleInternal->loadedModules_.size()); + int i = 0; + for(auto it = moduleInternal->loadedModules_.begin(); it != moduleInternal->loadedModules_.end(); ++it) { + bool success = result->Set(isolate->GetCurrentContext(), i++, tns::ToV8String(isolate, it->first)).FromMaybe(false); + tns::Assert(success, isolate); + } + info.GetReturnValue().Set(result); + + } catch (NativeScriptException& ex) { + ex.ReThrowToV8(isolate); + } + +} + Local ModuleInternal::GetRequireFunction(Isolate* isolate, const std::string& dirName) { Local requireFuncFactory = requireFactoryFunction_->Get(isolate); Local context = isolate->GetCurrentContext(); @@ -75,6 +211,16 @@ Local thiz = Object::New(isolate); bool success = requireFuncFactory->Call(context, thiz, 2, args).ToLocal(&result); tns::Assert(success && !result.IsEmpty() && result->IsFunction(), isolate); + v8::Local templ = ObjectTemplate::New(isolate); + Local cacheArgs = v8::Array::New(isolate, 2); + success = cacheArgs->Set(context, 0, External::New(isolate, this)).FromMaybe(false); + tns::Assert(success, isolate); + success = cacheArgs->Set(context, 1, tns::ToV8String(isolate, dirName.c_str())).FromMaybe(false); + tns::Assert(success, isolate); + templ->SetHandler(v8::NamedPropertyHandlerConfiguration(cacheGetterCallback, cacheSetterCallback, nullptr, cacheDeleterCallback, cacheEnumeratorCallback, cacheArgs)); + + success = result.As()->DefineOwnProperty(context, tns::ToV8String(isolate, "cache"), templ->NewInstance(context).ToLocalChecked()).FromMaybe(false); + tns::Assert(success, isolate); return result.As(); }