void ObjectManager::Link(const Local<Object>& object, uint32_t javaObjectID, jclass clazz) { auto isolate = Isolate::GetCurrent(); DEBUG_WRITE("Linking js object: %d and java instance id: %d", object->GetIdentityHash(), javaObjectID); JEnv env; auto jsInstanceInfo = new JSInstanceInfo(); jsInstanceInfo->JavaObjectID = javaObjectID; jsInstanceInfo->clazz = clazz; auto objectHandle = new Persistent<Object>(isolate, object); auto state = new ObjectWeakCallbackState(this, jsInstanceInfo, objectHandle); objectHandle->SetWeak(state, JSObjectWeakCallbackStatic); auto jsInfoIdx = static_cast<int>(MetadataNodeKeys::JsInfo); bool alreadyLinked = !object->GetInternalField(jsInfoIdx)->IsUndefined(); //ASSERT_MESSAGE(alreadyLinked, "object should not have been linked before"); auto jsInfo = External::New(isolate, jsInstanceInfo); object->SetInternalField(jsInfoIdx, jsInfo); idToObject.insert(make_pair(javaObjectID, objectHandle)); }
void ObjectManager::Link(const Local<Object>& object, uint32_t javaObjectID, jclass clazz) { int internalFieldCound = NativeScriptExtension::GetInternalFieldCount(object); const int count = static_cast<int>(MetadataNodeKeys::END); if (internalFieldCound != count) { string errMsg("Trying to link invalid 'this' to a Java object"); throw NativeScriptException(errMsg); } auto isolate = Isolate::GetCurrent(); DEBUG_WRITE("Linking js object: %d and java instance id: %d", object->GetIdentityHash(), javaObjectID); auto jsInstanceInfo = new JSInstanceInfo(); jsInstanceInfo->JavaObjectID = javaObjectID; jsInstanceInfo->clazz = clazz; auto objectHandle = new Persistent<Object>(isolate, object); auto state = new ObjectWeakCallbackState(this, jsInstanceInfo, objectHandle); objectHandle->SetWeak(state, JSObjectWeakCallbackStatic); auto jsInfoIdx = static_cast<int>(MetadataNodeKeys::JsInfo); bool alreadyLinked = !object->GetInternalField(jsInfoIdx)->IsUndefined(); //TODO: fail if alreadyLinked is true? auto jsInfo = External::New(isolate, jsInstanceInfo); object->SetInternalField(jsInfoIdx, jsInfo); idToObject.insert(make_pair(javaObjectID, objectHandle)); }
void NativePlatform::CreateJSInstanceNative(JNIEnv *_env, jobject obj, jobject javaObject, jint javaObjectID, jstring className) { SET_PROFILER_FRAME(); DEBUG_WRITE("createJSInstanceNative called"); auto isolate = g_isolate; Isolate::Scope isolate_scope(isolate); JEnv env(_env); HandleScope handleScope(isolate); // TODO: Do we need a TryCatch here? It is currently not used anywhere TryCatch tc; string existingClassName = ArgConverter::jstringToString(className); string jniName = Util::ConvertFromCanonicalToJniName(existingClassName); Local<Object> jsInstance; Local<Object> implementationObject; auto proxyClassName = g_objectManager->GetClassName(javaObject); // if (proxyClassName == "com/tns/NativeScriptActivity") { return; } // DEBUG_WRITE("createJSInstanceNative class %s", proxyClassName.c_str()); jsInstance = MetadataNode::CreateExtendedJSWrapper(isolate, proxyClassName); if (jsInstance.IsEmpty()) { throw NativeScriptException(string("NativeScript application not initialized correctly. Cannot create extended JS wrapper.")); } implementationObject = MetadataNode::GetImplementationObject(jsInstance); if (implementationObject.IsEmpty()) { string msg("createJSInstanceNative: implementationObject is empty"); throw NativeScriptException(msg); } DEBUG_WRITE("createJSInstanceNative: implementationObject :%d", implementationObject->GetIdentityHash()); jclass clazz = env.FindClass(jniName); g_objectManager->Link(jsInstance, javaObjectID, clazz); }
void JSZAttribute::jsRequestValue(const v8::FunctionCallbackInfo<v8::Value> &info) { Isolate *isolate = info.GetIsolate(); try { Local<Object> self = info.Holder(); Local<External> wrap = Local<External>::Cast(self->GetInternalField(0)); auto attribute = (ZCLAttribute *) wrap->Value(); if (info.Length() == 1) { if (!info[0]->IsFunction()) { throw JSExceptionArgNoFunction(0); } Local<External> wrap = Local<External>::Cast(self->GetInternalField(1)); JSZAttribute *This = (JSZAttribute *) wrap->Value(); v8::Persistent<v8::Value, v8::CopyablePersistentTraits<v8::Value>> persistenteObject; persistenteObject.Reset(isolate, info[0]); Local<Function> callback = Local<Function>::Cast(info[0]); int identity = callback->GetIdentityHash(); auto cluster = attribute->getClusterParent(); JsCallbackParameters callbackParameters; if (cluster != nullptr) { callbackParameters.nwkAddr = cluster->getNetworkAddress(); callbackParameters.endpointID = cluster->getEndpoint(); callbackParameters.clusterID = cluster->getId(); } callbackParameters.attributeId = attribute->getIdentifier(); auto con = attribute->onChange([This, isolate, identity, callbackParameters]() { This->changeSignalCallback(isolate, identity, callbackParameters); }); std::lock_guard<std::mutex> lock(This->mapFunctionMutex); This->mapFunction.insert({identity, std::make_tuple(con, attribute, persistenteObject)}); } attribute->requestValue(); } catch (std::exception &excp) { v8::Local<v8::String> errorMsg = v8::String::NewFromUtf8(isolate, excp.what()); isolate->ThrowException(errorMsg); } }
Local<Object> MetadataNode::GetImplementationObject(const Local<Object>& object) { DEBUG_WRITE("GetImplementationObject called on object:%d", object->GetIdentityHash()); auto target = object; Local<Value> currentPrototype = target; Local<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 Local<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; } Local<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) { //Local<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 Local<Object>(); } else { auto value = currentPrototype.As<Object>()->GetHiddenValue(V8StringConstants::GetClassImplementationObject()); if (!value.IsEmpty()) { implementationObject = currentPrototype.As<Object>(); } } lastPrototype = currentPrototype; } return implementationObject; }
Local<Value> NativeScriptRuntime::CallJSMethod(JNIEnv *_env, const Local<Object>& jsObject, const string& methodName, jobjectArray args) { SET_PROFILER_FRAME(); JEnv env(_env); Local<Value> result; auto isolate = Isolate::GetCurrent(); //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"; throw NativeScriptException(ss.str()); } else if (!method->IsFunction()) { stringstream ss; ss << "Property '" << methodName << "' is not a function"; throw NativeScriptException(ss.str()); } else { EscapableHandleScope handleScope(isolate); auto jsMethod = method.As<Function>(); auto jsArgs = ArgConverter::ConvertJavaArgsToJsArgs(args); int argc = jsArgs->Length(); Local<Value> arguments[argc]; for (int i = 0; i < argc; i++) { arguments[i] = jsArgs->Get(i); } DEBUG_WRITE("implementationObject->GetIdentityHash()=%d", jsObject->GetIdentityHash()); TryCatch tc; 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()) { stringstream ss; ss << "Calling js method " << methodName << " failed"; string exceptionMessage = ss.str(); throw NativeScriptException(tc, ss.str()); } result = handleScope.Escape(jsResult); } return result; }
void NativeScriptRuntime::CallJavaMethod(const Local<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 = nullptr; string *returnType = nullptr; auto retType = MethodReturnType::Unknown; MethodCache::CacheMethodInfo mi; if ((entry != nullptr) && entry->isResolved) { isStatic = entry->isStatic; if (entry->memberId == nullptr) { clazz = env.FindClass(className); if (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) { //todo: plamen5kov: throw exception here DEBUG_WRITE("Cannot resolve caller's class name: %s", callerClassName.c_str()); return; } entry->memberId = isStatic ? env.GetStaticMethodID(clazz, methodName, entry->sig) : env.GetMethodID(clazz, methodName, entry->sig); if (entry->memberId == nullptr) { //todo: plamen5kov: throw exception here DEBUG_WRITE("Cannot resolve a method %s on caller class: %s", methodName.c_str(), callerClassName.c_str()); return; } } else { entry->memberId = isStatic ? env.GetStaticMethodID(clazz, methodName, entry->sig) : env.GetMethodID(clazz, methodName, entry->sig); if (entry->memberId == nullptr) { //todo: plamen5kov: throw exception here DEBUG_WRITE("Cannot resolve a method %s on class: %s", methodName.c_str(), className.c_str()); return; } } entry->clazz = clazz; } mid = reinterpret_cast<jmethodID>(entry->memberId); clazz = entry->clazz; sig = &entry->sig; returnType = &entry->returnType; retType = entry->retType; } else { DEBUG_WRITE("Resolving method: %s on className %s", methodName.c_str(), className.c_str()); 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; returnType = &mi.returnType; retType = mi.retType; } 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, entry); if (!argConverter.IsValid()) { JsArgConverter::Error err = argConverter.GetError(); throw NativeScriptException(err.msg); } 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."; throw NativeScriptException(ss.str()); } } switch (retType) { case MethodReturnType::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 MethodReturnType::Boolean: { 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 MethodReturnType::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 MethodReturnType::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 MethodReturnType::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 MethodReturnType::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 MethodReturnType::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 MethodReturnType::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 MethodReturnType::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 MethodReturnType::String: { jobject result = nullptr; bool exceptionOccurred; 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); } if (result != nullptr) { auto objectResult = ArgConverter::jstringToV8String(static_cast<jstring>(result)); args.GetReturnValue().Set(objectResult); env.DeleteLocalRef(result); } else { args.GetReturnValue().Set(Null(isolate)); } break; } case MethodReturnType::Object: { jobject result = nullptr; bool exceptionOccurred; 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); } if (result != nullptr) { auto isString = env.IsInstanceOf(result, JAVA_LANG_STRING); Local<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); env.DeleteLocalRef(result); } else { args.GetReturnValue().Set(Null(isolate)); } break; } default: { assert(false); break; } } static uint32_t adjustMemCount = 0; if ((++adjustMemCount % 2) == 0) { AdjustAmountOfExternalAllocatedMemory(env, isolate); } }