jsuno: Implement a basic Rhino compatibility layer
Adds the following features in order to make the QuickJS engine slightly more compatible with the Rhino engine: Makes “Packages” be an alias for “uno.idl” Adds an implementation of importClass. This tries to get the name of the thing that is being imported and then sets a global variable with that name to point to that thing. The name is retrieved by adding a getImportClassName method to most of the objects returned by moduleGetOwnProperty. Inserts a com.sun.star.uno.UnoRuntime object with a hack on moduleGetOwnProperty. This object has a method called “queryInterface” to allow doing UnoRuntime.queryInterface(interface,obj). The implementation just calls obj.queryInterface(uno.type.interface(interface)) This is enough to run the HelloWorld example from the Rhino JavaScript provider. Change-Id: I569fe4dad4cd8cdc6029a60f8e5421a5de1588c2 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/204912 Tested-by: Jenkins Reviewed-by: Neil Roberts <nroberts@libreoffice.org>
This commit is contained in:
@@ -13,6 +13,7 @@ $(eval $(call gb_Library_add_defs,jsuno,-DLO_DLLIMPLEMENTATION_JSUNO))
|
||||
|
||||
$(eval $(call gb_Library_add_exception_objects,jsuno, \
|
||||
jsuno/source/jsuno \
|
||||
jsuno/source/rhinocompatibility \
|
||||
))
|
||||
|
||||
$(eval $(call gb_Library_use_externals,jsuno, \
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* This file is part of the LibreOffice project.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
// Check that we can access UNO entities with the Packages alias
|
||||
console.assert(Packages.org.libreoffice.embindtest.Constants.Short === -1234);
|
||||
|
||||
assertError(function() { importClass() },
|
||||
"TypeError: cannot read property 'getImportClassName' of undefined");
|
||||
|
||||
console.assert(Packages.org.libreoffice.embindtest.XTest.getImportClassName() == "XTest");
|
||||
console.assert(Packages.org.libreoffice.embindtest.Enum.getImportClassName() == "Enum");
|
||||
console.assert(Packages.org.getImportClassName() == "org");
|
||||
console.assert(Packages.org.libreoffice.embindtest.getImportClassName() == "embindtest");
|
||||
|
||||
importClass(Packages.org.libreoffice.embindtest.XTest);
|
||||
console.assert(XTest == Packages.org.libreoffice.embindtest.XTest);
|
||||
importClass(Packages.org.libreoffice.embindtest.Enum);
|
||||
console.assert(Enum == Packages.org.libreoffice.embindtest.Enum);
|
||||
importClass(Packages.org.libreoffice.embindtest);
|
||||
console.assert(embindtest == Packages.org.libreoffice.embindtest);
|
||||
|
||||
importClass(Packages.com.sun.star.uno.UnoRuntime);
|
||||
|
||||
let test = uno.idl.org.libreoffice.embindtest.Test.create(uno.componentContext);
|
||||
let xTest = UnoRuntime.queryInterface(XTest, test);
|
||||
console.assert(uno.sameUnoObject(xTest, test));
|
||||
|
||||
console.assert(UnoRuntime.queryInterface(XTest, uno.componentContext) === undefined);
|
||||
@@ -27,10 +27,12 @@ class TestUno : public CppUnit::TestFixture
|
||||
public:
|
||||
void test() { executeScript(u"test.js"); }
|
||||
void testMissingParams() { executeScript(u"testmissingparams.js"); }
|
||||
void testRhinoCompatibility() { executeScript(u"testrhinocompatibility.js"); }
|
||||
|
||||
CPPUNIT_TEST_SUITE(TestUno);
|
||||
CPPUNIT_TEST(test);
|
||||
CPPUNIT_TEST(testMissingParams);
|
||||
CPPUNIT_TEST(testRhinoCompatibility);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
};
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
#include <typelib/typedescription.hxx>
|
||||
|
||||
#include "jsvalue.hxx"
|
||||
#include "rhinocompatibility.hxx"
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -590,6 +591,25 @@ JSValue wrapUnoObject(JSContext* ctx, css::uno::Reference<css::uno::XInterface>
|
||||
return val;
|
||||
}
|
||||
|
||||
JSValue importClassNameFromFullName(JSContext* ctx, std::u16string_view fullName)
|
||||
{
|
||||
std::size_t lastDot = fullName.find_last_of(u'.');
|
||||
std::u16string_view shortName
|
||||
= lastDot == std::u16string_view::npos ? fullName : fullName.substr(lastDot + 1);
|
||||
return JS_NewStringUTF16(ctx, reinterpret_cast<std::uint16_t const*>(shortName.data()),
|
||||
shortName.length());
|
||||
}
|
||||
|
||||
JSValue interfaceGetImportClassName(JSContext* ctx, JSValueConst this_val, int, JSValueConst*)
|
||||
{
|
||||
typelib_TypeDescriptionReference* typeRef = static_cast<typelib_TypeDescriptionReference*>(
|
||||
JS_GetOpaque2(ctx, this_val, getRuntimeData(ctx)->interfaceClassId));
|
||||
if (typeRef == nullptr)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
return importClassNameFromFullName(ctx, OUString(typeRef->pTypeName));
|
||||
}
|
||||
|
||||
struct EnumeratorData
|
||||
{
|
||||
css::uno::Type type;
|
||||
@@ -855,6 +875,16 @@ JSValue unoTypeInterface(JSContext* ctx, JSValueConst, int argc, JSValueConst* a
|
||||
});
|
||||
}
|
||||
|
||||
JSValue enumGetImportClassName(JSContext* ctx, JSValueConst this_val, int, JSValueConst*)
|
||||
{
|
||||
typelib_TypeDescriptionReference* typeRef = static_cast<typelib_TypeDescriptionReference*>(
|
||||
JS_GetOpaque2(ctx, this_val, getRuntimeData(ctx)->enumClassId));
|
||||
if (typeRef == nullptr)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
return importClassNameFromFullName(ctx, OUString(typeRef->pTypeName));
|
||||
}
|
||||
|
||||
void enumFinalizer(JSRuntime* rt, JSValueConst val)
|
||||
{
|
||||
#if defined DBG_UTIL
|
||||
@@ -1213,6 +1243,16 @@ JSValue getSingleton(JSContext* ctx, JSValueConst, int argc, JSValueConst* argv,
|
||||
});
|
||||
}
|
||||
|
||||
JSValue moduleGetImportClassName(JSContext* ctx, JSValueConst this_val, int, JSValueConst*)
|
||||
{
|
||||
ModuleData* moduleData = static_cast<ModuleData*>(
|
||||
JS_GetOpaque2(ctx, this_val, getRuntimeData(ctx)->moduleClassId));
|
||||
if (moduleData == nullptr)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
return importClassNameFromFullName(ctx, moduleData->name);
|
||||
}
|
||||
|
||||
void moduleGCMark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* markFunc)
|
||||
{
|
||||
ModuleData* moduleData
|
||||
@@ -1234,6 +1274,10 @@ void moduleFinalizer(JSRuntime* rt, JSValueConst val)
|
||||
|
||||
ValueRef moduleGetOwnPropertyUncached(JSContext* ctx, const OUString& id)
|
||||
{
|
||||
// Special hack for rhino compatibility
|
||||
if (id == u"com.sun.star.uno.UnoRuntime")
|
||||
return jsuno::rhino_compatibility::createUnoRuntime(ctx);
|
||||
|
||||
css::uno::Reference<css::container::XHierarchicalNameAccess> mgr(
|
||||
comphelper::getProcessComponentContext()->getValueByName(
|
||||
u"/singletons/com.sun.star.reflection.theTypeDescriptionManager"_ustr),
|
||||
@@ -2584,6 +2628,18 @@ void initializeTypePrototype(JSContext* ctx)
|
||||
JS_SetClassProto(ctx, getRuntimeData(ctx)->typeClassId, proto.release());
|
||||
}
|
||||
|
||||
void initializeEnumPrototype(JSContext* ctx)
|
||||
{
|
||||
static const JSCFunctionListEntry functions[] = {
|
||||
JS_CFUNC_DEF("getImportClassName", 0, enumGetImportClassName),
|
||||
};
|
||||
|
||||
ValueRef proto(ctx, JS_NewObject(ctx));
|
||||
JS_SetPropertyFunctionList(ctx, proto, functions, SAL_N_ELEMENTS(functions));
|
||||
|
||||
JS_SetClassProto(ctx, getRuntimeData(ctx)->enumClassId, proto.release());
|
||||
}
|
||||
|
||||
void initializeEnumeratorPrototype(JSContext* ctx)
|
||||
{
|
||||
static const JSCFunctionListEntry functions[] = {
|
||||
@@ -2607,6 +2663,30 @@ void initializeWrapperPrototype(JSContext* ctx)
|
||||
|
||||
JS_SetClassProto(ctx, getRuntimeData(ctx)->wrapperClassId, proto.release());
|
||||
}
|
||||
|
||||
void initializeInterfacePrototype(JSContext* ctx)
|
||||
{
|
||||
static const JSCFunctionListEntry functions[] = {
|
||||
JS_CFUNC_DEF("getImportClassName", 0, interfaceGetImportClassName),
|
||||
};
|
||||
|
||||
ValueRef proto(ctx, JS_NewObject(ctx));
|
||||
JS_SetPropertyFunctionList(ctx, proto, functions, SAL_N_ELEMENTS(functions));
|
||||
|
||||
JS_SetClassProto(ctx, getRuntimeData(ctx)->interfaceClassId, proto.release());
|
||||
}
|
||||
|
||||
void initializeModulePrototype(JSContext* ctx)
|
||||
{
|
||||
static const JSCFunctionListEntry functions[] = {
|
||||
JS_CFUNC_DEF("getImportClassName", 0, moduleGetImportClassName),
|
||||
};
|
||||
|
||||
ValueRef proto(ctx, JS_NewObject(ctx));
|
||||
JS_SetPropertyFunctionList(ctx, proto, functions, SAL_N_ELEMENTS(functions));
|
||||
|
||||
JS_SetClassProto(ctx, getRuntimeData(ctx)->moduleClassId, proto.release());
|
||||
}
|
||||
}
|
||||
|
||||
OUString jsuno::execute(OUString const& script, VariableList aGlobalVariables)
|
||||
@@ -2676,7 +2756,10 @@ OUString jsuno::execute(OUString const& script, VariableList aGlobalVariables)
|
||||
auto const ctx = JS_NewContext(rt);
|
||||
initializeTypePrototype(ctx);
|
||||
initializeEnumeratorPrototype(ctx);
|
||||
initializeEnumPrototype(ctx);
|
||||
initializeWrapperPrototype(ctx);
|
||||
initializeInterfacePrototype(ctx);
|
||||
initializeModulePrototype(ctx);
|
||||
std::optional<ExceptionData> exc;
|
||||
OUString result;
|
||||
{
|
||||
@@ -2733,6 +2816,7 @@ OUString jsuno::execute(OUString const& script, VariableList aGlobalVariables)
|
||||
JS_SetPropertyStr(ctx, uno, "componentContext",
|
||||
wrapUnoObject(ctx, comphelper::getProcessComponentContext()));
|
||||
JS_SetPropertyStr(ctx, global, "uno", uno.release());
|
||||
rhino_compatibility::setUp(ctx);
|
||||
|
||||
for (const auto& rVariable : aGlobalVariables)
|
||||
JS_SetPropertyStr(ctx, global, rVariable.first, wrapUnoObject(ctx, rVariable.second));
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
|
||||
/*
|
||||
* This file is part of the LibreOffice project.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include <sal/config.h>
|
||||
|
||||
#include "rhinocompatibility.hxx"
|
||||
|
||||
#include <array>
|
||||
#include <sal/macros.h>
|
||||
|
||||
#include "jsvalue.hxx"
|
||||
|
||||
namespace jsuno::rhino_compatibility
|
||||
{
|
||||
namespace
|
||||
{
|
||||
JSValue importClass(JSContext* pContext, JSValueConst, int, JSValueConst* argv)
|
||||
{
|
||||
JSAtom pGetImportClassName = JS_NewAtom(pContext, "getImportClassName");
|
||||
|
||||
jsuno::ValueRef sShortName(pContext,
|
||||
JS_Invoke(pContext, argv[0], pGetImportClassName, 0, nullptr));
|
||||
JS_FreeAtom(pContext, pGetImportClassName);
|
||||
|
||||
if (JS_IsException(sShortName))
|
||||
return JS_EXCEPTION;
|
||||
|
||||
JSAtom pAtom = JS_ValueToAtom(pContext, sShortName);
|
||||
if (pAtom == JS_ATOM_NULL)
|
||||
return JS_EXCEPTION;
|
||||
|
||||
const jsuno::ValueRef xGlobal(pContext, JS_GetGlobalObject(pContext));
|
||||
int nRet = JS_SetProperty(pContext, xGlobal, pAtom, JS_DupValue(pContext, argv[0]));
|
||||
JS_FreeAtom(pContext, pAtom);
|
||||
|
||||
return nRet == -1 ? JS_EXCEPTION : JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSValue unoRuntimeGetImportClassName(JSContext* pContext, JSValueConst, int, JSValueConst*)
|
||||
{
|
||||
return JS_NewString(pContext, "UnoRuntime");
|
||||
}
|
||||
|
||||
JSValue queryInterface(JSContext* pContext, JSValueConst, int, JSValueConst* argv)
|
||||
{
|
||||
// Redirect UnoRuntime.queryInterface(a, b) to b.queryInterface(uno.type.interface(a))
|
||||
|
||||
jsuno::ValueRef xUnoTypeInterface(pContext, JS_GetGlobalObject(pContext));
|
||||
|
||||
for (const auto part : std::array<const char*, 3>{ "uno", "type", "interface" })
|
||||
{
|
||||
JSAtom pAtom = JS_NewAtom(pContext, part);
|
||||
xUnoTypeInterface = JS_GetProperty(pContext, xUnoTypeInterface, pAtom);
|
||||
JS_FreeAtom(pContext, pAtom);
|
||||
|
||||
if (JS_IsException(xUnoTypeInterface))
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
jsuno::ValueRef xType(pContext, JS_Call(pContext, xUnoTypeInterface, JS_NULL, 1, argv));
|
||||
if (JS_IsException(xType))
|
||||
return JS_EXCEPTION;
|
||||
|
||||
JSAtom pAtom = JS_NewAtom(pContext, "queryInterface");
|
||||
JSValueConst args[] = { xType };
|
||||
jsuno::ValueRef xRet(pContext, JS_Invoke(pContext, argv[1], pAtom, SAL_N_ELEMENTS(args), args));
|
||||
JS_FreeAtom(pContext, pAtom);
|
||||
|
||||
return xRet.release();
|
||||
}
|
||||
}
|
||||
|
||||
void setUp(JSContext* pContext)
|
||||
{
|
||||
// Make “Packages” an alias for “uno.idl”
|
||||
const jsuno::ValueRef xGlobal(pContext, JS_GetGlobalObject(pContext));
|
||||
const jsuno::ValueRef xUno(pContext, JS_GetPropertyStr(pContext, xGlobal, "uno"));
|
||||
assert(JS_IsObject(xUno));
|
||||
jsuno::ValueRef xIdl(pContext, JS_GetPropertyStr(pContext, xUno, "idl"));
|
||||
assert(JS_IsObject(xIdl));
|
||||
JS_SetPropertyStr(pContext, xGlobal, "Packages", xIdl.release());
|
||||
|
||||
jsuno::ValueRef xImportClass(pContext,
|
||||
JS_NewCFunction(pContext, importClass, "importClass", 1));
|
||||
JS_SetPropertyStr(pContext, xGlobal, "importClass", xImportClass.release());
|
||||
}
|
||||
|
||||
jsuno::ValueRef createUnoRuntime(JSContext* pContext)
|
||||
{
|
||||
jsuno::ValueRef xObject(pContext, JS_NewObject(pContext));
|
||||
if (JS_IsException(xObject))
|
||||
return xObject;
|
||||
|
||||
static const JSCFunctionListEntry functions[] = {
|
||||
JS_CFUNC_DEF("queryInterface", 2, queryInterface),
|
||||
JS_CFUNC_DEF("getImportClassName", 0, unoRuntimeGetImportClassName),
|
||||
};
|
||||
|
||||
ValueRef proto(pContext, JS_NewObject(pContext));
|
||||
JS_SetPropertyFunctionList(pContext, xObject, functions, SAL_N_ELEMENTS(functions));
|
||||
|
||||
return xObject;
|
||||
}
|
||||
}
|
||||
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
|
||||
@@ -0,0 +1,21 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
|
||||
/*
|
||||
* This file is part of the LibreOffice project.
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <quickjs.h>
|
||||
#include "jsvalue.hxx"
|
||||
|
||||
namespace jsuno::rhino_compatibility
|
||||
{
|
||||
void setUp(JSContext* pContext);
|
||||
jsuno::ValueRef createUnoRuntime(JSContext* pContext);
|
||||
}
|
||||
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
|
||||
Reference in New Issue
Block a user