shared_ptr<MethodBody> ContextBuilder::buildMethod( const shared_ptr<MethodDeclaration> &method) { std::map< PropertyName, ClassName > args; for (auto elem : method->getArgs()) { args[PropertyName(elem.first)] = ClassName(elem.second); } MethodName methodName(method->getName()); auto term = buildMethodBody(method->getBodyTerm()); return std::make_shared< MethodBody >( methodName, term, args ); }
JavaScriptObject::JavaScriptObject(JavaTypeMap& typeMap, JNIEnv* env, duk_context* context, jstring name, jobjectArray methods) : m_name(JString(env, name).str()) , m_context(context) , m_instance(nullptr) , m_nextFinalizer(nullptr) { CHECK_STACK(m_context); duk_push_global_object(m_context); if (!duk_get_prop_string(m_context, -1, m_name.c_str())) { duk_pop_2(m_context); throw std::invalid_argument("A global JavaScript object called " + m_name + " was not found"); } m_instance = duk_get_heapptr(m_context, -1); if (m_instance == nullptr) { duk_pop_2(m_context); throw std::invalid_argument("JavaScript global called " + m_name + " is not an object"); } // Make sure that the object has all of the methods we want. jmethodID getName = nullptr; const jsize numArgs = env->GetArrayLength(methods); for (jsize i = 0; i < numArgs; ++i) { auto method = env->GetObjectArrayElement(methods, i); if (getName == nullptr) { jclass methodClass = env->GetObjectClass(method); getName = env->GetMethodID(methodClass, "getName", "()Ljava/lang/String;"); } // Sanity check that as of right now, the object we're proxying has a function with this name. const JString methodName(env, static_cast<jstring>(env->CallObjectMethod(method, getName))); if (!duk_get_prop_string(m_context, -1, methodName)) { duk_pop_3(m_context); throw std::runtime_error("JavaScript global " + m_name + " has no method called " + methodName.str()); } else if (!duk_is_callable(m_context, -1)) { duk_pop_3(m_context); throw std::runtime_error("JavaScript property " + m_name + "." + methodName.str() + " not callable"); } try { // Build a call wrapper that handles marshalling the arguments and return value. m_methods.emplace(std::make_pair(env->FromReflectedMethod(method), buildMethodBody(typeMap, env, method, methodName.str()))); } catch (const std::invalid_argument& e) { duk_pop_3(m_context); throw std::invalid_argument("In proxied method \"" + m_name + "." + methodName.str() + "\": " + e.what()); } // Pop the method property. duk_pop(m_context); } // Keep track of any previously registered finalizer. duk_get_finalizer(m_context, -1); m_nextFinalizer = duk_is_c_function(m_context, -1) ? duk_get_c_function(m_context, -1) : nullptr; duk_pop(m_context); duk_push_c_function(m_context, JavaScriptObject::finalizer, 1); duk_set_finalizer(m_context, -2); // Add 'this' to the list of pointers attached to the proxied instance. // TODO: don't use an array here, just use a separate hidden property for each proxy. if (!duk_has_prop_string(m_context, -1, WRAPPER_THIS_PROP_NAME)) { duk_push_array(m_context); } else { duk_get_prop_string(m_context, -1, WRAPPER_THIS_PROP_NAME); } const duk_size_t length = duk_get_length(m_context, -1); duk_push_pointer(m_context, this); duk_put_prop_index(m_context, -2, static_cast<duk_uarridx_t>(length)); // Add the array (back) to the instance. duk_put_prop_string(m_context, -2, WRAPPER_THIS_PROP_NAME); // Pop the global and our instance. duk_pop_2(m_context); }