diff --git a/source/scriptinterface/ScriptInterface.cpp b/source/scriptinterface/ScriptInterface.cpp index 1092b147f3..95b6394afe 100644 --- a/source/scriptinterface/ScriptInterface.cpp +++ b/source/scriptinterface/ScriptInterface.cpp @@ -704,6 +704,36 @@ bool ScriptInterface::HasProperty(JS::HandleValue obj, const char* name) const return found; } +bool ScriptInterface::GetGlobalProperty(const ScriptRequest& rq, const std::string& name, JS::MutableHandleValue out) +{ + // Try to get the object as a property of the global object. + JS::RootedObject global(rq.cx, rq.glob); + if (!JS_GetProperty(rq.cx, global, name.c_str(), out)) + { + out.set(JS::NullHandleValue); + return false; + } + + if (!out.isNullOrUndefined()) + return true; + + // Some objects, such as const definitions, or Class definitions, are hidden inside closures. + // We must fetch those from the correct lexical scope. + //JS::RootedValue glob(cx); + JS::RootedObject lexical_environment(rq.cx, JS_GlobalLexicalEnvironment(rq.glob)); + if (!JS_GetProperty(rq.cx, lexical_environment, name.c_str(), out)) + { + out.set(JS::NullHandleValue); + return false; + } + + if (!out.isNullOrUndefined()) + return true; + + out.set(JS::NullHandleValue); + return false; +} + bool ScriptInterface::EnumeratePropertyNames(JS::HandleValue objVal, bool enumerableOnly, std::vector& out) const { ScriptRequest rq(this); diff --git a/source/scriptinterface/ScriptInterface.h b/source/scriptinterface/ScriptInterface.h index f5279581ff..15670c45b9 100644 --- a/source/scriptinterface/ScriptInterface.h +++ b/source/scriptinterface/ScriptInterface.h @@ -233,6 +233,15 @@ public: */ bool HasProperty(JS::HandleValue obj, const char* name) const; + /** + * Get an object from the global scope or any lexical scope. + * This can return globally accessible objects even if they are not properties + * of the global object (e.g. ES6 class definitions). + * @param name - Name of the property. + * @param out The object or null. + */ + static bool GetGlobalProperty(const ScriptRequest& rq, const std::string& name, JS::MutableHandleValue out); + /** * Returns all properties of the object, both own properties and inherited. * This is essentially equivalent to calling Object.getOwnPropertyNames() diff --git a/source/simulation2/scripting/EngineScriptConversions.cpp b/source/simulation2/scripting/EngineScriptConversions.cpp index 511a3309c9..5c12f9f16b 100644 --- a/source/simulation2/scripting/EngineScriptConversions.cpp +++ b/source/simulation2/scripting/EngineScriptConversions.cpp @@ -160,7 +160,7 @@ template<> void ScriptInterface::ToJSVal(const ScriptRequest& rq { JS::RootedObject global(rq.cx, rq.glob); JS::RootedValue valueVector3D(rq.cx); - if (!JS_GetProperty(rq.cx, global, "Vector3D", &valueVector3D)) + if (!ScriptInterface::GetGlobalProperty(rq, "Vector3D", &valueVector3D)) FAIL_VOID("Failed to get Vector3D constructor"); JS::RootedValueArray<3> args(rq.cx); @@ -196,7 +196,7 @@ template<> void ScriptInterface::ToJSVal(const ScriptRequest& rq { JS::RootedObject global(rq.cx, rq.glob); JS::RootedValue valueVector2D(rq.cx); - if (!JS_GetProperty(rq.cx, global, "Vector2D", &valueVector2D)) + if (!ScriptInterface::GetGlobalProperty(rq, "Vector2D", &valueVector2D)) FAIL_VOID("Failed to get Vector2D constructor"); JS::RootedValueArray<2> args(rq.cx);