diff --git a/src/jni/Constants.h b/src/jni/Constants.h index c970e4062..a53fdf82a 100644 --- a/src/jni/Constants.h +++ b/src/jni/Constants.h @@ -10,6 +10,8 @@ class Constants const static char CLASS_NAME_LOCATION_SEPARATOR = '_'; + static std::string APP_ROOT_FOLDER_PATH; + private: Constants() {} }; diff --git a/src/jni/MetadataNode.cpp b/src/jni/MetadataNode.cpp index 8fd7f7419..90235f8e3 100644 --- a/src/jni/MetadataNode.cpp +++ b/src/jni/MetadataNode.cpp @@ -1201,12 +1201,19 @@ bool MetadataNode::GetExtendLocation(string& extendLocation) } string srcFileName = ConvertToString(scriptName); - std::replace(srcFileName.begin(), srcFileName.end(), '/', '_'); - std::replace(srcFileName.begin(), srcFileName.end(), '.', '_'); + + string hardcodedPathToSkip = Constants::APP_ROOT_FOLDER_PATH; + + int startIndex = hardcodedPathToSkip.length(); + int strToTakeLen = (srcFileName.length() - startIndex - 3); // 3 refers to .js at the end of file name + string fullPathToFile = srcFileName.substr(startIndex, strToTakeLen); + + std::replace(fullPathToFile.begin(), fullPathToFile.end(), '/', '_'); + std::replace(fullPathToFile.begin(), fullPathToFile.end(), '.', '_'); int lineNumber = frame->GetLineNumber(); if (lineNumber < 0) { - extendLocationStream << srcFileName.c_str() << " unkown line number"; + extendLocationStream << fullPathToFile.c_str() << " unkown line number"; extendLocation = extendLocationStream.str(); return false; } @@ -1219,13 +1226,13 @@ bool MetadataNode::GetExtendLocation(string& extendLocation) int column = frame->GetColumn(); if (column < 0) { - extendLocationStream << srcFileName.c_str() << " line:" << lineNumber << " unkown column number"; + extendLocationStream << fullPathToFile.c_str() << " line:" << lineNumber << " unkown column number"; extendLocation = extendLocationStream.str(); return false; } - extendLocationStream << "f" << srcFileName.c_str() << "_l" << lineNumber << "_c" << column << "__"; + extendLocationStream << "f" << fullPathToFile.c_str() << "_l" << lineNumber << "_c" << column << "__"; //DEBUG_WRITE("EXTEND_LOCATION %s", extendLocationStream.str().c_str()); } } diff --git a/src/jni/NativeScriptRuntime.cpp b/src/jni/NativeScriptRuntime.cpp index 9b39ef75d..c21b5a32b 100644 --- a/src/jni/NativeScriptRuntime.cpp +++ b/src/jni/NativeScriptRuntime.cpp @@ -26,7 +26,6 @@ using namespace v8; using namespace std; using namespace tns; -// init void NativeScriptRuntime::Init(JavaVM *jvm, ObjectManager *objectManager) { NativeScriptRuntime::jvm = jvm; @@ -735,6 +734,103 @@ void NativeScriptRuntime::CreateGlobalCastFunctions(const Handle } +void NativeScriptRuntime::CompileAndRun(string modulePath, bool& hasError, Handle& moduleObj, bool isBootstrapCall) +{ + auto isolate = Isolate::GetCurrent(); + + Local < Value > 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()); + Local < String > 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!")) + { + loadedModules.erase(modulePath); + tmpExportObj->Reset(); + delete tmpExportObj; + 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()); + + TryCatch tcRequire; + + Local < Function > f = script->Run().As(); + if (ExceptionUtil::GetInstance()->HandleTryCatch(tc)) + { + DEBUG_WRITE("Exception was handled in java code"); + } + + //this is done so the initial bootstrap function is persistent (and to keep old logic) + if(isBootstrapCall) { + auto persistentAppModule = new Persistent(isolate, f); + } + + auto result = f->Call(Object::New(isolate), 1, &exportObj); + if(ExceptionUtil::GetInstance()->HandleTryCatch(tc)) + { + DEBUG_WRITE("Exception was handled in java code"); + } + else + { + moduleObj = result.As(); + } + + // introducing isBootstrapCall in order to save the flow as it was (latter on we can think about including the following code for the bootstrap (initial) call + if(!isBootstrapCall) { + DEBUG_WRITE("After Running script (module %s)", modulePath.c_str()); + + if (ExceptionUtil::GetInstance()->HandleTryCatch(tcRequire)) + { + loadedModules.erase(modulePath); + tmpExportObj->Reset(); + delete tmpExportObj; + hasError = true; + tcRequire.ReThrow(); + } + else + { + 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 (tc.HasCaught()) + { + tc.ReThrow(); + } +} + void NativeScriptRuntime::RequireCallback(const v8::FunctionCallbackInfo& args) { SET_PROFILER_FRAME(); @@ -782,67 +878,7 @@ void NativeScriptRuntime::RequireCallback(const v8::FunctionCallbackInfo 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)", moduleName.c_str()); - auto script = Script::Compile(scriptText, args[0].As()); - DEBUG_WRITE("Compiled script (module %s)", moduleName.c_str()); - - if(ExceptionUtil::GetInstance()->HandleTryCatch(tc)){ - loadedModules.erase(modulePath); - tmpExportObj->Reset(); - delete tmpExportObj; - hasError = true; - } - else { - DEBUG_WRITE("Running script (module %s)", moduleName.c_str()); - - TryCatch tcRequire; - - Local f = script->Run().As(); - auto result = f->Call(Object::New(isolate), 1, &exportObj); - - moduleObj = result.As(); - - DEBUG_WRITE("After Running script (module %s)", moduleName.c_str()); - - if(ExceptionUtil::GetInstance()->HandleTryCatch(tcRequire)){ - loadedModules.erase(modulePath); - tmpExportObj->Reset(); - delete tmpExportObj; - hasError = true; - tcRequire.ReThrow(); - } - else { - if (moduleObj.IsEmpty()) - { - auto objectTemplate = ObjectTemplate::New(); - moduleObj = objectTemplate->NewInstance(); - } - - DEBUG_WRITE("Script completed (module %s)", moduleName.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 (tc.HasCaught()) - { - tc.ReThrow(); - } + CompileAndRun(modulePath, hasError, moduleObj, false/*is bootstrap call*/); } else { diff --git a/src/jni/NativeScriptRuntime.h b/src/jni/NativeScriptRuntime.h index dead5be52..6a7bb9c25 100644 --- a/src/jni/NativeScriptRuntime.h +++ b/src/jni/NativeScriptRuntime.h @@ -88,6 +88,8 @@ namespace tns static void CreateTopLevelNamespaces(const v8::Handle& global); + static void CompileAndRun(std::string modulePath, bool& hasError, v8::Handle& moduleObj, bool isBootstrapCall); + static MetadataTreeNode *metadataRoot; static jclass PlatformClass; diff --git a/src/jni/com_tns_Platform.cpp b/src/jni/com_tns_Platform.cpp index 920facbc7..d10d128d6 100644 --- a/src/jni/com_tns_Platform.cpp +++ b/src/jni/com_tns_Platform.cpp @@ -38,6 +38,7 @@ int count = 0; Context::Scope *context_scope = nullptr; bool tns::LogEnabled = true; Isolate *g_isolate = nullptr; +std::string Constants::APP_ROOT_FOLDER_PATH = ""; ObjectManager *g_objectManager = nullptr; @@ -177,78 +178,29 @@ extern "C" void Java_com_tns_Platform_initNativeScript(JNIEnv *_env, jobject obj PrepareV8Runtime(isolate, env, filesPath, packageName); NativeScriptRuntime::APP_FILES_DIR = ArgConverter::jstringToString(filesPath); + Constants::APP_ROOT_FOLDER_PATH = NativeScriptRuntime::APP_FILES_DIR + "/app/"; } -extern "C" void Java_com_tns_Platform_runNativeScript(JNIEnv *_env, jobject obj, jstring appModuleName, jstring appCode) +extern "C" void Java_com_tns_Platform_runNativeScript(JNIEnv *_env, jobject obj, jstring appModuleName) { JEnv env(_env); - auto isolate = g_isolate; Isolate::Scope isolate_scope(isolate); - TryCatch tc; - HandleScope handleScope(isolate); - auto context = Local::New(isolate, *PrimaryContext); - - jstring retval; - jboolean isCopy; - - const char* code = env.GetStringUTFChars(appCode, &isCopy); - - auto cmd = ConvertToV8String(code); - - env.ReleaseStringUTFChars(appCode, code); - - DEBUG_WRITE("Compiling script"); - auto moduleName = ArgConverter::jstringToV8String(appModuleName); + Handle moduleObject; + string modulePath = ArgConverter::jstringToString(appModuleName); + bool hasError = false; - auto script = Script::Compile(cmd, moduleName); - - DEBUG_WRITE("Compile script"); - - if (ExceptionUtil::GetInstance()->HandleTryCatch(tc, "Bootstrap script has error(s).")) - { - DEBUG_WRITE("Exception was handled in java code"); - } - else if (script.IsEmpty()) - { - DEBUG_WRITE("Bootstrap was empty"); - } - else - { - DEBUG_WRITE("Running script"); - - auto appModuleObj = script->Run(); - if (ExceptionUtil::GetInstance()->HandleTryCatch(tc)) - { - DEBUG_WRITE("Exception was handled in java code"); - } - else if (!appModuleObj.IsEmpty() && appModuleObj->IsFunction()) - { - auto moduleFunc = appModuleObj.As(); - Handle exportsObj = Object::New(isolate); - auto thiz = Object::New(isolate); - auto res = moduleFunc->Call(thiz, 1, &exportsObj); - - if(ExceptionUtil::GetInstance()->HandleTryCatch(tc)) - { - DEBUG_WRITE("Exception was handled in java code"); - } - else - { - // TODO: Why do we need this line? - auto persistentAppModuleObject = new Persistent(Isolate::GetCurrent(), appModuleObj.As()); - } - } - else - { - ExceptionUtil::GetInstance()->ThrowExceptionToJava(tc, "Error running NativeScript bootstrap code."); - } - } + NativeScriptRuntime::CompileAndRun(modulePath, hasError, moduleObject, true /*is bootstrap call*/); - //NativeScriptRuntime::loadedModules.insert(make_pair(appModuleName, persistentAppModuleObject)); + /* + * moduleObject (export module) can be set to js variable but currently we start the script implicitly without returning the moduleObject (just calling it) + * + * we can do something like + * var appModule = require("app"); //but currently we call the appModule only once Platform.run() and no one else has access to it + */ //DEBUG_WRITE("Forcing V8 garbage collection"); //while (!V8::IdleNotification()); diff --git a/src/src/com/tns/DexFactory.java b/src/src/com/tns/DexFactory.java index 1494f2a55..4c39fcb89 100644 --- a/src/src/com/tns/DexFactory.java +++ b/src/src/com/tns/DexFactory.java @@ -81,9 +81,9 @@ public Class resolveClass(String name, String className, String[] methodOverr // try to get pre-generated binding classes ClassLoader cl = context.getClassLoader(); - try - { - Class pregeneratedClass = cl.loadClass(fullClassName); + try { + Class pregeneratedClass = cl.loadClass(fullClassName.replace("-", "_")); + return pregeneratedClass; } catch (Exception e) @@ -100,7 +100,8 @@ public Class resolveClass(String name, String className, String[] methodOverr String classToProxy = this.getClassToProxyName(className); String dexFilePath = classToProxy + CLASS_NAME_LOCATION_SEPARATOR + name; File dexFile = this.getDexFile(dexFilePath); - + + //generate dex file if (dexFile == null) { long startGenTime = System.nanoTime(); @@ -120,24 +121,27 @@ public Class resolveClass(String name, String className, String[] methodOverr } } + //creates jar file from already generated dex file String jarFilePath = dexFile.getPath().replace(".dex", ".jar"); File jarFile = new File(jarFilePath); if (!jarFile.exists()) { FileOutputStream jarFileStream = new FileOutputStream(jarFile); - ZipOutputStream out = new ZipOutputStream(jarFileStream); - out.putNextEntry(new ZipEntry("classes.dex")); - byte[] dexData = new byte[(int) dexFile.length()]; - FileInputStream fi = new FileInputStream(dexFile); - fi.read(dexData, 0, dexData.length); - fi.close(); - out.write(dexData); - out.closeEntry(); - out.close(); + + out.putNextEntry(new ZipEntry("classes.dex")); + byte[] dexData = new byte[(int)dexFile.length()]; + FileInputStream fi = new FileInputStream(dexFile); + fi.read(dexData, 0, dexData.length); + fi.close(); + + out.write(dexData); + out.closeEntry(); + out.close(); } - + // + Class result = null; DexFile df = null; try @@ -406,4 +410,4 @@ private String getCachedProxyThumb(File proxyDir) return null; } -} \ No newline at end of file +} diff --git a/src/src/com/tns/Platform.java b/src/src/com/tns/Platform.java index 86115cd52..705018616 100644 --- a/src/src/com/tns/Platform.java +++ b/src/src/com/tns/Platform.java @@ -30,7 +30,7 @@ public class Platform { private static native void initNativeScript(String filesPath, int appJavaObjectId, boolean verboseLoggingEnabled, String packageName); - private static native void runNativeScript(String appModuleName, String appContent); + private static native void runNativeScript(String appModuleName); private static native Object callJSMethodNative(int javaObjectID, String methodName, boolean isConstructor, Object... packagedArgs) throws NativeScriptException; @@ -181,8 +181,8 @@ static void setExtractPolicy(ExtractPolicy policy) public static void run() { - String[] bootstrapInfo = Require.bootstrapApp(); - runNativeScript(bootstrapInfo[0], bootstrapInfo[1]); + String bootstrapPath = Require.bootstrapApp(); + runNativeScript(bootstrapPath); } private static Class resolveClass(String fullClassName, String[] methodOverrides) throws ClassNotFoundException, IOException{ diff --git a/src/src/com/tns/Require.java b/src/src/com/tns/Require.java index dcaee81c1..9b2051481 100644 --- a/src/src/com/tns/Require.java +++ b/src/src/com/tns/Require.java @@ -17,11 +17,6 @@ public class Require private static String ModulesFilesPath; private static String NativeScriptModulesFilesPath; private static boolean initialized = false; - private static final String ModuleContent_Part1 = "(function(){\n var module = {}; module.exports = arguments[0];" + "var exports = module.exports; var __dirname = \""; - private static final String ModuleContent_Part2 = "\"; var __filename = \""; - private static final String ModuleContent_Part3 = "\";" + "function require(moduleName){ return __global.require(moduleName, __dirname); }" + "module.filename = __filename; this.__extends = __global.__extends; \n"; - private static final String ModuleContent_Part4 = "\n return module.exports; \n})"; - private static final StringBuffer ModuleContent = new StringBuffer(65536); // cache the already resolved absolute paths to folder modules to prevent JSON parsing each time private static final HashMap folderAsModuleCache = new HashMap(); @@ -58,7 +53,7 @@ public static String getApplicationFilesPath() return ApplicationFilesPath; } - public static String[] bootstrapApp() + public static String bootstrapApp() { // Bootstrap logic flows like: // 1. Check for package.json -> `main` field @@ -76,56 +71,9 @@ public static String[] bootstrapApp() Platform.APP_FAIL("Application entry point file not found. Please specify either package.json with main field, index.js or bootstrap.js!"); } - String[] bootstrapInfo = new String[2]; - bootstrapInfo[0] = bootstrapFile.getAbsolutePath(); - bootstrapInfo[1] = getModuleContent(bootstrapFile.getPath()); + String modulePath = bootstrapFile.getAbsolutePath(); - return bootstrapInfo; - } - - public static String getModuleContent(String modulePath) - { - File file = new File(modulePath); - if (file == null || !file.exists()) - { - // Return empty content. - // This case will be handled by the NativeScriptRuntime.cpp and a JS - // exception will be raised. - return ""; - } - - if (Platform.IsLogEnabled) - { - Log.d(Platform.DEFAULT_LOG_TAG, "Loading module from files " + file.getPath()); - } - - try - { - String moduleFileContent = FileSystem.readText(file); - // IMPORTANT: Update MODULE_LINES_OFFSET in NativeScript.h - // if you change the number of new lines that exists before the - // moduleFileContent for correct error reporting. - // We are inserting local require function in the scope of the - // module to pass the __fileName (calling file) variable in the - // global.require request. - ModuleContent.setLength(0); - ModuleContent.append(ModuleContent_Part1); - ModuleContent.append(file.getParent() + File.separator); - ModuleContent.append(ModuleContent_Part2); - ModuleContent.append(modulePath); - ModuleContent.append(ModuleContent_Part3); - ModuleContent.append(moduleFileContent); - ModuleContent.append(ModuleContent_Part4); - String content = ModuleContent.toString(); - return content; - } - catch (IOException e) - { - // Return empty content. - // This case will be handled by the NativeScriptRuntime.cpp and a JS - // exception will be raised. - return ""; - } + return modulePath; } public static String getModulePath(String moduleName, String callingDirName)