void DuktapeContext::set(JNIEnv *env, jstring name, jobject object, jobjectArray methods) { CHECK_STACK(m_context); duk_push_global_object(m_context); const JString instanceName(env, name); if (duk_has_prop_string(m_context, -1, instanceName)) { duk_pop(m_context); queueIllegalArgumentException(env, "A global object called " + instanceName.str() + " already exists"); return; } const duk_idx_t objIndex = duk_require_normalize_index(m_context, duk_push_object(m_context)); // Hook up a finalizer to decrement the refcount and clean up our JavaMethods. duk_push_c_function(m_context, javaObjectFinalizer, 1); duk_set_finalizer(m_context, objIndex); const jsize numMethods = env->GetArrayLength(methods); for (jsize i = 0; i < numMethods; ++i) { jobject method = env->GetObjectArrayElement(methods, i); const jmethodID getName = env->GetMethodID(env->GetObjectClass(method), "getName", "()Ljava/lang/String;"); const JString methodName(env, static_cast<jstring>(env->CallObjectMethod(method, getName))); std::unique_ptr<JavaMethod> javaMethod; try { javaMethod.reset(new JavaMethod(m_javaValues, env, method)); } catch (const std::invalid_argument& e) { queueIllegalArgumentException(env, "In bound method \"" + instanceName.str() + "." + methodName.str() + "\": " + e.what()); // Pop the object being bound and the duktape global object. duk_pop_2(m_context); return; } // Use VARARGS here to allow us to manually validate that the proper number of arguments are // given in the call. If we specify the actual number of arguments needed, Duktape will try to // be helpful by discarding extra or providing missing arguments. That's not quite what we want. // See http://duktape.org/api.html#duk_push_c_function for details. const duk_idx_t func = duk_push_c_function(m_context, javaMethodHandler, DUK_VARARGS); duk_push_pointer(m_context, javaMethod.release()); duk_put_prop_string(m_context, func, JAVA_METHOD_PROP_NAME); // Add this method to the bound object. duk_put_prop_string(m_context, objIndex, methodName); } // Keep a reference in JavaScript to the object being bound. duk_push_pointer(m_context, env->NewGlobalRef(object)); duk_put_prop_string(m_context, objIndex, JAVA_THIS_PROP_NAME); // Make our bound Java object a property of the Duktape global object (so it's a JS global). duk_put_prop_string(m_context, -2, instanceName); // Pop the Duktape global object off the stack. duk_pop(m_context); }
JNIEXPORT jobject JNICALL Java_com_squareup_duktape_Duktape_call(JNIEnv *env, jclass type, jlong context, jlong instance, jobject method, jobjectArray args) { // Validate our DuktapeContext first - if the context is null, we can't use the proxy. DuktapeContext* duktape = reinterpret_cast<DuktapeContext*>(context); if (duktape == nullptr) { queueNullPointerException(env, "Null Duktape context - did you close your Duktape?"); return nullptr; } const JavaScriptObject* object = reinterpret_cast<const JavaScriptObject*>(instance); if (object == nullptr) { queueNullPointerException(env, "Invalid JavaScript object"); return nullptr; } try { return object->call(env, method, args); } catch (const std::invalid_argument& e) { queueIllegalArgumentException(env, e.what()); } catch (const std::exception& e) { queueDuktapeException(env, e.what()); } return nullptr; }
JNIEXPORT void JNICALL Java_com_squareup_duktape_Duktape_set(JNIEnv *env, jclass type, jlong context, jstring name, jobject object, jobjectArray methods) { DuktapeContext* duktape = reinterpret_cast<DuktapeContext*>(context); if (duktape == nullptr) { queueNullPointerException(env, "Null Duktape context - did you close your Duktape?"); return; } try { duktape->set(env, name, object, methods); } catch (const std::invalid_argument& e) { queueIllegalArgumentException(env, e.what()); } catch (const std::exception& e) { queueDuktapeException(env, e.what()); } }
JNIEXPORT jobject JNICALL Java_com_squareup_duktape_Duktape_evaluate__JLjava_lang_String_2Ljava_lang_String_2( JNIEnv* env, jclass type, jlong context, jstring code, jstring fname) { DuktapeContext* duktape = reinterpret_cast<DuktapeContext*>(context); if (duktape == nullptr) { queueNullPointerException(env, "Null Duktape context - did you close your Duktape?"); return nullptr; } try { return duktape->evaluate(env, code, fname); } catch (const std::invalid_argument& e) { queueIllegalArgumentException(env, e.what()); } catch (const std::exception& e) { queueDuktapeException(env, e.what()); } return nullptr; }
JNIEXPORT jlong JNICALL Java_com_squareup_duktape_Duktape_proxy(JNIEnv *env, jclass type, jlong context, jstring name, jobjectArray methods) { DuktapeContext* duktape = reinterpret_cast<DuktapeContext*>(context); if (duktape == nullptr) { queueNullPointerException(env, "Null Duktape context - did you close your Duktape?"); return 0L; } try { return reinterpret_cast<jlong>(duktape->proxy(env, name, methods)); } catch (const std::invalid_argument& e) { queueIllegalArgumentException(env, e.what()); } catch (const std::runtime_error& e) { queueDuktapeException(env, e.what()); } return 0L; }