From bd4b29364b616358d1ebb314d9da9da51e4051dd Mon Sep 17 00:00:00 2001 From: Seonghyun Kim Date: Wed, 25 Oct 2023 09:55:05 +0900 Subject: [PATCH] Implement setExtraData, extraData on JavaScriptObject Signed-off-by: Seonghyun Kim --- .../samsung/lwe/escargot/EscargotTest.java | 34 ++++++++++++++ .../escargot/src/main/cpp/EscargotJNI.cpp | 26 +++++++++++ .../escargot/src/main/cpp/EscargotJNI.h | 18 ++++++++ .../escargot/src/main/cpp/JNIBridge.cpp | 13 ++---- .../src/main/cpp/JNIFunctionObject.cpp | 12 +---- .../escargot/src/main/cpp/JNIObject.cpp | 45 +++++++++++++++++++ .../lwe/escargot/JavaScriptObject.java | 13 ++++++ 7 files changed, 142 insertions(+), 19 deletions(-) diff --git a/build/android/escargot/src/androidTest/java/com/samsung/lwe/escargot/EscargotTest.java b/build/android/escargot/src/androidTest/java/com/samsung/lwe/escargot/EscargotTest.java index 743393888..d6cf5ad17 100644 --- a/build/android/escargot/src/androidTest/java/com/samsung/lwe/escargot/EscargotTest.java +++ b/build/android/escargot/src/androidTest/java/com/samsung/lwe/escargot/EscargotTest.java @@ -1326,4 +1326,38 @@ public Optional callback(Context context, JavaScriptValue recei finalizeEngine(); } + @Test + public void extraDataTest() + { + Globals.initializeGlobals(); + VMInstance vmInstance = VMInstance.create(Optional.of("en-US"), Optional.of("Asia/Seoul")); + Context context = Context.create(vmInstance); + + JavaScriptObject obj = JavaScriptObject.create(context); + assertTrue(obj.extraData() != null); + assertFalse(obj.extraData().isPresent()); + obj.setExtraData(null); + assertFalse(obj.extraData().isPresent()); + printNegativeTC("extraDataTest 1"); + + Object jObject = new Object(); + + obj.setExtraData(Optional.of(jObject)); + assertTrue(obj.extraData().isPresent()); + assertEquals(obj.extraData().get(), jObject); + obj.setExtraData(Optional.empty()); + assertFalse(obj.extraData().isPresent()); + printPositiveTC("promiseTest 2"); + + obj.setExtraData(Optional.of(jObject)); + assertTrue(obj.extraData().isPresent()); + assertEquals(obj.extraData().get(), jObject); + obj.setExtraData(null); + assertFalse(obj.extraData().isPresent()); + printNegativeTC("promiseTest 3"); + + context = null; + vmInstance = null; + finalizeEngine(); + } } \ No newline at end of file diff --git a/build/android/escargot/src/main/cpp/EscargotJNI.cpp b/build/android/escargot/src/main/cpp/EscargotJNI.cpp index f560fd360..f23a29920 100644 --- a/build/android/escargot/src/main/cpp/EscargotJNI.cpp +++ b/build/android/escargot/src/main/cpp/EscargotJNI.cpp @@ -262,4 +262,30 @@ jobject createOptionalValueFromEvaluatorDoubleResult(JNIEnv* env, jobject contex } return storeExceptionOnContextAndReturnsIt(env, contextObject, context, evaluatorResult); +} + +ScriptObjectExtraData* ensureScriptObjectExtraData(ObjectRef* ref) +{ + ScriptObjectExtraData* data = reinterpret_cast(ref->extraData()); + if (!data) { + data = new ScriptObjectExtraData; + ref->setExtraData(data); + + Memory::gcRegisterFinalizer(ref, [](void* self) { + ScriptObjectExtraData* extraData = reinterpret_cast(reinterpret_cast(self)->extraData()); + auto env = fetchJNIEnvFromCallback(); + if (env) { + if (extraData->implementSideData) { + env->DeleteGlobalRef(extraData->implementSideData); + extraData->implementSideData = nullptr; + } + if (extraData->userData) { + env->DeleteGlobalRef(extraData->userData); + extraData->userData = nullptr; + } + } + }); + + } + return data; } \ No newline at end of file diff --git a/build/android/escargot/src/main/cpp/EscargotJNI.h b/build/android/escargot/src/main/cpp/EscargotJNI.h index fe349cd63..aeb832e30 100644 --- a/build/android/escargot/src/main/cpp/EscargotJNI.h +++ b/build/android/escargot/src/main/cpp/EscargotJNI.h @@ -99,6 +99,24 @@ jobject createOptionalValueFromEvaluatorBooleanResult(JNIEnv* env, jobject conte jobject createOptionalValueFromEvaluatorIntegerResult(JNIEnv* env, jobject contextObject, ContextRef* context, Evaluator::EvaluatorResult& evaluatorResult); jobject createOptionalValueFromEvaluatorDoubleResult(JNIEnv* env, jobject contextObject, ContextRef* context, Evaluator::EvaluatorResult& evaluatorResult); +struct ScriptObjectExtraData { + jobject userData; + jobject implementSideData; + + ScriptObjectExtraData() + : userData(nullptr) + , implementSideData(nullptr) + { + } + + void* operator new(size_t t) + { + return Memory::gcMallocAtomic(sizeof(ScriptObjectExtraData)); + } +}; + +ScriptObjectExtraData* ensureScriptObjectExtraData(ObjectRef* ref); + template jobject nativeOptionalValueIntoJavaOptionalValue(JNIEnv* env, OptionalRef ref) { diff --git a/build/android/escargot/src/main/cpp/JNIBridge.cpp b/build/android/escargot/src/main/cpp/JNIBridge.cpp index 971521587..2f4ff7232 100644 --- a/build/android/escargot/src/main/cpp/JNIBridge.cpp +++ b/build/android/escargot/src/main/cpp/JNIBridge.cpp @@ -65,7 +65,9 @@ Java_com_samsung_lwe_escargot_Bridge_register(JNIEnv* env, jclass clazz, jobject ValueRef** argv, bool isConstructorCall) -> ValueRef* { FunctionObjectRef* callee = state->resolveCallee().get(); - jobject jo = static_cast(reinterpret_cast(callee)->extraData()); + + jobject jo = ensureScriptObjectExtraData( + reinterpret_cast(callee))->implementSideData; auto env = fetchJNIEnvFromCallback(); if (!env) { // give up @@ -141,14 +143,7 @@ Java_com_samsung_lwe_escargot_Bridge_register(JNIEnv* env, jclass clazz, jobject if (evalResult.isSuccessful()) { FunctionObjectRef* callback = evalResult.result->asFunctionObject(); - callback->setExtraData(adapter); - Memory::gcRegisterFinalizer(callback, [](void* self) { - jobject jo = static_cast(reinterpret_cast(self)->extraData()); - auto env = fetchJNIEnvFromCallback(); - if (env) { - env->DeleteGlobalRef(jo); - } - }); + ensureScriptObjectExtraData(callback)->implementSideData = adapter; } else { env->DeleteGlobalRef(adapter); } diff --git a/build/android/escargot/src/main/cpp/JNIFunctionObject.cpp b/build/android/escargot/src/main/cpp/JNIFunctionObject.cpp index aea3fc9d6..3adbdd332 100644 --- a/build/android/escargot/src/main/cpp/JNIFunctionObject.cpp +++ b/build/android/escargot/src/main/cpp/JNIFunctionObject.cpp @@ -57,7 +57,7 @@ Java_com_samsung_lwe_escargot_JavaScriptJavaCallbackFunctionObject_create(JNIEnv } env->PushLocalFrame(32); - jobject callback = reinterpret_cast(state->resolveCallee()->extraData()); + jobject callback = ensureScriptObjectExtraData(state->resolveCallee().get())->implementSideData; auto callbackMethodId = env->GetMethodID(env->GetObjectClass(callback), "callback", "(Lcom/samsung/lwe/escargot/Context;Lcom/samsung/lwe/escargot/JavaScriptValue;[Lcom/samsung/lwe/escargot/JavaScriptValue;)Ljava/util/Optional;"); @@ -107,14 +107,6 @@ Java_com_samsung_lwe_escargot_JavaScriptJavaCallbackFunctionObject_create(JNIEnv callback = env->NewGlobalRef(callback); FunctionObjectRef* fn = evaluatorResult.result->asFunctionObject(); - fn->setExtraData(callback); - Memory::gcRegisterFinalizer(fn, [](void* self) { - jobject jo = static_cast(reinterpret_cast(self)->extraData()); - auto env = fetchJNIEnvFromCallback(); - if (env) { - env->DeleteGlobalRef(jo); - } - }); - + ensureScriptObjectExtraData(fn)->implementSideData = callback; return createJavaValueObject(env, "com/samsung/lwe/escargot/JavaScriptJavaCallbackFunctionObject", fn); } \ No newline at end of file diff --git a/build/android/escargot/src/main/cpp/JNIObject.cpp b/build/android/escargot/src/main/cpp/JNIObject.cpp index fdb7e4608..fb092d770 100644 --- a/build/android/escargot/src/main/cpp/JNIObject.cpp +++ b/build/android/escargot/src/main/cpp/JNIObject.cpp @@ -125,4 +125,49 @@ Java_com_samsung_lwe_escargot_JavaScriptObject_getOwnProperty(JNIEnv* env, jobje return createOptionalValueFromEvaluatorJavaScriptValueResult(env, context, contextRef->get(), evaluatorResult); +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_samsung_lwe_escargot_JavaScriptObject_setExtraData(JNIEnv* env, jobject thiz, + jobject object) +{ + ObjectRef* thisValueRef = unwrapValueRefFromValue(env, env->GetObjectClass(thiz), thiz)->asObject(); + jobject extraData = nullptr; + + if (object) { + auto classOptional = env->GetObjectClass(object); + auto methodIsPresent = env->GetMethodID(classOptional, "isPresent", "()Z"); + if (env->CallBooleanMethod(object, methodIsPresent)) { + auto methodGet = env->GetMethodID(classOptional, "get", "()Ljava/lang/Object;"); + jboolean isSucceed; + extraData = env->CallObjectMethod(object, methodGet); + extraData = env->NewGlobalRef(extraData); + } + } + + ensureScriptObjectExtraData(thisValueRef)->userData = extraData; +} + +extern "C" +JNIEXPORT jobject JNICALL +Java_com_samsung_lwe_escargot_JavaScriptObject_extraData(JNIEnv* env, jobject thiz) +{ + ObjectRef* thisValueRef = unwrapValueRefFromValue(env, env->GetObjectClass(thiz), thiz)->asObject(); + jobject extraData = nullptr; + if (thisValueRef->extraData()) { + extraData = ensureScriptObjectExtraData(thisValueRef)->userData; + } + + jclass optionalClazz = env->FindClass("java/util/Optional"); + if (extraData) { + return env->CallStaticObjectMethod(optionalClazz, + env->GetStaticMethodID(optionalClazz, "of", + "(Ljava/lang/Object;)Ljava/util/Optional;"), + extraData); + } else { + return env->CallStaticObjectMethod(optionalClazz, + env->GetStaticMethodID(optionalClazz, "empty", + "()Ljava/util/Optional;")); + } } \ No newline at end of file diff --git a/build/android/escargot/src/main/java/com/samsung/lwe/escargot/JavaScriptObject.java b/build/android/escargot/src/main/java/com/samsung/lwe/escargot/JavaScriptObject.java index 11e36ebab..3c991a26b 100644 --- a/build/android/escargot/src/main/java/com/samsung/lwe/escargot/JavaScriptObject.java +++ b/build/android/escargot/src/main/java/com/samsung/lwe/escargot/JavaScriptObject.java @@ -54,4 +54,17 @@ public native Optional defineDataProperty(Context context, JavaScriptVa * @return JavaScriptValue or Optional.empty if there was a exception while executing getOwnProperty function */ public native Optional getOwnProperty(Context context, JavaScriptValue propertyName); + + /** + * store Java Object in JavaScriptObject + * @param object + */ + public native void setExtraData(Optional object); + + /** + * returns stored Java object in JavaScriptObject + * this function returns `Optional with Object` or `empty Optional` + * @return + */ + public native Optional extraData(); }