diff --git a/src/jni/Android.mk b/src/jni/Android.mk index 901a9c86a..a4d790051 100644 --- a/src/jni/Android.mk +++ b/src/jni/Android.mk @@ -72,7 +72,7 @@ LOCAL_SRC_FILES := com_tns_AssetExtractor.cpp com_tns_Platform.cpp com_tns_JsDeb FieldAccessor.cpp ArrayElementAccessor.cpp \ ExceptionUtil.cpp Util.cpp Logger.cpp Profiler.cpp \ ObjectManager.cpp NumericCasts.cpp WeakRef.cpp \ - MetadataMethodInfo.cpp SimpleProfiler.cpp JType.cpp File.cpp Require.cpp + MetadataMethodInfo.cpp SimpleProfiler.cpp JType.cpp File.cpp Require.cpp Constants.cpp LOCAL_C_INCLUDES := $(LOCAL_PATH)/include LOCAL_LDLIBS := -llog -landroid -lz ifeq ($(TARGET_ARCH_ABI),armeabi-v7a) diff --git a/src/jni/ArgConverter.h b/src/jni/ArgConverter.h index 8992eee0d..f9931adf6 100644 --- a/src/jni/ArgConverter.h +++ b/src/jni/ArgConverter.h @@ -37,22 +37,6 @@ namespace tns static bool ReadJStringInBuffer(jstring value, jsize& utfLength); - static jlong ObjectToLong(jobject object); - - static jboolean ObjectToBoolean(jobject object); - - static jchar ObjectToChar(jobject object); - - static jbyte ObjectToByte(jobject object); - - static jshort ObjectToShort(jobject object); - - static jint ObjectToInt(jobject object); - - static jfloat ObjectToFloat(jobject object); - - static jdouble ObjectToDouble(jobject object); - static jstring ObjectToString(jobject object); static v8::Local jcharToV8String(jchar value); diff --git a/src/jni/Constants.cpp b/src/jni/Constants.cpp new file mode 100644 index 000000000..b79ad93b5 --- /dev/null +++ b/src/jni/Constants.cpp @@ -0,0 +1,12 @@ +/* + * Constants.cpp + * + * Created on: Nov 6, 2015 + * Author: gatanasov + */ + +#include "Constants.h" + +std::string Constants::APP_ROOT_FOLDER_PATH = ""; +bool Constants::V8_CACHE_COMPILED_CODE = false; +std::string Constants::V8_STARTUP_FLAGS = ""; diff --git a/src/jni/Constants.h b/src/jni/Constants.h index a53fdf82a..a98b6b812 100644 --- a/src/jni/Constants.h +++ b/src/jni/Constants.h @@ -1,6 +1,8 @@ #ifndef CONSTANTS_H_ #define CONSTANTS_H_ +#include + class Constants { public: @@ -12,6 +14,10 @@ class Constants static std::string APP_ROOT_FOLDER_PATH; + static std::string V8_STARTUP_FLAGS; + + static bool V8_CACHE_COMPILED_CODE; + private: Constants() {} }; diff --git a/src/jni/File.cpp b/src/jni/File.cpp index e2cea3250..fb962645b 100644 --- a/src/jni/File.cpp +++ b/src/jni/File.cpp @@ -7,11 +7,17 @@ #include "File.h" #include +#include using namespace std; namespace tns { + bool File::Exists(const string& path) + { + std::ifstream infile(path.c_str()); + return infile.good(); + } string File::ReadText(const string& filePath) { int len; diff --git a/src/jni/File.h b/src/jni/File.h index 509c2d1b2..26c8b21eb 100644 --- a/src/jni/File.h +++ b/src/jni/File.h @@ -17,6 +17,7 @@ namespace tns public: static const char* ReadText(const std::string& filePath, int& length, bool& isNew); static std::string ReadText(const std::string& filePath); + static bool Exists(const std::string& filePath); private: static const int BUFFER_SIZE = 1024 * 1024; static char* Buffer; diff --git a/src/jni/JniLocalRef.cpp b/src/jni/JniLocalRef.cpp index 6974c8701..dd708461d 100644 --- a/src/jni/JniLocalRef.cpp +++ b/src/jni/JniLocalRef.cpp @@ -1,4 +1,5 @@ #include "JniLocalRef.h" +#include "JType.h" #include using namespace v8; @@ -70,6 +71,12 @@ JniLocalRef::operator jclass() const return (jclass)m_obj; } +JniLocalRef::operator jboolean() const +{ + JEnv env; + return JType::BooleanValue(env, m_obj); +} + JniLocalRef::operator jthrowable() const { diff --git a/src/jni/JniLocalRef.h b/src/jni/JniLocalRef.h index 4f685c05b..b161539b5 100644 --- a/src/jni/JniLocalRef.h +++ b/src/jni/JniLocalRef.h @@ -25,6 +25,8 @@ namespace tns operator jobject() const; + operator jboolean() const; + operator jclass() const; operator jstring() const; diff --git a/src/jni/NativeScriptRuntime.cpp b/src/jni/NativeScriptRuntime.cpp index 5a65d8203..d2b3e66bd 100644 --- a/src/jni/NativeScriptRuntime.cpp +++ b/src/jni/NativeScriptRuntime.cpp @@ -9,7 +9,6 @@ #include "JsArgToArrayConverter.h" #include "ArgConverter.h" #include "v8-profiler.h" -#include "Constants.h" #include #include #include @@ -29,6 +28,7 @@ using namespace tns; void NativeScriptRuntime::Init(JavaVM *jvm, ObjectManager *objectManager) { NativeScriptRuntime::jvm = jvm; + Require::Init(); JEnv env; @@ -38,9 +38,6 @@ void NativeScriptRuntime::Init(JavaVM *jvm, ObjectManager *objectManager) PlatformClass = env.FindClass("com/tns/Platform"); assert(PlatformClass != nullptr); - RequireClass = env.FindClass("com/tns/Require"); - assert(RequireClass != nullptr); - RESOLVE_CLASS_METHOD_ID = env.GetStaticMethodID(PlatformClass, "resolveClass", "(Ljava/lang/String;[Ljava/lang/String;)Ljava/lang/Class;"); assert(RESOLVE_CLASS_METHOD_ID != nullptr); @@ -56,9 +53,6 @@ void NativeScriptRuntime::Init(JavaVM *jvm, ObjectManager *objectManager) APP_FAIL_METHOD_ID = env.GetStaticMethodID(PlatformClass, "appFail", "(Ljava/lang/Throwable;Ljava/lang/String;)V"); assert(APP_FAIL_METHOD_ID != nullptr); - GET_MODULE_PATH_METHOD_ID = env.GetStaticMethodID(RequireClass, "getModulePath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); - assert(GET_MODULE_PATH_METHOD_ID != nullptr); - ENABLE_VERBOSE_LOGGING_METHOD_ID = env.GetStaticMethodID(PlatformClass, "enableVerboseLogging", "()V"); assert(ENABLE_VERBOSE_LOGGING_METHOD_ID != nullptr); @@ -753,164 +747,12 @@ void NativeScriptRuntime::AppFail(jthrowable throwable, const char *message) } -void NativeScriptRuntime::AddApplicationModule(const string& appName, v8::Persistent* applicationModule) -{ - loadedModules.insert(make_pair(appName, applicationModule)); -} - void NativeScriptRuntime::CreateGlobalCastFunctions(const Local& globalTemplate) { castFunctions.CreateGlobalCastFunctions(globalTemplate); } -void NativeScriptRuntime::CompileAndRun(string modulePath, bool& hasError, Local& moduleObj) -{ - auto isolate = Isolate::GetCurrent(); - - Local exportObj = Object::New(isolate); - auto tmpExportObj = new Persistent(isolate, exportObj.As()); - loadedModules.insert(make_pair(modulePath, tmpExportObj)); - - TryCatch tc; - - auto scriptText = Require::LoadModule(modulePath); - - DEBUG_WRITE("Compiling script (module %s)", modulePath.c_str()); - auto fullRequiredModulePath = ConvertToV8String(modulePath); - auto script = Script::Compile(scriptText, fullRequiredModulePath); - DEBUG_WRITE("Compiled script (module %s)", modulePath.c_str()); - - if (ExceptionUtil::GetInstance()->HandleTryCatch(tc, "Script " + modulePath + " contains compilation errors!")) - { - hasError = true; - } - else if (script.IsEmpty()) - { - //think about more descriptive message -> [script_name] was empty - DEBUG_WRITE("%s was empty", modulePath.c_str()); - } - else - { - DEBUG_WRITE("Running script (module %s)", modulePath.c_str()); - - auto f = script->Run().As(); - if (ExceptionUtil::GetInstance()->HandleTryCatch(tc, "Error running script " + modulePath)) - { - hasError = true; - } - else - { - auto result = f->Call(Object::New(isolate), 1, &exportObj); - if(ExceptionUtil::GetInstance()->HandleTryCatch(tc, "Error calling module function ")) - { - hasError = true; - } - else - { - moduleObj = result.As(); - if (moduleObj.IsEmpty()) - { - auto objectTemplate = ObjectTemplate::New(); - moduleObj = objectTemplate->NewInstance(); - } - - DEBUG_WRITE("Script completed (module %s)", modulePath.c_str()); - - if (!moduleObj->StrictEquals(exportObj)) - { - loadedModules.erase(modulePath); - tmpExportObj->Reset(); - delete tmpExportObj; - - auto persistentModuleObject = new Persistent(isolate, moduleObj.As()); - - loadedModules.insert(make_pair(modulePath, persistentModuleObject)); - } - } - } - } - - if(hasError) - { - loadedModules.erase(modulePath); - tmpExportObj->Reset(); - delete tmpExportObj; - - // this handles recursive require calls - tc.ReThrow(); - } -} - -void NativeScriptRuntime::RequireCallback(const v8::FunctionCallbackInfo& args) -{ - SET_PROFILER_FRAME(); - - auto isolate = Isolate::GetCurrent(); - - if (args.Length() != 2) - { - isolate->ThrowException(ConvertToV8String("require should be called with two parameters")); - return; - } - if (!args[0]->IsString()) - { - isolate->ThrowException(ConvertToV8String("require's first parameter should be string")); - return; - } - if (!args[1]->IsString()) - { - isolate->ThrowException(ConvertToV8String("require's second parameter should be string")); - return; - } - - string moduleName = ConvertToString(args[0].As()); - string callingModuleDirName = ConvertToString(args[1].As()); - - JEnv env; - JniLocalRef jsModulename(env.NewStringUTF(moduleName.c_str())); - JniLocalRef jsCallingModuleDirName(env.NewStringUTF(callingModuleDirName.c_str())); - JniLocalRef jsModulePath(env.CallStaticObjectMethod(RequireClass, GET_MODULE_PATH_METHOD_ID, (jstring) jsModulename, (jstring) jsCallingModuleDirName)); - - // cache the required modules by full path, not name only, since there might be some collisions with relative paths and names - string modulePath = ArgConverter::jstringToString((jstring) jsModulePath); - if(modulePath == ""){ - // module not found - stringstream ss; - ss << "Module \"" << moduleName << "\" not found"; - string exception = ss.str(); - ExceptionUtil::GetInstance()->ThrowExceptionToJs(exception); - return; - } - if (modulePath == "EXTERNAL_FILE_ERROR") - { - // module not found - stringstream ss; - ss << "Module \"" << moduleName << "\" is located on the external storage. Modules can be private application files ONLY"; - string exception = ss.str(); - ExceptionUtil::GetInstance()->ThrowExceptionToJs(exception); - return; - } - - auto it = loadedModules.find(modulePath); - - Local moduleObj; - bool hasError = false; - - if (it == loadedModules.end()) - { - CompileAndRun(modulePath, hasError, moduleObj); - } - else - { - moduleObj = Local::New(isolate, *((*it).second)); - } - - if(!hasError){ - args.GetReturnValue().Set(moduleObj); - } -} - vector NativeScriptRuntime::GetTypeMetadata(const string& name, int index) { JEnv env; @@ -1108,23 +950,19 @@ int NativeScriptRuntime::GetArrayLength(const Local& arr) JavaVM* NativeScriptRuntime::jvm = nullptr; jclass NativeScriptRuntime::PlatformClass = nullptr; -jclass NativeScriptRuntime::RequireClass = nullptr; jclass NativeScriptRuntime::JAVA_LANG_STRING = nullptr; jmethodID NativeScriptRuntime::RESOLVE_CLASS_METHOD_ID = nullptr; jmethodID NativeScriptRuntime::CREATE_INSTANCE_METHOD_ID = nullptr; jmethodID NativeScriptRuntime::CACHE_CONSTRUCTOR_METHOD_ID = nullptr; jmethodID NativeScriptRuntime::APP_FAIL_METHOD_ID = nullptr; -jmethodID NativeScriptRuntime::GET_MODULE_PATH_METHOD_ID = nullptr; jmethodID NativeScriptRuntime::GET_TYPE_METADATA = nullptr; jmethodID NativeScriptRuntime::ENABLE_VERBOSE_LOGGING_METHOD_ID = nullptr; jmethodID NativeScriptRuntime::DISABLE_VERBOSE_LOGGING_METHOD_ID = nullptr; jmethodID NativeScriptRuntime::GET_CHANGE_IN_BYTES_OF_USED_MEMORY_METHOD_ID = nullptr; -map*> NativeScriptRuntime::loadedModules; MetadataTreeNode* NativeScriptRuntime::metadataRoot = nullptr; ObjectManager* NativeScriptRuntime::objectManager = nullptr; NumericCasts NativeScriptRuntime::castFunctions; ArrayElementAccessor NativeScriptRuntime::arrayElementAccessor; FieldAccessor NativeScriptRuntime::fieldAccessor; -string NativeScriptRuntime::APP_FILES_DIR; map NativeScriptRuntime::s_constructorCache; map NativeScriptRuntime::s_classCache; diff --git a/src/jni/NativeScriptRuntime.h b/src/jni/NativeScriptRuntime.h index a219ffe3a..ca8cb8ed0 100644 --- a/src/jni/NativeScriptRuntime.h +++ b/src/jni/NativeScriptRuntime.h @@ -70,14 +70,10 @@ namespace tns static void AppFail(jthrowable throwable, const char *message); - static void RequireCallback(const v8::FunctionCallbackInfo& args); - static void CreateGlobalCastFunctions(const v8::Local& globalTemplate); static std::vector GetTypeMetadata(const std::string& name, int index); - static void AddApplicationModule(const std::string& appName, v8::Persistent* applicationModule); - static jobjectArray GetMethodOverrides(JEnv& env, const v8::Local& implementationObject); static std::string LogExceptionStackTrace(const v8::TryCatch& tryCatch); @@ -90,20 +86,13 @@ namespace tns static void CreateTopLevelNamespaces(const v8::Local& global); - static void CompileAndRun(std::string modulePath, bool& hasError, v8::Local& moduleObj); - static v8::Local FindClass(const std::string& className); static MetadataTreeNode *metadataRoot; static jclass PlatformClass; - static jclass RequireClass; - static jclass JAVA_LANG_STRING; - - static std::string APP_FILES_DIR; - // private: @@ -125,8 +114,6 @@ namespace tns static jmethodID APP_FAIL_METHOD_ID; - static jmethodID GET_MODULE_PATH_METHOD_ID; - static jmethodID GET_TYPE_METADATA; static jmethodID ENABLE_VERBOSE_LOGGING_METHOD_ID; @@ -135,8 +122,6 @@ namespace tns static jmethodID GET_CHANGE_IN_BYTES_OF_USED_MEMORY_METHOD_ID; - static std::map*> loadedModules; - static NumericCasts castFunctions; static ArrayElementAccessor arrayElementAccessor; diff --git a/src/jni/Require.cpp b/src/jni/Require.cpp index 97d2fc682..1ada81cb5 100644 --- a/src/jni/Require.cpp +++ b/src/jni/Require.cpp @@ -8,6 +8,11 @@ #include "File.h" #include "V8GlobalHelpers.h" #include "NativeScriptAssert.h" +#include "SimpleProfiler.h" +#include "ExceptionUtil.h" +#include "JniLocalRef.h" +#include "ArgConverter.h" +#include "Constants.h" #include @@ -16,7 +21,17 @@ using namespace std; namespace tns { - Local Require::LoadModule(const string& path) + void Require::Init() + { + JEnv env; + + RequireClass = env.FindClass("com/tns/Require"); + assert(RequireClass != nullptr); + + GET_MODULE_PATH_METHOD_ID = env.GetStaticMethodID(RequireClass, "getModulePath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); + assert(GET_MODULE_PATH_METHOD_ID != nullptr); + } + Local Require::LoadScriptText(const string& path) { string content = File::ReadText(path); @@ -36,8 +51,227 @@ namespace tns return ConvertToV8String(result); } + void Require::Callback(const v8::FunctionCallbackInfo& args) + { + SET_PROFILER_FRAME(); + + auto isolate = Isolate::GetCurrent(); + if (args.Length() != 2) + { + isolate->ThrowException(ConvertToV8String("require should be called with two parameters")); + return; + } + if (!args[0]->IsString()) + { + isolate->ThrowException(ConvertToV8String("require's first parameter should be string")); + return; + } + if (!args[1]->IsString()) + { + isolate->ThrowException(ConvertToV8String("require's second parameter should be string")); + return; + } + + string moduleName = ConvertToString(args[0].As()); + string callingModuleDirName = ConvertToString(args[1].As()); + + JEnv env; + JniLocalRef jsModulename(env.NewStringUTF(moduleName.c_str())); + JniLocalRef jsCallingModuleDirName(env.NewStringUTF(callingModuleDirName.c_str())); + JniLocalRef jsModulePath(env.CallStaticObjectMethod(RequireClass, GET_MODULE_PATH_METHOD_ID, (jstring) jsModulename, (jstring) jsCallingModuleDirName)); + + // cache the required modules by full path, not name only, since there might be some collisions with relative paths and names + string modulePath = ArgConverter::jstringToString((jstring) jsModulePath); + + if(modulePath == ""){ + // module not found + stringstream ss; + ss << "Module \"" << moduleName << "\" not found"; + string exception = ss.str(); + ExceptionUtil::GetInstance()->ThrowExceptionToJs(exception); + return; + } + if (modulePath == "EXTERNAL_FILE_ERROR") + { + // module not found + stringstream ss; + ss << "Module \"" << moduleName << "\" is located on the external storage. Modules can be private application files ONLY"; + string exception = ss.str(); + ExceptionUtil::GetInstance()->ThrowExceptionToJs(exception); + return; + } + + auto it = loadedModules.find(modulePath); + + Local moduleObj; + bool hasError = false; + + if (it == loadedModules.end()) + { + moduleObj = CompileAndRun(modulePath, hasError); + } + else + { + moduleObj = Local::New(isolate, *((*it).second)); + } + + if(!hasError){ + args.GetReturnValue().Set(moduleObj); + } + } + + Local Require::CompileAndRun(const string& path, bool& hasError) + { + auto isolate = Isolate::GetCurrent(); + + Local exportObj = Object::New(isolate); + Persistent* persistentExportObj = new Persistent(isolate, exportObj.As()); + loadedModules.insert(make_pair(path, persistentExportObj)); + + TryCatch tc; + Local moduleObj; + + auto scriptText = LoadScriptText(path); + + DEBUG_WRITE("Compiling script (module %s)", path.c_str()); + auto v8Path = ConvertToV8String(path); + + auto cacheData = TryLoadScriptCache(path); + + ScriptOrigin origin(v8Path); + ScriptCompiler::Source source(scriptText, origin, cacheData); + ScriptCompiler::CompileOptions option = ScriptCompiler::kNoCompileOptions; + Local