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);
}
Esempio n. 4
0
    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);
	}
}