void NativeScriptRuntime::RequireCallback(const v8::FunctionCallbackInfo<v8::Value>& args) { SET_PROFILER_FRAME(); ASSERT_MESSAGE(args.Length() == 2, "require should be called with two parameters"); ASSERT_MESSAGE(!args[0]->IsUndefined() && !args[0]->IsNull(), "require called with undefined moduleName parameter"); ASSERT_MESSAGE(!args[1]->IsUndefined() && !args[1]->IsNull(), "require called with undefined callingModulePath parameter"); ASSERT_MESSAGE(args[0]->IsString(), "require should be called with string parameter"); ASSERT_MESSAGE(args[1]->IsString(), "require should be called with string parameter"); string moduleName = ConvertToString(args[0].As<String>()); string callingModuleDirName = ConvertToString(args[1].As<String>()); 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)); auto isolate = Isolate::GetCurrent(); // 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); Handle<Object> moduleObj; bool hasError = false; if (it == loadedModules.end()) { CompileAndRun(modulePath, hasError, moduleObj, false/*is bootstrap call*/); } else { moduleObj = Local<Object>::New(isolate, *((*it).second)); } if(!hasError){ args.GetReturnValue().Set(moduleObj); } }
vector<string> NativeScriptRuntime::GetTypeMetadata(const string& name, int index) { JEnv env; string canonicalName = Util::ConvertFromJniToCanonicalName(name); JniLocalRef className(env.NewStringUTF(canonicalName.c_str())); jint idx = index; JniLocalRef pubApi(env.CallStaticObjectMethod(PlatformClass, GET_TYPE_METADATA, (jstring) className, idx)); jsize length = env.GetArrayLength(pubApi); assert(length > 0); vector<string> result; for (jsize i=0; i<length; i++) { JniLocalRef s(env.GetObjectArrayElement(pubApi, i)); const char *pc = env.GetStringUTFChars(s, nullptr); result.push_back(string(pc)); env.ReleaseStringUTFChars(s, pc); } return result; }
jclass NativeScriptRuntime::ResolveClass(const std::string& fullClassname, const Local<Object>& implementationObject) { auto itFound = s_classCache.find(fullClassname); jclass globalRefToGeneratedClass; if (itFound != s_classCache.end()) { globalRefToGeneratedClass = itFound->second; } else { JEnv env; //get needed arguments in order to load binding JniLocalRef javaFullClassName(env.NewStringUTF(fullClassname.c_str())); jobjectArray methodOverrides = GetMethodOverrides(env, implementationObject); //create or load generated binding (java class) JniLocalRef generatedClass(env.CallStaticObjectMethod(PlatformClass, RESOLVE_CLASS_METHOD_ID, (jstring)javaFullClassName, methodOverrides)); globalRefToGeneratedClass = reinterpret_cast<jclass>(env.NewGlobalRef(generatedClass)); s_classCache.insert(make_pair(fullClassname, globalRefToGeneratedClass)); } return globalRefToGeneratedClass; }
void NativeScriptRuntime::APP_FAIL(const char *message) { //ASSERT_FAIL(message); JEnv env; jstring msg = env.NewStringUTF(message); env.CallStaticVoidMethod(PlatformClass, APP_FAIL_METHOD_ID, msg); }
/* * * private method that takes debug message as json from v8 * after it gets the message the message handler passes it to enqueueMessage method in java */ void JsDebugger::MyMessageHandler(const v8::Debug::Message& message) { if (s_jsDebugger == nullptr) { return; } auto json = message.GetJSON(); auto str = ConvertToString(json); JEnv env; JniLocalRef s(env.NewStringUTF(str.c_str())); env.CallVoidMethod(s_jsDebugger, s_EnqueueMessage, (jstring) s); }
jclass NativeScriptRuntime::ResolveClass(const std::string& fullClassname, const Handle<Object>& implementationObject) { JEnv env; //get needed arguments in order to load binding JniLocalRef javaFullClassName(env.NewStringUTF(fullClassname.c_str())); jobjectArray methodOverrides = GetMethodOverrides(env, implementationObject); //create or load generated binding (java class) JniLocalRef generatedClass(env.CallStaticObjectMethod(PlatformClass, RESOLVE_CLASS_METHOD_ID, (jstring)javaFullClassName, methodOverrides)); jclass globalRefToGeneratedClass = reinterpret_cast<jclass>(env.NewGlobalRef(generatedClass)); return globalRefToGeneratedClass; }
//delete the returned local reference after use jobjectArray NativeScriptRuntime::GetMethodOverrides(JEnv& env, const Local<Object>& implementationObject) { if (implementationObject.IsEmpty()) { return JavaObjectArrayCache::GetJavaStringArray(0); } vector<jstring> methodNames; auto propNames = implementationObject->GetOwnPropertyNames(); for (int i = 0; i < propNames->Length(); i++) { auto name = propNames->Get(i).As<String>(); auto method = implementationObject->Get(name); bool methodFound = !method.IsEmpty() && method->IsFunction(); if (methodFound) { String::Utf8Value stringValue(name); jstring value = env.NewStringUTF(*stringValue); methodNames.push_back(value); } } int methodCount = methodNames.size(); jobjectArray methodOverrides = JavaObjectArrayCache::GetJavaStringArray(methodCount); for (int i = 0; i < methodCount; i++) { env.SetObjectArrayElement(methodOverrides, i, methodNames[i]); } for (int i = 0; i < methodCount; i++) { env.DeleteLocalRef(methodNames[i]); } return methodOverrides; }
Handle<Object> NativeScriptRuntime::FindClass(const string& className) { Handle<Object> clazz; JEnv env; jmethodID mid = env.GetStaticMethodID(PlatformClass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;"); assert(mid != nullptr); JniLocalRef name(env.NewStringUTF(className.c_str())); jclass c = (jclass)env.CallStaticObjectMethod(PlatformClass, mid, (jstring)name); if (env.ExceptionCheck() == JNI_FALSE) { jint javaObjectID = objectManager->GetOrCreateObjectId(c); clazz = objectManager->GetJsObjectByJavaObject(javaObjectID); if (clazz.IsEmpty()) { clazz = objectManager->CreateJSWrapper(javaObjectID, "Ljava/lang/Class;", c); } env.DeleteLocalRef(c); } return clazz; }
void FieldAccessor::SetJavaField(Isolate *isolate, const Local<Object>& target, const Local<Value>& value, FieldCallbackData *fieldData) { JEnv env; HandleScope handleScope(isolate); auto runtime = Runtime::GetRuntime(isolate); auto objectManager = runtime->GetObjectManager(); JniLocalRef targetJavaObject; const auto& fieldTypeName = fieldData->signature; auto isStatic = fieldData->isStatic; auto isPrimitiveType = fieldTypeName.size() == 1; auto isFieldArray = fieldTypeName[0] == '['; if (fieldData->fid == nullptr) { auto fieldJniSig = isPrimitiveType ? fieldTypeName : (isFieldArray ? fieldTypeName : ("L" + fieldTypeName + ";")); if (isStatic) { fieldData->clazz = env.FindClass(fieldData->declaringType); assert(fieldData->clazz != nullptr); fieldData->fid = env.GetStaticFieldID(fieldData->clazz, fieldData->name, fieldJniSig); assert(fieldData->fid != nullptr); } else { fieldData->clazz = env.FindClass(fieldData->declaringType); assert(fieldData->clazz != nullptr); fieldData->fid = env.GetFieldID(fieldData->clazz, fieldData->name, fieldJniSig); assert(fieldData->fid != nullptr); } } if (!isStatic) { targetJavaObject = objectManager->GetJavaObjectByJsObject(target); if (targetJavaObject.IsNull()) { stringstream ss; ss << "Cannot access property '" << fieldData->name << "' because there is no corresponding Java object"; throw NativeScriptException(ss.str()); } } auto fieldId = fieldData->fid; auto clazz = fieldData->clazz; if (isPrimitiveType) { switch (fieldTypeName[0]) { case 'Z': //bool { //TODO: validate value is a boolean before calling if (isStatic) { env.SetStaticBooleanField(clazz, fieldId, value->BooleanValue()); } else { env.SetBooleanField(targetJavaObject, fieldId, value->BooleanValue()); } break; } case 'B': //byte { //TODO: validate value is a byte before calling if (isStatic) { env.SetStaticByteField(clazz, fieldId, value->Int32Value()); } else { env.SetByteField(targetJavaObject, fieldId, value->Int32Value()); } break; } case 'C': //char { //TODO: validate value is a single char String::Utf8Value stringValue(value->ToString()); JniLocalRef value(env.NewStringUTF(*stringValue)); const char* chars = env.GetStringUTFChars(value, 0); if (isStatic) { env.SetStaticCharField(clazz, fieldId, chars[0]); } else { env.SetCharField(targetJavaObject, fieldId, chars[0]); } env.ReleaseStringUTFChars(value, chars); break; } case 'S': //short { //TODO: validate value is a short before calling if (isStatic) { env.SetStaticShortField(clazz, fieldId, value->Int32Value()); } else { env.SetShortField(targetJavaObject, fieldId, value->Int32Value()); } break; } case 'I': //int { //TODO: validate value is a int before calling if (isStatic) { env.SetStaticIntField(clazz, fieldId, value->Int32Value()); } else { env.SetIntField(targetJavaObject, fieldId, value->Int32Value()); } break; } case 'J': //long { jlong longValue = static_cast<jlong>(ArgConverter::ConvertToJavaLong(value)); if (isStatic) { env.SetStaticLongField(clazz, fieldId, longValue); } else { env.SetLongField(targetJavaObject, fieldId, longValue); } break; } case 'F': //float { if (isStatic) { env.SetStaticFloatField(clazz, fieldId, static_cast<jfloat>(value->NumberValue())); } else { env.SetFloatField(targetJavaObject, fieldId, static_cast<jfloat>(value->NumberValue())); } break; } case 'D': //double { if (isStatic) { env.SetStaticDoubleField(clazz, fieldId, value->NumberValue()); } else { env.SetDoubleField(targetJavaObject, fieldId, value->NumberValue()); } break; } default: { stringstream ss; ss << "(InternalError): in FieldAccessor::SetJavaField: Unknown field type: '" << fieldTypeName[0] << "'"; throw NativeScriptException(ss.str()); } } } else { bool isString = fieldTypeName == "java/lang/String"; JniLocalRef result; if (!value->IsNull()) { if (isString) { //TODO: validate valie is a string; result = ConvertToJavaString(value); } else { auto objectWithHiddenID = value->ToObject(); result = objectManager->GetJavaObjectByJsObject(objectWithHiddenID); } } if (isStatic) { env.SetStaticObjectField(clazz, fieldId, result); } else { env.SetObjectField(targetJavaObject, fieldId, result); } } }
void FieldAccessor::SetJavaField(const Local<Object>& target, const Local<Value>& value, FieldCallbackData *fieldData) { JEnv env; auto isolate = Isolate::GetCurrent(); HandleScope handleScope(isolate); jweak targetJavaObject; const auto& fieldTypeName = fieldData->signature; auto isStatic = fieldData->isStatic; auto isPrimitiveType = fieldTypeName.size() == 1; auto isFieldArray = fieldTypeName[0] == '['; if (fieldData->fid == nullptr) { auto fieldJniSig = isPrimitiveType ? fieldTypeName : (isFieldArray ? fieldTypeName : ("L" + fieldTypeName + ";")); if (isStatic) { fieldData->clazz = env.FindClass(fieldData->declaringType); assert(fieldData->clazz != nullptr); fieldData->fid = env.GetStaticFieldID(fieldData->clazz, fieldData->name, fieldJniSig); assert(fieldData->fid != nullptr); } else { fieldData->clazz = env.FindClass(fieldData->declaringType); assert(fieldData->clazz != nullptr); fieldData->fid = env.GetFieldID(fieldData->clazz, fieldData->name, fieldJniSig); assert(fieldData->fid != nullptr); } } if (!isStatic) { targetJavaObject = objectManager->GetJavaObjectByJsObject(target); } auto fieldId = fieldData->fid; auto clazz = fieldData->clazz; if (isPrimitiveType) { switch (fieldTypeName[0]) { case 'Z': //bool { //TODO: validate value is a boolean before calling if (isStatic) { env.SetStaticBooleanField(clazz, fieldId, value->BooleanValue()); } else { env.SetBooleanField(targetJavaObject, fieldId, value->BooleanValue()); } break; } case 'B': //byte { //TODO: validate value is a byte before calling if (isStatic) { env.SetStaticByteField(clazz, fieldId, value->Int32Value()); } else { env.SetByteField(targetJavaObject, fieldId, value->Int32Value()); } break; } case 'C': //char { //TODO: validate value is a single char String::Utf8Value stringValue(value->ToString()); JniLocalRef value(env.NewStringUTF(*stringValue)); const char* chars = env.GetStringUTFChars(value, 0); if (isStatic) { env.SetStaticCharField(clazz, fieldId, chars[0]); } else { env.SetCharField(targetJavaObject, fieldId, chars[0]); } env.ReleaseStringUTFChars(value, chars); break; } case 'S': //short { //TODO: validate value is a short before calling if (isStatic) { env.SetStaticShortField(clazz, fieldId, value->Int32Value()); } else { env.SetShortField(targetJavaObject, fieldId, value->Int32Value()); } break; } case 'I': //int { //TODO: validate value is a int before calling if (isStatic) { env.SetStaticIntField(clazz, fieldId, value->Int32Value()); } else { env.SetIntField(targetJavaObject, fieldId, value->Int32Value()); } break; } case 'J': //long { jlong longValue = static_cast<jlong>(ArgConverter::ConvertToJavaLong(value)); if (isStatic) { env.SetStaticLongField(clazz, fieldId, longValue); } else { env.SetLongField(targetJavaObject, fieldId, longValue); } break; } case 'F': //float { if (isStatic) { env.SetStaticFloatField(clazz, fieldId, static_cast<jfloat>(value->NumberValue())); } else { env.SetFloatField(targetJavaObject, fieldId, static_cast<jfloat>(value->NumberValue())); } break; } case 'D': //double { if (isStatic) { env.SetStaticDoubleField(clazz, fieldId, value->NumberValue()); } else { env.SetDoubleField(targetJavaObject, fieldId, value->NumberValue()); } break; } default: { // TODO: ASSERT_FAIL("Unknown field type"); break; } } } else { bool isString = fieldTypeName == "java/lang/String"; jobject result = nullptr; if(!value->IsNull()) { if (isString) { //TODO: validate valie is a string; result = ConvertToJavaString(value); } else { auto objectWithHiddenID = value->ToObject(); result =objectManager->GetJavaObjectByJsObject(objectWithHiddenID); } } if (isStatic) { env.SetStaticObjectField(clazz, fieldId, result); } else { env.SetObjectField(targetJavaObject, fieldId, result); } if (isString) { env.DeleteLocalRef(result); } } }
void JsDebugger::ConsoleMessage(const v8::FunctionCallbackInfo<v8::Value>& args) { if ((args.Length() > 0) && args[0]->IsString()) { std::string message = ConvertToString(args[0]->ToString()); //jboolean isError = (jboolean) = args[1]->ToBoolean()->BooleanValue(); std:string level = "log"; if (args.Length() > 1 && args[1]->IsString()) { level = ConvertToString(args[1]->ToString()); } string srcFileName = ""; int lineNumber = 0; int columnNumber = 0; auto stackTrace = StackTrace::CurrentStackTrace(Isolate::GetCurrent(), 2, StackTrace::kOverview); if (!stackTrace.IsEmpty()) { auto frame = stackTrace->GetFrame(1); if (!frame.IsEmpty()) { auto scriptName = frame->GetScriptName(); if (!scriptName.IsEmpty()) { srcFileName = ConvertToString(scriptName); } lineNumber = frame->GetLineNumber(); columnNumber = frame->GetColumn(); } } // var consoleEvent = { // "seq":0, // "type":"event", // "event":"messageAdded", // "success":true, // "body": // { // "message": // { // "source":"console-api", // "type": "log", // "level": 'error', // "line": 0, // "column": 0, // "url": "", // "groupLevel": 7, // "repeatCount": 1, // "text": "My message" // } // } // }; stringstream consoleEventSS; consoleEventSS << "{\"seq\":0, \"type\":\"event\", \"event\":\"messageAdded\", \"success\":true, \"body\": { \"message\": { \"source\":\"console-api\", " << " \"type\": \"log\"," << " \"level\": \"" << level << "\", " << " \"line\": " << lineNumber << "," << " \"column\": " << columnNumber << "," << " \"url\" : \"" << srcFileName << "\"," << " \"groupLevel\": 7, \"repeatCount\": 1, " << " \"text\": \"" << message << "\" } } }"; JEnv env; JniLocalRef s(env.NewStringUTF(consoleEventSS.str().c_str())); env.CallVoidMethod(s_jsDebugger, s_EnqueueMessage, (jstring) s); } }
void Module::RequireCallback(const v8::FunctionCallbackInfo<v8::Value>& args) { try { auto isolate = Isolate::GetCurrent(); if (args.Length() != 2) { throw NativeScriptException(string("require should be called with two parameters")); } if (!args[0]->IsString()) { throw NativeScriptException(string("require's first parameter should be string")); } if (!args[1]->IsString()) { throw NativeScriptException(string("require's second parameter should be string")); } string moduleName = ConvertToString(args[0].As<String>()); string callingModuleDirName = ConvertToString(args[1].As<String>()); JEnv env; JniLocalRef jsModulename(env.NewStringUTF(moduleName.c_str())); JniLocalRef jsCallingModuleDirName(env.NewStringUTF(callingModuleDirName.c_str())); JniLocalRef jsModulePath(env.CallStaticObjectMethod(MODULE_CLASS, RESOLVE_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); auto isData = false; auto moduleObj = Load(modulePath, isData); if (isData) { assert(!moduleObj.IsEmpty()); args.GetReturnValue().Set(moduleObj); } else { auto exportsObj = moduleObj->Get(ConvertToV8String("exports")); assert(!exportsObj.IsEmpty()); args.GetReturnValue().Set(exportsObj); } } catch (NativeScriptException& e) { e.ReThrowToV8(); } catch (std::exception e) { stringstream ss; ss << "Error: c++ exception: " << e.what() << endl; NativeScriptException nsEx(ss.str()); nsEx.ReThrowToV8(); } catch (...) { NativeScriptException nsEx(std::string("Error: c++ exception!")); nsEx.ReThrowToV8(); } }