/* -*- 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 \n\n"
"where each 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"
"/ and corresponding JavaScript scaffolding code is\n"
"written to . The 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(\" 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 \n"
"#include \n"
"#include \n"
"#include \n"
"#include \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
<< 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: */
Messung V0.5 C=97 H=83 G=90
¤ Dauer der Verarbeitung: 0.19 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland