1
0
Fork 0
libreoffice/static/source/embindmaker/embindmaker.cxx
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

1398 lines
50 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 <cassert>
#include <cstddef>
#include <cstdlib>
#include <fstream>
#include <ios>
#include <iostream>
#include <list>
#include <map>
#include <memory>
#include <ostream>
#include <set>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include <codemaker/commoncpp.hxx>
#include <codemaker/global.hxx>
#include <codemaker/typemanager.hxx>
#include <osl/file.hxx>
#include <osl/process.h>
#include <osl/thread.h>
#include <rtl/process.h>
#include <rtl/ref.hxx>
#include <rtl/string.hxx>
#include <rtl/textcvt.h>
#include <rtl/ustrbuf.hxx>
#include <rtl/ustring.hxx>
#include <sal/main.h>
#include <sal/types.h>
#include <unoidl/unoidl.hxx>
namespace
{
[[noreturn]] void badUsage()
{
std::cerr
<< "Usage:\n\n"
" embindmaker <name> <cpp-output> <hpp-output> <js-output> <registries>\n\n"
"where each <registry> is '+' (primary) or ':' (secondary), followed by: either a\n"
"new- or legacy-format .rdb file, a single .idl file, or a root directory of an\n"
".idl file tree. For all primary registries, Embind code is written to\n"
"<cpp-output>/<hpp-output> and corresponding JavaScript scaffolding code is\n"
"written to <js-output>. The <name> is used as part of some of the identifiers\n"
"in those generated files.\n";
std::exit(EXIT_FAILURE);
}
std::string getPathnameArgument(sal_uInt32 argument)
{
OUString arg;
rtl_getAppCommandArg(argument, &arg.pData);
OString path;
auto const enc = osl_getThreadTextEncoding();
if (!arg.convertToString(&path, enc,
RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
| RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR))
{
std::cerr << "Cannot convert \"" << arg << "\" to system encoding " << enc << "\n";
std::exit(EXIT_FAILURE);
}
return std::string(path);
}
std::pair<OUString, bool> parseRegistryArgument(sal_uInt32 argument)
{
OUString arg;
rtl_getAppCommandArg(argument, &arg.pData);
bool primary;
if (arg.startsWith(u"+", &arg))
{
primary = true;
}
else if (arg.startsWith(u":", &arg))
{
primary = false;
}
else
{
std::cerr << "Bad registry argument \"" << arg << "\"\n";
std::exit(EXIT_FAILURE);
}
OUString url;
auto const e1 = osl::FileBase::getFileURLFromSystemPath(arg, url);
if (e1 != osl::FileBase::E_None)
{
std::cerr << "Cannot convert \"" << arg << "\" to file URL, error code " << +e1 << "\n";
std::exit(EXIT_FAILURE);
}
OUString cwd;
auto const e2 = osl_getProcessWorkingDir(&cwd.pData);
if (e2 != osl_Process_E_None)
{
std::cerr << "Cannot obtain working directory, error code " << +e2 << "\n";
std::exit(EXIT_FAILURE);
}
OUString abs;
auto const e3 = osl::FileBase::getAbsoluteFileURL(cwd, url, abs);
if (e3 != osl::FileBase::E_None)
{
std::cerr << "Cannot make \"" << url << "\" into an absolute file URL, error code " << +e3
<< "\n";
std::exit(EXIT_FAILURE);
}
return { abs, primary };
}
struct Module
{
std::map<OUString, std::shared_ptr<Module>> modules;
std::vector<std::pair<OUString, OUString>> mappings;
};
OUString
getServiceConstructorName(unoidl::SingleInterfaceBasedServiceEntity::Constructor const& constructor)
{
return constructor.defaultConstructor ? u"create"_ustr : constructor.name;
}
OUString jsName(OUString const& name) { return name.replace(' ', '_').replace('.', '$'); }
OUString
jsServiceConstructor(OUString const& service,
unoidl::SingleInterfaceBasedServiceEntity::Constructor const& constructor)
{
return "uno_Function_" + jsName(service) + "$$" + getServiceConstructorName(constructor);
}
OUString jsSingleton(OUString const& singleton) { return "uno_Function_" + jsName(singleton); }
void scan(rtl::Reference<unoidl::MapCursor> const& cursor, std::u16string_view prefix,
Module* module, std::vector<OUString>& enums, std::vector<OUString>& structs,
std::vector<OUString>& exceptions, std::vector<OUString>& interfaces,
std::vector<OUString>& services, std::vector<OUString>& singletons)
{
assert(cursor.is());
assert(module != nullptr);
for (;;)
{
OUString id;
auto const ent = cursor->getNext(&id);
if (!ent.is())
{
break;
}
OUString name(prefix + id);
switch (ent->getSort())
{
case unoidl::Entity::SORT_MODULE:
{
auto& sub = module->modules[id];
if (!sub)
{
sub = std::make_shared<Module>();
}
scan(static_cast<unoidl::ModuleEntity*>(ent.get())->createCursor(),
Concat2View(name + "."), sub.get(), enums, structs, exceptions, interfaces,
services, singletons);
break;
}
case unoidl::Entity::SORT_ENUM_TYPE:
module->mappings.emplace_back(id, "instance.uno_Type_" + jsName(name));
enums.emplace_back(name);
break;
case unoidl::Entity::SORT_PLAIN_STRUCT_TYPE:
module->mappings.emplace_back(id, "instance.uno_Type_" + jsName(name));
structs.emplace_back(name);
break;
case unoidl::Entity::SORT_EXCEPTION_TYPE:
module->mappings.emplace_back(id, "instance.uno_Type_" + jsName(name));
exceptions.emplace_back(name);
break;
case unoidl::Entity::SORT_INTERFACE_TYPE:
module->mappings.emplace_back(id, "instance.uno_Type_" + jsName(name));
interfaces.emplace_back(name);
break;
case unoidl::Entity::SORT_CONSTANT_GROUP:
{
auto const& members
= static_cast<unoidl::ConstantGroupEntity*>(ent.get())->getMembers();
if (!members.empty())
{
auto sub = std::make_shared<Module>();
for (auto const& member : members)
{
OUString value;
switch (member.value.type)
{
case unoidl::ConstantValue::TYPE_BOOLEAN:
value = member.value.booleanValue ? u"true"_ustr : u"false"_ustr;
break;
case unoidl::ConstantValue::TYPE_BYTE:
value = OUString::number(member.value.byteValue);
break;
case unoidl::ConstantValue::TYPE_SHORT:
value = OUString::number(member.value.shortValue);
break;
case unoidl::ConstantValue::TYPE_UNSIGNED_SHORT:
value = OUString::number(member.value.unsignedShortValue);
break;
case unoidl::ConstantValue::TYPE_LONG:
value = OUString::number(member.value.longValue);
break;
case unoidl::ConstantValue::TYPE_UNSIGNED_LONG:
value = OUString::number(member.value.unsignedLongValue);
break;
case unoidl::ConstantValue::TYPE_HYPER:
value = OUString::number(member.value.hyperValue) + "n";
break;
case unoidl::ConstantValue::TYPE_UNSIGNED_HYPER:
value = OUString::number(member.value.unsignedHyperValue) + "n";
break;
case unoidl::ConstantValue::TYPE_FLOAT:
value = OUString::number(member.value.floatValue);
break;
case unoidl::ConstantValue::TYPE_DOUBLE:
value = OUString::number(member.value.doubleValue);
break;
}
sub->mappings.emplace_back(member.name, value);
}
module->modules[id] = sub;
}
break;
}
case unoidl::Entity::SORT_SINGLE_INTERFACE_BASED_SERVICE:
{
auto const& ctors
= static_cast<unoidl::SingleInterfaceBasedServiceEntity*>(ent.get())
->getConstructors();
if (!ctors.empty())
{
auto sub = std::make_shared<Module>();
for (auto const& ctor : ctors)
{
sub->mappings.emplace_back(getServiceConstructorName(ctor),
"instance." + jsServiceConstructor(name, ctor));
}
module->modules[id] = sub;
services.emplace_back(name);
}
}
break;
case unoidl::Entity::SORT_INTERFACE_BASED_SINGLETON:
module->mappings.emplace_back(id, "instance." + jsSingleton(name));
singletons.emplace_back(name);
break;
default:
break;
}
}
}
OUString cppName(OUString const& name)
{
sal_Int32 k;
std::vector<OString> args;
OUString n(b2u(codemaker::UnoType::decompose(u2b(name), &k, &args)));
OUStringBuffer buf;
for (sal_Int32 i = 0; i != k; ++i)
{
buf.append("::com::sun::star::uno::Sequence<");
}
if (n == "boolean")
{
buf.append("::sal_Bool");
}
else if (n == "byte")
{
buf.append("::sal_Int8");
}
else if (n == "short")
{
buf.append("::sal_Int16");
}
else if (n == "unsigned short")
{
buf.append("::sal_uInt16");
}
else if (n == "long")
{
buf.append("::sal_Int32");
}
else if (n == "unsigned long")
{
buf.append("::sal_uInt32");
}
else if (n == "hyper")
{
buf.append("::sal_Int64");
}
else if (n == "unsigned hyper")
{
buf.append("::sal_uInt64");
}
else if (n == "float")
{
buf.append("float");
}
else if (n == "double")
{
buf.append("double");
}
else if (n == "char")
{
buf.append("::sal_Unicode");
}
else if (n == "string")
{
buf.append("::rtl::OUString");
}
else if (n == "type")
{
buf.append("::com::sun::star::uno::Type");
}
else if (n == "any")
{
buf.append("::com::sun::star::uno::Any");
}
else
{
buf.append("::" + n.replaceAll(u".", u"::"));
}
if (!args.empty())
{
buf.append('<');
bool first = true;
for (auto const& i : args)
{
if (first)
{
first = false;
}
else
{
buf.append(", ");
}
buf.append(cppName(b2u(i)));
}
buf.append('>');
}
for (sal_Int32 i = 0; i != k; ++i)
{
buf.append('>');
}
return buf.makeStringAndClear();
}
OUString resolveOuterTypedefs(rtl::Reference<TypeManager> const& manager, OUString const& name)
{
for (OUString n(name);;)
{
rtl::Reference<unoidl::Entity> ent;
if (manager->getSort(n, &ent) != codemaker::UnoType::Sort::Typedef)
{
return n;
}
n = dynamic_cast<unoidl::TypedefEntity&>(*ent).getType();
}
}
OUString resolveAllTypedefs(rtl::Reference<TypeManager> const& manager, std::u16string_view name)
{
sal_Int32 k1;
OUString n(b2u(codemaker::UnoType::decompose(u2b(name), &k1)));
for (;;)
{
rtl::Reference<unoidl::Entity> ent;
if (manager->getSort(n, &ent) != codemaker::UnoType::Sort::Typedef)
{
break;
}
sal_Int32 k2;
n = b2u(codemaker::UnoType::decompose(
u2b(static_cast<unoidl::TypedefEntity*>(ent.get())->getType()), &k2));
k1 += k2; //TODO: overflow
}
OUStringBuffer b;
for (sal_Int32 i = 0; i != k1; ++i)
{
b.append("[]");
}
b.append(n);
return b.makeStringAndClear();
}
bool passByReference(rtl::Reference<TypeManager> const& manager, OUString const& name)
{
switch (manager->getSort(resolveOuterTypedefs(manager, name)))
{
case codemaker::UnoType::Sort::Boolean:
case codemaker::UnoType::Sort::Byte:
case codemaker::UnoType::Sort::Short:
case codemaker::UnoType::Sort::UnsignedShort:
case codemaker::UnoType::Sort::Long:
case codemaker::UnoType::Sort::UnsignedLong:
case codemaker::UnoType::Sort::Hyper:
case codemaker::UnoType::Sort::UnsignedHyper:
case codemaker::UnoType::Sort::Float:
case codemaker::UnoType::Sort::Double:
case codemaker::UnoType::Sort::Char:
case codemaker::UnoType::Sort::Enum:
return false;
case codemaker::UnoType::Sort::String:
case codemaker::UnoType::Sort::Type:
case codemaker::UnoType::Sort::Any:
case codemaker::UnoType::Sort::Sequence:
case codemaker::UnoType::Sort::PlainStruct:
case codemaker::UnoType::Sort::InstantiatedPolymorphicStruct:
case codemaker::UnoType::Sort::Interface:
return true;
default:
throw CannotDumpException("unexpected entity \"" + name
+ "\" in call to passByReference");
}
}
void dumpType(std::ostream& out, rtl::Reference<TypeManager> const& manager,
std::u16string_view name)
{
sal_Int32 k;
std::vector<OString> args;
OUString n(
b2u(codemaker::UnoType::decompose(u2b(resolveAllTypedefs(manager, name)), &k, &args)));
for (sal_Int32 i = 0; i != k; ++i)
{
out << "::com::sun::star::uno::Sequence<";
}
switch (manager->getSort(n))
{
case codemaker::UnoType::Sort::Void:
out << "void";
break;
case codemaker::UnoType::Sort::Boolean:
out << "::sal_Bool";
break;
case codemaker::UnoType::Sort::Byte:
out << "::sal_Int8";
break;
case codemaker::UnoType::Sort::Short:
out << "::sal_Int16";
break;
case codemaker::UnoType::Sort::UnsignedShort:
out << "::sal_uInt16";
break;
case codemaker::UnoType::Sort::Long:
out << "::sal_Int32";
break;
case codemaker::UnoType::Sort::UnsignedLong:
out << "::sal_uInt32";
break;
case codemaker::UnoType::Sort::Hyper:
out << "::sal_Int64";
break;
case codemaker::UnoType::Sort::UnsignedHyper:
out << "::sal_uInt64";
break;
case codemaker::UnoType::Sort::Float:
out << "float";
break;
case codemaker::UnoType::Sort::Double:
out << "double";
break;
case codemaker::UnoType::Sort::Char:
out << "::sal_Unicode";
break;
case codemaker::UnoType::Sort::String:
out << "::rtl::OUString";
break;
case codemaker::UnoType::Sort::Type:
out << "::com::sun::star::uno::Type";
break;
case codemaker::UnoType::Sort::Any:
out << "::com::sun::star::uno::Any";
break;
case codemaker::UnoType::Sort::Enum:
case codemaker::UnoType::Sort::PlainStruct:
case codemaker::UnoType::Sort::Exception:
out << cppName(n);
break;
case codemaker::UnoType::Sort::PolymorphicStructTemplate:
out << cppName(n);
if (!args.empty())
{
out << "<";
bool first = true;
for (auto const& arg : args)
{
if (first)
{
first = false;
}
else
{
out << ", ";
}
dumpType(out, manager, b2u(arg));
}
out << ">";
}
break;
case codemaker::UnoType::Sort::Interface:
out << "::com::sun::star::uno::Reference<";
out << cppName(n);
out << ">";
break;
default:
throw CannotDumpException(OUString::Concat("unexpected entity \"") + name
+ "\" in call to dumpType");
}
for (sal_Int32 i = 0; i != k; ++i)
{
out << ">";
}
}
void dumpStructMembers(std::ostream& out, rtl::Reference<TypeManager> const& manager,
OUString const& name, rtl::Reference<unoidl::PlainStructTypeEntity> struc)
{
auto const& base = struc->getDirectBase();
if (!base.isEmpty())
{
auto const ent = manager->getManager()->findEntity(base);
if (!ent.is() || ent->getSort() != unoidl::Entity::SORT_PLAIN_STRUCT_TYPE)
{
throw CannotDumpException("bad struct base \"" + base + "\"");
}
dumpStructMembers(out, manager, name,
static_cast<unoidl::PlainStructTypeEntity*>(ent.get()));
}
for (auto const& mem : struc->getDirectMembers())
{
out << "\n .field(\"" << mem.name << "\", &" << cppName(name) << "::" << mem.name
<< ")";
}
}
void dumpInstantiationMembers(std::ostream& out, OUString const& name,
rtl::Reference<unoidl::PolymorphicStructTypeTemplateEntity> poly)
{
for (auto const& mem : poly->getMembers())
{
out << "\n .field(\"" << mem.name << "\", &" << cppName(name) << "::" << mem.name
<< ")";
}
}
void dumpExceptionMembers(std::ostream& out, rtl::Reference<TypeManager> const& manager,
OUString const& name,
rtl::Reference<unoidl::ExceptionTypeEntity> exception)
{
auto const& base = exception->getDirectBase();
if (!base.isEmpty())
{
auto const ent = manager->getManager()->findEntity(base);
if (!ent.is() || ent->getSort() != unoidl::Entity::SORT_EXCEPTION_TYPE)
{
throw CannotDumpException("bad exception base \"" + base + "\"");
}
dumpExceptionMembers(out, manager, name,
static_cast<unoidl::ExceptionTypeEntity*>(ent.get()));
}
for (auto const& mem : exception->getDirectMembers())
{
out << "\n .field(\"" << mem.name << "\", &" << cppName(name) << "::" << mem.name
<< ")";
}
}
void dumpAttributes(std::ostream& out, rtl::Reference<TypeManager> const& manager,
OUString const& name, rtl::Reference<unoidl::InterfaceTypeEntity> const& entity,
std::list<OUString> const& baseTrail)
{
for (auto const& attr : entity->getDirectAttributes())
{
out << " .property<";
dumpType(out, manager, attr.type);
out << ">(\"" << attr.name << "\", +[](" << cppName(name) << " const & the_self) { return ";
for (auto const& base : baseTrail)
{
out << "static_cast<" << cppName(base) << " &>(";
}
out << "const_cast<" << cppName(name) << " &>(the_self)";
for (std::size_t i = 0; i != baseTrail.size(); ++i)
{
out << ")";
}
out << ".get" << attr.name << "(); }";
if (!attr.readOnly)
{
out << ", +[](" << cppName(name) << " & the_self, ";
dumpType(out, manager, attr.type);
if (passByReference(manager, attr.type))
{
out << " const &";
}
out << " the_value) { ";
for (auto const& base : baseTrail)
{
out << "static_cast<" << cppName(base) << " &>(";
}
out << "the_self";
for (std::size_t i = 0; i != baseTrail.size(); ++i)
{
out << ")";
}
out << ".set" << attr.name << "(the_value); }";
}
out << "/*only supported since "
"<https://github.com/emscripten-core/emscripten/commit/"
"09b765f76e052e6bfcf741ed6d2bae1788200734> \"[embind] Return value policy support "
"for properties. (#21935)\" towards emsdk 3.1.62: , "
"::emscripten::pure_virtual()*/)\n";
}
}
bool hasInOutParameters(unoidl::InterfaceTypeEntity::Method const& method)
{
return std::any_of(method.parameters.begin(), method.parameters.end(),
[](auto const& parameter) {
return parameter.direction
!= unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN;
});
}
void dumpParameters(std::ostream& out, rtl::Reference<TypeManager> const& manager,
unoidl::InterfaceTypeEntity::Method const& method, bool declarations)
{
bool first = true;
for (auto const& param : method.parameters)
{
if (first)
{
first = false;
}
else
{
out << ", ";
}
if (declarations)
{
if (param.direction != unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN)
{
out << "::unoembindhelpers::UnoInOutParam<";
}
dumpType(out, manager, param.type);
if (param.direction == unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN)
{
if (passByReference(manager, param.type))
{
out << " const &";
}
}
else
{
out << "> *";
}
out << " ";
}
out << param.name;
if (!declarations
&& param.direction != unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN)
{
out << "->value";
}
}
}
void dumpWrapper(std::ostream& out, rtl::Reference<TypeManager> const& manager,
OUString const& interfaceName, unoidl::InterfaceTypeEntity::Method const& method,
std::list<OUString> const& baseTrail)
{
out << " .function(\"" << method.name << "\", +[](::com::sun::star::uno::Reference<"
<< cppName(interfaceName);
out << "> const & the_self";
if (!method.parameters.empty())
{
out << ", ";
}
dumpParameters(out, manager, method, true);
out << ") { return ";
for (auto const& base : baseTrail)
{
out << "static_cast<" << cppName(base) << " *>(";
}
out << "the_self";
if (!baseTrail.empty())
{
out << ".get()";
}
for (std::size_t i = 0; i != baseTrail.size(); ++i)
{
out << ")";
}
out << "->" << method.name << "(";
dumpParameters(out, manager, method, false);
out << "); }";
if (hasInOutParameters(method))
{
out << ", ::emscripten::allow_raw_pointers()";
}
out << ", ::emscripten::pure_virtual())\n";
}
void dumpMethods(std::ostream& out, rtl::Reference<TypeManager> const& manager,
OUString const& name, rtl::Reference<unoidl::InterfaceTypeEntity> const& entity,
std::list<OUString> const& baseTrail)
{
if (name != "com.sun.star.uno.XInterface")
{
for (auto const& meth : entity->getDirectMethods())
{
if (!baseTrail.empty() || hasInOutParameters(meth))
{
dumpWrapper(out, manager, name, meth, baseTrail);
}
else
{
out << " .function(\"" << meth.name << "\", &" << cppName(name)
<< "::" << meth.name << ", ::emscripten::pure_virtual())\n";
}
}
}
}
rtl::Reference<unoidl::InterfaceTypeEntity>
resolveInterface(rtl::Reference<TypeManager> const& manager, OUString const& name)
{
auto const ent = manager->getManager()->findEntity(name);
if (!ent.is() || ent->getSort() != unoidl::Entity::SORT_INTERFACE_TYPE)
{
throw CannotDumpException("bad interface \"" + name + "\"");
}
return static_cast<unoidl::InterfaceTypeEntity*>(ent.get());
}
void recordVisitedBases(rtl::Reference<TypeManager> const& manager, OUString const& name,
std::set<OUString>& visitedBases)
{
auto const ent = resolveInterface(manager, name);
for (auto const& base : ent->getDirectMandatoryBases())
{
if (visitedBases.insert(base.name).second)
{
recordVisitedBases(manager, base.name, visitedBases);
}
}
}
void dumpBase(std::ostream& out, rtl::Reference<TypeManager> const& manager,
OUString const& interface, OUString const& name, std::set<OUString>& visitedBases,
std::list<OUString> const& baseTrail)
{
auto const ent = resolveInterface(manager, name);
for (auto const& base : ent->getDirectMandatoryBases())
{
if (visitedBases.insert(base.name).second)
{
auto trail = baseTrail;
trail.push_front(base.name);
dumpBase(out, manager, interface, base.name, visitedBases, trail);
}
}
dumpAttributes(out, manager, interface, ent, baseTrail);
dumpMethods(out, manager, interface, ent, baseTrail);
}
void dumpWrapperClassMembers(std::ostream& out, rtl::Reference<TypeManager> const& manager,
OUString const& interface, OUString const& name,
std::set<OUString>& visitedBases)
{
auto const ent = resolveInterface(manager, name);
for (auto const& base : ent->getDirectMandatoryBases())
{
if (visitedBases.insert(base.name).second)
{
dumpWrapperClassMembers(out, manager, interface, base.name, visitedBases);
}
}
for (auto const& attr : ent->getDirectAttributes())
{
out << " ";
dumpType(out, manager, attr.type);
out << " get" << attr.name << "() override {";
if (attr.type == "any" || attr.type.startsWith("[]"))
{
out << "\n"
" auto & the_ptr = call<";
dumpType(out, manager, attr.type);
out << " const &>(\"get" << attr.name
<< "\");\n"
" auto const the_copy(the_ptr);\n"
" delete &the_ptr;\n"
" return the_copy;\n"
" }\n";
}
else
{
out << " return call<";
dumpType(out, manager, attr.type);
out << ">(\"get" << attr.name << "\"); }\n";
}
if (!attr.readOnly)
{
out << " void set" << attr.name << "(";
dumpType(out, manager, attr.type);
switch (manager->getSort(resolveOuterTypedefs(manager, attr.type)))
{
case codemaker::UnoType::Sort::Boolean:
case codemaker::UnoType::Sort::Byte:
case codemaker::UnoType::Sort::Short:
case codemaker::UnoType::Sort::UnsignedShort:
case codemaker::UnoType::Sort::Long:
case codemaker::UnoType::Sort::UnsignedLong:
case codemaker::UnoType::Sort::Hyper:
case codemaker::UnoType::Sort::UnsignedHyper:
case codemaker::UnoType::Sort::Float:
case codemaker::UnoType::Sort::Double:
case codemaker::UnoType::Sort::Char:
case codemaker::UnoType::Sort::Enum:
break;
case codemaker::UnoType::Sort::String:
case codemaker::UnoType::Sort::Type:
case codemaker::UnoType::Sort::Any:
case codemaker::UnoType::Sort::Sequence:
case codemaker::UnoType::Sort::PlainStruct:
case codemaker::UnoType::Sort::InstantiatedPolymorphicStruct:
case codemaker::UnoType::Sort::Interface:
out << " const &";
break;
default:
throw CannotDumpException("unexpected entity \"" + attr.type
+ "\" as attribute type");
}
out << " the_value) override { return call<void>(\"set" << attr.name
<< "\", the_value); }\n";
}
}
for (auto const& meth : ent->getDirectMethods())
{
out << " ";
dumpType(out, manager, meth.returnType);
out << " " << meth.name << "(";
bool first = true;
for (auto const& param : meth.parameters)
{
if (first)
{
first = false;
}
else
{
out << ", ";
}
dumpType(out, manager, param.type);
if (param.direction == unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN)
{
switch (manager->getSort(resolveOuterTypedefs(manager, param.type)))
{
case codemaker::UnoType::Sort::Boolean:
case codemaker::UnoType::Sort::Byte:
case codemaker::UnoType::Sort::Short:
case codemaker::UnoType::Sort::UnsignedShort:
case codemaker::UnoType::Sort::Long:
case codemaker::UnoType::Sort::UnsignedLong:
case codemaker::UnoType::Sort::Hyper:
case codemaker::UnoType::Sort::UnsignedHyper:
case codemaker::UnoType::Sort::Float:
case codemaker::UnoType::Sort::Double:
case codemaker::UnoType::Sort::Char:
case codemaker::UnoType::Sort::Enum:
break;
case codemaker::UnoType::Sort::String:
case codemaker::UnoType::Sort::Type:
case codemaker::UnoType::Sort::Any:
case codemaker::UnoType::Sort::Sequence:
case codemaker::UnoType::Sort::PlainStruct:
case codemaker::UnoType::Sort::InstantiatedPolymorphicStruct:
case codemaker::UnoType::Sort::Interface:
out << " const &";
break;
default:
throw CannotDumpException("unexpected entity \"" + param.type
+ "\" as parameter type");
}
}
else
{
out << " &";
}
out << " " << param.name;
}
out << ") override {";
if (meth.returnType == "any" || meth.returnType.startsWith("[]"))
{
out << "\n"
" auto & the_ptr = call<";
dumpType(out, manager, meth.returnType);
out << " const &>(\"" << meth.name << "\"";
for (auto const& param : meth.parameters)
{
out << ", " << param.name;
}
out << ");\n"
" auto const the_copy(the_ptr);\n"
" delete &the_ptr;\n"
" return the_copy;\n"
" }\n";
}
else
{
out << " return call<";
dumpType(out, manager, meth.returnType);
out << ">(\"" << meth.name << "\"";
for (auto const& param : meth.parameters)
{
out << ", " << param.name;
}
out << "); }\n";
}
}
}
void dumpRegisterFunctionProlog(std::ostream& out, unsigned long long& counter)
{
out << "static void __attribute__((noinline)) register" << counter << "() {\n";
}
void dumpRegisterFunctionEpilog(std::ostream& out, unsigned long long& counter)
{
out << "}\n";
++counter;
if (counter == 0)
{
std::cerr << "Emitting too many register functions\n";
std::exit(EXIT_FAILURE);
}
}
void recordGenericTypes(rtl::Reference<TypeManager> const& manager, OUString const& type,
std::set<OUString>& sequences, std::set<OUString>& instantiations)
{
auto const res = resolveAllTypedefs(manager, type);
switch (manager->getSort(res))
{
case codemaker::UnoType::Sort::Sequence:
if (sequences.insert(res).second)
{
assert(res.startsWith("[]"));
recordGenericTypes(manager, res.copy(2), sequences, instantiations);
}
break;
case codemaker::UnoType::Sort::InstantiatedPolymorphicStruct:
if (instantiations.insert(res).second)
{
std::vector<OString> args;
codemaker::UnoType::decompose(u2b(res), nullptr, &args);
for (auto const& i : args)
{
recordGenericTypes(manager, b2u(i), sequences, instantiations);
}
}
break;
default:
break;
}
}
void recordInOutParameterType(rtl::Reference<TypeManager> const& manager, OUString const& type,
std::set<OUString>& inOutParameters)
{
auto const res = resolveAllTypedefs(manager, type);
inOutParameters.insert(res);
}
void writeJsMap(std::ostream& out, Module const& module, std::string const& prefix)
{
auto comma = false;
for (auto const & [ id, to ] : module.mappings)
{
if (comma)
{
out << ",\n";
}
out << prefix << "'" << id << "': " << to;
comma = true;
}
for (auto const & [ id, sub ] : module.modules)
{
if (comma)
{
out << ",\n";
}
out << prefix << "'" << id << "': {\n";
writeJsMap(out, *sub, prefix + " ");
out << prefix << "}";
comma = true;
}
if (comma)
{
out << "\n";
}
}
}
SAL_IMPLEMENT_MAIN()
{
try
{
auto const args = rtl_getAppCommandArgCount();
if (args < 4)
{
badUsage();
}
OUString name;
rtl_getAppCommandArg(0, &name.pData);
auto const cppPathname = getPathnameArgument(1);
auto const hppPathname = getPathnameArgument(2);
auto const jsPathname = getPathnameArgument(3);
rtl::Reference<TypeManager> mgr(new TypeManager);
for (sal_uInt32 i = 4; i != args; ++i)
{
auto const & [ uri, primary ] = parseRegistryArgument(i);
try
{
mgr->loadProvider(uri, primary);
}
catch (unoidl::NoSuchFileException&)
{
std::cerr << "Input <" << uri << "> does not exist\n";
std::exit(EXIT_FAILURE);
}
}
auto const module = std::make_shared<Module>();
std::vector<OUString> enums;
std::vector<OUString> structs;
std::vector<OUString> exceptions;
std::vector<OUString> interfaces;
std::vector<OUString> services;
std::vector<OUString> singletons;
for (auto const& prov : mgr->getPrimaryProviders())
{
scan(prov->createRootCursor(), u"", module.get(), enums, structs, exceptions,
interfaces, services, singletons);
}
std::ofstream cppOut(cppPathname, std::ios_base::out | std::ios_base::trunc);
if (!cppOut)
{
std::cerr << "Cannot open \"" << cppPathname << "\" for writing\n";
std::exit(EXIT_FAILURE);
}
cppOut << "#include <emscripten/bind.h>\n"
"#include <com/sun/star/uno/Any.hxx>\n"
"#include <com/sun/star/uno/Reference.hxx>\n"
"#include <o3tl/unreachable.hxx>\n"
"#include <static/unoembindhelpers/PrimaryBindings.hxx>\n";
for (auto const& enm : enums)
{
cppOut << "#include <" << enm.replace('.', '/') << ".hpp>\n";
}
for (auto const& str : structs)
{
cppOut << "#include <" << str.replace('.', '/') << ".hpp>\n";
}
for (auto const& exc : exceptions)
{
cppOut << "#include <" << exc.replace('.', '/') << ".hpp>\n";
}
for (auto const& ifc : interfaces)
{
cppOut << "#include <" << ifc.replace('.', '/') << ".hpp>\n";
}
for (auto const& srv : services)
{
cppOut << "#include <" << srv.replace('.', '/') << ".hpp>\n";
}
for (auto const& sng : singletons)
{
cppOut << "#include <" << sng.replace('.', '/') << ".hpp>\n";
}
cppOut << "\n"
"namespace emscripten::internal {\n";
for (auto const& ifc : interfaces)
{
cppOut << " template<> void raw_destructor<" << cppName(ifc) << ">(" << cppName(ifc)
<< " *) { O3TL_UNREACHABLE; }\n";
}
cppOut << "}\n\n";
unsigned long long n = 0;
for (auto const& enm : enums)
{
auto const ent = mgr->getManager()->findEntity(enm);
assert(ent.is());
assert(ent->getSort() == unoidl::Entity::SORT_ENUM_TYPE);
rtl::Reference const enmEnt(static_cast<unoidl::EnumTypeEntity*>(ent.get()));
dumpRegisterFunctionProlog(cppOut, n);
cppOut << " ::emscripten::enum_<" << cppName(enm) << ">(\"uno_Type_" << jsName(enm)
<< "\")";
for (auto const& mem : enmEnt->getMembers())
{
cppOut << "\n .value(\"" << mem.name << "\", " << cppName(enm) << "_"
<< mem.name << ")";
}
cppOut << ";\n";
cppOut << " ::unoembindhelpers::registerUnoType<" << cppName(enm) << ">();\n";
dumpRegisterFunctionEpilog(cppOut, n);
}
std::set<OUString> sequences;
std::set<OUString> instantiations;
for (auto const& str : structs)
{
auto const ent = mgr->getManager()->findEntity(str);
assert(ent.is());
assert(ent->getSort() == unoidl::Entity::SORT_PLAIN_STRUCT_TYPE);
rtl::Reference const strEnt(static_cast<unoidl::PlainStructTypeEntity*>(ent.get()));
dumpRegisterFunctionProlog(cppOut, n);
cppOut << " ::emscripten::value_object<" << cppName(str) << ">(\"uno_Type_"
<< jsName(str) << "\")";
dumpStructMembers(cppOut, mgr, str, strEnt);
cppOut << ";\n";
cppOut << " ::unoembindhelpers::registerUnoType<" << cppName(str) << ">();\n";
dumpRegisterFunctionEpilog(cppOut, n);
for (auto const& mem : strEnt->getDirectMembers())
{
recordGenericTypes(mgr, mem.type, sequences, instantiations);
}
}
for (auto const& exc : exceptions)
{
auto const ent = mgr->getManager()->findEntity(exc);
assert(ent.is());
assert(ent->getSort() == unoidl::Entity::SORT_EXCEPTION_TYPE);
rtl::Reference const excEnt(static_cast<unoidl::ExceptionTypeEntity*>(ent.get()));
dumpRegisterFunctionProlog(cppOut, n);
cppOut << " ::emscripten::value_object<" << cppName(exc) << ">(\"uno_Type_"
<< jsName(exc) << "\")";
dumpExceptionMembers(cppOut, mgr, exc, excEnt);
cppOut << ";\n";
cppOut << " ::unoembindhelpers::registerUnoType<" << cppName(exc) << ">();\n";
dumpRegisterFunctionEpilog(cppOut, n);
for (auto const& mem : excEnt->getDirectMembers())
{
recordGenericTypes(mgr, mem.type, sequences, instantiations);
}
}
std::set<OUString> inOutParams;
for (auto const& ifc : interfaces)
{
auto const ent = mgr->getManager()->findEntity(ifc);
assert(ent.is());
assert(ent->getSort() == unoidl::Entity::SORT_INTERFACE_TYPE);
rtl::Reference const ifcEnt(static_cast<unoidl::InterfaceTypeEntity*>(ent.get()));
{
auto i = ifc.lastIndexOf('.');
auto j = i + 1;
if (i == -1)
{
i = 0;
}
cppOut << "namespace the_wrappers" << cppName(ifc.copy(0, i)) << " {\n"
<< "struct " << ifc.copy(j) << " final: public ::emscripten::wrapper<"
<< cppName(ifc) << "> {\n"
<< " EMSCRIPTEN_WRAPPER(" << ifc.copy(j) << ");\n";
std::set<OUString> visitedBases;
dumpWrapperClassMembers(cppOut, mgr, ifc, ifc, visitedBases);
cppOut << "};\n}\n";
}
dumpRegisterFunctionProlog(cppOut, n);
cppOut << " ::emscripten::class_<" << cppName(ifc);
//TODO: Embind only supports single inheritance, so use that support at least for a UNO
// interface's first base, and explicitly spell out the attributes and methods of any
// remaining bases:
auto const& bases = ifcEnt->getDirectMandatoryBases();
if (bases.size() != 0)
{
cppOut << ", ::emscripten::base<" << cppName(bases[0].name) << ">";
}
cppOut << ">(\"uno_Type_" << jsName(ifc)
<< "\")\n"
" .allow_subclass<the_wrappers"
<< cppName(ifc) << ">(\"uno_Wrapper_" << jsName(ifc)
<< "\")\n"
" .smart_ptr<::com::sun::star::uno::Reference<"
<< cppName(ifc) << ">>(\"uno_Reference_" << jsName(ifc)
<< "\")\n"
" .class_function(\"query\", "
"+[](::com::sun::star::uno::Reference<::com::sun::star::uno::XInterface> "
"const & the_object) { return ::com::sun::star::uno::Reference<"
<< cppName(ifc)
<< ">(the_object, ::com::sun::star::uno::UNO_QUERY); })\n"
" .class_function(\"reference\", +[]("
<< cppName(ifc)
<< " * the_interface) { return ::com::sun::star::uno::Reference(the_interface); "
"}, ::emscripten::allow_raw_pointers())\n";
if (bases.size() > 1)
{
std::set<OUString> visitedBases;
recordVisitedBases(mgr, bases[0].name, visitedBases);
for (std::size_t i = 1; i != bases.size(); ++i)
{
dumpBase(cppOut, mgr, ifc, bases[i].name, visitedBases, { bases[i].name });
}
}
dumpAttributes(cppOut, mgr, ifc, ifcEnt, {});
dumpMethods(cppOut, mgr, ifc, ifcEnt, {});
cppOut << " ;\n"
" ::unoembindhelpers::registerUnoType<::com::sun::star::uno::Reference<"
<< cppName(ifc) << ">>();\n";
dumpRegisterFunctionEpilog(cppOut, n);
for (auto const& attr : ifcEnt->getDirectAttributes())
{
recordGenericTypes(mgr, attr.type, sequences, instantiations);
}
for (auto const& meth : ifcEnt->getDirectMethods())
{
for (auto const& param : meth.parameters)
{
recordGenericTypes(mgr, param.type, sequences, instantiations);
if (param.direction
!= unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN)
{
recordInOutParameterType(mgr, param.type, inOutParams);
}
}
recordGenericTypes(mgr, meth.returnType, sequences, instantiations);
}
}
for (auto const& ins : instantiations)
{
std::vector<OString> templArgs;
auto const templ = b2u(codemaker::UnoType::decompose(u2b(ins), nullptr, &templArgs));
auto const ent = mgr->getManager()->findEntity(templ);
assert(ent.is());
assert(ent->getSort() == unoidl::Entity::SORT_POLYMORPHIC_STRUCT_TYPE_TEMPLATE);
rtl::Reference const polEnt(
static_cast<unoidl::PolymorphicStructTypeTemplateEntity*>(ent.get()));
dumpRegisterFunctionProlog(cppOut, n);
cppOut << " ::emscripten::value_object<" << cppName(ins) << ">(\"uno_Type_"
<< jsName(ins) << "\")";
dumpInstantiationMembers(cppOut, ins, polEnt);
cppOut << ";\n";
cppOut << " ::unoembindhelpers::registerUnoType<" << cppName(ins) << ">();\n";
dumpRegisterFunctionEpilog(cppOut, n);
for (auto const& arg : templArgs)
{
recordGenericTypes(mgr, b2u(arg), sequences, instantiations);
}
for (auto const& mem : polEnt->getMembers())
{
if (!mem.parameterized)
{
recordGenericTypes(mgr, mem.type, sequences, instantiations);
}
}
}
for (auto const& srv : services)
{
auto const ent = mgr->getManager()->findEntity(srv);
assert(ent.is());
assert(ent->getSort() == unoidl::Entity::SORT_SINGLE_INTERFACE_BASED_SERVICE);
rtl::Reference const srvEnt(
static_cast<unoidl::SingleInterfaceBasedServiceEntity*>(ent.get()));
dumpRegisterFunctionProlog(cppOut, n);
for (auto const& ctor : srvEnt->getConstructors())
{
cppOut << " ::emscripten::function(\"" << jsServiceConstructor(srv, ctor)
<< "\", &" << cppName(srv) << "::" << getServiceConstructorName(ctor)
<< ");\n";
}
dumpRegisterFunctionEpilog(cppOut, n);
}
for (auto const& sng : singletons)
{
dumpRegisterFunctionProlog(cppOut, n);
cppOut << " ::emscripten::function(\"" << jsSingleton(sng) << "\", &" << cppName(sng)
<< "::get);\n";
dumpRegisterFunctionEpilog(cppOut, n);
}
cppOut << "void init_unoembind_" << name << "() {\n";
for (unsigned long long i = 0; i != n; ++i)
{
cppOut << " register" << i << "();\n";
}
for (auto const& seq : sequences)
{
cppOut << " ::unoembindhelpers::registerSequence<";
assert(seq.startsWith("[]"));
dumpType(cppOut, mgr, seq.copy(2));
cppOut << ">(\"uno_Sequence";
sal_Int32 k;
auto const nuc = b2u(codemaker::UnoType::decompose(u2b(seq), &k));
assert(k >= 1);
if (k > 1)
{
cppOut << k;
}
cppOut << "_" << jsName(nuc) << "\");\n";
}
for (auto const& par : inOutParams)
{
cppOut << " ::unoembindhelpers::registerInOutParameter<";
dumpType(cppOut, mgr, par);
cppOut << ">(\"uno_InOutParam_";
sal_Int32 k;
auto const nuc = b2u(codemaker::UnoType::decompose(u2b(par), &k));
if (k >= 1)
{
cppOut << "sequence";
if (k > 1)
{
cppOut << k;
}
cppOut << "_";
}
cppOut << jsName(nuc.replace(' ', '_')) << "\");\n";
}
cppOut << "}\n";
cppOut.close();
if (!cppOut)
{
std::cerr << "Failed to write \"" << cppPathname << "\"\n";
std::exit(EXIT_FAILURE);
}
std::ofstream hppOut(hppPathname, std::ios_base::out | std::ios_base::trunc);
if (!hppOut)
{
std::cerr << "Cannot open \"" << hppPathname << "\" for writing\n";
std::exit(EXIT_FAILURE);
}
hppOut << "void init_unoembind_" << name << "();\n";
hppOut.close();
if (!hppOut)
{
std::cerr << "Failed to write \"" << hppPathname << "\"\n";
std::exit(EXIT_FAILURE);
}
std::ofstream jsOut(jsPathname, std::ios_base::out | std::ios_base::trunc);
if (!jsOut)
{
std::cerr << "Cannot open \"" << jsPathname << "\" for writing\n";
std::exit(EXIT_FAILURE);
}
jsOut << "function init_unoembind_" << name << "(instance, tagSymbol) {\n";
for (auto const& enm : enums)
{
auto const ent = mgr->getManager()->findEntity(enm);
if (!ent.is() || ent->getSort() != unoidl::Entity::SORT_ENUM_TYPE)
{
throw CannotDumpException("bad enum type \"" + enm + "\"");
}
for (auto const& mem :
static_cast<unoidl::EnumTypeEntity const*>(ent.get())->getMembers())
{
jsOut << " instance.uno_Type_" << enm.replace('.', '$') << "." << mem.name
<< "[tagSymbol] = {kind: 'enumerator', type: '" << enm << "'};\n";
}
}
jsOut << " return {\n";
writeJsMap(jsOut, *module, " ");
jsOut << " };\n"
"};\n";
jsOut.close();
if (!jsOut)
{
std::cerr << "Failed to write \"" << jsPathname << "\"\n";
std::exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
catch (unoidl::FileFormatException const& e)
{
std::cerr << "Bad input <" << e.getUri() << ">: " << e.getDetail() << "\n";
std::exit(EXIT_FAILURE);
}
catch (CannotDumpException const& e)
{
std::cerr << "Failure: " << e.getMessage() << "\n";
std::exit(EXIT_FAILURE);
}
catch (std::exception const& e)
{
std::cerr << "Failure: " << e.what() << "\n";
std::exit(EXIT_FAILURE);
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */