void SvMap::add(Handle<Object> object, long ptr) { objects.insert( pair<int, SimpleObjectData*>( object->GetIdentityHash(), new SimpleObjectData(object, ptr) ) ); }
void NativeScriptRuntime::CallJavaMethod(const Handle<Object>& caller, const string& className, const string& methodName, MetadataEntry *entry, bool isStatic, bool isSuper, const v8::FunctionCallbackInfo<v8::Value>& args) { SET_PROFILER_FRAME(); JEnv env; jclass clazz; jmethodID mid; string sig; if ((entry != nullptr) && entry->isResolved) { isStatic = entry->isStatic; if (entry->memberId == nullptr) { entry->clazz = env.FindClass(className); if (entry->clazz == nullptr) { MetadataNode* callerNode = MetadataNode::GetNodeFromHandle(caller); const string callerClassName = callerNode->GetName(); DEBUG_WRITE("Cannot resolve class: %s while calling method: %s callerClassName: %s", className.c_str(), methodName.c_str(), callerClassName.c_str()); clazz = env.FindClass(callerClassName); if (clazz == nullptr) { DEBUG_WRITE("Cannot resolve caller's class name: %s", callerClassName.c_str()); return; } mid = isStatic ? env.GetStaticMethodID(clazz, methodName, entry->sig) : env.GetMethodID(clazz, methodName, entry->sig); if (mid == nullptr) { DEBUG_WRITE("Cannot resolve a method %s on caller class: %s", methodName.c_str(), callerClassName.c_str()); return; } } else { entry->memberId = isStatic ? env.GetStaticMethodID(entry->clazz, methodName, entry->sig) : env.GetMethodID(entry->clazz, methodName, entry->sig); if (entry->memberId == nullptr) { DEBUG_WRITE("Cannot resolve a method %s on class: %s", methodName.c_str(), className.c_str()); return; } } } if (entry->clazz != nullptr) { clazz = entry->clazz; mid = reinterpret_cast<jmethodID>(entry->memberId); } sig = entry->sig; } else { DEBUG_WRITE("Resolving method: %s on className %s", methodName.c_str(), className.c_str()); MethodCache::CacheMethodInfo mi; clazz = env.FindClass(className); if (clazz != nullptr) { mi = MethodCache::ResolveMethodSignature(className, methodName, args, isStatic); if (mi.mid == nullptr) { DEBUG_WRITE("Cannot resolve class=%s, method=%s, isStatic=%d, isSuper=%d", className.c_str(), methodName.c_str(), isStatic, isSuper); return; } } else { MetadataNode* callerNode = MetadataNode::GetNodeFromHandle(caller); const string callerClassName = callerNode->GetName(); DEBUG_WRITE("Resolving method on caller class: %s.%s on className %s", callerClassName.c_str(), methodName.c_str(), className.c_str()); mi = MethodCache::ResolveMethodSignature(callerClassName, methodName, args, isStatic); if (mi.mid == nullptr) { DEBUG_WRITE("Cannot resolve class=%s, method=%s, isStatic=%d, isSuper=%d, callerClass=%s", className.c_str(), methodName.c_str(), isStatic, isSuper, callerClassName.c_str()); return; } } clazz = mi.clazz; mid = mi.mid; sig = mi.signature; } if (!isStatic) { DEBUG_WRITE("CallJavaMethod called %s.%s. Instance id: %d, isSuper=%d", className.c_str(), methodName.c_str(), caller.IsEmpty() ? -42 : caller->GetIdentityHash(), isSuper); } else { DEBUG_WRITE("CallJavaMethod called %s.%s. static method", className.c_str(), methodName.c_str()); } JsArgConverter argConverter(args, false, sig); if (!argConverter.IsValid()) { JsArgConverter::Error err = argConverter.GetError(); ExceptionUtil::GetInstance()->ThrowExceptionToJs(err.msg); return; } auto isolate = Isolate::GetCurrent(); jweak callerJavaObject; jvalue* javaArgs = argConverter.ToArgs(); if (!isStatic) { callerJavaObject = objectManager->GetJavaObjectByJsObject(caller); if(callerJavaObject == nullptr) { stringstream ss; ss << "No java object found on which to call \"" << methodName << "\" method. It is possible your Javascript object is not linked with the corresponding Java class. Try passing context(this) to the constructor function."; string exceptionMessage = ss.str(); isolate->ThrowException(v8::Exception::ReferenceError(ConvertToV8String(exceptionMessage))); return; } } auto returnType = GetReturnType(sig); switch (returnType[0]) { case 'V': //void { if (isStatic) { env.CallStaticVoidMethodA(clazz, mid, javaArgs); } else if (isSuper) { env.CallNonvirtualVoidMethodA(callerJavaObject, clazz, mid, javaArgs); } else { env.CallVoidMethodA(callerJavaObject, mid, javaArgs); } break; } case 'Z': //bool { jboolean result; if (isStatic) { result = env.CallStaticBooleanMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualBooleanMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallBooleanMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set(result != 0 ? True(isolate) : False(isolate)); break; } case 'B': //byte { jbyte result; if (isStatic) { result = env.CallStaticByteMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualByteMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallByteMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set(result); break; } case 'C': //char { jchar result; if (isStatic) { result = env.CallStaticCharMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualCharMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallCharMethodA(callerJavaObject, mid, javaArgs); } JniLocalRef str(env.NewString(&result, 1)); jboolean bol = true; const char* resP = env.GetStringUTFChars(str, &bol); args.GetReturnValue().Set(ConvertToV8String(resP, 1)); env.ReleaseStringUTFChars(str, resP); break; } case 'S': //short { jshort result; if (isStatic) { result = env.CallStaticShortMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualShortMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallShortMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set(result); break; } case 'I': //int { jint result; if (isStatic) { result = env.CallStaticIntMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualIntMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallIntMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set(result); break; } case 'J': //long { jlong result; if (isStatic) { result = env.CallStaticLongMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualLongMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallLongMethodA(callerJavaObject, mid, javaArgs); } auto jsLong = ArgConverter::ConvertFromJavaLong(result); args.GetReturnValue().Set(jsLong); break; } case 'F': //float { jfloat result; if (isStatic) { result = env.CallStaticFloatMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualFloatMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallFloatMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set((double) result); //TODO: handle float value here correctly. break; } case 'D': //double { jdouble result; if (isStatic) { result = env.CallStaticDoubleMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualDoubleMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallDoubleMethodA(callerJavaObject, mid, javaArgs); } args.GetReturnValue().Set(result); break; } case '[': case 'L': { bool isString = returnType == "Ljava/lang/String;"; jobject result = nullptr; bool exceptionOccurred; if (isString) { if (isStatic) { result = env.CallStaticObjectMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualObjectMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallObjectMethodA(callerJavaObject, mid, javaArgs); } exceptionOccurred = env.ExceptionCheck() == JNI_TRUE; if (!exceptionOccurred) { auto resultV8String = ArgConverter::jstringToV8String((jstring) result); args.GetReturnValue().Set(resultV8String); } } else { if (isStatic) { result = env.CallStaticObjectMethodA(clazz, mid, javaArgs); } else if (isSuper) { result = env.CallNonvirtualObjectMethodA(callerJavaObject, clazz, mid, javaArgs); } else { result = env.CallObjectMethodA(callerJavaObject, mid, javaArgs); } exceptionOccurred = env.ExceptionCheck() == JNI_TRUE; if (!exceptionOccurred) { if (result != nullptr) { isString = env.IsInstanceOf(result, JAVA_LANG_STRING); Handle<Value> objectResult; if (isString) { objectResult = ArgConverter::jstringToV8String((jstring)result); } else { jint javaObjectID = objectManager->GetOrCreateObjectId(result); objectResult = objectManager->GetJsObjectByJavaObject(javaObjectID); if (objectResult.IsEmpty()) { objectResult = objectManager->CreateJSWrapper(javaObjectID, returnType, result); } } args.GetReturnValue().Set(objectResult); } else { args.GetReturnValue().Set(Null(isolate)); } } } if (!exceptionOccurred) { env.DeleteLocalRef(result); } break; } default: { // TODO: ASSERT_FAIL("Unknown return type"); break; } } if (!ExceptionUtil::GetInstance()->CheckForJavaException(env)) { AdjustAmountOfExternalAllocatedMemory(env, isolate); } }
Handle<Object> MetadataNode::GetImplementationObject(const Handle<Object>& object) { DEBUG_WRITE("GetImplementationObject called on object:%d", object->GetIdentityHash()); auto target = object; Handle<Value> currentPrototype = target; Handle<Object> implementationObject; implementationObject = object->GetHiddenValue(ConvertToV8String("t::implObj")).As<Object>(); if (!implementationObject.IsEmpty()) { return implementationObject; } if (object->HasOwnProperty(V8StringConstants::GetIsPrototypeImplementationObject())) { auto v8Prototype = V8StringConstants::GetPrototype(); if (!object->HasOwnProperty(v8Prototype)) { return Handle<Object>(); } DEBUG_WRITE("GetImplementationObject returning the prototype of the object :%d", object->GetIdentityHash()); return object->Get(v8Prototype).As<Object>(); } auto obj = V8GetHiddenValue(object, "t::ActivityImplementationObject").As<Object>(); if (!obj.IsEmpty()) { DEBUG_WRITE("GetImplementationObject returning ActivityImplementationObject property on object: %d", object->GetIdentityHash()); return obj; } Handle<Value> lastPrototype; bool prototypeCycleDetected = false; while (implementationObject.IsEmpty()) { // currentPrototype = currentPrototype.As<Object>()->GetPrototype(); if (currentPrototype->IsNull()) break; //DEBUG_WRITE("GetImplementationObject currentPrototypeObject:%d", (currentPrototype.IsEmpty() || currentPrototype.As<Object>().IsEmpty()) ? -1 : currentPrototype.As<Object>()->GetIdentityHash()); //DEBUG_WRITE("GetImplementationObject lastPrototypeObject:%d", (lastPrototype.IsEmpty() || lastPrototype.As<Object>().IsEmpty()) ? -1 : lastPrototype.As<Object>()->GetIdentityHash()); if (currentPrototype == lastPrototype) { auto abovePrototype = currentPrototype.As<Object>()->GetPrototype(); prototypeCycleDetected = abovePrototype == currentPrototype; } if (currentPrototype.IsEmpty() || prototypeCycleDetected) { //Handle<Value> abovePrototype = currentPrototype.As<Object>()->GetPrototype(); //DEBUG_WRITE("GetImplementationObject not found since cycle parents reached abovePrototype:%d", (abovePrototype.IsEmpty() || abovePrototype.As<Object>().IsEmpty()) ? -1 : abovePrototype.As<Object>()->GetIdentityHash()); return Handle<Object>(); } else { auto value = currentPrototype.As<Object>()->GetHiddenValue(V8StringConstants::GetClassImplementationObject()); if (!value.IsEmpty()) { implementationObject = currentPrototype.As<Object>(); } } lastPrototype = currentPrototype; } return implementationObject; }
Handle<Value> NativeScriptRuntime::CallJSMethod(JNIEnv *_env, const Handle<Object>& jsObject, const string& methodName, jobjectArray args, TryCatch& tc) { SET_PROFILER_FRAME(); JEnv env(_env); Handle<Value> result; auto isolate = Isolate::GetCurrent(); auto exceptionUtil = ExceptionUtil::GetInstance(); //auto method = MetadataNode::GetPropertyFromImplementationObject(jsObject, jsMethodName); auto method = jsObject->Get(ConvertToV8String(methodName)); if (method.IsEmpty() || method->IsUndefined()) { stringstream ss; ss << "Cannot find method '" << methodName << "' implementation"; ExceptionUtil::GetInstance()->ThrowExceptionToJs(ss.str()); result = Undefined(isolate); } else if (!method->IsFunction()) { stringstream ss; ss << "Property '" << methodName << "' is not a function"; ExceptionUtil::GetInstance()->ThrowExceptionToJs(ss.str()); result = Undefined(isolate); } else { EscapableHandleScope handleScope(isolate); auto jsMethod = method.As<Function>(); auto jsArgs = ArgConverter::ConvertJavaArgsToJsArgs(args); int argc = jsArgs->Length(); Handle<Value> arguments[argc]; for (int i = 0; i < argc; i++) { arguments[i] = jsArgs->Get(i); } DEBUG_WRITE("implementationObject->GetIdentityHash()=%d", jsObject->GetIdentityHash()); Local<Value> jsResult; { SET_PROFILER_FRAME(); jsResult = jsMethod->Call(jsObject, argc, argc == 0 ? nullptr : arguments); } //TODO: if javaResult is a pure js object create a java object that represents this object in java land if (tc.HasCaught()) { jsResult = Undefined(isolate); } result = handleScope.Escape(jsResult); } return result; }