Embind: We no longer need interface FromAny ctor
[LibreOffice.git] / static / source / embindmaker / embindmaker.cxx
blobea7879ecceb0bc8d58c7896fce38480a5d4de3d9
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <sal/config.h>
12 #include <cassert>
13 #include <cstddef>
14 #include <cstdlib>
15 #include <fstream>
16 #include <ios>
17 #include <iostream>
18 #include <list>
19 #include <map>
20 #include <memory>
21 #include <ostream>
22 #include <set>
23 #include <string>
24 #include <string_view>
25 #include <utility>
26 #include <vector>
28 #include <codemaker/commoncpp.hxx>
29 #include <codemaker/global.hxx>
30 #include <codemaker/typemanager.hxx>
31 #include <osl/file.hxx>
32 #include <osl/process.h>
33 #include <osl/thread.h>
34 #include <rtl/process.h>
35 #include <rtl/ref.hxx>
36 #include <rtl/string.hxx>
37 #include <rtl/textcvt.h>
38 #include <rtl/ustrbuf.hxx>
39 #include <rtl/ustring.hxx>
40 #include <sal/main.h>
41 #include <sal/types.h>
42 #include <unoidl/unoidl.hxx>
44 namespace
46 void badUsage()
48 std::cerr
49 << "Usage:\n\n"
50 " embindmaker <name> <cpp-output> <hpp-output> <js-output> <registries>\n\n"
51 "where each <registry> is '+' (primary) or ':' (secondary), followed by: either a\n"
52 "new- or legacy-format .rdb file, a single .idl file, or a root directory of an\n"
53 ".idl file tree. For all primary registries, Embind code is written to\n"
54 "<cpp-output>/<hpp-output> and corresponding JavaScript scaffolding code is\n"
55 "written to <js-output>. The <name> is used as part of some of the identifiers\n"
56 "in those generated files.\n";
57 std::exit(EXIT_FAILURE);
60 std::string getPathnameArgument(sal_uInt32 argument)
62 OUString arg;
63 rtl_getAppCommandArg(argument, &arg.pData);
64 OString path;
65 auto const enc = osl_getThreadTextEncoding();
66 if (!arg.convertToString(&path, enc,
67 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
68 | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR))
70 std::cerr << "Cannot convert \"" << arg << "\" to system encoding " << enc << "\n";
71 std::exit(EXIT_FAILURE);
73 return std::string(path);
76 std::pair<OUString, bool> parseRegistryArgument(sal_uInt32 argument)
78 OUString arg;
79 rtl_getAppCommandArg(argument, &arg.pData);
80 bool primary;
81 if (arg.startsWith(u"+", &arg))
83 primary = true;
85 else if (arg.startsWith(u":", &arg))
87 primary = false;
89 else
91 std::cerr << "Bad registry argument \"" << arg << "\"\n";
92 std::exit(EXIT_FAILURE);
94 OUString url;
95 auto const e1 = osl::FileBase::getFileURLFromSystemPath(arg, url);
96 if (e1 != osl::FileBase::E_None)
98 std::cerr << "Cannot convert \"" << arg << "\" to file URL, error code " << +e1 << "\n";
99 std::exit(EXIT_FAILURE);
101 OUString cwd;
102 auto const e2 = osl_getProcessWorkingDir(&cwd.pData);
103 if (e2 != osl_Process_E_None)
105 std::cerr << "Cannot obtain working directory, error code " << +e2 << "\n";
106 std::exit(EXIT_FAILURE);
108 OUString abs;
109 auto const e3 = osl::FileBase::getAbsoluteFileURL(cwd, url, abs);
110 if (e3 != osl::FileBase::E_None)
112 std::cerr << "Cannot make \"" << url << "\" into an absolute file URL, error code " << +e3
113 << "\n";
114 std::exit(EXIT_FAILURE);
116 return { abs, primary };
119 struct Module
121 std::map<OUString, std::shared_ptr<Module>> modules;
122 std::vector<std::pair<OUString, OUString>> mappings;
125 OUString
126 getServiceConstructorName(unoidl::SingleInterfaceBasedServiceEntity::Constructor const& constructor)
128 return constructor.defaultConstructor ? u"create"_ustr : constructor.name;
131 OUString jsName(OUString const& name) { return name.replace('.', '$'); }
133 OUString
134 jsServiceConstructor(OUString const& service,
135 unoidl::SingleInterfaceBasedServiceEntity::Constructor const& constructor)
137 return "uno_Function_" + jsName(service) + "$$" + getServiceConstructorName(constructor);
140 OUString jsSingleton(OUString const& singleton) { return "uno_Function_" + jsName(singleton); }
142 void scan(rtl::Reference<unoidl::MapCursor> const& cursor, std::u16string_view prefix,
143 Module* module, std::vector<OUString>& enums, std::vector<OUString>& structs,
144 std::vector<OUString>& exceptions, std::vector<OUString>& interfaces,
145 std::vector<OUString>& services, std::vector<OUString>& singletons)
147 assert(cursor.is());
148 assert(module != nullptr);
149 for (;;)
151 OUString id;
152 auto const ent = cursor->getNext(&id);
153 if (!ent.is())
155 break;
157 OUString name(prefix + id);
158 switch (ent->getSort())
160 case unoidl::Entity::SORT_MODULE:
162 auto& sub = module->modules[id];
163 if (!sub)
165 sub = std::make_shared<Module>();
167 scan(static_cast<unoidl::ModuleEntity*>(ent.get())->createCursor(),
168 Concat2View(name + "."), sub.get(), enums, structs, exceptions, interfaces,
169 services, singletons);
170 break;
172 case unoidl::Entity::SORT_ENUM_TYPE:
173 module->mappings.emplace_back(id, "instance.uno_Type_" + jsName(name));
174 enums.emplace_back(name);
175 break;
176 case unoidl::Entity::SORT_PLAIN_STRUCT_TYPE:
177 module->mappings.emplace_back(id, "instance.uno_Type_" + jsName(name));
178 structs.emplace_back(name);
179 break;
180 case unoidl::Entity::SORT_EXCEPTION_TYPE:
181 module->mappings.emplace_back(id, "instance.uno_Type_" + jsName(name));
182 exceptions.emplace_back(name);
183 break;
184 case unoidl::Entity::SORT_INTERFACE_TYPE:
185 module->mappings.emplace_back(id, "instance.uno_Type_" + jsName(name));
186 interfaces.emplace_back(name);
187 break;
188 case unoidl::Entity::SORT_CONSTANT_GROUP:
190 auto const& members
191 = static_cast<unoidl::ConstantGroupEntity*>(ent.get())->getMembers();
192 if (!members.empty())
194 auto sub = std::make_shared<Module>();
195 for (auto const& member : members)
197 OUString value;
198 switch (member.value.type)
200 case unoidl::ConstantValue::TYPE_BOOLEAN:
201 value = member.value.booleanValue ? u"true"_ustr : u"false"_ustr;
202 break;
203 case unoidl::ConstantValue::TYPE_BYTE:
204 value = OUString::number(member.value.byteValue);
205 break;
206 case unoidl::ConstantValue::TYPE_SHORT:
207 value = OUString::number(member.value.shortValue);
208 break;
209 case unoidl::ConstantValue::TYPE_UNSIGNED_SHORT:
210 value = OUString::number(member.value.unsignedShortValue);
211 break;
212 case unoidl::ConstantValue::TYPE_LONG:
213 value = OUString::number(member.value.longValue);
214 break;
215 case unoidl::ConstantValue::TYPE_UNSIGNED_LONG:
216 value = OUString::number(member.value.unsignedLongValue);
217 break;
218 case unoidl::ConstantValue::TYPE_HYPER:
219 value = OUString::number(member.value.hyperValue) + "n";
220 break;
221 case unoidl::ConstantValue::TYPE_UNSIGNED_HYPER:
222 value = OUString::number(member.value.unsignedHyperValue) + "n";
223 break;
224 case unoidl::ConstantValue::TYPE_FLOAT:
225 value = OUString::number(member.value.floatValue);
226 break;
227 case unoidl::ConstantValue::TYPE_DOUBLE:
228 value = OUString::number(member.value.doubleValue);
229 break;
231 sub->mappings.emplace_back(member.name, value);
233 module->modules[id] = sub;
235 break;
237 case unoidl::Entity::SORT_SINGLE_INTERFACE_BASED_SERVICE:
239 auto const& ctors
240 = static_cast<unoidl::SingleInterfaceBasedServiceEntity*>(ent.get())
241 ->getConstructors();
242 if (!ctors.empty())
244 auto sub = std::make_shared<Module>();
245 for (auto const& ctor : ctors)
247 sub->mappings.emplace_back(getServiceConstructorName(ctor),
248 "instance." + jsServiceConstructor(name, ctor));
250 module->modules[id] = sub;
251 services.emplace_back(name);
254 break;
255 case unoidl::Entity::SORT_INTERFACE_BASED_SINGLETON:
256 module->mappings.emplace_back(id, "instance." + jsSingleton(name));
257 singletons.emplace_back(name);
258 break;
259 default:
260 break;
265 OUString cppName(OUString const& name) { return "::" + name.replaceAll(u".", u"::"); }
267 OUString resolveOuterTypedefs(rtl::Reference<TypeManager> const& manager, OUString const& name)
269 for (OUString n(name);;)
271 rtl::Reference<unoidl::Entity> ent;
272 if (manager->getSort(n, &ent) != codemaker::UnoType::Sort::Typedef)
274 return n;
276 n = dynamic_cast<unoidl::TypedefEntity&>(*ent).getType();
280 OUString resolveAllTypedefs(rtl::Reference<TypeManager> const& manager, std::u16string_view name)
282 sal_Int32 k1;
283 OUString n(b2u(codemaker::UnoType::decompose(u2b(name), &k1)));
284 for (;;)
286 rtl::Reference<unoidl::Entity> ent;
287 if (manager->getSort(n, &ent) != codemaker::UnoType::Sort::Typedef)
289 break;
291 sal_Int32 k2;
292 n = b2u(codemaker::UnoType::decompose(
293 u2b(static_cast<unoidl::TypedefEntity*>(ent.get())->getType()), &k2));
294 k1 += k2; //TODO: overflow
296 OUStringBuffer b;
297 for (sal_Int32 i = 0; i != k1; ++i)
299 b.append("[]");
301 b.append(n);
302 return b.makeStringAndClear();
305 bool passByReference(rtl::Reference<TypeManager> const& manager, OUString const& name)
307 switch (manager->getSort(resolveOuterTypedefs(manager, name)))
309 case codemaker::UnoType::Sort::Boolean:
310 case codemaker::UnoType::Sort::Byte:
311 case codemaker::UnoType::Sort::Short:
312 case codemaker::UnoType::Sort::UnsignedShort:
313 case codemaker::UnoType::Sort::Long:
314 case codemaker::UnoType::Sort::UnsignedLong:
315 case codemaker::UnoType::Sort::Hyper:
316 case codemaker::UnoType::Sort::UnsignedHyper:
317 case codemaker::UnoType::Sort::Float:
318 case codemaker::UnoType::Sort::Double:
319 case codemaker::UnoType::Sort::Char:
320 case codemaker::UnoType::Sort::Enum:
321 return false;
322 case codemaker::UnoType::Sort::String:
323 case codemaker::UnoType::Sort::Type:
324 case codemaker::UnoType::Sort::Any:
325 case codemaker::UnoType::Sort::Sequence:
326 case codemaker::UnoType::Sort::PlainStruct:
327 case codemaker::UnoType::Sort::InstantiatedPolymorphicStruct:
328 case codemaker::UnoType::Sort::Interface:
329 return true;
330 default:
331 throw CannotDumpException("unexpected entity \"" + name
332 + "\" in call to passByReference");
336 void dumpType(std::ostream& out, rtl::Reference<TypeManager> const& manager,
337 std::u16string_view name)
339 sal_Int32 k;
340 std::vector<OString> args;
341 OUString n(
342 b2u(codemaker::UnoType::decompose(u2b(resolveAllTypedefs(manager, name)), &k, &args)));
343 for (sal_Int32 i = 0; i != k; ++i)
345 out << "::com::sun::star::uno::Sequence<";
347 switch (manager->getSort(n))
349 case codemaker::UnoType::Sort::Void:
350 out << "void";
351 break;
352 case codemaker::UnoType::Sort::Boolean:
353 out << "::sal_Bool";
354 break;
355 case codemaker::UnoType::Sort::Byte:
356 out << "::sal_Int8";
357 break;
358 case codemaker::UnoType::Sort::Short:
359 out << "::sal_Int16";
360 break;
361 case codemaker::UnoType::Sort::UnsignedShort:
362 out << "::sal_uInt16";
363 break;
364 case codemaker::UnoType::Sort::Long:
365 out << "::sal_Int32";
366 break;
367 case codemaker::UnoType::Sort::UnsignedLong:
368 out << "::sal_uInt32";
369 break;
370 case codemaker::UnoType::Sort::Hyper:
371 out << "::sal_Int64";
372 break;
373 case codemaker::UnoType::Sort::UnsignedHyper:
374 out << "::sal_uInt64";
375 break;
376 case codemaker::UnoType::Sort::Float:
377 out << "float";
378 break;
379 case codemaker::UnoType::Sort::Double:
380 out << "double";
381 break;
382 case codemaker::UnoType::Sort::Char:
383 out << "::sal_Unicode";
384 break;
385 case codemaker::UnoType::Sort::String:
386 out << "::rtl::OUString";
387 break;
388 case codemaker::UnoType::Sort::Type:
389 out << "::com::sun::star::uno::Type";
390 break;
391 case codemaker::UnoType::Sort::Any:
392 out << "::com::sun::star::uno::Any";
393 break;
394 case codemaker::UnoType::Sort::Enum:
395 case codemaker::UnoType::Sort::PlainStruct:
396 case codemaker::UnoType::Sort::Exception:
397 out << cppName(n);
398 break;
399 case codemaker::UnoType::Sort::PolymorphicStructTemplate:
400 out << cppName(n);
401 if (!args.empty())
403 out << "<";
404 bool first = true;
405 for (auto const& arg : args)
407 if (first)
409 first = false;
411 else
413 out << ", ";
415 dumpType(out, manager, b2u(arg));
417 out << ">";
419 break;
420 case codemaker::UnoType::Sort::Interface:
421 out << "::com::sun::star::uno::Reference<";
422 out << cppName(n);
423 out << ">";
424 break;
425 default:
426 throw CannotDumpException(OUString::Concat("unexpected entity \"") + name
427 + "\" in call to dumpType");
429 for (sal_Int32 i = 0; i != k; ++i)
431 out << ">";
435 void dumpStructMembers(std::ostream& out, rtl::Reference<TypeManager> const& manager,
436 OUString const& name, rtl::Reference<unoidl::PlainStructTypeEntity> struc)
438 auto const& base = struc->getDirectBase();
439 if (!base.isEmpty())
441 auto const ent = manager->getManager()->findEntity(base);
442 if (!ent.is() || ent->getSort() != unoidl::Entity::SORT_PLAIN_STRUCT_TYPE)
444 throw CannotDumpException("bad struct base \"" + base + "\"");
446 dumpStructMembers(out, manager, name,
447 static_cast<unoidl::PlainStructTypeEntity*>(ent.get()));
449 for (auto const& mem : struc->getDirectMembers())
451 out << "\n .field(\"" << mem.name << "\", &" << cppName(name) << "::" << mem.name
452 << ")";
456 void dumpExceptionMembers(std::ostream& out, rtl::Reference<TypeManager> const& manager,
457 OUString const& name,
458 rtl::Reference<unoidl::ExceptionTypeEntity> exception)
460 auto const& base = exception->getDirectBase();
461 if (!base.isEmpty())
463 auto const ent = manager->getManager()->findEntity(base);
464 if (!ent.is() || ent->getSort() != unoidl::Entity::SORT_EXCEPTION_TYPE)
466 throw CannotDumpException("bad exception base \"" + base + "\"");
468 dumpExceptionMembers(out, manager, name,
469 static_cast<unoidl::ExceptionTypeEntity*>(ent.get()));
471 for (auto const& mem : exception->getDirectMembers())
473 out << "\n .field(\"" << mem.name << "\", &" << cppName(name) << "::" << mem.name
474 << ")";
478 void dumpAttributes(std::ostream& out, rtl::Reference<TypeManager> const& manager,
479 OUString const& name, rtl::Reference<unoidl::InterfaceTypeEntity> const& entity,
480 std::list<OUString> const& baseTrail)
482 for (auto const& attr : entity->getDirectAttributes())
484 out << " .function(\"get" << attr.name << "\", ";
485 if (baseTrail.empty())
487 out << "&" << cppName(name) << "::get" << attr.name;
489 else
491 out << "+[](::com::sun::star::uno::Reference<" << cppName(name)
492 << "> const & the_self) { return ";
493 for (auto const& base : baseTrail)
495 out << "static_cast<" << cppName(base) << " *>(";
497 out << "the_self.get()";
498 for (std::size_t i = 0; i != baseTrail.size(); ++i)
500 out << ")";
502 out << "->get" << attr.name << "(); }";
504 out << ", ::emscripten::pure_virtual())\n";
505 if (!attr.readOnly)
507 out << " .function(\"set" << attr.name << "\", ";
508 if (baseTrail.empty())
510 out << "&" << cppName(name) << "::set" << attr.name;
512 else
514 out << "+[](::com::sun::star::uno::Reference<" << cppName(name)
515 << "> const & the_self, ";
516 dumpType(out, manager, attr.type);
517 if (passByReference(manager, attr.type))
519 out << " const &";
521 out << " the_value) { ";
522 for (auto const& base : baseTrail)
524 out << "static_cast<" << cppName(base) << " *>(";
526 out << "the_self.get()";
527 for (std::size_t i = 0; i != baseTrail.size(); ++i)
529 out << ")";
531 out << "->set" << attr.name << "(the_value); }";
533 out << ", ::emscripten::pure_virtual())\n";
538 bool hasInOutParameters(unoidl::InterfaceTypeEntity::Method const& method)
540 return std::any_of(method.parameters.begin(), method.parameters.end(),
541 [](auto const& parameter) {
542 return parameter.direction
543 != unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN;
547 void dumpParameters(std::ostream& out, rtl::Reference<TypeManager> const& manager,
548 unoidl::InterfaceTypeEntity::Method const& method, bool declarations)
550 bool first = true;
551 for (auto const& param : method.parameters)
553 if (first)
555 first = false;
557 else
559 out << ", ";
561 bool wrap = false;
562 if (param.direction != unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN)
564 switch (manager->getSort(resolveOuterTypedefs(manager, param.type)))
566 case codemaker::UnoType::Sort::Boolean:
567 case codemaker::UnoType::Sort::Byte:
568 case codemaker::UnoType::Sort::Short:
569 case codemaker::UnoType::Sort::UnsignedShort:
570 case codemaker::UnoType::Sort::Long:
571 case codemaker::UnoType::Sort::UnsignedLong:
572 case codemaker::UnoType::Sort::Hyper:
573 case codemaker::UnoType::Sort::UnsignedHyper:
574 case codemaker::UnoType::Sort::Float:
575 case codemaker::UnoType::Sort::Double:
576 case codemaker::UnoType::Sort::Char:
577 case codemaker::UnoType::Sort::Enum:
578 wrap = true;
579 break;
580 case codemaker::UnoType::Sort::String:
581 case codemaker::UnoType::Sort::Type:
582 case codemaker::UnoType::Sort::Any:
583 case codemaker::UnoType::Sort::Sequence:
584 case codemaker::UnoType::Sort::PlainStruct:
585 case codemaker::UnoType::Sort::InstantiatedPolymorphicStruct:
586 case codemaker::UnoType::Sort::Interface:
587 break;
588 default:
589 throw CannotDumpException("unexpected entity \"" + param.type
590 + "\" as parameter type");
593 if (declarations)
595 if (wrap)
597 out << "::unoembindhelpers::UnoInOutParam<";
599 dumpType(out, manager, param.type);
600 if (wrap)
602 out << ">";
604 if (param.direction == unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN)
606 if (passByReference(manager, param.type))
608 out << " const &";
611 else
613 out << " *";
615 out << " ";
617 else if (param.direction != unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN
618 && !wrap)
620 out << "*";
622 out << param.name;
623 if (!declarations && wrap)
625 out << "->value";
630 void dumpWrapper(std::ostream& out, rtl::Reference<TypeManager> const& manager,
631 OUString const& interfaceName, unoidl::InterfaceTypeEntity::Method const& method,
632 std::list<OUString> const& baseTrail)
634 out << " .function(\"" << method.name << "\", +[](::com::sun::star::uno::Reference<"
635 << cppName(interfaceName);
636 out << "> const & the_self";
637 if (!method.parameters.empty())
639 out << ", ";
641 dumpParameters(out, manager, method, true);
642 out << ") { return ";
643 for (auto const& base : baseTrail)
645 out << "static_cast<" << cppName(base) << " *>(";
647 out << "the_self";
648 if (!baseTrail.empty())
650 out << ".get()";
652 for (std::size_t i = 0; i != baseTrail.size(); ++i)
654 out << ")";
656 out << "->" << method.name << "(";
657 dumpParameters(out, manager, method, false);
658 out << "); }";
659 if (hasInOutParameters(method))
661 out << ", ::emscripten::allow_raw_pointers()";
663 out << ", ::emscripten::pure_virtual())\n";
666 void dumpMethods(std::ostream& out, rtl::Reference<TypeManager> const& manager,
667 OUString const& name, rtl::Reference<unoidl::InterfaceTypeEntity> const& entity,
668 std::list<OUString> const& baseTrail)
670 for (auto const& meth : entity->getDirectMethods())
672 if (!baseTrail.empty() || hasInOutParameters(meth))
674 dumpWrapper(out, manager, name, meth, baseTrail);
676 else
678 out << " .function(\"" << meth.name << "\", &" << cppName(name)
679 << "::" << meth.name << ", ::emscripten::pure_virtual())\n";
684 rtl::Reference<unoidl::InterfaceTypeEntity>
685 resolveInterface(rtl::Reference<TypeManager> const& manager, OUString const& name)
687 auto const ent = manager->getManager()->findEntity(name);
688 if (!ent.is() || ent->getSort() != unoidl::Entity::SORT_INTERFACE_TYPE)
690 throw CannotDumpException("bad interface \"" + name + "\"");
692 return static_cast<unoidl::InterfaceTypeEntity*>(ent.get());
695 void recordVisitedBases(rtl::Reference<TypeManager> const& manager, OUString const& name,
696 std::set<OUString>& visitedBases)
698 auto const ent = resolveInterface(manager, name);
699 for (auto const& base : ent->getDirectMandatoryBases())
701 if (visitedBases.insert(base.name).second)
703 recordVisitedBases(manager, base.name, visitedBases);
708 void dumpBase(std::ostream& out, rtl::Reference<TypeManager> const& manager,
709 OUString const& interface, OUString const& name, std::set<OUString>& visitedBases,
710 std::list<OUString> const& baseTrail)
712 auto const ent = resolveInterface(manager, name);
713 for (auto const& base : ent->getDirectMandatoryBases())
715 if (visitedBases.insert(base.name).second)
717 auto trail = baseTrail;
718 trail.push_front(base.name);
719 dumpBase(out, manager, interface, base.name, visitedBases, trail);
722 dumpAttributes(out, manager, interface, ent, baseTrail);
723 dumpMethods(out, manager, interface, ent, baseTrail);
726 void dumpWrapperClassMembers(std::ostream& out, rtl::Reference<TypeManager> const& manager,
727 OUString const& interface, OUString const& name,
728 std::set<OUString>& visitedBases)
730 auto const ent = resolveInterface(manager, name);
731 for (auto const& base : ent->getDirectMandatoryBases())
733 if (visitedBases.insert(base.name).second)
735 dumpWrapperClassMembers(out, manager, interface, base.name, visitedBases);
738 for (auto const& attr : ent->getDirectAttributes())
740 out << " ";
741 dumpType(out, manager, attr.type);
742 out << " get" << attr.name << "() override { return call<";
743 dumpType(out, manager, attr.type);
744 out << ">(\"get" << attr.name << "\"); }\n";
745 if (!attr.readOnly)
747 out << " void set" << attr.name << "(";
748 dumpType(out, manager, attr.type);
749 switch (manager->getSort(resolveOuterTypedefs(manager, attr.type)))
751 case codemaker::UnoType::Sort::Boolean:
752 case codemaker::UnoType::Sort::Byte:
753 case codemaker::UnoType::Sort::Short:
754 case codemaker::UnoType::Sort::UnsignedShort:
755 case codemaker::UnoType::Sort::Long:
756 case codemaker::UnoType::Sort::UnsignedLong:
757 case codemaker::UnoType::Sort::Hyper:
758 case codemaker::UnoType::Sort::UnsignedHyper:
759 case codemaker::UnoType::Sort::Float:
760 case codemaker::UnoType::Sort::Double:
761 case codemaker::UnoType::Sort::Char:
762 case codemaker::UnoType::Sort::Enum:
763 break;
764 case codemaker::UnoType::Sort::String:
765 case codemaker::UnoType::Sort::Type:
766 case codemaker::UnoType::Sort::Any:
767 case codemaker::UnoType::Sort::Sequence:
768 case codemaker::UnoType::Sort::PlainStruct:
769 case codemaker::UnoType::Sort::InstantiatedPolymorphicStruct:
770 case codemaker::UnoType::Sort::Interface:
771 out << " const &";
772 break;
773 default:
774 throw CannotDumpException("unexpected entity \"" + attr.type
775 + "\" as attribute type");
777 out << " the_value) override { return call<void>(\"set" << attr.name
778 << "\", the_value); }\n";
781 for (auto const& meth : ent->getDirectMethods())
783 out << " ";
784 dumpType(out, manager, meth.returnType);
785 out << " " << meth.name << "(";
786 bool first = true;
787 for (auto const& param : meth.parameters)
789 if (first)
791 first = false;
793 else
795 out << ", ";
797 dumpType(out, manager, param.type);
798 if (param.direction == unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN)
800 switch (manager->getSort(resolveOuterTypedefs(manager, param.type)))
802 case codemaker::UnoType::Sort::Boolean:
803 case codemaker::UnoType::Sort::Byte:
804 case codemaker::UnoType::Sort::Short:
805 case codemaker::UnoType::Sort::UnsignedShort:
806 case codemaker::UnoType::Sort::Long:
807 case codemaker::UnoType::Sort::UnsignedLong:
808 case codemaker::UnoType::Sort::Hyper:
809 case codemaker::UnoType::Sort::UnsignedHyper:
810 case codemaker::UnoType::Sort::Float:
811 case codemaker::UnoType::Sort::Double:
812 case codemaker::UnoType::Sort::Char:
813 case codemaker::UnoType::Sort::Enum:
814 break;
815 case codemaker::UnoType::Sort::String:
816 case codemaker::UnoType::Sort::Type:
817 case codemaker::UnoType::Sort::Any:
818 case codemaker::UnoType::Sort::Sequence:
819 case codemaker::UnoType::Sort::PlainStruct:
820 case codemaker::UnoType::Sort::InstantiatedPolymorphicStruct:
821 case codemaker::UnoType::Sort::Interface:
822 out << " const &";
823 break;
824 default:
825 throw CannotDumpException("unexpected entity \"" + param.type
826 + "\" as parameter type");
829 else
831 out << " &";
833 out << " " << param.name;
835 out << ") override { return call<";
836 dumpType(out, manager, meth.returnType);
837 out << ">(\"" << meth.name << "\"";
838 for (auto const& param : meth.parameters)
840 out << ", " << param.name;
842 out << "); }\n";
846 void dumpRegisterFunctionProlog(std::ostream& out, unsigned long long& counter)
848 out << "static void __attribute__((noinline)) register" << counter << "() {\n";
851 void dumpRegisterFunctionEpilog(std::ostream& out, unsigned long long& counter)
853 out << "}\n";
854 ++counter;
855 if (counter == 0)
857 std::cerr << "Emitting too many register functions\n";
858 std::exit(EXIT_FAILURE);
862 void recordSequenceTypes(rtl::Reference<TypeManager> const& manager, OUString const& type,
863 std::set<OUString>& sequences)
865 auto const res = resolveAllTypedefs(manager, type);
866 if (manager->getSort(res) == codemaker::UnoType::Sort::Sequence)
868 sequences.insert(res);
872 void writeJsMap(std::ostream& out, Module const& module, std::string const& prefix)
874 auto comma = false;
875 for (auto const & [ id, to ] : module.mappings)
877 if (comma)
879 out << ",\n";
881 out << prefix << "'" << id << "': " << to;
882 comma = true;
884 for (auto const & [ id, sub ] : module.modules)
886 if (comma)
888 out << ",\n";
890 out << prefix << "'" << id << "': {\n";
891 writeJsMap(out, *sub, prefix + " ");
892 out << prefix << "}";
893 comma = true;
895 if (comma)
897 out << "\n";
902 SAL_IMPLEMENT_MAIN()
906 auto const args = rtl_getAppCommandArgCount();
907 if (args < 4)
909 badUsage();
911 OUString name;
912 rtl_getAppCommandArg(0, &name.pData);
913 auto const cppPathname = getPathnameArgument(1);
914 auto const hppPathname = getPathnameArgument(2);
915 auto const jsPathname = getPathnameArgument(3);
916 rtl::Reference<TypeManager> mgr(new TypeManager);
917 for (sal_uInt32 i = 4; i != args; ++i)
919 auto const & [ uri, primary ] = parseRegistryArgument(i);
922 mgr->loadProvider(uri, primary);
924 catch (unoidl::NoSuchFileException&)
926 std::cerr << "Input <" << uri << "> does not exist\n";
927 std::exit(EXIT_FAILURE);
930 auto const module = std::make_shared<Module>();
931 std::vector<OUString> enums;
932 std::vector<OUString> structs;
933 std::vector<OUString> exceptions;
934 std::vector<OUString> interfaces;
935 std::vector<OUString> services;
936 std::vector<OUString> singletons;
937 for (auto const& prov : mgr->getPrimaryProviders())
939 scan(prov->createRootCursor(), u"", module.get(), enums, structs, exceptions,
940 interfaces, services, singletons);
942 std::ofstream cppOut(cppPathname, std::ios_base::out | std::ios_base::trunc);
943 if (!cppOut)
945 std::cerr << "Cannot open \"" << cppPathname << "\" for writing\n";
946 std::exit(EXIT_FAILURE);
948 cppOut << "#include <emscripten/bind.h>\n"
949 "#include <com/sun/star/uno/Any.hxx>\n"
950 "#include <com/sun/star/uno/Reference.hxx>\n"
951 "#include <static/unoembindhelpers/PrimaryBindings.hxx>\n";
952 for (auto const& enm : enums)
954 cppOut << "#include <" << enm.replace('.', '/') << ".hpp>\n";
956 for (auto const& str : structs)
958 cppOut << "#include <" << str.replace('.', '/') << ".hpp>\n";
960 for (auto const& exc : exceptions)
962 cppOut << "#include <" << exc.replace('.', '/') << ".hpp>\n";
964 for (auto const& ifc : interfaces)
966 cppOut << "#include <" << ifc.replace('.', '/') << ".hpp>\n";
968 for (auto const& srv : services)
970 cppOut << "#include <" << srv.replace('.', '/') << ".hpp>\n";
972 for (auto const& sng : singletons)
974 cppOut << "#include <" << sng.replace('.', '/') << ".hpp>\n";
976 cppOut << "\n"
977 "// TODO: This is a temporary workaround that likely causes the Embind UNO\n"
978 "// bindings to leak memory. Reference counting and cloning mechanisms of\n"
979 "// Embind should be investigated to figure out what exactly we need here:\n"
980 "namespace emscripten::internal {\n";
981 for (auto const& ifc : interfaces)
983 cppOut << " template<> void raw_destructor<" << cppName(ifc) << ">(" << cppName(ifc)
984 << " *) {}\n";
986 cppOut << "}\n\n";
987 unsigned long long n = 0;
988 for (auto const& enm : enums)
990 auto const ent = mgr->getManager()->findEntity(enm);
991 assert(ent.is());
992 assert(ent->getSort() == unoidl::Entity::SORT_ENUM_TYPE);
993 rtl::Reference const enmEnt(static_cast<unoidl::EnumTypeEntity*>(ent.get()));
994 dumpRegisterFunctionProlog(cppOut, n);
995 cppOut << " ::emscripten::enum_<" << cppName(enm) << ">(\"uno_Type_" << jsName(enm)
996 << "\")";
997 for (auto const& mem : enmEnt->getMembers())
999 cppOut << "\n .value(\"" << mem.name << "\", " << cppName(enm) << "_"
1000 << mem.name << ")";
1002 cppOut << ";\n";
1003 cppOut << " ::unoembindhelpers::registerUnoType<" << cppName(enm) << ">();\n";
1004 dumpRegisterFunctionEpilog(cppOut, n);
1006 std::set<OUString> sequences;
1007 for (auto const& str : structs)
1009 auto const ent = mgr->getManager()->findEntity(str);
1010 assert(ent.is());
1011 assert(ent->getSort() == unoidl::Entity::SORT_PLAIN_STRUCT_TYPE);
1012 rtl::Reference const strEnt(static_cast<unoidl::PlainStructTypeEntity*>(ent.get()));
1013 dumpRegisterFunctionProlog(cppOut, n);
1014 cppOut << " ::emscripten::value_object<" << cppName(str) << ">(\"uno_Type_"
1015 << jsName(str) << "\")";
1016 dumpStructMembers(cppOut, mgr, str, strEnt);
1017 cppOut << ";\n";
1018 cppOut << " ::unoembindhelpers::registerUnoType<" << cppName(str) << ">();\n";
1019 dumpRegisterFunctionEpilog(cppOut, n);
1020 for (auto const& mem : strEnt->getDirectMembers())
1022 recordSequenceTypes(mgr, mem.type, sequences);
1025 for (auto const& exc : exceptions)
1027 auto const ent = mgr->getManager()->findEntity(exc);
1028 assert(ent.is());
1029 assert(ent->getSort() == unoidl::Entity::SORT_EXCEPTION_TYPE);
1030 rtl::Reference const excEnt(static_cast<unoidl::ExceptionTypeEntity*>(ent.get()));
1031 dumpRegisterFunctionProlog(cppOut, n);
1032 cppOut << " ::emscripten::value_object<" << cppName(exc) << ">(\"uno_Type_"
1033 << jsName(exc) << "\")";
1034 dumpExceptionMembers(cppOut, mgr, exc, excEnt);
1035 cppOut << ";\n";
1036 cppOut << " ::unoembindhelpers::registerUnoType<" << cppName(exc) << ">();\n";
1037 dumpRegisterFunctionEpilog(cppOut, n);
1038 for (auto const& mem : excEnt->getDirectMembers())
1040 recordSequenceTypes(mgr, mem.type, sequences);
1043 for (auto const& ifc : interfaces)
1045 auto const ent = mgr->getManager()->findEntity(ifc);
1046 assert(ent.is());
1047 assert(ent->getSort() == unoidl::Entity::SORT_INTERFACE_TYPE);
1048 rtl::Reference const ifcEnt(static_cast<unoidl::InterfaceTypeEntity*>(ent.get()));
1050 auto i = ifc.lastIndexOf('.');
1051 auto j = i + 1;
1052 if (i == -1)
1054 i = 0;
1056 cppOut << "namespace the_wrappers" << cppName(ifc.copy(0, i)) << " {\n"
1057 << "struct " << ifc.copy(j) << " final: public ::emscripten::wrapper<"
1058 << cppName(ifc) << "> {\n"
1059 << " EMSCRIPTEN_WRAPPER(" << ifc.copy(j) << ");\n";
1060 std::set<OUString> visitedBases;
1061 dumpWrapperClassMembers(cppOut, mgr, ifc, ifc, visitedBases);
1062 cppOut << "};\n}\n";
1064 dumpRegisterFunctionProlog(cppOut, n);
1065 cppOut << " ::emscripten::class_<" << cppName(ifc);
1066 //TODO: Embind only supports single inheritance, so use that support at least for a UNO
1067 // interface's first base, and explicitly spell out the attributes and methods of any
1068 // remaining bases:
1069 auto const& bases = ifcEnt->getDirectMandatoryBases();
1070 if (bases.size() != 0)
1072 cppOut << ", ::emscripten::base<" << cppName(bases[0].name) << ">";
1074 cppOut << ">(\"uno_Type_" << jsName(ifc)
1075 << "\")\n"
1076 " .allow_subclass<the_wrappers"
1077 << cppName(ifc) << ">(\"uno_Wrapper_" << jsName(ifc)
1078 << "\")\n"
1079 " .smart_ptr<::com::sun::star::uno::Reference<"
1080 << cppName(ifc) << ">>(\"uno_Reference_" << jsName(ifc)
1081 << "\")\n"
1082 " .class_function(\"reference\", +[]("
1083 << cppName(ifc)
1084 << " * the_interface) { return ::com::sun::star::uno::Reference(the_interface); "
1085 "}, ::emscripten::allow_raw_pointers())\n"
1087 ".constructor(+[](::com::sun::star::uno::Reference<::com::sun::star::uno::"
1088 "XInterface> const & the_object) { return ::com::sun::star::uno::Reference<"
1089 << cppName(ifc)
1090 << ">(the_object, ::com::sun::star::uno::UNO_QUERY); })\n"
1091 " .function(\"$is\", +[](::com::sun::star::uno::Reference<"
1092 << cppName(ifc)
1093 << "> const & the_self) { return the_self.is(); })\n"
1094 " .function(\"$equals\", +[](::com::sun::star::uno::Reference<"
1095 << cppName(ifc)
1096 << "> const & the_self, "
1097 "::com::sun::star::uno::Reference<::com::sun::star::uno::XInterface> const & "
1098 "the_other) { return the_self == the_other; })\n";
1099 if (bases.size() > 1)
1101 std::set<OUString> visitedBases;
1102 recordVisitedBases(mgr, bases[0].name, visitedBases);
1103 for (std::size_t i = 1; i != bases.size(); ++i)
1105 dumpBase(cppOut, mgr, ifc, bases[i].name, visitedBases, { bases[i].name });
1108 dumpAttributes(cppOut, mgr, ifc, ifcEnt, {});
1109 dumpMethods(cppOut, mgr, ifc, ifcEnt, {});
1110 cppOut << " ;\n"
1111 " ::unoembindhelpers::registerUnoType<::com::sun::star::uno::Reference<"
1112 << cppName(ifc) << ">>();\n";
1113 dumpRegisterFunctionEpilog(cppOut, n);
1114 for (auto const& attr : ifcEnt->getDirectAttributes())
1116 recordSequenceTypes(mgr, attr.type, sequences);
1118 for (auto const& meth : ifcEnt->getDirectMethods())
1120 for (auto const& param : meth.parameters)
1122 recordSequenceTypes(mgr, param.type, sequences);
1124 recordSequenceTypes(mgr, meth.returnType, sequences);
1127 for (auto const& srv : services)
1129 auto const ent = mgr->getManager()->findEntity(srv);
1130 assert(ent.is());
1131 assert(ent->getSort() == unoidl::Entity::SORT_SINGLE_INTERFACE_BASED_SERVICE);
1132 rtl::Reference const srvEnt(
1133 static_cast<unoidl::SingleInterfaceBasedServiceEntity*>(ent.get()));
1134 dumpRegisterFunctionProlog(cppOut, n);
1135 for (auto const& ctor : srvEnt->getConstructors())
1137 cppOut << " ::emscripten::function(\"" << jsServiceConstructor(srv, ctor)
1138 << "\", &" << cppName(srv) << "::" << getServiceConstructorName(ctor)
1139 << ");\n";
1141 dumpRegisterFunctionEpilog(cppOut, n);
1143 for (auto const& sng : singletons)
1145 dumpRegisterFunctionProlog(cppOut, n);
1146 cppOut << " ::emscripten::function(\"" << jsSingleton(sng) << "\", &" << cppName(sng)
1147 << "::get);\n";
1148 dumpRegisterFunctionEpilog(cppOut, n);
1150 cppOut << "void init_unoembind_" << name << "() {\n";
1151 for (unsigned long long i = 0; i != n; ++i)
1153 cppOut << " register" << i << "();\n";
1155 for (auto const& seq : sequences)
1157 cppOut << " ::unoembindhelpers::registerSequence<";
1158 assert(seq.startsWith("[]"));
1159 dumpType(cppOut, mgr, seq.copy(2));
1160 cppOut << ">(\"uno_Sequence";
1161 sal_Int32 k;
1162 auto const nuc = b2u(codemaker::UnoType::decompose(u2b(seq), &k));
1163 assert(k >= 1);
1164 if (k > 1)
1166 cppOut << k;
1168 cppOut << "_" << jsName(nuc) << "\");\n";
1170 cppOut << "}\n";
1171 cppOut.close();
1172 if (!cppOut)
1174 std::cerr << "Failed to write \"" << cppPathname << "\"\n";
1175 std::exit(EXIT_FAILURE);
1177 std::ofstream hppOut(hppPathname, std::ios_base::out | std::ios_base::trunc);
1178 if (!hppOut)
1180 std::cerr << "Cannot open \"" << hppPathname << "\" for writing\n";
1181 std::exit(EXIT_FAILURE);
1183 hppOut << "void init_unoembind_" << name << "();\n";
1184 hppOut.close();
1185 if (!hppOut)
1187 std::cerr << "Failed to write \"" << hppPathname << "\"\n";
1188 std::exit(EXIT_FAILURE);
1190 std::ofstream jsOut(jsPathname, std::ios_base::out | std::ios_base::trunc);
1191 if (!jsOut)
1193 std::cerr << "Cannot open \"" << jsPathname << "\" for writing\n";
1194 std::exit(EXIT_FAILURE);
1196 jsOut << "function init_unoembind_" << name
1197 << "(instance) {\n"
1198 " return {\n";
1199 writeJsMap(jsOut, *module, " ");
1200 jsOut << " };\n"
1201 "};\n";
1202 jsOut.close();
1203 if (!jsOut)
1205 std::cerr << "Failed to write \"" << jsPathname << "\"\n";
1206 std::exit(EXIT_FAILURE);
1208 return EXIT_SUCCESS;
1210 catch (unoidl::FileFormatException const& e)
1212 std::cerr << "Bad input <" << e.getUri() << ">: " << e.getDetail() << "\n";
1213 std::exit(EXIT_FAILURE);
1215 catch (CannotDumpException const& e)
1217 std::cerr << "Failure: " << e.getMessage() << "\n";
1218 std::exit(EXIT_FAILURE);
1220 catch (std::exception const& e)
1222 std::cerr << "Failure: " << e.what() << "\n";
1223 std::exit(EXIT_FAILURE);
1227 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */