Files
odfstudio/jsuno/source/jsuno.cxx
T
Stephan Bergmann 11d6547889 Handle missing default values in JS UNO structs
Signed-off-by: Stephan Bergmann <stephan.bergmann@collabora.com>
Change-Id: I63b589645a8c1efc95b6fb023931c45b1d08b65e
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/7624
Reviewed-by: Michael Stahl <michael.stahl@collabora.com>
Tested-by: Jenkins CPCI <releng@collaboraoffice.com>
(cherry picked from commit 0e55d63d948f177f3b3dabf459000b576e169815)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/208737
Tested-by: Jenkins
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
2026-07-28 23:32:25 +02:00

2884 lines
105 KiB
C++

/* -*- 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 <cmath>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <exception>
#include <iostream>
#include <limits>
#include <memory>
#include <optional>
#include <sstream>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include <quickjs.h>
#include <com/sun/star/container/XEnumeration.hpp>
#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
#include <com/sun/star/reflection/InvocationTargetException.hpp>
#include <com/sun/star/reflection/XCompoundTypeDescription.hpp>
#include <com/sun/star/reflection/XConstantTypeDescription.hpp>
#include <com/sun/star/reflection/XConstantsTypeDescription.hpp>
#include <com/sun/star/reflection/XServiceConstructorDescription.hpp>
#include <com/sun/star/reflection/XServiceTypeDescription2.hpp>
#include <com/sun/star/reflection/XStructTypeDescription.hpp>
#include <com/sun/star/reflection/XTypeDescription.hpp>
#include <com/sun/star/script/Invocation.hpp>
#include <com/sun/star/script/InvocationInfo.hpp>
#include <com/sun/star/script/XInvocation2.hpp>
#include <com/sun/star/script/provider/ScriptExceptionRaisedException.hpp>
#include <com/sun/star/uno/Any.hxx>
#include <com/sun/star/uno/Reference.hxx>
#include <com/sun/star/uno/RuntimeException.hpp>
#include <com/sun/star/uno/XComponentContext.hpp>
#include <com/sun/star/uno/XInterface.hpp>
#include <com/sun/star/uno/genfunc.hxx>
#include <comphelper/processfactory.hxx>
#include <cppuhelper/exc_hlp.hxx>
#include <jsuno/detail/dllapi.hxx>
#include <jsuno/jsuno.hxx>
#include <o3tl/any.hxx>
#include <o3tl/safeint.hxx>
#include <o3tl/string_view.hxx>
#include <o3tl/unreachable.hxx>
#include <rtl/string.h>
#include <rtl/ustrbuf.hxx>
#include <rtl/ustring.hxx>
#include <sal/log.hxx>
#include <sal/types.h>
#include <typelib/typedescription.hxx>
#include "jsvalue.hxx"
#include "rhinocompatibility.hxx"
namespace
{
using jsuno::ValueRef;
struct JsException
{
};
class AtomRef
{
public:
AtomRef(JSRuntime* rt)
: rt_(rt)
, atom_(JS_ATOM_NULL)
{
}
AtomRef(JSContext* ctx, JSAtom atom)
: rt_(JS_GetRuntime(ctx))
, atom_(atom)
{
}
~AtomRef() { JS_FreeAtomRT(rt_, atom_); }
AtomRef& operator=(JSAtom atom)
{
std::swap(atom_, atom);
JS_FreeAtomRT(rt_, atom);
return *this;
}
operator JSAtom() const { return atom_; }
private:
AtomRef(AtomRef const&) = delete;
AtomRef(AtomRef&&) = delete;
void operator=(AtomRef const&) = delete;
void operator=(AtomRef&&) = delete;
JSRuntime* rt_;
JSAtom atom_;
};
class UniqueCString8
{
public:
UniqueCString8(JSContext* ctx, char const* ptr)
: ctx_(ctx)
, ptr_(ptr)
{
}
~UniqueCString8() { JS_FreeCString(ctx_, ptr_); }
char const* get() const { return ptr_; }
private:
UniqueCString8(UniqueCString8 const&) = delete;
UniqueCString8(UniqueCString8&&) = delete;
void operator=(UniqueCString8 const&) = delete;
void operator=(UniqueCString8&&) = delete;
JSContext* ctx_;
char const* ptr_;
};
class UniqueCString16
{
public:
UniqueCString16(JSContext* ctx, std::uint16_t const* ptr)
: ctx_(ctx)
, ptr_(ptr)
{
}
~UniqueCString16() { JS_FreeCStringUTF16(ctx_, ptr_); }
char16_t const* get() const { return reinterpret_cast<char16_t const*>(ptr_); }
private:
UniqueCString16(UniqueCString16 const&) = delete;
UniqueCString16(UniqueCString16&&) = delete;
void operator=(UniqueCString16 const&) = delete;
void operator=(UniqueCString16&&) = delete;
JSContext* ctx_;
std::uint16_t const* ptr_;
};
ValueRef createDefaultValue(JSContext* ctx, css::uno::Type const& type);
css::uno::Any fromJs(JSContext* ctx, css::uno::Type const& type, JSValueConst val);
JSValue invokeUno(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int magic,
JSValueConst* func_data);
ValueRef mapTypeToJs(JSContext* ctx, css::uno::Type const& type);
ValueRef toJs(JSContext* ctx, css::uno::Any const& value);
#if defined DBG_UTIL
class Counter
{
public:
~Counter() { assert(count_ == 0); }
void inc()
{
assert(count_ != std::numeric_limits<std::uintptr_t>::max());
++count_;
}
void dec()
{
assert(count_ != 0);
--count_;
}
private:
std::uintptr_t count_ = 0;
};
#endif
struct RuntimeData
{
RuntimeData(JSRuntime* rt)
: symbolIteratorAtom(rt)
{
}
void clear() { symbolIteratorAtom = JS_ATOM_NULL; }
JSClassID pointerClassId = 0;
JSClassID wrapperClassId = 0;
JSClassID enumeratorClassId = 0;
JSClassID compoundClassId = 0;
JSClassID typeClassId = 0;
JSClassID enumClassId = 0;
JSClassID structClassId = 0;
JSClassID exceptionClassId = 0;
JSClassID interfaceClassId = 0;
JSClassID ctorClassId = 0;
JSClassID singletonClassId = 0;
JSClassID moduleClassId = 0;
AtomRef symbolIteratorAtom;
#if defined DBG_UTIL
Counter toFinalize;
#endif
};
RuntimeData* getRuntimeData(JSRuntime* rt)
{
return static_cast<RuntimeData*>(JS_GetRuntimeOpaque(rt));
}
RuntimeData* getRuntimeData(JSContext* ctx) { return getRuntimeData(JS_GetRuntime(ctx)); }
struct WrapperData
{
WrapperData(JSContext* ctx, const css::uno::Reference<css::uno::XInterface>& obj)
: methodCache(ctx)
, interface(obj)
{
}
// A lazily-created object used as a hash table for caching methods
ValueRef methodCache;
css::uno::Reference<css::uno::XInterface> interface;
};
struct ModuleData
{
ModuleData(JSContext* ctx, const OUString& rName)
: memberCache(ctx)
, name(rName)
{
}
// A lazily-created object used as a hash table for caching members of the module
ValueRef memberCache;
// The fully qualified name of the module
OUString name;
};
template <typename F> JSValue callFromJs(JSContext* ctx, F&& f)
{
try
{
return f();
}
catch (JsException)
{
return JS_EXCEPTION;
}
catch (css::uno::Exception)
{
auto const e = cppu::getCaughtException();
ValueRef val = toJs(ctx, e);
return JS_Throw(ctx, val.release());
}
catch (std::exception& e)
{
return JS_ThrowPlainError(ctx, "std exception: %s", e.what());
}
}
// A stripped-down and modified version of <https://console.spec.whatwg.org/#assert> (which only
// takes a single argument and aborts when the assertion is not met), just enough for using it in
// jsunit/qa/unit/testuno.cxx:
JSValue consoleAssert(JSContext* ctx, JSValueConst, int argc, JSValueConst* argv)
{
return callFromJs(ctx, [ctx, argc, argv] {
if (argc < 1)
return JS_ThrowPlainError(ctx, "missing argument to console.assert");
auto const ok = JS_ToBool(ctx, argv[0]);
if (ok == -1)
{
throw JsException();
}
if (ok == 0)
{
ValueRef const errorCtor(ctx, JS_GetPropertyStr(ctx, JS_GetGlobalObject(ctx), "Error"));
assert(!JS_IsException(errorCtor));
ValueRef const err(ctx, JS_CallConstructor(ctx, errorCtor, 0, nullptr));
assert(!JS_IsException(err));
ValueRef const stack(ctx, JS_GetPropertyStr(ctx, err, "stack"));
assert(!JS_IsException(stack));
UniqueCString8 const s(ctx, JS_ToCString(ctx, stack));
assert(s.get() != nullptr);
std::cerr << "console.assert at: " << s.get() << "\n";
std::abort();
}
return JS_UNDEFINED;
});
}
// A stripped-down version of <https://console.spec.whatwg.org/#log> (which simply prints all its
// arguments, not using the <https://console.spec.whatwg.org/#formatter> logic):
JSValue consoleLog(JSContext* ctx, JSValueConst, int argc, JSValueConst* argv)
{
return callFromJs(ctx, [ctx, argc, argv] {
OUStringBuffer buf("console.log:");
for (int i = 0; i != argc; ++i)
{
std::size_t n;
UniqueCString16 const s(ctx, JS_ToCStringLenUTF16(ctx, &n, argv[i]));
if (s.get() == nullptr)
{
throw JsException();
}
buf.append(OUString::Concat(" ") + std::u16string_view(s.get(), n));
}
buf.append('\n');
std::cout << buf.makeStringAndClear() << std::flush;
return JS_UNDEFINED;
});
}
rtl_uString* stringAcquire(OUString const& id)
{
rtl_uString_acquire(id.pData);
return id.pData;
}
void pointerFinalizer(JSRuntime* rt, JSValueConst val)
{
#if defined DBG_UTIL
getRuntimeData(rt)->toFinalize.dec();
#endif
delete static_cast<css::script::InvocationInfo const*>(
JS_GetOpaque(val, getRuntimeData(rt)->pointerClassId));
}
JSValue enumerationIteratorNext(JSContext* ctx, JSValueConst, int, JSValueConst*, int,
JSValueConst* func_data)
{
return callFromJs(ctx, [ctx, func_data] {
WrapperData* wrapperData = static_cast<WrapperData*>(
JS_GetOpaque2(ctx, func_data[0], getRuntimeData(ctx)->wrapperClassId));
if (wrapperData == nullptr)
return JS_EXCEPTION;
css::uno::Reference<css::container::XEnumeration> en(wrapperData->interface,
css::uno::UNO_QUERY_THROW);
ValueRef val(ctx, JS_NewObject(ctx));
if (en->hasMoreElements())
{
JS_SetPropertyStr(ctx, val, "value", toJs(ctx, en->nextElement()).release());
JS_SetPropertyStr(ctx, val, "done", JS_FALSE);
}
else
{
JS_SetPropertyStr(ctx, val, "value", JS_UNDEFINED);
JS_SetPropertyStr(ctx, val, "done", JS_TRUE);
}
return val.release();
});
}
JSValue enumerationIterator(JSContext* ctx, JSValueConst this_val, int, JSValueConst*)
{
return callFromJs(ctx, [ctx, this_val] {
ValueRef iter(ctx, JS_NewObject(ctx));
JS_SetPropertyStr(ctx, iter, "next",
JS_NewCFunctionData(ctx, enumerationIteratorNext, 0, 0, 1,
const_cast<JSValueConst*>(&this_val)));
return iter.release();
});
}
int cacheAndReturnWrapperMethod(JSContext* ctx, WrapperData* wrapperData,
JSPropertyDescriptor* desc, JSAtom atom, ValueRef val)
{
if (JS_IsUninitialized(wrapperData->methodCache))
{
wrapperData->methodCache = JS_NewObject(ctx);
if (JS_IsException(wrapperData->methodCache))
return -1;
}
if (JS_SetProperty(ctx, wrapperData->methodCache, atom, val.dup()) == -1)
return -1;
if (desc)
{
desc->flags = 0;
desc->value = val.release();
desc->getter = JS_UNDEFINED;
desc->setter = JS_UNDEFINED;
}
return int(true);
}
int wrapperGetOwnProperty(JSContext* ctx, JSPropertyDescriptor* desc, JSValueConst obj, JSAtom atom)
{
try
{
WrapperData* wrapperData = static_cast<WrapperData*>(
JS_GetOpaque2(ctx, obj, getRuntimeData(ctx)->wrapperClassId));
if (wrapperData == nullptr)
return -1;
// Check if we already have a cached value for this atom
if (!JS_IsUninitialized(wrapperData->methodCache))
{
ValueRef cachedValue(ctx, JS_GetProperty(ctx, wrapperData->methodCache, atom));
if (!JS_IsUndefined(cachedValue))
{
if (desc)
{
desc->flags = 0;
desc->value = cachedValue.release();
desc->getter = JS_UNDEFINED;
desc->setter = JS_UNDEFINED;
}
return int(true);
}
}
if (atom == getRuntimeData(ctx)->symbolIteratorAtom)
{
css::uno::Reference<css::container::XEnumeration> en(wrapperData->interface,
css::uno::UNO_QUERY);
if (!en.is())
{
return int(false);
}
ValueRef val(ctx, JS_NewCFunction(ctx, enumerationIterator, "[Symbol.iterator]", 0));
return cacheAndReturnWrapperMethod(ctx, wrapperData, desc, atom, std::move(val));
}
css::uno::Reference<css::script::XInvocation2> invoke(
css::script::Invocation::create(comphelper::getProcessComponentContext())
->createInstanceWithArguments({ css::uno::Any(wrapperData->interface) }),
css::uno::UNO_QUERY_THROW);
css::script::InvocationInfo info;
try
{
info = invoke->getInfoForName(OUString::fromUtf8(JS_AtomToCString(ctx, atom)), false);
}
catch (css::lang::IllegalArgumentException)
{
return int(false);
}
switch (info.eMemberType)
{
case css::script::MemberType_METHOD:
{
ValueRef data(ctx, JS_NewObjectClass(ctx, getRuntimeData(ctx)->pointerClassId));
[[maybe_unused]] auto const e
= JS_SetOpaque(data, new css::script::InvocationInfo(info));
assert(e == 0); //TODO
#if defined DBG_UTIL
getRuntimeData(ctx)->toFinalize.inc();
#endif
ValueRef val(ctx, JS_NewCFunctionData(ctx, invokeUno, info.aParamTypes.getLength(),
0, 1, data.ptr()));
return cacheAndReturnWrapperMethod(ctx, wrapperData, desc, atom, std::move(val));
}
case css::script::MemberType_PROPERTY:
if (desc)
{
desc->flags = 0;
desc->value = toJs(ctx, invoke->getValue(info.aName)).release();
desc->getter = JS_UNDEFINED;
desc->setter = JS_UNDEFINED;
}
return int(true);
default:
O3TL_UNREACHABLE;
}
}
catch (JsException)
{
return -1;
}
catch (css::uno::Exception)
{
auto const e = cppu::getCaughtException();
ValueRef val = toJs(ctx, e);
JS_Throw(ctx, val.release());
return -1;
}
}
int wrapperSetProperty(JSContext* ctx, JSValueConst obj, JSAtom atom, JSValueConst value,
JSValueConst, int flags)
{
try
{
ValueRef const v(ctx, JS_AtomToString(ctx, atom));
if (JS_IsString(v))
{
WrapperData* wrapperData = static_cast<WrapperData*>(
JS_GetOpaque2(ctx, obj, getRuntimeData(ctx)->wrapperClassId));
if (wrapperData == nullptr)
return -1;
css::uno::Reference<css::script::XInvocation2> invoke(
css::script::Invocation::create(comphelper::getProcessComponentContext())
->createInstanceWithArguments({ css::uno::Any(wrapperData->interface) }),
css::uno::UNO_QUERY_THROW);
css::script::InvocationInfo info;
auto prop = false;
try
{
info = invoke->getInfoForName(OUString::fromUtf8(JS_AtomToCString(ctx, atom)),
false);
prop = info.eMemberType == css::script::MemberType_PROPERTY;
}
catch (css::lang::IllegalArgumentException)
{
}
if (prop)
{
invoke->setValue(info.aName, fromJs(ctx, info.aType, value));
return 1;
}
}
return JS_DefinePropertyValue(ctx, obj, atom, JS_DupValue(ctx, value),
JS_PROP_C_W_E | (flags & JS_PROP_THROW));
}
catch (JsException)
{
return -1;
}
catch (css::uno::Exception)
{
auto const e = cppu::getCaughtException();
ValueRef val(ctx, toJs(ctx, e).release());
JS_Throw(ctx, val.release());
return -1;
}
catch (std::exception& e)
{
JS_ThrowPlainError(ctx, "std exception: %s", e.what());
return -1;
}
}
void wrapperGCMark(JSRuntime* rt, JSValueConst val, JS_MarkFunc* markFunc)
{
WrapperData* wrapperData
= static_cast<WrapperData*>(JS_GetOpaque(val, getRuntimeData(rt)->wrapperClassId));
if (wrapperData != nullptr)
JS_MarkValue(rt, wrapperData->methodCache, markFunc);
}
void wrapperFinalizer(JSRuntime* rt, JSValueConst val)
{
#if defined DBG_UTIL
getRuntimeData(rt)->toFinalize.dec();
#endif
WrapperData* wrapperData
= static_cast<WrapperData*>(JS_GetOpaque(val, getRuntimeData(rt)->wrapperClassId));
assert(wrapperData);
delete wrapperData;
}
JSValue wrapperToString(JSContext* ctx, JSValueConst this_val, int, JSValueConst*)
{
return callFromJs(ctx, [ctx, this_val] {
WrapperData* wrapperData = static_cast<WrapperData*>(
JS_GetOpaque2(ctx, this_val, getRuntimeData(ctx)->wrapperClassId));
if (wrapperData == nullptr)
return JS_EXCEPTION;
std::ostringstream s;
s << wrapperData->interface;
return JS_NewString(ctx, s.str().c_str());
});
}
JSValue wrapUnoObject(JSContext* ctx, css::uno::Reference<css::uno::XInterface> const& obj)
{
if (!obj.is())
{
return JS_NULL;
}
auto const val = JS_NewObjectClass(ctx, getRuntimeData(ctx)->wrapperClassId);
[[maybe_unused]] auto const e = JS_SetOpaque(val, new WrapperData(ctx, obj));
assert(e == 0); //TODO
#if defined DBG_UTIL
getRuntimeData(ctx)->toFinalize.inc();
#endif
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;
sal_Int32 value;
OUString name;
};
void enumeratorFinalizer(JSRuntime* rt, JSValueConst val)
{
#if defined DBG_UTIL
getRuntimeData(rt)->toFinalize.dec();
#endif
delete static_cast<EnumeratorData const*>(
JS_GetOpaque(val, getRuntimeData(rt)->enumeratorClassId));
}
JSValue enumeratorToString(JSContext* ctx, JSValueConst this_val, int, JSValueConst*)
{
return callFromJs(ctx, [ctx, this_val] {
auto const data = static_cast<EnumeratorData const*>(
JS_GetOpaque(this_val, getRuntimeData(ctx)->enumeratorClassId));
OUString const s(data->type.getTypeName() + "." + data->name);
return JS_NewStringUTF16(ctx, reinterpret_cast<std::uint16_t const*>(s.getStr()),
s.getLength());
});
}
void compoundFinalizer(JSRuntime* rt, JSValueConst val)
{
#if defined DBG_UTIL
getRuntimeData(rt)->toFinalize.dec();
#endif
typelib_typedescriptionreference_release(static_cast<typelib_TypeDescriptionReference*>(
JS_GetOpaque(val, getRuntimeData(rt)->compoundClassId)));
}
typelib_TypeDescriptionReference* typeAcquire(css::uno::Type const& type)
{
auto const ref = type.getTypeLibType();
typelib_typedescriptionreference_acquire(ref);
return ref;
}
void typeFinalizer(JSRuntime* rt, JSValueConst val)
{
#if defined DBG_UTIL
getRuntimeData(rt)->toFinalize.dec();
#endif
typelib_typedescriptionreference_release(static_cast<typelib_TypeDescriptionReference*>(
JS_GetOpaque(val, getRuntimeData(rt)->typeClassId)));
}
JSValue typeToString(JSContext* ctx, JSValueConst this_val, int, JSValueConst*)
{
return callFromJs(ctx, [ctx, this_val] {
auto const name
= css::uno::Type(static_cast<typelib_TypeDescriptionReference*>(
JS_GetOpaque(this_val, getRuntimeData(ctx)->typeClassId)))
.getTypeName();
return JS_NewStringUTF16(ctx, reinterpret_cast<std::uint16_t const*>(name.getStr()),
name.getLength());
});
}
void setTypeProperty(JSContext* ctx, JSValueConst obj, char const* prop, css::uno::Type const& type)
{
ValueRef val(ctx, JS_NewObjectClass(ctx, getRuntimeData(ctx)->typeClassId));
[[maybe_unused]] auto const e = JS_SetOpaque(val, typeAcquire(type));
assert(e == 0); //TODO
#if defined DBG_UTIL
getRuntimeData(ctx)->toFinalize.inc();
#endif
JS_SetPropertyStr(ctx, obj, prop, val.release());
}
JSValue unoTypeSequence(JSContext* ctx, JSValueConst, int argc, JSValueConst* argv)
{
return callFromJs(ctx, [ctx, argc, argv] {
if (argc < 1)
return JS_ThrowPlainError(ctx, "missing argument to uno.type.sequence");
if (JS_GetClassID(argv[0]) != getRuntimeData(ctx)->typeClassId)
{
JS_ThrowTypeError(ctx, "TODO: BAD UNO TYPE VALUE");
throw JsException();
}
css::uno::Type const type(
css::uno::TypeClass_SEQUENCE,
"[]"
+ css::uno::Type(static_cast<typelib_TypeDescriptionReference*>(
JS_GetOpaque(argv[0], getRuntimeData(ctx)->typeClassId)))
.getTypeName());
ValueRef val(ctx, JS_NewObjectClass(ctx, getRuntimeData(ctx)->typeClassId));
[[maybe_unused]] auto const e = JS_SetOpaque(val, typeAcquire(type));
assert(e == 0); //TODO
#if defined DBG_UTIL
getRuntimeData(ctx)->toFinalize.inc();
#endif
return val.release();
});
}
JSValue unoTypeEnum(JSContext* ctx, JSValueConst, int argc, JSValueConst* argv)
{
return callFromJs(ctx, [ctx, argc, argv] {
if (argc < 1)
return JS_ThrowPlainError(ctx, "missing argument to uno.type.enum");
if (JS_GetClassID(argv[0]) != getRuntimeData(ctx)->enumClassId)
{
JS_ThrowTypeError(ctx, "TODO: BAD UNO TYPE VALUE");
throw JsException();
}
css::uno::Type const type(static_cast<typelib_TypeDescriptionReference*>(
JS_GetOpaque(argv[0], getRuntimeData(ctx)->enumClassId)));
ValueRef val(ctx, JS_NewObjectClass(ctx, getRuntimeData(ctx)->typeClassId));
[[maybe_unused]] auto const e = JS_SetOpaque(val, typeAcquire(type));
assert(e == 0); //TODO
#if defined DBG_UTIL
getRuntimeData(ctx)->toFinalize.inc();
#endif
return val.release();
});
}
JSValue unoTypeStruct(JSContext* ctx, JSValueConst, int argc, JSValueConst* argv)
{
return callFromJs(ctx, [ctx, argc, argv] {
if (argc < 1)
return JS_ThrowPlainError(ctx, "missing argument to uno.type.struct");
ValueRef data(ctx, JS_GetPropertyStr(ctx, argv[0], "'data"));
if (JS_IsException(data))
{
throw JsException();
}
if (JS_GetClassID(data) != getRuntimeData(ctx)->structClassId)
{
JS_ThrowTypeError(ctx, "TODO: BAD UNO TYPE VALUE");
throw JsException();
}
css::uno::Reference td(static_cast<css::reflection::XStructTypeDescription*>(
JS_GetOpaque(data, getRuntimeData(ctx)->structClassId)));
OUStringBuffer buf(td->getName());
auto const params = td->getTypeParameters();
if (params.hasElements())
{
if (argc < 2)
{
JS_ThrowSyntaxError(ctx, "TODO: MISSING TYPE ARGUMENTS");
throw JsException();
}
if (!JS_IsArray(argv[1]))
{
JS_ThrowTypeError(ctx, "TODO: BAD TYPE ARGUMENTS");
throw JsException();
}
ValueRef len1(ctx, JS_GetPropertyStr(ctx, argv[1], "length"));
if (JS_IsException(len1))
{
throw JsException();
}
std::int64_t len2;
auto const e = JS_ToInt64(ctx, &len2, len1);
if (e != 0)
{
throw JsException();
}
if (len2 != params.getLength())
{
JS_ThrowTypeError(ctx, "TODO: BAD TYPE ARGUMENTS");
throw JsException();
}
buf.append('<');
for (sal_Int32 i = 0; i != params.getLength(); ++i)
{
if (i != 0)
{
buf.append(',');
}
ValueRef arg(ctx, JS_GetPropertyInt64(ctx, argv[1], i));
if (JS_IsException(arg))
{
throw JsException();
}
if (JS_GetClassID(arg) != getRuntimeData(ctx)->typeClassId)
{
JS_ThrowTypeError(ctx, "TODO: BAD TYPE ARGUMENT");
throw JsException();
}
css::uno::Type argType(static_cast<typelib_TypeDescriptionReference*>(
JS_GetOpaque(arg, getRuntimeData(ctx)->typeClassId)));
switch (argType.getTypeClass())
{
case css::uno::TypeClass_VOID:
case css::uno::TypeClass_UNSIGNED_SHORT:
case css::uno::TypeClass_UNSIGNED_LONG:
case css::uno::TypeClass_UNSIGNED_HYPER:
case css::uno::TypeClass_EXCEPTION:
JS_ThrowTypeError(ctx, "TODO: BAD TYPE ARGUMENT");
throw JsException();
default:
break;
}
buf.append(argType.getTypeName());
}
buf.append('>');
}
css::uno::Type const type(td->getTypeClass(), buf.makeStringAndClear());
ValueRef val(ctx, JS_NewObjectClass(ctx, getRuntimeData(ctx)->typeClassId));
[[maybe_unused]] auto const e = JS_SetOpaque(val, typeAcquire(type));
assert(e == 0); //TODO
#if defined DBG_UTIL
getRuntimeData(ctx)->toFinalize.inc();
#endif
return val.release();
});
}
JSValue unoTypeException(JSContext* ctx, JSValueConst, int argc, JSValueConst* argv)
{
return callFromJs(ctx, [ctx, argc, argv] {
if (argc < 1)
return JS_ThrowPlainError(ctx, "missing argument to uno.type.exception");
ValueRef data(ctx, JS_GetPropertyStr(ctx, argv[0], "'data"));
if (JS_IsException(data))
{
throw JsException();
}
if (JS_GetClassID(data) != getRuntimeData(ctx)->exceptionClassId)
{
JS_ThrowTypeError(ctx, "TODO: BAD UNO TYPE VALUE");
throw JsException();
}
css::uno::Type const type(static_cast<typelib_TypeDescriptionReference*>(
JS_GetOpaque(data, getRuntimeData(ctx)->exceptionClassId)));
ValueRef val(ctx, JS_NewObjectClass(ctx, getRuntimeData(ctx)->typeClassId));
[[maybe_unused]] auto const e = JS_SetOpaque(val, typeAcquire(type));
assert(e == 0); //TODO
#if defined DBG_UTIL
getRuntimeData(ctx)->toFinalize.inc();
#endif
return val.release();
});
}
JSValue unoTypeInterface(JSContext* ctx, JSValueConst, int argc, JSValueConst* argv)
{
return callFromJs(ctx, [ctx, argc, argv] {
if (argc < 1)
return JS_ThrowPlainError(ctx, "missing argument to uno.type.interface");
if (JS_GetClassID(argv[0]) != getRuntimeData(ctx)->interfaceClassId)
{
JS_ThrowTypeError(ctx, "TODO: BAD UNO TYPE VALUE");
throw JsException();
}
css::uno::Type const type(static_cast<typelib_TypeDescriptionReference*>(
JS_GetOpaque(argv[0], getRuntimeData(ctx)->interfaceClassId)));
ValueRef val(ctx, JS_NewObjectClass(ctx, getRuntimeData(ctx)->typeClassId));
[[maybe_unused]] auto const e = JS_SetOpaque(val, typeAcquire(type));
assert(e == 0); //TODO
#if defined DBG_UTIL
getRuntimeData(ctx)->toFinalize.inc();
#endif
return val.release();
});
}
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
getRuntimeData(rt)->toFinalize.dec();
#endif
typelib_typedescriptionreference_release(static_cast<typelib_TypeDescriptionReference*>(
JS_GetOpaque(val, getRuntimeData(rt)->enumClassId)));
}
void structFinalizer(JSRuntime* rt, JSValueConst val)
{
#if defined DBG_UTIL
getRuntimeData(rt)->toFinalize.dec();
#endif
static_cast<css::reflection::XStructTypeDescription*>(
JS_GetOpaque(val, getRuntimeData(rt)->structClassId))
->release();
}
JSValue structCtor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv)
{
return callFromJs(ctx, [ctx, new_target, argc, argv] {
ValueRef data(ctx, JS_GetPropertyStr(ctx, new_target, "'data"));
if (JS_IsException(data))
{
throw JsException();
}
css::uno::Reference td(static_cast<css::reflection::XStructTypeDescription*>(
JS_GetOpaque(data, getRuntimeData(ctx)->structClassId)));
int argIdx = 0;
auto const params = td->getTypeParameters();
std::vector<css::uno::Type> typeArgs;
OUStringBuffer buf(td->getName());
if (params.hasElements())
{
assert(!td->getBaseType());
if (argc < 1)
{
JS_ThrowSyntaxError(ctx, "TODO: MISSING TYPE ARGUMENTS");
throw JsException();
}
if (!JS_IsArray(argv[0]))
{
JS_ThrowTypeError(ctx, "TODO: BAD TYPE ARGUMENTS");
throw JsException();
}
ValueRef len1(ctx, JS_GetPropertyStr(ctx, argv[0], "length"));
if (JS_IsException(len1))
{
throw JsException();
}
std::int64_t len2;
auto const e = JS_ToInt64(ctx, &len2, len1);
if (e != 0)
{
throw JsException();
}
if (len2 != params.getLength())
{
JS_ThrowTypeError(ctx, "TODO: BAD TYPE ARGUMENTS");
throw JsException();
}
buf.append('<');
for (std::int64_t i = 0; i != len2; ++i)
{
if (i != 0)
{
buf.append(',');
}
ValueRef arg(ctx, JS_GetPropertyInt64(ctx, argv[0], i));
if (JS_IsException(arg))
{
throw JsException();
}
if (JS_GetClassID(arg) != getRuntimeData(ctx)->typeClassId)
{
JS_ThrowTypeError(ctx, "TODO: BAD TYPE ARGUMENT");
throw JsException();
}
css::uno::Type argType(static_cast<typelib_TypeDescriptionReference*>(
JS_GetOpaque(arg, getRuntimeData(ctx)->typeClassId)));
switch (argType.getTypeClass())
{
case css::uno::TypeClass_VOID:
case css::uno::TypeClass_UNSIGNED_SHORT:
case css::uno::TypeClass_UNSIGNED_LONG:
case css::uno::TypeClass_UNSIGNED_HYPER:
case css::uno::TypeClass_EXCEPTION:
JS_ThrowTypeError(ctx, "TODO: BAD TYPE ARGUMENT");
throw JsException();
default:
break;
}
typeArgs.push_back(argType);
buf.append(argType.getTypeName());
}
buf.append('>');
++argIdx;
}
css::uno::Type type(css::uno::TypeClass_STRUCT, buf.makeStringAndClear());
ValueRef proto(ctx, JS_GetPropertyStr(ctx, new_target, "prototype"));
if (JS_IsException(proto))
{
throw JsException();
}
ValueRef obj(ctx, JS_NewObjectProtoClass(ctx, proto, getRuntimeData(ctx)->compoundClassId));
if (JS_IsException(obj))
{
throw JsException();
}
JS_SetOpaque(obj, typeAcquire(type));
#if defined DBG_UTIL
getRuntimeData(ctx)->toFinalize.inc();
#endif
for (css::uno::Reference<css::reflection::XCompoundTypeDescription> ctd(
td, css::uno::UNO_QUERY_THROW);
;)
{
auto const memberNames = ctd->getMemberNames();
auto const memberTypes = ctd->getMemberTypes();
assert(memberNames.getLength() == memberTypes.getLength());
for (sal_Int32 i = 0; i != memberNames.getLength(); ++i)
{
auto const name = memberNames[i].toUtf8();
AtomRef const a(ctx, JS_NewAtomLen(ctx, name.getStr(), name.getLength()));
assert(a != JS_ATOM_NULL); //TODO
ValueRef val(ctx);
int has = 0;
if (argc > argIdx)
{
has = JS_HasProperty(ctx, argv[argIdx], a);
if (has == -1)
{
throw JsException();
}
if (has == 1)
{
val = JS_GetProperty(ctx, argv[argIdx], a);
if (JS_IsException(val))
{
throw JsException();
}
}
}
if (has == 0)
{
css::uno::Type memType;
if (memberTypes[i]->getTypeClass() == css::uno::TypeClass_UNKNOWN)
{
sal_Int32 j = 0;
for (auto const& param : params)
{
if (memberTypes[i]->getName() == param)
{
break;
}
++j;
}
assert(o3tl::make_unsigned(j) < typeArgs.size());
memType = typeArgs[j];
}
else
{
memType = css::uno::Type(memberTypes[i]->getTypeClass(),
memberTypes[i]->getName());
}
val = createDefaultValue(ctx, memType);
}
JS_SetProperty(ctx, obj, a, val.release());
}
auto const base = ctd->getBaseType();
if (!base.is())
{
break;
}
ctd.set(base, css::uno::UNO_QUERY_THROW);
}
return obj.release();
});
}
void exceptionFinalizer(JSRuntime* rt, JSValueConst val)
{
#if defined DBG_UTIL
getRuntimeData(rt)->toFinalize.dec();
#endif
typelib_typedescriptionreference_release(static_cast<typelib_TypeDescriptionReference*>(
JS_GetOpaque(val, getRuntimeData(rt)->exceptionClassId)));
}
JSValue exceptionCtor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv)
{
return callFromJs(ctx, [ctx, new_target, argc, argv] {
ValueRef data(ctx, JS_GetPropertyStr(ctx, new_target, "'data"));
if (JS_IsException(data))
{
throw JsException();
}
css::uno::Type const type(static_cast<typelib_TypeDescriptionReference*>(
JS_GetOpaque(data, getRuntimeData(ctx)->exceptionClassId)));
css::uno::TypeDescription desc(type);
auto compDesc = reinterpret_cast<typelib_CompoundTypeDescription const*>(desc.get());
ValueRef proto(ctx, JS_GetPropertyStr(ctx, new_target, "prototype"));
if (JS_IsException(proto))
{
throw JsException();
}
ValueRef obj(ctx, JS_NewObjectProtoClass(ctx, proto, getRuntimeData(ctx)->compoundClassId));
if (JS_IsException(obj))
{
throw JsException();
}
JS_SetOpaque(obj, typeAcquire(type));
#if defined DBG_UTIL
getRuntimeData(ctx)->toFinalize.inc();
#endif
for (;;)
{
for (sal_Int32 i = 0; i != compDesc->nMembers; ++i)
{
auto const name = OUString::unacquired(&compDesc->ppMemberNames[i]).toUtf8();
AtomRef const a(ctx, JS_NewAtomLen(ctx, name.getStr(), name.getLength()));
assert(a != JS_ATOM_NULL); //TODO
ValueRef val(ctx);
int has = 0;
if (argc >= 1)
{
has = JS_HasProperty(ctx, argv[0], a);
if (has == -1)
{
throw JsException();
}
if (has == 1)
{
val = JS_GetProperty(ctx, argv[0], a);
if (JS_IsException(val))
{
throw JsException();
}
}
}
if (has == 0)
{
val = createDefaultValue(ctx, compDesc->ppTypeRefs[i]);
}
JS_SetProperty(ctx, obj, a, val.release());
}
compDesc = compDesc->pBaseTypeDescription;
if (compDesc == nullptr)
{
break;
}
}
return obj.release();
});
}
void interfaceFinalizer(JSRuntime* rt, JSValueConst val)
{
#if defined DBG_UTIL
getRuntimeData(rt)->toFinalize.dec();
#endif
typelib_typedescriptionreference_release(static_cast<typelib_TypeDescriptionReference*>(
JS_GetOpaque(val, getRuntimeData(rt)->interfaceClassId)));
}
struct CtorData
{
OUString service;
css::uno::Reference<css::reflection::XServiceConstructorDescription> ctor;
};
void ctorFinalizer(JSRuntime* rt, JSValueConst val)
{
#if defined DBG_UTIL
getRuntimeData(rt)->toFinalize.dec();
#endif
delete static_cast<CtorData*>(JS_GetOpaque(val, getRuntimeData(rt)->ctorClassId));
}
JSValue createService(JSContext* ctx, JSValueConst, int argc, JSValueConst* argv, int,
JSValueConst* func_data)
{
return callFromJs(ctx, [ctx, argc, argv, func_data] {
auto const data
= static_cast<CtorData*>(JS_GetOpaque(func_data[0], getRuntimeData(ctx)->ctorClassId));
auto const params = data->ctor->getParameters();
auto const variadic
= params.hasElements() && params[params.getLength() - 1]->isRestParameter();
if (variadic ? (argc < params.getLength()) : (argc != params.getLength() + 1))
{
JS_ThrowSyntaxError(ctx, "TODO: BAD NUMBER OF ARGUMENTS");
throw JsException();
}
css::uno::Reference<css::uno::XComponentContext> context(
fromJs(ctx, cppu::UnoType<css::uno::XComponentContext>::get(), argv[0]),
css::uno::UNO_QUERY_THROW);
css::uno::Sequence<css::uno::Any> args(argc - 1);
for (sal_Int32 i = 0; i != params.getLength(); ++i)
{
assert(params[i]->isIn());
assert(!params[i]->isOut());
if (params[i]->isRestParameter())
{
assert(i == params.getLength() - 1);
for (sal_Int32 j = i; j != argc - 1; ++j)
{
args.getArray()[j] = fromJs(ctx,
css::uno::Type(params[i]->getType()->getTypeClass(),
params[i]->getType()->getName()),
argv[j + 1]);
}
}
else
{
args.getArray()[i] = fromJs(ctx,
css::uno::Type(params[i]->getType()->getTypeClass(),
params[i]->getType()->getName()),
argv[i + 1]);
}
}
css::uno::Reference<css::uno::XInterface> ifc(
data->ctor->isDefaultConstructor()
? context->getServiceManager()->createInstanceWithContext(data->service, context)
: context->getServiceManager()->createInstanceWithArgumentsAndContext(
data->service, args, context),
css::uno::UNO_SET_THROW);
return toJs(ctx, css::uno::Any(ifc)).release();
});
}
void singletonFinalizer(JSRuntime* rt, JSValueConst val)
{
#if defined DBG_UTIL
getRuntimeData(rt)->toFinalize.dec();
#endif
rtl_uString_release(
static_cast<rtl_uString*>(JS_GetOpaque(val, getRuntimeData(rt)->singletonClassId)));
}
JSValue getSingleton(JSContext* ctx, JSValueConst, int argc, JSValueConst* argv, int,
JSValueConst* func_data)
{
return callFromJs(ctx, [ctx, argc, argv, func_data] {
if (argc < 1)
return JS_ThrowPlainError(ctx, "missing argument to getSingleton");
auto const s = static_cast<rtl_uString*>(
JS_GetOpaque(func_data[0], getRuntimeData(ctx)->singletonClassId));
css::uno::Reference<css::uno::XComponentContext> context(
fromJs(ctx, cppu::UnoType<css::uno::XComponentContext>::get(), argv[0]),
css::uno::UNO_QUERY_THROW);
css::uno::Reference<css::uno::XInterface> ifc(
context->getValueByName("/singletons/" + OUString::unacquired(&s)),
css::uno::UNO_QUERY_THROW);
return toJs(ctx, css::uno::Any(ifc)).release();
});
}
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
= static_cast<ModuleData*>(JS_GetOpaque(val, getRuntimeData(rt)->moduleClassId));
if (moduleData != nullptr)
JS_MarkValue(rt, moduleData->memberCache, markFunc);
}
void moduleFinalizer(JSRuntime* rt, JSValueConst val)
{
#if defined DBG_UTIL
getRuntimeData(rt)->toFinalize.dec();
#endif
ModuleData* moduleData
= static_cast<ModuleData*>(JS_GetOpaque(val, getRuntimeData(rt)->moduleClassId));
assert(moduleData);
delete moduleData;
}
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),
css::uno::UNO_QUERY_THROW);
if (!mgr->hasByHierarchicalName(id))
{
return ValueRef(ctx, JS_UNDEFINED);
}
css::uno::Reference<css::reflection::XTypeDescription> td(mgr->getByHierarchicalName(id),
css::uno::UNO_QUERY_THROW);
auto const tc = td->getTypeClass();
ValueRef val(ctx);
switch (tc)
{
case css::uno::TypeClass_ENUM:
{
css::uno::Type type(tc, id);
val = JS_NewObjectClass(ctx, getRuntimeData(ctx)->enumClassId);
[[maybe_unused]] auto e = JS_SetOpaque(val, typeAcquire(type));
assert(e == 0); //TODO
#if defined DBG_UTIL
getRuntimeData(ctx)->toFinalize.inc();
#endif
css::uno::TypeDescription desc(type);
auto const enumDesc = reinterpret_cast<typelib_EnumTypeDescription const*>(desc.get());
for (sal_Int32 i = 0; i != enumDesc->nEnumValues; ++i)
{
ValueRef val2(ctx, JS_NewObjectClass(ctx, getRuntimeData(ctx)->enumeratorClassId));
e = JS_SetOpaque(
val2, new EnumeratorData{ type, enumDesc->pEnumValues[i],
OUString::unacquired(&enumDesc->ppEnumNames[i]) });
assert(e == 0); //TODO
#if defined DBG_UTIL
getRuntimeData(ctx)->toFinalize.inc();
#endif
JS_SetPropertyStr(ctx, val,
OUString::unacquired(&enumDesc->ppEnumNames[i]).toUtf8().getStr(),
val2.release());
}
break;
}
case css::uno::TypeClass_STRUCT:
{
css::uno::Reference<css::reflection::XStructTypeDescription> std(
td, css::uno::UNO_QUERY_THROW);
assert(!std->getTypeArguments().hasElements());
ValueRef proto(ctx, JS_NewObject(ctx));
assert(!JS_IsException(proto)); //TODO
val = JS_NewCFunction2(ctx, structCtor, "TODO", 0, JS_CFUNC_constructor, 0);
assert(!JS_IsException(val)); //TODO
JS_SetConstructor(ctx, val, proto);
ValueRef data(ctx, JS_NewObjectClass(ctx, getRuntimeData(ctx)->structClassId));
[[maybe_unused]] auto const e = JS_SetOpaque(data, std.get());
assert(e == 0); //TODO
std->acquire();
#if defined DBG_UTIL
getRuntimeData(ctx)->toFinalize.inc();
#endif
JS_DefinePropertyValueStr(ctx, val, "'data", data.release(), 0);
break;
}
case css::uno::TypeClass_EXCEPTION:
{
ValueRef proto(ctx, JS_NewObject(ctx));
assert(!JS_IsException(proto)); //TODO
val = JS_NewCFunction2(ctx, exceptionCtor, "TODO", 0, JS_CFUNC_constructor, 0);
assert(!JS_IsException(val)); //TODO
JS_SetConstructor(ctx, val, proto);
ValueRef data(ctx, JS_NewObjectClass(ctx, getRuntimeData(ctx)->exceptionClassId));
[[maybe_unused]] auto const e = JS_SetOpaque(data, typeAcquire(css::uno::Type(tc, id)));
assert(e == 0); //TODO
#if defined DBG_UTIL
getRuntimeData(ctx)->toFinalize.inc();
#endif
JS_DefinePropertyValueStr(ctx, val, "'data", data.release(), 0);
break;
}
case css::uno::TypeClass_INTERFACE:
{
val = JS_NewObjectClass(ctx, getRuntimeData(ctx)->interfaceClassId);
[[maybe_unused]] auto const e = JS_SetOpaque(val, typeAcquire(css::uno::Type(tc, id)));
assert(e == 0); //TODO
#if defined DBG_UTIL
getRuntimeData(ctx)->toFinalize.inc();
#endif
break;
}
case css::uno::TypeClass_MODULE:
{
val = JS_NewObjectClass(ctx, getRuntimeData(ctx)->moduleClassId);
[[maybe_unused]] auto const e = JS_SetOpaque(val, new ModuleData(ctx, id));
assert(e == 0); //TODO
#if defined DBG_UTIL
getRuntimeData(ctx)->toFinalize.inc();
#endif
break;
}
case css::uno::TypeClass_CONSTANTS:
{
val = JS_NewObject(ctx);
if (JS_IsException(val))
return val;
css::uno::Reference<css::reflection::XConstantsTypeDescription> cstd(
td, css::uno::UNO_QUERY_THROW);
for (auto const& ctd : cstd->getConstants())
{
ValueRef con = toJs(ctx, ctd->getConstantValue());
auto const name = ctd->getName();
auto const n = name.lastIndexOf('.');
if (JS_SetPropertyStr(ctx, val, name.copy(n + 1).toUtf8().getStr(), con.release())
== -1)
{
return ValueRef(ctx, JS_EXCEPTION);
}
}
break;
}
case css::uno::TypeClass_SERVICE:
{
css::uno::Reference<css::reflection::XServiceTypeDescription2> std(
td, css::uno::UNO_QUERY_THROW);
if (!std->isSingleInterfaceBased())
{
val = JS_UNDEFINED;
break;
}
val = JS_NewObject(ctx);
if (JS_IsException(val))
{
return val;
}
for (auto const& ctor : std->getConstructors())
{
ValueRef data(ctx, JS_NewObjectClass(ctx, getRuntimeData(ctx)->ctorClassId));
[[maybe_unused]] auto const e = JS_SetOpaque(data, new CtorData{ id, ctor });
assert(e == 0); //TODO
#if defined DBG_UTIL
getRuntimeData(ctx)->toFinalize.inc();
#endif
ValueRef fun(ctx, JS_NewCFunctionData(ctx, createService,
ctor->getParameters().getLength() + 1, 0, 1,
data.ptr()));
if (JS_IsException(fun))
{
return fun;
}
if (JS_SetPropertyStr(
ctx, val,
ctor->isDefaultConstructor() ? "create" : ctor->getName().toUtf8().getStr(),
fun.release())
== -1)
{
return ValueRef(ctx, JS_EXCEPTION);
}
}
break;
}
case css::uno::TypeClass_SINGLETON:
{
ValueRef data(ctx, JS_NewObjectClass(ctx, getRuntimeData(ctx)->singletonClassId));
[[maybe_unused]] auto const e = JS_SetOpaque(data, stringAcquire(id));
assert(e == 0); //TODO
#if defined DBG_UTIL
getRuntimeData(ctx)->toFinalize.inc();
#endif
val = JS_NewCFunctionData(ctx, getSingleton, 1, 0, 1, data.ptr());
break;
}
case css::uno::TypeClass_TYPEDEF:
val = JS_UNDEFINED;
break;
default:
O3TL_UNREACHABLE;
}
return val;
}
int moduleGetOwnProperty(JSContext* ctx, JSPropertyDescriptor* propertyDesc, JSValueConst obj,
JSAtom atom)
{
ModuleData* moduleData
= static_cast<ModuleData*>(JS_GetOpaque2(ctx, obj, getRuntimeData(ctx)->moduleClassId));
if (moduleData == nullptr)
return -1;
// Check if we already have a cached value for this atom
if (!JS_IsUninitialized(moduleData->memberCache))
{
ValueRef cachedValue(ctx, JS_GetProperty(ctx, moduleData->memberCache, atom));
if (!JS_IsUndefined(cachedValue))
{
if (propertyDesc)
{
propertyDesc->flags = 0;
propertyDesc->value = cachedValue.release();
propertyDesc->getter = JS_UNDEFINED;
propertyDesc->setter = JS_UNDEFINED;
}
return int(true);
}
}
OUStringBuffer buf(moduleData->name);
if (!buf.isEmpty())
{
buf.append('.');
}
buf.append(OUString::fromUtf8(JS_AtomToCString(ctx, atom)));
auto const id = buf.makeStringAndClear();
ValueRef val = moduleGetOwnPropertyUncached(ctx, id);
if (JS_IsUninitialized(val) || JS_IsUndefined(val))
return int(false);
if (JS_IsException(val))
return -1;
if (JS_IsUninitialized(moduleData->memberCache))
{
moduleData->memberCache = JS_NewObject(ctx);
if (JS_IsException(moduleData->memberCache))
return -1;
}
if (JS_SetProperty(ctx, moduleData->memberCache, atom, val.dup()) == -1)
return -1;
if (propertyDesc)
{
propertyDesc->flags = 0;
propertyDesc->value = val.release();
propertyDesc->getter = JS_UNDEFINED;
propertyDesc->setter = JS_UNDEFINED;
}
return int(true);
}
ValueRef getUnoidlRepresentation(JSContext* ctx, std::u16string_view id)
{
ValueRef const global(ctx, JS_GetGlobalObject(ctx));
ValueRef const uno(ctx, JS_GetPropertyStr(ctx, global, "uno"));
assert(JS_IsObject(uno));
ValueRef obj(ctx, JS_GetPropertyStr(ctx, uno, "idl"));
assert(JS_IsObject(obj));
std::size_t idx = 0;
do
{
ValueRef next(
ctx,
JS_GetPropertyStr(ctx, obj, OUString(o3tl::getToken(id, u'.', idx)).toUtf8().getStr()));
assert(JS_IsObject(next)); //TODO
obj = std::move(next);
} while (idx != std::u16string_view::npos);
return obj;
}
ValueRef createDefaultValue(JSContext* ctx, css::uno::Type const& type)
{
switch (type.getTypeClass())
{
case css::uno::TypeClass_BOOLEAN:
return ValueRef(ctx, JS_NewBool(ctx, false));
case css::uno::TypeClass_BYTE:
case css::uno::TypeClass_SHORT:
case css::uno::TypeClass_UNSIGNED_SHORT:
case css::uno::TypeClass_LONG:
case css::uno::TypeClass_UNSIGNED_LONG:
case css::uno::TypeClass_FLOAT:
case css::uno::TypeClass_DOUBLE:
return ValueRef(ctx, JS_NewInt32(ctx, 0));
case css::uno::TypeClass_HYPER:
case css::uno::TypeClass_UNSIGNED_HYPER:
return ValueRef(ctx, JS_NewBigInt64(ctx, 0));
case css::uno::TypeClass_CHAR:
return ValueRef(ctx, JS_NewStringLen(ctx, "\0", 1));
case css::uno::TypeClass_STRING:
return ValueRef(ctx, JS_NewString(ctx, ""));
case css::uno::TypeClass_TYPE:
return mapTypeToJs(ctx, cppu::UnoType<void>::get());
case css::uno::TypeClass_ANY:
return ValueRef(ctx, JS_UNDEFINED);
case css::uno::TypeClass_SEQUENCE:
return ValueRef(ctx, JS_NewArray(ctx));
case css::uno::TypeClass_ENUM:
{
css::uno::TypeDescription desc(type);
auto const enumDesc = reinterpret_cast<typelib_EnumTypeDescription const*>(desc.get());
auto val = getUnoidlRepresentation(
ctx, Concat2View(type.getTypeName() + "."
+ OUString::unacquired(&enumDesc->ppEnumNames[0])));
assert(JS_GetClassID(val) == getRuntimeData(ctx)->enumeratorClassId); //TODO
return val;
}
case css::uno::TypeClass_STRUCT:
{
auto const id = type.getTypeName();
auto n = id.indexOf('<');
auto const rep = getUnoidlRepresentation(ctx, n == -1 ? id : id.subView(0, n));
ValueRef val(ctx);
if (n == -1)
{
val = JS_CallConstructor(ctx, rep, 0, nullptr);
if (JS_IsException(val))
{
throw JsException();
}
}
else
{
ValueRef args(ctx, JS_NewArray(ctx));
if (JS_IsException(args))
{
throw JsException();
}
std::uint32_t arg = 0;
assert(id.endsWith(">"));
sal_Int32 nest = 0;
++n;
css::uno::Reference<css::container::XHierarchicalNameAccess> mgr(
comphelper::getProcessComponentContext()->getValueByName(
u"/singletons/com.sun.star.reflection.theTypeDescriptionManager"_ustr),
css::uno::UNO_QUERY_THROW);
for (sal_Int32 i = n;; ++i)
{
if (i == id.getLength() - 1 || (nest == 0 && id[i] == ','))
{
assert(nest == 0);
css::uno::Reference<css::reflection::XTypeDescription> td(
mgr->getByHierarchicalName(id.copy(n, i - n)),
css::uno::UNO_QUERY_THROW);
if (JS_SetPropertyUint32(
ctx, args, arg++,
mapTypeToJs(ctx, css::uno::Type(td->getTypeClass(), td->getName()))
.release())
== -1)
{
throw JsException();
}
if (i == id.getLength() - 1)
{
break;
}
n = i + 1;
}
else if (id[i] == '<')
{
++nest;
}
else if (id[i] == '>')
{
assert(nest != 0);
--nest;
}
}
val = JS_CallConstructor(ctx, rep, 1, args.ptr());
if (JS_IsException(val))
{
throw JsException();
}
}
return val;
}
case css::uno::TypeClass_INTERFACE:
return ValueRef(ctx, JS_NULL);
default:
O3TL_UNREACHABLE;
}
}
JSValue unoAny(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv)
{
return callFromJs(ctx, [ctx, new_target, argc, argv] {
if (argc < 1)
return JS_ThrowPlainError(ctx, "missing argument to uno.Any constructor");
ValueRef proto(ctx, JS_GetPropertyStr(ctx, new_target, "prototype"));
if (JS_IsException(proto))
{
throw JsException();
}
ValueRef obj(ctx, JS_NewObjectProto(ctx, proto));
if (JS_IsException(obj))
{
throw JsException();
}
JS_SetPropertyStr(ctx, obj, "type", JS_DupValue(ctx, argv[0]));
JS_SetPropertyStr(ctx, obj, "val", argc == 1 ? JS_UNDEFINED : JS_DupValue(ctx, argv[1]));
return obj.release();
});
}
css::uno::Any fromJsString(JSContext* ctx, JSValueConst val)
{
std::size_t n;
UniqueCString16 const p(ctx, JS_ToCStringLenUTF16(ctx, &n, val));
if (p.get() == nullptr)
{
throw JsException();
}
if (n > std::numeric_limits<sal_Int32>::max())
{
JS_ThrowRangeError(ctx, "Bad UNO string value");
throw JsException();
}
OUString s(p.get(), n);
//TODO: check for valid UTF-16
return css::uno::Any(s);
}
css::uno::Any fromJsSequence(JSContext* ctx, css::uno::Type const& type, JSValueConst val)
{
if (!JS_IsArray(val))
{
JS_ThrowTypeError(ctx, "TODO: BAD UNO SEQUENCE VALUE");
throw JsException();
}
ValueRef len1(ctx, JS_GetPropertyStr(ctx, val, "length"));
if (JS_IsException(len1))
{
throw JsException();
}
std::int64_t len2;
auto const e = JS_ToInt64(ctx, &len2, len1);
if (e != 0)
{
throw JsException();
}
if (len2 < 0 || len2 > std::numeric_limits<sal_Int32>::max())
{
JS_ThrowTypeError(ctx, "TODO: BAD UNO SEQUENCE VALUE");
throw JsException();
}
css::uno::TypeDescription desc(type);
css::uno::TypeDescription elemDesc(
reinterpret_cast<typelib_IndirectTypeDescription const*>(desc.get())->pType);
uno_Sequence* seq;
uno_sequence_construct(&seq, desc.get(), nullptr, len2, css::uno::cpp_acquire);
for (std::int64_t i = 0; i != len2; ++i)
{
ValueRef elem(ctx, JS_GetPropertyInt64(ctx, val, i));
if (JS_IsException(elem))
{
uno_type_sequence_destroy(seq, desc.get()->pWeakRef, css::uno::cpp_release);
throw JsException();
}
auto const any = fromJs(ctx, elemDesc.get()->pWeakRef, elem);
uno_copyData(
seq->elements + (i * elemDesc.get()->nSize),
const_cast<void*>(type == cppu::UnoType<css::uno::Sequence<css::uno::Any>>::get()
? &any
: any.getValue()),
elemDesc.get(), css::uno::cpp_acquire);
}
return { &seq, type };
}
css::uno::Any fromJsEnum(JSContext* ctx, JSValueConst val)
{
if (JS_GetClassID(val) != getRuntimeData(ctx)->enumeratorClassId)
{
JS_ThrowTypeError(ctx, "TODO: BAD UNO ENUM VALUE");
throw JsException();
}
auto const data = static_cast<EnumeratorData const*>(
JS_GetOpaque(val, getRuntimeData(ctx)->enumeratorClassId));
return { &data->value, data->type };
}
css::uno::Any fromJsInterface(JSContext* ctx, css::uno::Type const& type, JSValueConst val)
{
if (JS_IsNull(val))
{
return { nullptr, type };
}
if (JS_GetClassID(val) != getRuntimeData(ctx)->wrapperClassId)
{
JS_ThrowTypeError(ctx, "TODO: BAD UNO ENUM VALUE");
throw JsException();
}
WrapperData* wrapperData
= static_cast<WrapperData*>(JS_GetOpaque2(ctx, val, getRuntimeData(ctx)->wrapperClassId));
if (wrapperData == nullptr)
throw JsException();
auto const a = wrapperData->interface->queryInterface(type);
if (!a.hasValue())
{
JS_ThrowTypeError(ctx, "TODO: BAD UNO INTERFACE VALUE");
throw JsException();
}
return a;
}
css::uno::Any fromJs(JSContext* ctx, css::uno::Type const& type, JSValueConst val)
{
switch (type.getTypeClass())
{
case css::uno::TypeClass_VOID:
return {};
case css::uno::TypeClass_BOOLEAN:
{
auto const n = JS_ToBool(ctx, val);
assert(n != -1);
return css::uno::Any(n == 1);
}
case css::uno::TypeClass_BYTE:
{
double n;
auto const e = JS_ToFloat64(ctx, &n, val);
if (e != 0)
{
throw JsException();
}
n = std::trunc(n);
if (n < SAL_MIN_INT8 || n > SAL_MAX_INT8)
{
JS_ThrowRangeError(ctx, "Bad UNO byte val %f", n);
throw JsException();
}
return css::uno::Any(static_cast<sal_Int8>(n));
}
case css::uno::TypeClass_SHORT:
{
double n;
auto const e = JS_ToFloat64(ctx, &n, val);
if (e != 0)
{
throw JsException();
}
n = std::trunc(n);
if (n < SAL_MIN_INT16 || n > SAL_MAX_INT16)
{
JS_ThrowRangeError(ctx, "Bad UNO short value %f", n);
throw JsException();
}
return css::uno::Any(static_cast<sal_Int16>(n));
}
case css::uno::TypeClass_UNSIGNED_SHORT:
{
double n;
auto const e = JS_ToFloat64(ctx, &n, val);
if (e != 0)
{
throw JsException();
}
n = std::trunc(n);
if (n < 0 || n > SAL_MAX_UINT16)
{
JS_ThrowRangeError(ctx, "Bad UNO unsigned short value %f", n);
throw JsException();
}
return css::uno::Any(static_cast<sal_uInt16>(n));
}
case css::uno::TypeClass_LONG:
{
double n;
auto const e = JS_ToFloat64(ctx, &n, val);
if (e != 0)
{
throw JsException();
}
n = std::trunc(n);
if (n < SAL_MIN_INT32 || n > SAL_MAX_INT32)
{
JS_ThrowRangeError(ctx, "Bad UNO short value %f", n);
throw JsException();
}
return css::uno::Any(static_cast<sal_Int32>(n));
}
case css::uno::TypeClass_UNSIGNED_LONG:
{
double n;
auto const e = JS_ToFloat64(ctx, &n, val);
if (e != 0)
{
throw JsException();
}
n = std::trunc(n);
if (n < 0 || n > SAL_MAX_UINT32)
{
JS_ThrowRangeError(ctx, "Bad UNO unsigned long value %f", n);
throw JsException();
}
return css::uno::Any(static_cast<sal_uInt32>(n));
}
case css::uno::TypeClass_HYPER:
{
ValueRef const check(ctx, JS_Eval(ctx,
RTL_CONSTASCII_STRINGPARAM(
"(v) => v >= -(2n ** 63n) && v < 2n ** 63n"),
"<input>", JS_EVAL_TYPE_GLOBAL));
ValueRef const res(ctx, JS_Call(ctx, check, JS_NULL, 1, &val));
if (JS_IsException(res))
{
throw JsException();
}
auto const ok = JS_ToBool(ctx, res);
assert(ok != -1);
if (ok == 0)
{
JS_ThrowRangeError(ctx, "Bad UNO hyper value");
throw JsException();
}
std::int64_t n;
[[maybe_unused]] auto const e = JS_ToBigInt64(ctx, &n, val);
assert(e == 0);
return css::uno::Any(static_cast<sal_Int64>(n));
}
case css::uno::TypeClass_UNSIGNED_HYPER:
{
ValueRef const check(
ctx, JS_Eval(ctx, RTL_CONSTASCII_STRINGPARAM("(v) => v >= 0n && v < 2n ** 64n"),
"<input>", JS_EVAL_TYPE_GLOBAL));
ValueRef const res(ctx, JS_Call(ctx, check, JS_NULL, 1, &val));
if (JS_IsException(res))
{
throw JsException();
}
auto const ok = JS_ToBool(ctx, res);
assert(ok != -1);
if (ok == 0)
{
JS_ThrowRangeError(ctx, "Bad UNO unsigned hyper value");
throw JsException();
}
std::uint64_t n;
[[maybe_unused]] auto const e = JS_ToBigUint64(ctx, &n, val);
assert(e == 0);
return css::uno::Any(static_cast<sal_uInt64>(n));
}
case css::uno::TypeClass_FLOAT:
{
double n;
auto const e = JS_ToFloat64(ctx, &n, val);
if (e != 0)
{
throw JsException();
}
return css::uno::Any(static_cast<float>(n));
}
case css::uno::TypeClass_DOUBLE:
{
double n;
auto const e = JS_ToFloat64(ctx, &n, val);
if (e != 0)
{
throw JsException();
}
return css::uno::Any(n);
}
case css::uno::TypeClass_CHAR:
{
std::size_t n;
UniqueCString16 const p(ctx, JS_ToCStringLenUTF16(ctx, &n, val));
if (p.get() == nullptr)
{
throw JsException();
}
if (n != 1)
{
JS_ThrowRangeError(ctx, "Bad UNO char value");
throw JsException();
}
auto const c = p.get()[0];
return css::uno::Any(char16_t(c));
}
case css::uno::TypeClass_STRING:
return fromJsString(ctx, val);
case css::uno::TypeClass_TYPE:
if (JS_GetClassID(val) != getRuntimeData(ctx)->typeClassId)
{
JS_ThrowTypeError(ctx, "TODO: BAD UNO TYPE VALUE");
throw JsException();
}
return css::uno::Any(css::uno::Type(static_cast<typelib_TypeDescriptionReference*>(
JS_GetOpaque(val, getRuntimeData(ctx)->typeClassId))));
case css::uno::TypeClass_ANY:
if (JS_IsUndefined(val))
{
return {};
}
if (JS_IsBool(val))
{
auto const b = JS_ToBool(ctx, val);
if (b == -1)
{
throw JsException();
}
return css::uno::Any(b == 1);
}
if (JS_IsNumber(val))
{
double n;
[[maybe_unused]] auto const e = JS_ToFloat64(ctx, &n, val); //TODO
assert(e == 0); //TODO
if (n == std::trunc(n))
{
if (n >= SAL_MIN_INT32 && n <= SAL_MAX_INT32)
{
return css::uno::Any(sal_Int32(n));
}
if (n >= 0 && n <= SAL_MAX_UINT32)
{
return css::uno::Any(sal_uInt32(n));
}
}
return css::uno::Any(n);
}
if (JS_IsBigInt(val))
{
ValueRef const check1(ctx, JS_Eval(ctx,
RTL_CONSTASCII_STRINGPARAM(
"(v) => v >= -(2n ** 63n) && v < 2n ** 63n"),
"<input>", JS_EVAL_TYPE_GLOBAL));
ValueRef const res1(ctx, JS_Call(ctx, check1, JS_NULL, 1, &val));
if (JS_IsException(res1))
{
throw JsException();
}
auto const ok1 = JS_ToBool(ctx, res1);
assert(ok1 != -1);
if (ok1 == 1)
{
std::int64_t n;
[[maybe_unused]] auto const e = JS_ToBigInt64(ctx, &n, val);
assert(e == 0);
return css::uno::Any(static_cast<sal_Int64>(n));
}
ValueRef const check2(
ctx, JS_Eval(ctx, RTL_CONSTASCII_STRINGPARAM("(v) => v >= 0n && v < 2n ** 64n"),
"<input>", JS_EVAL_TYPE_GLOBAL));
ValueRef const res2(ctx, JS_Call(ctx, check2, JS_NULL, 1, &val));
if (JS_IsException(res2))
{
throw JsException();
}
auto const ok2 = JS_ToBool(ctx, res2);
assert(ok2 != -1);
if (ok2 == 1)
{
std::uint64_t n;
[[maybe_unused]] auto const e = JS_ToBigUint64(ctx, &n, val);
assert(e == 0);
return css::uno::Any(static_cast<sal_uInt64>(n));
}
JS_ThrowRangeError(ctx, "Bad BigInt UNO any value");
throw JsException();
}
if (JS_IsString(val))
{
return fromJsString(ctx, val);
}
if (JS_IsArray(val))
{
return fromJsSequence(ctx, cppu::UnoType<css::uno::Sequence<css::uno::Any>>::get(),
val);
}
if (JS_IsNull(val))
{
return css::uno::Any(css::uno::Reference<css::uno::XInterface>());
}
if (JS_IsObject(val))
{
if (JS_GetClassID(val) == getRuntimeData(ctx)->wrapperClassId)
{
return fromJsInterface(ctx, cppu::UnoType<css::uno::XInterface>::get(), val);
}
if (JS_GetClassID(val) == getRuntimeData(ctx)->enumeratorClassId)
{
return fromJsEnum(ctx, val);
}
if (JS_GetClassID(val) == getRuntimeData(ctx)->compoundClassId)
{
return fromJs(ctx,
css::uno::Type(static_cast<typelib_TypeDescriptionReference*>(
JS_GetOpaque(val, getRuntimeData(ctx)->compoundClassId))),
val);
}
if (JS_GetClassID(val) == getRuntimeData(ctx)->typeClassId)
{
return css::uno::Any(
css::uno::Type(static_cast<typelib_TypeDescriptionReference*>(
JS_GetOpaque(val, getRuntimeData(ctx)->typeClassId))));
}
ValueRef const global(ctx, JS_GetGlobalObject(ctx));
if (JS_IsException(global))
{
throw JsException();
}
ValueRef const uno(ctx, JS_GetPropertyStr(ctx, global, "uno"));
if (JS_IsException(uno))
{
throw JsException();
}
ValueRef const anyCtor(ctx, JS_GetPropertyStr(ctx, uno, "Any"));
if (JS_IsException(anyCtor))
{
throw JsException();
}
auto const isAny = JS_IsInstanceOf(ctx, val, anyCtor);
if (isAny == -1)
{
throw JsException();
}
if (isAny == 1)
{
ValueRef const anyType(ctx, JS_GetPropertyStr(ctx, val, "type"));
if (JS_IsException(anyType))
{
throw JsException();
}
ValueRef const anyVal(ctx, JS_GetPropertyStr(ctx, val, "val"));
if (JS_IsException(anyVal))
{
throw JsException();
}
if (JS_GetClassID(anyType) != getRuntimeData(ctx)->typeClassId)
{
JS_ThrowTypeError(ctx, "TODO: BAD ANY TYPE VALUE");
throw JsException();
}
return fromJs(ctx,
css::uno::Type(static_cast<typelib_TypeDescriptionReference*>(
JS_GetOpaque(anyType, getRuntimeData(ctx)->typeClassId))),
anyVal);
}
}
JS_ThrowRangeError(ctx, "TODO: BAD UNO ANY VALUE");
throw JsException();
case css::uno::TypeClass_SEQUENCE:
return fromJsSequence(ctx, type, val);
case css::uno::TypeClass_ENUM:
return fromJsEnum(ctx, val);
case css::uno::TypeClass_STRUCT:
case css::uno::TypeClass_EXCEPTION:
{
css::uno::TypeDescription desc(type);
auto compDesc = reinterpret_cast<typelib_CompoundTypeDescription const*>(desc.get());
std::vector<css::uno::Any> mems;
for (;;)
{
for (sal_Int32 i = 0; i != compDesc->nMembers; ++i)
{
auto const name = OUString::unacquired(&compDesc->ppMemberNames[i]).toUtf8();
AtomRef const a(ctx, JS_NewAtomLen(ctx, name.getStr(), name.getLength()));
assert(a != JS_ATOM_NULL); //TODO
auto const has = JS_HasProperty(ctx, val, a);
if (has == -1)
{
throw JsException();
}
if (has == 0)
{
if (compDesc->ppTypeRefs[i]->eTypeClass == typelib_TypeClass_ANY)
{
mems.emplace_back();
}
else
{
css::uno::TypeDescription memDesc(compDesc->ppTypeRefs[i]);
memDesc.makeComplete();
std::vector<char> buf(memDesc.get()->nSize, 0);
uno_constructData(buf.data(), memDesc.get());
mems.emplace_back(buf.data(), css::uno::Type(compDesc->ppTypeRefs[i]));
uno_destructData(buf.data(), memDesc.get(), css::uno::cpp_release);
}
}
else
{
ValueRef mem(ctx, JS_GetProperty(ctx, val, a));
if (JS_IsException(mem))
{
throw JsException();
}
mems.push_back(fromJs(ctx, compDesc->ppTypeRefs[i], mem));
}
}
compDesc = compDesc->pBaseTypeDescription;
if (compDesc == nullptr)
{
break;
}
}
auto const buf = rtl_allocateMemory(desc.get()->nSize);
std::size_t mem = 0;
for (compDesc = reinterpret_cast<typelib_CompoundTypeDescription const*>(desc.get());;)
{
for (sal_Int32 i = 0; i != compDesc->nMembers; ++i)
{
css::uno::TypeDescription memDesc(compDesc->ppTypeRefs[i]);
uno_copyData(static_cast<std::byte*>(buf) + compDesc->pMemberOffsets[i],
const_cast<void*>(compDesc->ppTypeRefs[i]->eTypeClass
== typelib_TypeClass_ANY
? &mems[mem]
: mems[mem].getValue()),
memDesc.get(), css::uno::cpp_acquire);
++mem;
}
compDesc = compDesc->pBaseTypeDescription;
if (compDesc == nullptr)
{
break;
}
}
return { buf, type };
}
case css::uno::TypeClass_INTERFACE:
return fromJsInterface(ctx, type, val);
default:
O3TL_UNREACHABLE;
}
}
ValueRef mapTypeToJs(JSContext* ctx, css::uno::Type const& type)
{
switch (type.getTypeClass())
{
case css::uno::TypeClass_VOID:
case css::uno::TypeClass_BOOLEAN:
case css::uno::TypeClass_BYTE:
case css::uno::TypeClass_SHORT:
case css::uno::TypeClass_UNSIGNED_SHORT:
case css::uno::TypeClass_LONG:
case css::uno::TypeClass_UNSIGNED_LONG:
case css::uno::TypeClass_HYPER:
case css::uno::TypeClass_UNSIGNED_HYPER:
case css::uno::TypeClass_FLOAT:
case css::uno::TypeClass_DOUBLE:
case css::uno::TypeClass_CHAR:
case css::uno::TypeClass_STRING:
case css::uno::TypeClass_TYPE:
case css::uno::TypeClass_ANY:
{
ValueRef const global(ctx, JS_GetGlobalObject(ctx));
ValueRef const uno(ctx, JS_GetPropertyStr(ctx, global, "uno"));
assert(JS_IsObject(uno));
ValueRef typeObj(ctx, JS_GetPropertyStr(ctx, uno, "type"));
assert(JS_IsObject(typeObj));
ValueRef val(ctx,
JS_GetPropertyStr(ctx, typeObj, type.getTypeName().toUtf8().getStr()));
assert(JS_GetClassID(val) == getRuntimeData(ctx)->typeClassId); //TODO
return val;
}
case css::uno::TypeClass_SEQUENCE:
{
css::uno::TypeDescription desc(type);
css::uno::TypeDescription elemDesc(
reinterpret_cast<typelib_IndirectTypeDescription const*>(desc.get())->pType);
auto elem = mapTypeToJs(ctx, elemDesc.get()->pWeakRef);
ValueRef val(ctx, unoTypeSequence(ctx, JS_NULL, 1, elem.ptr()));
if (JS_IsException(val))
{
throw JsException();
}
return val;
}
case css::uno::TypeClass_ENUM:
{
auto rep = getUnoidlRepresentation(ctx, type.getTypeName());
ValueRef val(ctx, unoTypeEnum(ctx, JS_NULL, 1, rep.ptr()));
if (JS_IsException(val))
{
throw JsException();
}
return val;
}
case css::uno::TypeClass_STRUCT:
{
auto rep = getUnoidlRepresentation(ctx, type.getTypeName());
ValueRef val(ctx, unoTypeStruct(ctx, JS_NULL, 1, rep.ptr()));
if (JS_IsException(val))
{
throw JsException();
}
return val;
}
case css::uno::TypeClass_EXCEPTION:
{
auto rep = getUnoidlRepresentation(ctx, type.getTypeName());
ValueRef val(ctx, unoTypeException(ctx, JS_NULL, 1, rep.ptr()));
if (JS_IsException(val))
{
throw JsException();
}
return val;
}
case css::uno::TypeClass_INTERFACE:
{
auto rep = getUnoidlRepresentation(ctx, type.getTypeName());
ValueRef val(ctx, unoTypeInterface(ctx, JS_NULL, 1, rep.ptr()));
if (JS_IsException(val))
{
throw JsException();
}
return val;
}
default:
O3TL_UNREACHABLE;
}
}
ValueRef toJs(JSContext* ctx, css::uno::Type const& type, void const* value)
{
switch (type.getTypeClass())
{
case css::uno::TypeClass_VOID:
return ValueRef(ctx, JS_UNDEFINED);
case css::uno::TypeClass_BOOLEAN:
return ValueRef(ctx, JS_NewBool(ctx, *static_cast<sal_Bool const*>(value)));
case css::uno::TypeClass_BYTE:
return ValueRef(ctx, JS_NewInt32(ctx, *static_cast<sal_Int8 const*>(value)));
case css::uno::TypeClass_SHORT:
return ValueRef(ctx, JS_NewInt32(ctx, *static_cast<sal_Int16 const*>(value)));
case css::uno::TypeClass_UNSIGNED_SHORT:
return ValueRef(ctx, JS_NewInt32(ctx, *static_cast<sal_uInt16 const*>(value)));
case css::uno::TypeClass_LONG:
return ValueRef(ctx, JS_NewInt32(ctx, *static_cast<sal_Int32 const*>(value)));
case css::uno::TypeClass_UNSIGNED_LONG:
return ValueRef(ctx, JS_NewUint32(ctx, *static_cast<sal_uInt32 const*>(value)));
case css::uno::TypeClass_HYPER:
return ValueRef(ctx, JS_NewBigInt64(ctx, *static_cast<sal_Int64 const*>(value)));
case css::uno::TypeClass_UNSIGNED_HYPER:
return ValueRef(ctx, JS_NewBigUint64(ctx, *static_cast<sal_uInt64 const*>(value)));
case css::uno::TypeClass_FLOAT:
return ValueRef(ctx, JS_NewFloat64(ctx, *static_cast<float const*>(value)));
case css::uno::TypeClass_DOUBLE:
return ValueRef(ctx, JS_NewFloat64(ctx, *static_cast<double const*>(value)));
case css::uno::TypeClass_CHAR:
{
auto const s = *static_cast<char16_t const*>(value);
return ValueRef(ctx,
JS_NewStringUTF16(ctx, reinterpret_cast<std::uint16_t const*>(&s), 1));
}
case css::uno::TypeClass_STRING:
{
auto const& s = OUString::unacquired(static_cast<rtl_uString* const*>(value));
return ValueRef(
ctx, JS_NewStringUTF16(ctx, reinterpret_cast<std::uint16_t const*>(s.getStr()),
s.getLength()));
}
case css::uno::TypeClass_TYPE:
return mapTypeToJs(ctx, *static_cast<typelib_TypeDescriptionReference* const*>(value));
case css::uno::TypeClass_ANY:
{
auto const any = static_cast<uno_Any const*>(value);
return toJs(ctx, *reinterpret_cast<css::uno::Type const*>(&any->pType), any->pData);
}
case css::uno::TypeClass_SEQUENCE:
{
auto const seq = *static_cast<uno_Sequence* const*>(value);
css::uno::TypeDescription desc(type);
css::uno::TypeDescription elemDesc(
reinterpret_cast<typelib_IndirectTypeDescription const*>(desc.get())->pType);
std::vector<ValueRef> refs;
for (sal_Int32 i = 0; i != seq->nElements; ++i)
{
refs.push_back(toJs(ctx, elemDesc.get()->pWeakRef,
seq->elements + (i * elemDesc.get()->nSize)));
}
std::vector<JSValue> vals;
for (auto& ref : refs)
{
vals.push_back(ref);
}
ValueRef arr(ctx, JS_NewArrayFrom(ctx, vals.size(), vals.data()));
for (auto& ref : refs)
{
ref.release();
}
if (JS_IsException(arr))
{
throw JsException();
}
return arr;
}
case css::uno::TypeClass_ENUM:
{
auto const val = *static_cast<sal_Int32 const*>(value);
css::uno::TypeDescription desc(type);
auto const enumDesc = reinterpret_cast<typelib_EnumTypeDescription const*>(desc.get());
sal_Int32 i = 0;
for (; i != enumDesc->nEnumValues; ++i)
{
if (enumDesc->pEnumValues[i] == val)
{
break;
}
}
assert(i != enumDesc->nEnumValues); //TODO
auto obj = getUnoidlRepresentation(
ctx, Concat2View(type.getTypeName() + "."
+ OUString::unacquired(&enumDesc->ppEnumNames[i])));
assert(JS_GetClassID(obj) == getRuntimeData(ctx)->enumeratorClassId); //TODO
return obj;
}
case css::uno::TypeClass_STRUCT:
case css::uno::TypeClass_EXCEPTION:
{
ValueRef mems(ctx, JS_NewObject(ctx));
if (JS_IsException(mems))
{
throw JsException();
}
css::uno::TypeDescription desc(type);
auto compDesc = reinterpret_cast<typelib_CompoundTypeDescription const*>(desc.get());
for (;;)
{
for (sal_Int32 i = 0; i != compDesc->nMembers; ++i)
{
auto mem
= toJs(ctx, compDesc->ppTypeRefs[i],
static_cast<std::byte const*>(value) + compDesc->pMemberOffsets[i]);
if (JS_SetPropertyStr(
ctx, mems,
OUString::unacquired(&compDesc->ppMemberNames[i]).toUtf8().getStr(),
mem.release())
== -1)
{
throw JsException();
}
}
compDesc = compDesc->pBaseTypeDescription;
if (compDesc == nullptr)
{
break;
}
}
auto const id = type.getTypeName();
auto n = id.indexOf('<');
auto const rep = getUnoidlRepresentation(ctx, n == -1 ? id : id.subView(0, n));
ValueRef val(ctx);
if (n == -1)
{
val = JS_CallConstructor(ctx, rep, 1, mems.ptr());
if (JS_IsException(val))
{
throw JsException();
}
}
else
{
ValueRef args(ctx, JS_NewArray(ctx));
if (JS_IsException(args))
{
throw JsException();
}
std::uint32_t arg = 0;
assert(id.endsWith(">"));
sal_Int32 nest = 0;
++n;
css::uno::Reference<css::container::XHierarchicalNameAccess> mgr(
comphelper::getProcessComponentContext()->getValueByName(
u"/singletons/com.sun.star.reflection.theTypeDescriptionManager"_ustr),
css::uno::UNO_QUERY_THROW);
for (sal_Int32 i = n;; ++i)
{
if (i == id.getLength() - 1 || (nest == 0 && id[i] == ','))
{
assert(nest == 0);
css::uno::Reference<css::reflection::XTypeDescription> td(
mgr->getByHierarchicalName(id.copy(n, i - n)),
css::uno::UNO_QUERY_THROW);
if (JS_SetPropertyUint32(
ctx, args, arg++,
mapTypeToJs(ctx, css::uno::Type(td->getTypeClass(), td->getName()))
.release())
== -1)
{
throw JsException();
}
if (i == id.getLength() - 1)
{
break;
}
n = i + 1;
}
else if (id[i] == '<')
{
++nest;
}
else if (id[i] == '>')
{
assert(nest != 0);
--nest;
}
}
JSValueConst argv[] = { args, mems };
val = JS_CallConstructor(ctx, rep, 2, argv);
if (JS_IsException(val))
{
throw JsException();
}
}
return val;
}
case css::uno::TypeClass_INTERFACE:
return ValueRef(ctx,
wrapUnoObject(ctx, *static_cast<css::uno::XInterface* const*>(value)));
default:
O3TL_UNREACHABLE;
}
}
ValueRef toJs(JSContext* ctx, css::uno::Any const& value)
{
return toJs(ctx, value.getValueType(), value.getValue());
}
JSValue sameUnoObject(JSContext* ctx, JSValueConst, int argc, JSValueConst* argv)
{
return callFromJs(ctx, [ctx, argc, argv] {
if (argc < 2)
return JS_ThrowPlainError(ctx, "missing argument to uno.sameUnoObject");
return JS_NewBool(
ctx,
*o3tl::forceAccess<css::uno::Reference<css::uno::XInterface>>(
fromJsInterface(ctx, cppu::UnoType<css::uno::XInterface>::get(), argv[0]))
== *o3tl::forceAccess<css::uno::Reference<css::uno::XInterface>>(
fromJsInterface(ctx, cppu::UnoType<css::uno::XInterface>::get(), argv[1])));
});
}
JSValue invokeUno(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv, int,
JSValueConst* func_data)
{
return callFromJs(ctx, [ctx, this_val, argc, argv, func_data] {
auto const info = static_cast<css::script::InvocationInfo const*>(
JS_GetOpaque(func_data[0], getRuntimeData(ctx)->pointerClassId));
if (argc != info->aParamTypes.getLength())
{
JS_ThrowSyntaxError(ctx, "TODO: BAD NUMBER OF ARGUMENTS");
throw JsException();
}
css::uno::Sequence<css::uno::Any> args(info->aParamTypes.getLength());
for (int i = 0; i != argc; ++i)
{
if (info->aParamModes[i] == css::reflection::ParamMode_OUT)
{
continue;
}
ValueRef arg(ctx);
if (info->aParamModes[i] == css::reflection::ParamMode_IN)
{
arg = JS_DupValue(ctx, argv[i]);
}
else
{
AtomRef const a(ctx, JS_NewAtom(ctx, "val"));
assert(a != JS_ATOM_NULL); //TODO
auto const has = JS_HasProperty(ctx, argv[i], a);
if (has == -1)
{
throw JsException();
}
if (has == 0)
{
JS_ThrowTypeError(ctx, "TODO: BAD INOUT ARG");
throw JsException();
}
arg = JS_GetProperty(ctx, argv[i], a);
if (JS_IsException(arg))
{
throw JsException();
}
}
args.getArray()[i] = fromJs(ctx, info->aParamTypes[i], arg);
}
css::uno::Sequence<sal_Int16> outParamIndex; //TODO
css::uno::Sequence<css::uno::Any> outParam;
ValueRef ret(ctx);
try
{
WrapperData* wrapperData = static_cast<WrapperData*>(
JS_GetOpaque2(ctx, this_val, getRuntimeData(ctx)->wrapperClassId));
if (wrapperData == nullptr)
throw JsException();
ret = toJs(
ctx,
css::uno::Reference<css::script::XInvocation>(
css::script::Invocation::create(comphelper::getProcessComponentContext())
->createInstanceWithArguments({ css::uno::Any(wrapperData->interface) }),
css::uno::UNO_QUERY_THROW)
->invoke(info->aName, args, outParamIndex, outParam));
}
catch (css::reflection::InvocationTargetException e)
{
cppu::throwException(e.TargetException);
}
assert(outParamIndex.getLength() == outParam.getLength());
for (sal_Int32 i = 0; i != outParamIndex.getLength(); ++i)
{
auto out = toJs(ctx, outParam[i]);
if (JS_SetPropertyStr(ctx, argv[outParamIndex[i]], "val", out.release()) == -1)
{
throw JsException();
}
}
return ret.release();
});
}
struct ExceptionData
{
OUString type;
OUString message;
};
ExceptionData extractExceptionData(JSContext* ctx, ValueRef const& err)
{
ExceptionData exc;
bool haveMessage = false;
if (JS_IsObject(err))
{
ValueRef const nameVal(ctx, JS_GetPropertyStr(ctx, err, "name"));
if (JS_IsString(nameVal))
{
std::size_t n;
UniqueCString16 const p(ctx, JS_ToCStringLenUTF16(ctx, &n, nameVal));
if (p.get() != nullptr)
{
exc.type = OUString(p.get(), n);
}
}
ValueRef const msgVal(ctx, JS_GetPropertyStr(ctx, err, "message"));
if (JS_IsString(msgVal))
{
std::size_t n;
UniqueCString16 const p(ctx, JS_ToCStringLenUTF16(ctx, &n, msgVal));
if (p.get() != nullptr)
{
exc.message = OUString(p.get(), n);
haveMessage = true;
}
}
}
if (!haveMessage)
{
ValueRef const str(ctx, JS_ToString(ctx, err));
if (!JS_IsException(str))
{
std::size_t n;
UniqueCString16 const p(ctx, JS_ToCStringLenUTF16(ctx, &n, str));
if (p.get() != nullptr)
{
exc.message = OUString(p.get(), n);
}
}
}
return exc;
}
void initializeTypePrototype(JSContext* ctx)
{
static const JSCFunctionListEntry functions[] = {
JS_CFUNC_DEF("toString", 0, typeToString),
};
ValueRef proto(ctx, JS_NewObject(ctx));
JS_SetPropertyFunctionList(ctx, proto, functions, SAL_N_ELEMENTS(functions));
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[] = {
JS_CFUNC_DEF("toString", 0, enumeratorToString),
};
ValueRef proto(ctx, JS_NewObject(ctx));
JS_SetPropertyFunctionList(ctx, proto, functions, SAL_N_ELEMENTS(functions));
JS_SetClassProto(ctx, getRuntimeData(ctx)->enumeratorClassId, proto.release());
}
void initializeWrapperPrototype(JSContext* ctx)
{
static const JSCFunctionListEntry functions[] = {
JS_CFUNC_DEF("toString", 0, wrapperToString),
};
ValueRef proto(ctx, JS_NewObject(ctx));
JS_SetPropertyFunctionList(ctx, proto, functions, SAL_N_ELEMENTS(functions));
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)
{
auto const rt = JS_NewRuntime();
JS_SetRuntimeOpaque(rt, new RuntimeData(rt));
JS_NewClassID(rt, &getRuntimeData(rt)->pointerClassId);
JSClassDef pointerClass{ "InternalPointer", pointerFinalizer, nullptr, nullptr, nullptr };
[[maybe_unused]] auto e = JS_NewClass(rt, getRuntimeData(rt)->pointerClassId, &pointerClass);
assert(e == 0); //TODO
JS_NewClassID(rt, &getRuntimeData(rt)->wrapperClassId);
JSClassExoticMethods wrapperMethods{
wrapperGetOwnProperty, nullptr /*TODO*/, nullptr, nullptr, nullptr, nullptr,
wrapperSetProperty
};
JSClassDef wrapperClass{ "UnoWrapper", wrapperFinalizer, wrapperGCMark, nullptr,
&wrapperMethods };
e = JS_NewClass(rt, getRuntimeData(rt)->wrapperClassId, &wrapperClass);
assert(e == 0); //TODO
JS_NewClassID(rt, &getRuntimeData(rt)->enumeratorClassId);
JSClassDef enumeratorClass{ "UnoidlEnumerator", enumeratorFinalizer, nullptr, nullptr,
nullptr };
e = JS_NewClass(rt, getRuntimeData(rt)->enumeratorClassId, &enumeratorClass);
assert(e == 0); //TODO
JS_NewClassID(rt, &getRuntimeData(rt)->compoundClassId);
JSClassDef compoundClass{ "UnoStruct", compoundFinalizer, nullptr, nullptr, nullptr };
e = JS_NewClass(rt, getRuntimeData(rt)->compoundClassId, &compoundClass);
assert(e == 0); //TODO
JS_NewClassID(rt, &getRuntimeData(rt)->typeClassId);
JSClassDef typeClass{ "UnoType", typeFinalizer, nullptr, nullptr, nullptr };
e = JS_NewClass(rt, getRuntimeData(rt)->typeClassId, &typeClass);
assert(e == 0); //TODO
JS_NewClassID(rt, &getRuntimeData(rt)->enumClassId);
JSClassDef enumClass{ "UnoidlEnumType", enumFinalizer, nullptr, nullptr, nullptr };
e = JS_NewClass(rt, getRuntimeData(rt)->enumClassId, &enumClass);
assert(e == 0); //TODO
JS_NewClassID(rt, &getRuntimeData(rt)->structClassId);
JSClassDef structClass{ "UnoidlStructType", structFinalizer, nullptr, nullptr, nullptr };
e = JS_NewClass(rt, getRuntimeData(rt)->structClassId, &structClass);
assert(e == 0); //TODO
JS_NewClassID(rt, &getRuntimeData(rt)->exceptionClassId);
JSClassDef exceptionClass{ "UnoidlExceptionType", exceptionFinalizer, nullptr, nullptr,
nullptr };
e = JS_NewClass(rt, getRuntimeData(rt)->exceptionClassId, &exceptionClass);
assert(e == 0); //TODO
JS_NewClassID(rt, &getRuntimeData(rt)->interfaceClassId);
JSClassDef interfaceClass{ "UnoidlInterfaceType", interfaceFinalizer, nullptr, nullptr,
nullptr };
e = JS_NewClass(rt, getRuntimeData(rt)->interfaceClassId, &interfaceClass);
assert(e == 0); //TODO
JS_NewClassID(rt, &getRuntimeData(rt)->ctorClassId);
JSClassDef ctorClass{ "UnoidlSingleton", ctorFinalizer, nullptr, nullptr, nullptr };
e = JS_NewClass(rt, getRuntimeData(rt)->ctorClassId, &ctorClass);
assert(e == 0); //TODO
JS_NewClassID(rt, &getRuntimeData(rt)->singletonClassId);
JSClassDef singletonClass{ "UnoidlSingleton", singletonFinalizer, nullptr, nullptr, nullptr };
e = JS_NewClass(rt, getRuntimeData(rt)->singletonClassId, &singletonClass);
assert(e == 0); //TODO
JS_NewClassID(rt, &getRuntimeData(rt)->moduleClassId);
JSClassExoticMethods moduleMethods{
moduleGetOwnProperty, nullptr /*TODO*/, nullptr, nullptr, nullptr, nullptr, nullptr
};
JSClassDef moduleClass{ "UnoidlModule", moduleFinalizer, moduleGCMark, nullptr,
&moduleMethods };
e = JS_NewClass(rt, getRuntimeData(rt)->moduleClassId, &moduleClass);
assert(e == 0); //TODO
auto const ctx = JS_NewContext(rt);
initializeTypePrototype(ctx);
initializeEnumeratorPrototype(ctx);
initializeEnumPrototype(ctx);
initializeWrapperPrototype(ctx);
initializeInterfacePrototype(ctx);
initializeModulePrototype(ctx);
std::optional<ExceptionData> exc;
OUString result;
{
ValueRef const global(ctx, JS_GetGlobalObject(ctx));
getRuntimeData(ctx)->symbolIteratorAtom = JS_ValueToAtom(
ctx, ValueRef(ctx, JS_GetPropertyStr(
ctx, ValueRef(ctx, JS_GetPropertyStr(ctx, global, "Symbol")),
"iterator")));
ValueRef console(ctx, JS_NewObject(ctx));
JS_SetPropertyStr(ctx, console, "assert", JS_NewCFunction(ctx, consoleAssert, "assert", 1));
JS_SetPropertyStr(ctx, console, "log", JS_NewCFunction(ctx, consoleLog, "log", 0));
JS_SetPropertyStr(ctx, global, "console", console.release());
ValueRef uno(ctx, JS_NewObject(ctx));
ValueRef type(ctx, JS_NewObject(ctx));
setTypeProperty(ctx, type, "void", cppu::UnoType<void>::get());
setTypeProperty(ctx, type, "boolean", cppu::UnoType<bool>::get());
setTypeProperty(ctx, type, "byte", cppu::UnoType<sal_Int8>::get());
setTypeProperty(ctx, type, "short", cppu::UnoType<sal_Int16>::get());
setTypeProperty(ctx, type, "unsigned_short", cppu::UnoType<sal_uInt16>::get());
setTypeProperty(ctx, type, "long", cppu::UnoType<sal_Int32>::get());
setTypeProperty(ctx, type, "unsigned_long", cppu::UnoType<sal_uInt32>::get());
setTypeProperty(ctx, type, "hyper", cppu::UnoType<sal_Int64>::get());
setTypeProperty(ctx, type, "unsigned_hyper", cppu::UnoType<sal_uInt64>::get());
setTypeProperty(ctx, type, "float", cppu::UnoType<float>::get());
setTypeProperty(ctx, type, "double", cppu::UnoType<double>::get());
setTypeProperty(ctx, type, "char", cppu::UnoType<char16_t>::get());
setTypeProperty(ctx, type, "string", cppu::UnoType<OUString>::get());
setTypeProperty(ctx, type, "type", cppu::UnoType<css::uno::Type>::get());
setTypeProperty(ctx, type, "any", cppu::UnoType<css::uno::Any>::get());
JS_SetPropertyStr(ctx, type, "sequence",
JS_NewCFunction(ctx, unoTypeSequence, "sequence", 1));
JS_SetPropertyStr(ctx, type, "enum", JS_NewCFunction(ctx, unoTypeEnum, "enum", 1));
JS_SetPropertyStr(ctx, type, "struct", JS_NewCFunction(ctx, unoTypeStruct, "struct", 1));
JS_SetPropertyStr(ctx, type, "exception",
JS_NewCFunction(ctx, unoTypeException, "exception", 1));
JS_SetPropertyStr(ctx, type, "interface",
JS_NewCFunction(ctx, unoTypeInterface, "interface", 1));
JS_SetPropertyStr(ctx, uno, "type", type.release());
ValueRef idl(ctx, JS_NewObjectClass(ctx, getRuntimeData(ctx)->moduleClassId));
e = JS_SetOpaque(idl, new ModuleData(ctx, u""_ustr));
#if defined DBG_UTIL
getRuntimeData(ctx)->toFinalize.inc();
#endif
assert(e == 0); //TODO
JS_SetPropertyStr(ctx, uno, "idl", idl.release());
ValueRef anyProto(ctx, JS_NewObject(ctx));
assert(!JS_IsException(anyProto)); //TODO
ValueRef anyCtor(ctx, JS_NewCFunction2(ctx, unoAny, "Any", 1, JS_CFUNC_constructor, 0));
assert(!JS_IsException(anyCtor)); //TODO
JS_SetConstructor(ctx, anyCtor, anyProto);
JS_SetPropertyStr(ctx, uno, "Any", anyCtor.release());
JS_SetPropertyStr(ctx, uno, "sameUnoObject",
JS_NewCFunction(ctx, sameUnoObject, "sameUnoObject", 2));
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));
auto const input = script.toUtf8();
ValueRef const evalRes(
ctx, JS_Eval(ctx, input.getStr(), input.getLength(), "<input>", JS_EVAL_TYPE_GLOBAL));
if (JS_IsException(evalRes))
{
//TODO: reconstruct UNO exceptions
ValueRef const err(ctx, JS_GetException(ctx));
assert(!JS_IsException(err)); //TODO?
exc = extractExceptionData(ctx, err);
}
else
{
ValueRef const json(ctx, JS_JSONStringify(ctx, evalRes, JS_UNDEFINED, JS_UNDEFINED));
if (JS_IsException(json))
{
// JSON.stringify itself can throw, e.g. on BigInt values or circular references:
ValueRef const err(ctx, JS_GetException(ctx));
assert(!JS_IsException(err)); //TODO?
exc = extractExceptionData(ctx, err);
}
else if (!JS_IsUndefined(json))
{
// Values that JSON.stringify drops (undefined, functions, symbols) come back as
// JS_UNDEFINED, for which we use an empty OUString:
std::size_t n;
UniqueCString16 const p(ctx, JS_ToCStringLenUTF16(ctx, &n, json));
if (p.get() != nullptr)
{
result = OUString(p.get(), n);
}
}
}
}
JS_FreeContext(ctx);
std::unique_ptr<RuntimeData> data(getRuntimeData(rt));
data->clear();
JS_FreeRuntime(rt);
if (exc)
{
throw css::script::provider::ScriptExceptionRaisedException(
exc->message, {}, u"<input>"_ustr, u"JavaScript"_ustr, -1, exc->type);
}
return result;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */