summaryrefslogtreecommitdiffstats
path: root/unoidl
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--unoidl/CustomTarget_unoidl-check_test.mk206
-rw-r--r--unoidl/CustomTarget_unoidl-write_test.mk95
-rw-r--r--unoidl/Executable_unoidl-check.mk31
-rw-r--r--unoidl/Executable_unoidl-read.mk24
-rw-r--r--unoidl/Executable_unoidl-write.mk31
-rw-r--r--unoidl/IwyuFilter_unoidl.yaml6
-rw-r--r--unoidl/Library_unoidl.mk43
-rw-r--r--unoidl/Makefile14
-rw-r--r--unoidl/Module_unoidl.mk30
-rw-r--r--unoidl/README.md283
-rw-r--r--unoidl/qa/unoidl-check/other1.idl1
-rw-r--r--unoidl/qa/unoidl-check/other2.idl1
-rw-r--r--unoidl/qa/unoidl-check/pe.idl1
-rw-r--r--unoidl/qa/unoidl-check/psa.idl1
-rw-r--r--unoidl/qa/unoidl-check/psb.idl1
-rw-r--r--unoidl/qa/unoidl-check/ue.idl1
-rw-r--r--unoidl/qa/unoidl-check/usa.idl1
-rw-r--r--unoidl/qa/unoidl-check/usb.idl1
-rw-r--r--unoidl/source/legacyprovider.cxx829
-rw-r--r--unoidl/source/legacyprovider.hxx41
-rw-r--r--unoidl/source/sourcefileprovider.cxx135
-rw-r--r--unoidl/source/sourcefileprovider.hxx40
-rw-r--r--unoidl/source/sourceprovider-parser-requires.hxx138
-rw-r--r--unoidl/source/sourceprovider-parser.y4476
-rw-r--r--unoidl/source/sourceprovider-scanner.hxx328
-rw-r--r--unoidl/source/sourceprovider-scanner.l253
-rw-r--r--unoidl/source/sourcetreeprovider.cxx315
-rw-r--r--unoidl/source/sourcetreeprovider.hxx42
-rw-r--r--unoidl/source/unoidl-check.cxx1167
-rw-r--r--unoidl/source/unoidl-read.cxx1139
-rw-r--r--unoidl/source/unoidl-write.cxx951
-rw-r--r--unoidl/source/unoidl.cxx224
-rw-r--r--unoidl/source/unoidlprovider.cxx1431
-rw-r--r--unoidl/source/unoidlprovider.hxx56
34 files changed, 12336 insertions, 0 deletions
diff --git a/unoidl/CustomTarget_unoidl-check_test.mk b/unoidl/CustomTarget_unoidl-check_test.mk
new file mode 100644
index 000000000..52ef4d6c3
--- /dev/null
+++ b/unoidl/CustomTarget_unoidl-check_test.mk
@@ -0,0 +1,206 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+# This file incorporates work covered by the following license notice:
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed
+# with this work for additional information regarding copyright
+# ownership. The ASF licenses this file to you under the Apache
+# License, Version 2.0 (the "License"); you may not use this file
+# except in compliance with the License. You may obtain a copy of
+# the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+$(eval $(call gb_CustomTarget_CustomTarget,unoidl/unoidl-check_test))
+
+.PHONY: $(call gb_CustomTarget_get_target,unoidl/unoidl-check_test)
+
+$(call gb_CustomTarget_get_target,unoidl/unoidl-check_test): \
+ $(call gb_Executable_get_runtime_dependencies,unoidl-check) \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/other1.rdb \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/other2.rdb \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/pe.rdb \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psa.rdb \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psb.rdb \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/ue.rdb \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usa.rdb \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usb.rdb
+ifneq ($(gb_SUPPRESS_TESTS),)
+ @true
+else
+ $(call gb_Helper_abbreviate_dirs,( \
+ $(call gb_Executable_get_command,unoidl-check) --ignore-unpublished \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psa.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ ! $(call gb_Executable_get_command,unoidl-check) --ignore-unpublished \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psb.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ ! $(call gb_Executable_get_command,unoidl-check) --ignore-unpublished \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usa.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ ! $(call gb_Executable_get_command,unoidl-check) --ignore-unpublished \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usb.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ ! $(call gb_Executable_get_command,unoidl-check) --ignore-unpublished \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/pe.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ ! $(call gb_Executable_get_command,unoidl-check) --ignore-unpublished \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/ue.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ ! $(call gb_Executable_get_command,unoidl-check) --ignore-unpublished \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/other1.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ ! $(call gb_Executable_get_command,unoidl-check) --ignore-unpublished \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/other2.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ $(call gb_Executable_get_command,unoidl-check) \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psa.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ ! $(call gb_Executable_get_command,unoidl-check) \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psb.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ ! $(call gb_Executable_get_command,unoidl-check) \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usa.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ ! $(call gb_Executable_get_command,unoidl-check) \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usb.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ ! $(call gb_Executable_get_command,unoidl-check) \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/pe.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ ! $(call gb_Executable_get_command,unoidl-check) \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/ue.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ ! $(call gb_Executable_get_command,unoidl-check) \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/other1.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ ! $(call gb_Executable_get_command,unoidl-check) \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/other2.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ $(call gb_Executable_get_command,unoidl-check) --ignore-unpublished \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psa.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ $(call gb_Executable_get_command,unoidl-check) --ignore-unpublished \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psb.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ $(call gb_Executable_get_command,unoidl-check) --ignore-unpublished \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usa.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ $(call gb_Executable_get_command,unoidl-check) --ignore-unpublished \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usb.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ $(call gb_Executable_get_command,unoidl-check) --ignore-unpublished \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/pe.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ $(call gb_Executable_get_command,unoidl-check) --ignore-unpublished \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/ue.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ $(call gb_Executable_get_command,unoidl-check) --ignore-unpublished \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/other1.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ $(call gb_Executable_get_command,unoidl-check) --ignore-unpublished \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/other2.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ $(call gb_Executable_get_command,unoidl-check) \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psa.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ ! $(call gb_Executable_get_command,unoidl-check) \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/psb.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ $(call gb_Executable_get_command,unoidl-check) \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usa.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ ! $(call gb_Executable_get_command,unoidl-check) \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usb.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ ! $(call gb_Executable_get_command,unoidl-check) \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/pe.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ ! $(call gb_Executable_get_command,unoidl-check) \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/ue.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ ! $(call gb_Executable_get_command,unoidl-check) \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/other1.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+ $(call gb_Helper_abbreviate_dirs,( \
+ ! $(call gb_Executable_get_command,unoidl-check) \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/usa.rdb \
+ -- $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/other2.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+endif
+
+$(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/%.rdb: \
+ $(SRCDIR)/unoidl/qa/unoidl-check/%.idl \
+ $(call gb_Executable_get_runtime_dependencies,unoidl-write) \
+ | $(call gb_CustomTarget_get_workdir,unoidl/unoidl-check_test)/.dir
+ $(call gb_Helper_abbreviate_dirs,( \
+ $(call gb_Executable_get_command,unoidl-write) $< $@))
+
+# vim: set noet sw=4 ts=4:
diff --git a/unoidl/CustomTarget_unoidl-write_test.mk b/unoidl/CustomTarget_unoidl-write_test.mk
new file mode 100644
index 000000000..a1c17c488
--- /dev/null
+++ b/unoidl/CustomTarget_unoidl-write_test.mk
@@ -0,0 +1,95 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_CustomTarget_CustomTarget,unoidl/unoidl-write_test))
+
+# this target is phony to run it every time
+.PHONY : $(call gb_CustomTarget_get_target,unoidl/unoidl-write_test)
+
+$(call gb_CustomTarget_get_target,unoidl/unoidl-write_test) : \
+ $(call gb_Executable_get_runtime_dependencies,unoidl-write) \
+ $(SRCDIR)/solenv/bin/exectest.pl \
+ $(SRCDIR)/idlc/test/parser/attribute.tests \
+ $(SRCDIR)/idlc/test/parser/constant.tests \
+ $(SRCDIR)/idlc/test/parser/constructor.tests \
+ $(SRCDIR)/idlc/test/parser/interfaceinheritance.tests \
+ $(SRCDIR)/idlc/test/parser/methodoverload.tests \
+ $(SRCDIR)/idlc/test/parser/polystruct.tests \
+ $(SRCDIR)/idlc/test/parser/published.tests \
+ $(SRCDIR)/idlc/test/parser/struct.tests \
+ $(SRCDIR)/idlc/test/parser/typedef.tests \
+ | $(call gb_CustomTarget_get_workdir,unoidl/unoidl-write_test)/.dir
+ifneq ($(gb_SUPPRESS_TESTS),)
+ @true
+else
+ $(call gb_Helper_abbreviate_dirs,( \
+ $(PERL) $(SRCDIR)/solenv/bin/exectest.pl \
+ $(SRCDIR)/idlc/test/parser/attribute.tests \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-write_test)/in.idl \
+ 1 $(call gb_Executable_get_command,unoidl-write) $(SRCDIR)/udkapi \
+ {} \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-write_test)/out.rdb \
+ && $(PERL) $(SRCDIR)/solenv/bin/exectest.pl \
+ $(SRCDIR)/idlc/test/parser/constant.tests \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-write_test)/in.idl \
+ 1 $(call gb_Executable_get_command,unoidl-write) $(SRCDIR)/udkapi \
+ {} \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-write_test)/out.rdb \
+ && $(PERL) $(SRCDIR)/solenv/bin/exectest.pl \
+ $(SRCDIR)/idlc/test/parser/constructor.tests \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-write_test)/in.idl \
+ 1 $(call gb_Executable_get_command,unoidl-write) $(SRCDIR)/udkapi \
+ {} \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-write_test)/out.rdb \
+ && $(PERL) $(SRCDIR)/solenv/bin/exectest.pl \
+ $(SRCDIR)/idlc/test/parser/interfaceinheritance.tests \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-write_test)/in.idl \
+ 1 $(call gb_Executable_get_command,unoidl-write) $(SRCDIR)/udkapi \
+ {} \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-write_test)/out.rdb \
+ && $(PERL) $(SRCDIR)/solenv/bin/exectest.pl \
+ $(SRCDIR)/idlc/test/parser/methodoverload.tests \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-write_test)/in.idl \
+ 1 $(call gb_Executable_get_command,unoidl-write) $(SRCDIR)/udkapi \
+ {} \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-write_test)/out.rdb \
+ && $(PERL) $(SRCDIR)/solenv/bin/exectest.pl \
+ $(SRCDIR)/idlc/test/parser/oldstyle.tests \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-write_test)/in.idl \
+ 1 $(call gb_Executable_get_command,unoidl-write) $(SRCDIR)/udkapi \
+ {} \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-write_test)/out.rdb \
+ && $(PERL) $(SRCDIR)/solenv/bin/exectest.pl \
+ $(SRCDIR)/idlc/test/parser/polystruct.tests \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-write_test)/in.idl \
+ 1 $(call gb_Executable_get_command,unoidl-write) $(SRCDIR)/udkapi \
+ {} \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-write_test)/out.rdb \
+ && $(PERL) $(SRCDIR)/solenv/bin/exectest.pl \
+ $(SRCDIR)/idlc/test/parser/published.tests \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-write_test)/in.idl \
+ 1 $(call gb_Executable_get_command,unoidl-write) $(SRCDIR)/udkapi \
+ {} \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-write_test)/out.rdb \
+ && $(PERL) $(SRCDIR)/solenv/bin/exectest.pl \
+ $(SRCDIR)/idlc/test/parser/struct.tests \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-write_test)/in.idl \
+ 1 $(call gb_Executable_get_command,unoidl-write) $(SRCDIR)/udkapi \
+ {} \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-write_test)/out.rdb \
+ && $(PERL) $(SRCDIR)/solenv/bin/exectest.pl \
+ $(SRCDIR)/idlc/test/parser/typedef.tests \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-write_test)/in.idl \
+ 1 $(call gb_Executable_get_command,unoidl-write) $(SRCDIR)/udkapi \
+ {} \
+ $(call gb_CustomTarget_get_workdir,unoidl/unoidl-write_test)/out.rdb) \
+ > $@.log 2>&1 || (cat $@.log && false))
+endif
+
+# vim: set noet sw=4 ts=4:
diff --git a/unoidl/Executable_unoidl-check.mk b/unoidl/Executable_unoidl-check.mk
new file mode 100644
index 000000000..671ee38de
--- /dev/null
+++ b/unoidl/Executable_unoidl-check.mk
@@ -0,0 +1,31 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_Executable_Executable,unoidl-check))
+
+$(eval $(call gb_Executable_add_exception_objects,unoidl-check, \
+ unoidl/source/unoidl-check \
+))
+
+$(eval $(call gb_Executable_use_libraries,unoidl-check, \
+ unoidl \
+ $(if $(filter TRUE,$(DISABLE_DYNLOADING)),reg) \
+ $(if $(filter TRUE,$(DISABLE_DYNLOADING)),store) \
+ salhelper \
+ sal \
+))
+
+ifeq ($(DISABLE_DYNLOADING),TRUE)
+$(eval $(call gb_Executable_use_externals,unoidl-check,\
+ dtoa \
+ zlib \
+))
+endif
+
+# vim: set noet sw=4 ts=4:
diff --git a/unoidl/Executable_unoidl-read.mk b/unoidl/Executable_unoidl-read.mk
new file mode 100644
index 000000000..b134c30b1
--- /dev/null
+++ b/unoidl/Executable_unoidl-read.mk
@@ -0,0 +1,24 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_Executable_Executable,unoidl-read))
+
+$(eval $(call gb_Executable_add_exception_objects,unoidl-read, \
+ unoidl/source/unoidl-read \
+))
+
+$(eval $(call gb_Executable_use_libraries,unoidl-read, \
+ unoidl \
+ $(if $(filter TRUE,$(DISABLE_DYNLOADING)),reg) \
+ $(if $(filter TRUE,$(DISABLE_DYNLOADING)),store) \
+ salhelper \
+ sal \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/unoidl/Executable_unoidl-write.mk b/unoidl/Executable_unoidl-write.mk
new file mode 100644
index 000000000..cc15bc34d
--- /dev/null
+++ b/unoidl/Executable_unoidl-write.mk
@@ -0,0 +1,31 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_Executable_Executable,unoidl-write))
+
+$(eval $(call gb_Executable_add_exception_objects,unoidl-write, \
+ unoidl/source/unoidl-write \
+))
+
+$(eval $(call gb_Executable_use_libraries,unoidl-write, \
+ unoidl \
+ $(if $(filter TRUE,$(DISABLE_DYNLOADING)),reg) \
+ $(if $(filter TRUE,$(DISABLE_DYNLOADING)),store) \
+ salhelper \
+ sal \
+))
+
+ifeq ($(DISABLE_DYNLOADING),TRUE)
+$(eval $(call gb_Executable_use_externals,unoidl-write,\
+ dtoa \
+ zlib \
+))
+endif
+
+# vim: set noet sw=4 ts=4:
diff --git a/unoidl/IwyuFilter_unoidl.yaml b/unoidl/IwyuFilter_unoidl.yaml
new file mode 100644
index 000000000..506f96403
--- /dev/null
+++ b/unoidl/IwyuFilter_unoidl.yaml
@@ -0,0 +1,6 @@
+---
+assumeFilename: unoidl/source/unoidl.cxx
+excludelist:
+ unoidl/source/unoidl-write.cxx:
+ # Avoid loplugin:oslendian error
+ - osl/endian.h
diff --git a/unoidl/Library_unoidl.mk b/unoidl/Library_unoidl.mk
new file mode 100644
index 000000000..ff37972ee
--- /dev/null
+++ b/unoidl/Library_unoidl.mk
@@ -0,0 +1,43 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_Library_Library,unoidl))
+
+$(eval $(call gb_Library_add_defs,unoidl,-DLO_DLLIMPLEMENTATION_UNOIDL))
+
+$(eval $(call gb_Library_add_exception_objects,unoidl, \
+ unoidl/source/legacyprovider \
+ unoidl/source/sourcefileprovider \
+ unoidl/source/sourcetreeprovider \
+ unoidl/source/unoidl \
+ unoidl/source/unoidlprovider \
+))
+
+$(eval $(call gb_Library_add_grammars,unoidl, \
+ unoidl/source/sourceprovider-parser \
+))
+
+$(eval $(call gb_Library_add_scanners,unoidl, \
+ unoidl/source/sourceprovider-scanner \
+))
+
+$(eval $(call gb_Library_set_include,unoidl, \
+ $$(INCLUDE) \
+ -I$(SRCDIR)/unoidl/source \
+))
+
+$(eval $(call gb_Library_set_is_ure_library_or_dependency,unoidl))
+
+$(eval $(call gb_Library_use_libraries,unoidl, \
+ reg \
+ sal \
+ salhelper \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/unoidl/Makefile b/unoidl/Makefile
new file mode 100644
index 000000000..0997e6284
--- /dev/null
+++ b/unoidl/Makefile
@@ -0,0 +1,14 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+module_directory:=$(dir $(realpath $(firstword $(MAKEFILE_LIST))))
+
+include $(module_directory)/../solenv/gbuild/partial_build.mk
+
+# vim: set noet sw=4 ts=4:
diff --git a/unoidl/Module_unoidl.mk b/unoidl/Module_unoidl.mk
new file mode 100644
index 000000000..155337177
--- /dev/null
+++ b/unoidl/Module_unoidl.mk
@@ -0,0 +1,30 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_Module_Module,unoidl))
+
+# unoidl-check is needed as a component of the ODK / SDK package and
+# a build-tool. In case of cross-compiling a build-native tool must
+# be provided in addtion to the ODK one (!CROSS_COMPLING phase).
+
+$(eval $(call gb_Module_add_targets,unoidl, \
+ $(if $(filter DESKTOP,$(BUILD_TYPE)), \
+ Executable_unoidl-read) \
+ $(if $(or $(filter ODK,$(BUILD_TYPE)),$(call gb_not,$(CROSS_COMPILING))), \
+ Executable_unoidl-check \
+ Executable_unoidl-write \
+ ) \
+ Library_unoidl \
+))
+
+$(eval $(call gb_Module_add_check_targets,unoidl, \
+ CustomTarget_unoidl-write_test \
+ $(if $(filter ODK,$(BUILD_TYPE)),CustomTarget_unoidl-check_test) \
+))
+# vim: set noet sw=4 ts=4:
diff --git a/unoidl/README.md b/unoidl/README.md
new file mode 100644
index 000000000..ed2ec7bcc
--- /dev/null
+++ b/unoidl/README.md
@@ -0,0 +1,283 @@
+# Support for UNOIDL Registry Formats
+
+`Library_unoidl` contains the `unoidl::Manager` and `unoidl::Provider` implementations
+for the following registry formats:
+
+* The new `UNOIDL` binary `types.rdb` format.
+* The old legacy binary `types.rdb` format (based on modules "store" and
+ "registry").
+* A source-file format, reading (multiple) `UNOIDL` entity definitions directly
+ from a single `.idl` source file.
+* A source-tree format, reading `UNOIDL` entity definitions directly from a tree
+ of `.idl` source files rooted at a given directory. (Where an entity named
+ `foo.bar.Baz` is expected in a file named `foo/bar/Baz.idl` within that tree.)
+
+(While `.idl` files still contain `#include` directives for legacy idlc, the source-
+based formats ignore any preprocessing directives starting with `#` in the `.idl`
+files.) `unoidl::Manager::addProvider` transparently detects the registry format
+for a given URI and instantiates the corresponding provider implementation.
+
+`Executable_unoidl-write` is a helper tool to convert from any of the registry
+formats to the `UNOIDL` format. It is used at build-time to compile `UNOIDL` format
+`.rdb` files (that are used at build-time only, or included in installation sets
+in `URE` or `program/types/` or as part of bundled extensions that are created
+during the build and not merely included as pre-built `.oxt` files) from source
+`.idl` files. (The SDK still supports deprecated idlc generating legacy format `.rdb` files for
+now.)
+
+`Executable_unoidl-read` is a helper tool to convert from any of the registry
+formats to the source-file format. It can be used manually after a LibreOffice
+version update to create new reference registries for `Executable_unoidl-check`.
+
+`Executable_unoidl-check` is a helper tool to check that one registry is
+backwards-compatible with another registry. It is used at build-time to detect
+inadvertent breakage of the udkapi and offapi APIs.
+
+## Specification of the New UNOIDL types.rdb Format
+
+The format uses byte-oriented, platform-independent, binary files. Larger
+quantities are stored LSB first, without alignment requirements. Offsets are
+32 bit, effectively limiting the overall file size to 4GB, but that is not
+considered a limitation in practice (and avoids unnecessary bloat compared to
+64 bit offsets).
+
+Annotations can be added for (non-module) entities and certain parts of such
+entities (e.g., both for an interface type definition and for a direct method of
+an interface type definition; the idea is that it can be added for direct parts
+that forma a "many-to-one" relationship; there is a tradeoff between generality
+of concept and size of representation, esp. for the C++ representation types in
+namespace `unoidl`) and consist of arbitrary sequences of name/value strings.
+Each name/value string is encoded as a single UTF-8 string containing a name (an
+arbitrary sequence of Unicode code points not containing `U+003D EQUALS SIGN`),
+optionally followed by `U+003D EQUALS SIGN` and a value (an arbitrary sequence of
+Unicode code points). The only annotation name currently in use is "deprecated"
+(without a value).
+
+The following definitions are used throughout:
+
+* `UInt16`: 2-byte value, LSB first
+* `UInt32`: 4-byte value, LSB first
+* `UInt64`: 8-byte value, LSB first
+* Offset: `UInt32` value, counting bytes from start of file
+* `NUL`-Name: zero or more non-`NUL` US-ASCII bytes followed by a `NUL` byte
+* Len-String: UInt32 number of characters, with `0x80000000` bit 0, followed by
+ that many US-ASCII (for `UNOIDL` related names) resp. UTF-8 (for annotations)
+ bytes
+* Idx-String: either an Offset (with `0x80000000` bit 1) of a Len-String, or a
+ Len-String
+* Annotations: `UInt32` number `N` of annotations followed by `N * Idx-String`
+* Entry: Offset of `NUL`-Name followed by Offset of payload
+* Map: zero or more Entries
+
+The file starts with an 8 byte header, followed by information about the root
+map (`unoidl-write` generates files in a single depth-first pass, so the root map
+itself is at the end of the file):
+
+* 7 byte magic header `UNOIDL\xFF`
+* version byte 0
+* Offset of root Map
+* `UInt32` number of entries of root Map
+...
+
+Files generated by unoidl-write follow that by a
+
+ "\0** Created by LibreOffice " LIBO_VERSION_DOTTED " unoidl-write **\0"
+
+banner (cf. `config_host/config_version.h.in`), as a debugging aid. (Old versions
+used `reg2unoidl` instead of `unoidl-write` in that banner.)
+
+Layout of per-entry payload in the root or a module Map:
+
+* kind byte:
+
+ * 0: module
+ * followed by:
+ * `UInt32` number `N1` of entries of Map
+ * `N1 * Entry`
+
+ * otherwise:
+ * `0x80` bit: 1 if published
+ * `0x40` bit: 1 if annotated
+ * `0x20` bit: flag (may only be 1 for certain kinds, see below)
+ * remaining bits:
+
+ * 1: enum type
+ * followed by:
+ * `UInt32` number N1 of members
+ * `N1 * tuple` of:
+ * `Idx-String`
+ * `UInt32`
+ * if annotated: Annotations
+
+ * 2: plain struct type (with base if flag is 1)
+ * followed by:
+ * if "with base": `Idx-String`
+ * `UInt32` number `N1` of direct members
+ * `N1 * tuple` of:
+ * `Idx-String` name
+ * `Idx-String` type
+ * if annotated: Annotations
+
+ * 3: polymorphic struct type template
+ * followed by:
+ * `UInt32` number `N1` of type parameters
+ * `N1 * Idx-String`
+ * `UInt32` number `N2` of members
+ * `N2 * tuple` of:
+ * kind byte: `0x01` bit is 1 if parameterized type
+ * `Idx-String` name
+ * `Idx-String` type
+ * if annotated: Annotations
+
+ * 4: exception type (with base if flag is 1)
+ * followed by:
+ * if "with base": `Idx-String`
+ * `UInt32` number `N1` of direct members
+ * `N1 * tuple` of:
+ * `Idx-String` name
+ * `Idx-String` type
+ * if annotated: Annotations
+
+ * 5: interface type
+ * followed by:
+ * `UInt32` number `N1` of direct mandatory bases
+ * `N1 * tuple` of:
+ * `Idx-String`
+ * if annotated: Annotations
+ * `UInt32` number `N2` of direct optional bases
+ * `N2 * tuple` of:
+ * `Idx-String`
+ * if annotated: Annotations
+ * `UInt32` number `N3` of direct attributes
+ * `N3 * tuple` of:
+ * kind byte:
+ * `0x02` bit: 1 if read-only
+ * `0x01` bit: 1 if bound
+ * `Idx-String` name
+ * `Idx-String` type
+ * `UInt32` number `N4` of get exceptions
+ * `N4 * Idx-String`
+ * `UInt32` number `N5` of set exceptions
+ * `N5 * Idx-String`
+ * if annotated: Annotations
+ * `UInt32` number `N6` of direct methods
+ * `N6 * tuple` of:
+ * `Idx-String` name
+ * `Idx-String` return type
+ * `UInt32` number `N7` of parameters
+ * `N7 * tuple` of:
+ * direction byte: 0 for in, 1 for out, 2 for in-out
+ * `Idx-String` name
+ * `Idx-String` type
+ * `UInt32` number `N8` of exceptions
+ * N8 * Idx-String
+ * if annotated: Annotations
+
+ * 6: typedef
+ * followed by:
+ * `Idx-String`
+
+ * 7: constant group
+ * followed by:
+ * `UInt32` number `N1` of entries of Map
+ * `N1 * Entry`
+
+ * 8: single-interface--based service (with default constructor if flag is 1)
+ * followed by:
+ * `Idx-String`
+ * if not "with default constructor":
+ * `UInt32` number `N1` of constructors
+ * `N1 * tuple` of:
+ * `Idx-String`
+ * `UInt32` number `N2` of parameters
+ * `N2 * tuple` of
+ * kind byte: `0x04` bit is 1 if rest parameter
+ * `Idx-String` name
+ * `Idx-String` type
+ * `UInt32` number `N3` of exceptions
+ * `N3 * Idx-String`
+ * if annotated: Annotations
+
+ * 9: accumulation-based service
+ * followed by:
+ * `UInt32` number `N1` of direct mandatory base services
+ * `N1 * tuple` of:
+ * `Idx-String`
+ * if annotated: Annotations
+ * `UInt32` number `N2` of direct optional base services
+ * `N2 * tuple` of:
+ * `Idx-String`
+ * if annotated: Annotations
+ * `UInt32` number `N3` of direct mandatory base interfaces
+ * `N3 * tuple` of:
+ * `Idx-String`
+ * if annotated: Annotations
+ * `UInt32` number `N4` of direct optional base interfaces
+ * `N4 * tuple` of:
+ * `Idx-String`
+ * if annotated: Annotations
+ * `UInt32` number `N5` of direct properties
+ * `N5 * tuple` of:
+ * `UInt16` kind:
+ * `0x0100` bit: 1 if optional
+ * `0x0080` bit: 1 if removable
+ * `0x0040` bit: 1 if maybedefault
+ * `0x0020` bit: 1 if maybeambiguous
+ * `0x0010` bit: 1 if readonly
+ * `0x0008` bit: 1 if transient
+ * `0x0004` bit: 1 if constrained
+ * `0x0002` bit: 1 if bound
+ * `0x0001` bit: 1 if maybevoid
+ * `Idx-String` name
+ * `Idx-String` type
+ * if annotated: Annotations
+
+ * 10: interface-based singleton
+ * followed by:
+ * `Idx-String`
+
+ * 11: service-based singleton
+ * followed by:
+ * `Idx-String`
+
+ * if annotated, followed by: Annotations
+
+Layout of per-entry payload in a constant group Map:
+
+* kind byte:
+ * `0x80` bit: 1 if annotated
+ * remaining bits:
+
+ * 0: `BOOLEAN`
+ * followed by value byte, 0 represents false, 1 represents true
+
+ * 1: `BYTE`
+ * followed by value byte, representing values with two's complement
+
+ * 2: `SHORT`
+ * followed by `UInt16` value, representing values with two's complement
+
+ * 3: `UNSIGNED SHORT`
+ * followed by `UInt16` value
+
+ * 4: `LONG`
+ * followed by `UInt32` value, representing values with two's complement
+
+ * 5: `UNSIGNED LONG`
+ * followed by `UInt32` value
+
+ * 6: `HYPER`
+ * followed by `UInt64` value, representing values with two's complement
+
+ * 7: `UNSIGNED HYPER`
+ * followed by `UInt64` value
+
+ * 8: `FLOAT`
+ * followed by 4-byte value, representing values in ISO 60599 binary32 format,
+ LSB first
+
+ * 9: `DOUBLE`
+ * followed by 8-byte value, representing values in ISO 60599 binary64 format,
+ LSB first
+
+* if annotated, followed by: Annotations
diff --git a/unoidl/qa/unoidl-check/other1.idl b/unoidl/qa/unoidl-check/other1.idl
new file mode 100644
index 000000000..75473015b
--- /dev/null
+++ b/unoidl/qa/unoidl-check/other1.idl
@@ -0,0 +1 @@
+module M { enum X { V }; };
diff --git a/unoidl/qa/unoidl-check/other2.idl b/unoidl/qa/unoidl-check/other2.idl
new file mode 100644
index 000000000..f6c94ef66
--- /dev/null
+++ b/unoidl/qa/unoidl-check/other2.idl
@@ -0,0 +1 @@
+enum X { V };
diff --git a/unoidl/qa/unoidl-check/pe.idl b/unoidl/qa/unoidl-check/pe.idl
new file mode 100644
index 000000000..c597b29f0
--- /dev/null
+++ b/unoidl/qa/unoidl-check/pe.idl
@@ -0,0 +1 @@
+module M { published enum N { V }; };
diff --git a/unoidl/qa/unoidl-check/psa.idl b/unoidl/qa/unoidl-check/psa.idl
new file mode 100644
index 000000000..b8079c9ad
--- /dev/null
+++ b/unoidl/qa/unoidl-check/psa.idl
@@ -0,0 +1 @@
+module M { published struct N { long m; }; };
diff --git a/unoidl/qa/unoidl-check/psb.idl b/unoidl/qa/unoidl-check/psb.idl
new file mode 100644
index 000000000..03d02b302
--- /dev/null
+++ b/unoidl/qa/unoidl-check/psb.idl
@@ -0,0 +1 @@
+module M { published struct N { short m; }; };
diff --git a/unoidl/qa/unoidl-check/ue.idl b/unoidl/qa/unoidl-check/ue.idl
new file mode 100644
index 000000000..0face3356
--- /dev/null
+++ b/unoidl/qa/unoidl-check/ue.idl
@@ -0,0 +1 @@
+module M { enum N { V }; };
diff --git a/unoidl/qa/unoidl-check/usa.idl b/unoidl/qa/unoidl-check/usa.idl
new file mode 100644
index 000000000..80fe2c2f4
--- /dev/null
+++ b/unoidl/qa/unoidl-check/usa.idl
@@ -0,0 +1 @@
+module M { struct N { long m; }; };
diff --git a/unoidl/qa/unoidl-check/usb.idl b/unoidl/qa/unoidl-check/usb.idl
new file mode 100644
index 000000000..08e7e9daa
--- /dev/null
+++ b/unoidl/qa/unoidl-check/usb.idl
@@ -0,0 +1 @@
+module M { struct N { short m; }; };
diff --git a/unoidl/source/legacyprovider.cxx b/unoidl/source/legacyprovider.cxx
new file mode 100644
index 000000000..9299ae7ce
--- /dev/null
+++ b/unoidl/source/legacyprovider.cxx
@@ -0,0 +1,829 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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 <utility>
+#include <vector>
+
+#include <registry/reader.hxx>
+#include <registry/registry.hxx>
+#include <registry/regtype.h>
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <unoidl/unoidl.hxx>
+
+#include "legacyprovider.hxx"
+
+namespace unoidl::detail {
+
+namespace {
+
+std::vector< OUString > translateAnnotations(std::u16string_view documentation) {
+ std::vector< OUString > ans;
+ if (documentation.find(u"@deprecated") != std::u16string_view::npos) {
+ //TODO: this check is somewhat crude
+ ans.push_back("deprecated");
+ }
+ return ans;
+}
+
+ConstantValue translateConstantValue(
+ RegistryKey & key, RTConstValue const & value)
+{
+ switch (value.m_type) {
+ case RT_TYPE_BOOL:
+ return ConstantValue(value.m_value.aBool);
+ case RT_TYPE_BYTE:
+ return ConstantValue(value.m_value.aByte);
+ case RT_TYPE_INT16:
+ return ConstantValue(value.m_value.aShort);
+ case RT_TYPE_UINT16:
+ return ConstantValue(value.m_value.aUShort);
+ case RT_TYPE_INT32:
+ return ConstantValue(value.m_value.aLong);
+ case RT_TYPE_UINT32:
+ return ConstantValue(value.m_value.aULong);
+ case RT_TYPE_INT64:
+ return ConstantValue(value.m_value.aHyper);
+ case RT_TYPE_UINT64:
+ return ConstantValue(value.m_value.aUHyper);
+ case RT_TYPE_FLOAT:
+ return ConstantValue(value.m_value.aFloat);
+ case RT_TYPE_DOUBLE:
+ return ConstantValue(value.m_value.aDouble);
+ default:
+ throw FileFormatException(
+ key.getRegistryName(),
+ ("legacy format: unexpected type " + OUString::number(value.m_type)
+ + " of value of a field of constant group with key "
+ + key.getName()));
+ }
+}
+
+rtl::Reference< Entity > readEntity(
+ rtl::Reference< Manager > const & manager, RegistryKey & ucr,
+ RegistryKey & key, OUString const & path, bool probe);
+
+class Cursor: public MapCursor {
+public:
+ Cursor(
+ rtl::Reference< Manager > manager, RegistryKey const & ucr,
+ RegistryKey const & key);
+
+private:
+ virtual ~Cursor() noexcept override {}
+
+ virtual rtl::Reference< Entity > getNext(OUString * name) override;
+
+ rtl::Reference< Manager > manager_;
+ RegistryKey ucr_;
+ RegistryKey key_;
+ OUString prefix_;
+ RegistryKeyNames names_;
+ sal_uInt32 index_;
+};
+
+Cursor::Cursor(
+ rtl::Reference< Manager > manager, RegistryKey const & ucr,
+ RegistryKey const & key):
+ manager_(std::move(manager)), ucr_(ucr), key_(key), index_(0)
+{
+ if (!ucr_.isValid())
+ return;
+
+ prefix_ = key_.getName();
+ if (!prefix_.endsWith("/")) {
+ prefix_ += "/";
+ }
+ RegError e = key_.getKeyNames("", names_);
+ if (e != RegError::NO_ERROR) {
+ throw FileFormatException(
+ key_.getRegistryName(),
+ ("legacy format: cannot get sub-key names of " + key_.getName()
+ + ": " + OUString::number(static_cast<int>(e))));
+ }
+}
+
+rtl::Reference< Entity > Cursor::getNext(OUString * name) {
+ assert(name != nullptr);
+ rtl::Reference< Entity > ent;
+ if (index_ != names_.getLength()) {
+ OUString path(names_.getElement(index_));
+ assert(path.match(prefix_));
+ *name = path.copy(prefix_.getLength());
+ ent = readEntity(manager_, ucr_, key_, *name, false);
+ assert(ent.is());
+ ++index_;
+ }
+ return ent;
+}
+
+class Module: public ModuleEntity {
+public:
+ Module(
+ rtl::Reference< Manager > manager, RegistryKey const & ucr,
+ RegistryKey const & key):
+ manager_(std::move(manager)), ucr_(ucr), key_(key)
+ {}
+
+private:
+ virtual ~Module() noexcept override {}
+
+ virtual std::vector< OUString > getMemberNames() const override;
+
+ virtual rtl::Reference< MapCursor > createCursor() const override
+ { return new Cursor(manager_, ucr_, key_); }
+
+ rtl::Reference< Manager > manager_;
+ RegistryKey ucr_;
+ mutable RegistryKey key_;
+};
+
+std::vector< OUString > Module::getMemberNames() const {
+ RegistryKeyNames names;
+ RegError e = key_.getKeyNames("", names);
+ if (e != RegError::NO_ERROR) {
+ throw FileFormatException(
+ key_.getRegistryName(),
+ ("legacy format: cannot get sub-key names of " + key_.getName()
+ + ": " + OUString::number(static_cast<int>(e))));
+ }
+ std::vector< OUString > ns;
+ for (sal_uInt32 i = 0; i != names.getLength(); ++i) {
+ ns.push_back(names.getElement(i));
+ }
+ return ns;
+}
+
+typereg::Reader getReader(RegistryKey & key, std::vector< char > * buffer) {
+ assert(buffer != nullptr);
+ RegValueType type;
+ sal_uInt32 size;
+ RegError e = key.getValueInfo("", &type, &size);
+ if (e != RegError::NO_ERROR) {
+ throw FileFormatException(
+ key.getRegistryName(),
+ ("legacy format: cannot get value info about key " + key.getName()
+ + ": " + OUString::number(static_cast<int>(e))));
+ }
+ if (type != RegValueType::BINARY) {
+ throw FileFormatException(
+ key.getRegistryName(),
+ ("legacy format: unexpected value type " + OUString::number(static_cast<int>(type))
+ + " of key " + key.getName()));
+ }
+ if (size == 0
+ /*TODO: || size > std::numeric_limits< std::vector< char >::size_type >::max() */)
+ {
+ throw FileFormatException(
+ key.getRegistryName(),
+ ("legacy format: bad binary value size " + OUString::number(size)
+ + " of key " + key.getName()));
+ }
+ buffer->resize(static_cast< std::vector< char >::size_type >(size));
+ e = key.getValue("", buffer->data());
+ if (e != RegError::NO_ERROR) {
+ throw FileFormatException(
+ key.getRegistryName(),
+ ("legacy format: cannot get binary value of key " + key.getName()
+ + ": " + OUString::number(static_cast<int>(e))));
+ }
+ typereg::Reader reader(buffer->data(), size);
+ if (!reader.isValid()) {
+ throw FileFormatException(
+ key.getRegistryName(),
+ "legacy format: malformed binary value of key " + key.getName());
+ }
+ return reader;
+}
+
+rtl::Reference< Entity > readEntity(
+ rtl::Reference< Manager > const & manager, RegistryKey & ucr,
+ RegistryKey & key, OUString const & path, bool probe)
+{
+ assert(manager.is());
+ RegistryKey sub;
+ RegError e = key.openKey(path, sub);
+ switch (e) {
+ case RegError::NO_ERROR:
+ break;
+ case RegError::KEY_NOT_EXISTS:
+ if (probe) {
+ return rtl::Reference< Entity >();
+ }
+ [[fallthrough]];
+ default:
+ throw FileFormatException(
+ key.getRegistryName(),
+ ("legacy format: cannot open sub-key " + path + " of "
+ + key.getName() + ": " + OUString::number(static_cast<int>(e))));
+ }
+ std::vector< char > buf;
+ typereg::Reader reader(getReader(sub, &buf));
+ switch (reader.getTypeClass()) {
+ case RT_TYPE_INTERFACE:
+ {
+ std::vector< AnnotatedReference > mandBases;
+ sal_uInt16 n = reader.getSuperTypeCount();
+ for (sal_uInt16 j = 0; j != n; ++j) {
+ mandBases.emplace_back(
+ reader.getSuperTypeName(j).replace('/', '.'),
+ std::vector< OUString >());
+ }
+ std::vector< AnnotatedReference > optBases;
+ n = reader.getReferenceCount();
+ for (sal_uInt16 j = 0; j != n; ++j) {
+ optBases.emplace_back(
+ reader.getReferenceTypeName(j).replace('/', '.'),
+ translateAnnotations(reader.getReferenceDocumentation(j)));
+ }
+ sal_uInt16 methodCount = reader.getMethodCount();
+ std::vector< InterfaceTypeEntity::Attribute > attrs;
+ n = reader.getFieldCount(); // attributes
+ for (sal_uInt16 j = 0; j != n; ++j) {
+ OUString attrName(reader.getFieldName(j));
+ std::vector< OUString > getExcs;
+ std::vector< OUString > setExcs;
+ for (sal_uInt16 k = 0; k != methodCount; ++k) {
+ if (reader.getMethodName(k) == attrName) {
+ switch (reader.getMethodFlags(k)) {
+ case RTMethodMode::ATTRIBUTE_GET:
+ {
+ sal_uInt16 m
+ = reader.getMethodExceptionCount(k);
+ // cid#1213376 unhelpfully warns about an
+ // untrusted loop bound here:
+ // coverity[tainted_data] - trusted data source
+ for (sal_uInt16 l = 0; l != m; ++l) {
+ getExcs.push_back(
+ reader.getMethodExceptionTypeName(k, l).
+ replace('/', '.'));
+ }
+ break;
+ }
+ case RTMethodMode::ATTRIBUTE_SET:
+ {
+ sal_uInt16 m
+ = reader.getMethodExceptionCount(k);
+ // cid#1213376 unhelpfully warns about an
+ // untrusted loop bound here:
+ // coverity[tainted_data] - trusted data source
+ for (sal_uInt16 l = 0; l != m; ++l) {
+ setExcs.push_back(
+ reader.getMethodExceptionTypeName(k, l).
+ replace('/', '.'));
+ }
+ break;
+ }
+ default:
+ throw FileFormatException(
+ key.getRegistryName(),
+ ("legacy format: method and attribute with same"
+ " name " + attrName
+ + " in interface type with key "
+ + sub.getName()));
+ }
+ }
+ }
+ RTFieldAccess flags = reader.getFieldFlags(j);
+ attrs.emplace_back(
+ attrName, reader.getFieldTypeName(j).replace('/', '.'),
+ bool(flags & RTFieldAccess::BOUND),
+ bool(flags & RTFieldAccess::READONLY), std::move(getExcs), std::move(setExcs),
+ translateAnnotations(reader.getFieldDocumentation(j)));
+ }
+ std::vector< InterfaceTypeEntity::Method > meths;
+ meths.reserve(methodCount);
+ for (sal_uInt16 j = 0; j != methodCount; ++j) {
+ RTMethodMode flags = reader.getMethodFlags(j);
+ if (flags != RTMethodMode::ATTRIBUTE_GET
+ && flags != RTMethodMode::ATTRIBUTE_SET)
+ {
+ std::vector< InterfaceTypeEntity::Method::Parameter >
+ params;
+ sal_uInt16 m = reader.getMethodParameterCount(j);
+ // cid#1213376 unhelpfully warns about an untrusted loop
+ // bound here:
+ // coverity[tainted_data] - trusted data source
+ for (sal_uInt16 k = 0; k != m; ++k) {
+ RTParamMode mode = reader.getMethodParameterFlags(j, k);
+ InterfaceTypeEntity::Method::Parameter::Direction dir;
+ switch (mode) {
+ case RT_PARAM_IN:
+ dir = InterfaceTypeEntity::Method::Parameter::DIRECTION_IN;
+ break;
+ case RT_PARAM_OUT:
+ dir = InterfaceTypeEntity::Method::Parameter::DIRECTION_OUT;
+ break;
+ case RT_PARAM_INOUT:
+ dir = InterfaceTypeEntity::Method::Parameter::DIRECTION_IN_OUT;
+ break;
+ default:
+ throw FileFormatException(
+ key.getRegistryName(),
+ ("legacy format: unexpected mode "
+ + OUString::number(mode) + " of parameter "
+ + reader.getMethodParameterName(j, k)
+ + " of method " + reader.getMethodName(j)
+ + " in interface type with key "
+ + sub.getName()));
+ }
+ params.emplace_back(
+ reader.getMethodParameterName(j, k),
+ (reader.getMethodParameterTypeName(j, k).
+ replace('/', '.')),
+ dir);
+ }
+ std::vector< OUString > excs;
+ m = reader.getMethodExceptionCount(j);
+ // cid#1213376 unhelpfully warns about an untrusted loop
+ // bound here:
+ // coverity[tainted_data] - trusted data source
+ for (sal_uInt16 k = 0; k != m; ++k) {
+ excs.push_back(
+ reader.getMethodExceptionTypeName(j, k).replace(
+ '/', '.'));
+ }
+ meths.emplace_back(
+ reader.getMethodName(j),
+ reader.getMethodReturnTypeName(j).replace('/', '.'),
+ std::move(params), std::move(excs),
+ translateAnnotations(
+ reader.getMethodDocumentation(j)));
+ }
+ }
+ return new InterfaceTypeEntity(
+ reader.isPublished(), std::move(mandBases), std::move(optBases), std::move(attrs), std::move(meths),
+ translateAnnotations(reader.getDocumentation()));
+ }
+ case RT_TYPE_MODULE:
+ return new Module(manager, ucr, sub);
+ case RT_TYPE_STRUCT:
+ {
+ sal_uInt16 n = reader.getReferenceCount();
+ if (n == 0) {
+ OUString base;
+ switch (reader.getSuperTypeCount()) {
+ case 0:
+ break;
+ case 1:
+ base = reader.getSuperTypeName(0).replace('/', '.');
+ break;
+ default:
+ throw FileFormatException(
+ key.getRegistryName(),
+ ("legacy format: unexpected number "
+ + OUString::number(reader.getSuperTypeCount())
+ + " of super-types of plain struct type with key "
+ + sub.getName()));
+ }
+ std::vector< PlainStructTypeEntity::Member > mems;
+ n = reader.getFieldCount();
+ for (sal_uInt16 j = 0; j != n; ++j) {
+ mems.emplace_back(
+ reader.getFieldName(j),
+ reader.getFieldTypeName(j).replace('/', '.'),
+ translateAnnotations(reader.getFieldDocumentation(j)));
+ }
+ return new PlainStructTypeEntity(
+ reader.isPublished(), base, std::move(mems),
+ translateAnnotations(reader.getDocumentation()));
+ } else {
+ if (reader.getSuperTypeCount() != 0) {
+ throw FileFormatException(
+ key.getRegistryName(),
+ ("legacy format: unexpected number "
+ + OUString::number(reader.getSuperTypeCount())
+ + " of super-types of polymorphic struct type template"
+ " with key " + sub.getName()));
+ }
+ std::vector< OUString > params;
+ for (sal_uInt16 j = 0; j != n; ++j) {
+ params.push_back(
+ reader.getReferenceTypeName(j).replace('/', '.'));
+ }
+ std::vector< PolymorphicStructTypeTemplateEntity::Member > mems;
+ n = reader.getFieldCount();
+ for (sal_uInt16 j = 0; j != n; ++j) {
+ mems.emplace_back(
+ reader.getFieldName(j),
+ reader.getFieldTypeName(j).replace('/', '.'),
+ bool(
+ reader.getFieldFlags(j)
+ & RTFieldAccess::PARAMETERIZED_TYPE),
+ translateAnnotations(reader.getFieldDocumentation(j)));
+ }
+ return new PolymorphicStructTypeTemplateEntity(
+ reader.isPublished(), std::move(params), std::move(mems),
+ translateAnnotations(reader.getDocumentation()));
+ }
+ }
+ case RT_TYPE_ENUM:
+ {
+ std::vector< EnumTypeEntity::Member > mems;
+ sal_uInt16 n = reader.getFieldCount();
+ for (sal_uInt16 j = 0; j != n; ++j) {
+ RTConstValue v(reader.getFieldValue(j));
+ if (v.m_type != RT_TYPE_INT32) {
+ throw FileFormatException(
+ key.getRegistryName(),
+ ("legacy format: unexpected type "
+ + OUString::number(v.m_type) + " of value of field "
+ + reader.getFieldName(j) + " of enum type with key "
+ + sub.getName()));
+ }
+ mems.emplace_back(
+ reader.getFieldName(j), v.m_value.aLong,
+ translateAnnotations(reader.getFieldDocumentation(j)));
+
+ }
+ return new EnumTypeEntity(
+ reader.isPublished(), std::move(mems),
+ translateAnnotations(reader.getDocumentation()));
+ }
+ case RT_TYPE_EXCEPTION:
+ {
+ OUString base;
+ switch (reader.getSuperTypeCount()) {
+ case 0:
+ break;
+ case 1:
+ base = reader.getSuperTypeName(0).replace('/', '.');
+ break;
+ default:
+ throw FileFormatException(
+ key.getRegistryName(),
+ ("legacy format: unexpected number "
+ + OUString::number(reader.getSuperTypeCount())
+ + " of super-types of exception type with key "
+ + sub.getName()));
+ }
+ std::vector< ExceptionTypeEntity::Member > mems;
+ sal_uInt16 n = reader.getFieldCount();
+ for (sal_uInt16 j = 0; j != n; ++j) {
+ mems.emplace_back(
+ reader.getFieldName(j),
+ reader.getFieldTypeName(j).replace('/', '.'),
+ translateAnnotations(reader.getFieldDocumentation(j)));
+ }
+ return new ExceptionTypeEntity(
+ reader.isPublished(), base, std::move(mems),
+ translateAnnotations(reader.getDocumentation()));
+ }
+ case RT_TYPE_TYPEDEF:
+ if (reader.getSuperTypeCount() != 1) {
+ throw FileFormatException(
+ key.getRegistryName(),
+ ("legacy format: unexpected number "
+ + OUString::number(reader.getSuperTypeCount())
+ + " of super-types of typedef with key " + sub.getName()));
+ }
+ return new TypedefEntity(
+ reader.isPublished(), reader.getSuperTypeName(0).replace('/', '.'),
+ translateAnnotations(reader.getDocumentation()));
+ case RT_TYPE_SERVICE:
+ switch (reader.getSuperTypeCount()) {
+ case 0:
+ {
+ std::vector< AnnotatedReference > mandServs;
+ std::vector< AnnotatedReference > optServs;
+ std::vector< AnnotatedReference > mandIfcs;
+ std::vector< AnnotatedReference > optIfcs;
+ sal_uInt16 n = reader.getReferenceCount();
+ for (sal_uInt16 j = 0; j != n; ++j) {
+ AnnotatedReference base{
+ reader.getReferenceTypeName(j).replace('/', '.'),
+ translateAnnotations(
+ reader.getReferenceDocumentation(j))};
+ switch (reader.getReferenceSort(j)) {
+ case RTReferenceType::EXPORTS:
+ if (!(reader.getReferenceFlags(j) & RTFieldAccess::OPTIONAL))
+ {
+ mandServs.push_back(base);
+ } else {
+ optServs.push_back(base);
+ }
+ break;
+ case RTReferenceType::SUPPORTS:
+ if (!(reader.getReferenceFlags(j) & RTFieldAccess::OPTIONAL))
+ {
+ mandIfcs.push_back(base);
+ } else {
+ optIfcs.push_back(base);
+ }
+ break;
+ default:
+ throw FileFormatException(
+ key.getRegistryName(),
+ ("legacy format: unexpected mode "
+ + OUString::number(static_cast<int>(reader.getReferenceSort(j)))
+ + " of reference " + reader.getReferenceTypeName(j)
+ + " in service with key " + sub.getName()));
+ }
+ }
+ std::vector< AccumulationBasedServiceEntity::Property > props;
+ n = reader.getFieldCount();
+ for (sal_uInt16 j = 0; j != n; ++j) {
+ RTFieldAccess acc = reader.getFieldFlags(j);
+ int attrs = 0;
+ if (acc & RTFieldAccess::READONLY) {
+ attrs |= AccumulationBasedServiceEntity::Property::
+ ATTRIBUTE_READ_ONLY;
+ }
+ if (acc & RTFieldAccess::OPTIONAL) {
+ attrs |= AccumulationBasedServiceEntity::Property::
+ ATTRIBUTE_OPTIONAL;
+ }
+ if (acc & RTFieldAccess::MAYBEVOID) {
+ attrs |= AccumulationBasedServiceEntity::Property::
+ ATTRIBUTE_MAYBE_VOID;
+ }
+ if (acc & RTFieldAccess::BOUND) {
+ attrs |= AccumulationBasedServiceEntity::Property::
+ ATTRIBUTE_BOUND;
+ }
+ if (acc & RTFieldAccess::CONSTRAINED) {
+ attrs |= AccumulationBasedServiceEntity::Property::
+ ATTRIBUTE_CONSTRAINED;
+ }
+ if (acc & RTFieldAccess::TRANSIENT) {
+ attrs |= AccumulationBasedServiceEntity::Property::
+ ATTRIBUTE_TRANSIENT;
+ }
+ if (acc & RTFieldAccess::MAYBEAMBIGUOUS) {
+ attrs |= AccumulationBasedServiceEntity::Property::
+ ATTRIBUTE_MAYBE_AMBIGUOUS;
+ }
+ if (acc & RTFieldAccess::MAYBEDEFAULT) {
+ attrs |= AccumulationBasedServiceEntity::Property::
+ ATTRIBUTE_MAYBE_DEFAULT;
+ }
+ if (acc & RTFieldAccess::REMOVABLE) {
+ attrs |= AccumulationBasedServiceEntity::Property::
+ ATTRIBUTE_REMOVABLE;
+ }
+ props.emplace_back(
+ reader.getFieldName(j),
+ reader.getFieldTypeName(j).replace('/', '.'),
+ static_cast<
+ AccumulationBasedServiceEntity::Property::
+ Attributes >(attrs),
+ translateAnnotations(reader.getFieldDocumentation(j)));
+ }
+ return new AccumulationBasedServiceEntity(
+ reader.isPublished(), std::move(mandServs), std::move(optServs), std::move(mandIfcs),
+ std::move(optIfcs), std::move(props),
+ translateAnnotations(reader.getDocumentation()));
+ }
+ case 1:
+ {
+ std::vector< SingleInterfaceBasedServiceEntity::Constructor >
+ ctors;
+ sal_uInt16 n = reader.getMethodCount();
+ if (n == 1 && reader.getMethodFlags(0) == RTMethodMode::TWOWAY
+ && reader.getMethodName(0).isEmpty()
+ && reader.getMethodReturnTypeName(0) == "void"
+ && reader.getMethodParameterCount(0) == 0
+ && reader.getMethodExceptionCount(0) == 0)
+ {
+ ctors.push_back(
+ SingleInterfaceBasedServiceEntity::Constructor());
+ } else {
+ for (sal_uInt16 j = 0; j != n; ++j) {
+ if (reader.getMethodFlags(j) != RTMethodMode::TWOWAY) {
+ throw FileFormatException(
+ key.getRegistryName(),
+ ("legacy format: unexpected mode "
+ + OUString::number(static_cast<int>(reader.getMethodFlags(j)))
+ + " of constructor " + reader.getMethodName(j)
+ + " in service with key " + sub.getName()));
+ }
+ std::vector<
+ SingleInterfaceBasedServiceEntity::Constructor::
+ Parameter > params;
+ sal_uInt16 m = reader.getMethodParameterCount(j);
+ // cid#1213376 unhelpfully warns about an untrusted
+ // loop bound here:
+ // coverity[tainted_data] - trusted data source
+ for (sal_uInt16 k = 0; k != m; ++k) {
+ RTParamMode mode
+ = reader.getMethodParameterFlags(j, k);
+ if ((mode & ~RT_PARAM_REST) != RT_PARAM_IN) {
+ throw FileFormatException(
+ key.getRegistryName(),
+ ("legacy format: unexpected mode "
+ + OUString::number(mode)
+ + " of parameter "
+ + reader.getMethodParameterName(j, k)
+ + " of constructor "
+ + reader.getMethodName(j)
+ + " in service with key "
+ + sub.getName()));
+ }
+ if ((mode & RT_PARAM_REST) != 0
+ && (m != 1
+ || ((reader.getMethodParameterTypeName(
+ j, 0))
+ != "any")))
+ {
+ throw FileFormatException(
+ key.getRegistryName(),
+ ("legacy format: bad rest parameter "
+ + reader.getMethodParameterName(j, k)
+ + " of constructor "
+ + reader.getMethodName(j)
+ + " in service with key "
+ + sub.getName()));
+ }
+ params.emplace_back(
+ reader.getMethodParameterName(j, k),
+ (reader.getMethodParameterTypeName(j, k).
+ replace('/', '.')),
+ (mode & RT_PARAM_REST) != 0);
+ }
+ std::vector< OUString > excs;
+ m = reader.getMethodExceptionCount(j);
+ // cid#1213376 unhelpfully warns about an untrusted
+ // loop bound here:
+ // coverity[tainted_data] - trusted data source
+ for (sal_uInt16 k = 0; k != m; ++k) {
+ excs.push_back(
+ reader.getMethodExceptionTypeName(j, k).replace(
+ '/', '.'));
+ }
+ ctors.push_back(
+ SingleInterfaceBasedServiceEntity::Constructor(
+ reader.getMethodName(j), std::move(params), std::move(excs),
+ translateAnnotations(
+ reader.getMethodDocumentation(j))));
+ }
+ }
+ return new SingleInterfaceBasedServiceEntity(
+ reader.isPublished(),
+ reader.getSuperTypeName(0).replace('/', '.'), std::move(ctors),
+ translateAnnotations(reader.getDocumentation()));
+ }
+ default:
+ throw FileFormatException(
+ key.getRegistryName(),
+ ("legacy format: unexpected number "
+ + OUString::number(reader.getSuperTypeCount())
+ + " of super-types of service with key " + sub.getName()));
+ }
+ case RT_TYPE_SINGLETON:
+ {
+ if (reader.getSuperTypeCount() != 1) {
+ throw FileFormatException(
+ key.getRegistryName(),
+ ("legacy format: unexpected number "
+ + OUString::number(reader.getSuperTypeCount())
+ + " of super-types of singleton with key "
+ + sub.getName()));
+ }
+ OUString basePath(reader.getSuperTypeName(0));
+ OUString baseName(basePath.replace('/', '.'));
+ bool newStyle;
+ rtl::Reference< Entity > base(manager->findEntity(baseName));
+ if (base.is()) {
+ switch (base->getSort()) {
+ case Entity::SORT_INTERFACE_TYPE:
+ newStyle = true;
+ break;
+ case Entity::SORT_ACCUMULATION_BASED_SERVICE:
+ newStyle = false;
+ break;
+ default:
+ throw FileFormatException(
+ key.getRegistryName(),
+ ("legacy format: unexpected sort "
+ + OUString::number(base->getSort()) + " of base "
+ + baseName + " of singleton with key "
+ + sub.getName()));
+ }
+ } else {
+ RegistryKey key2;
+ e = ucr.openKey(basePath, key2);
+ switch (e) {
+ case RegError::NO_ERROR:
+ break;
+ case RegError::KEY_NOT_EXISTS:
+ throw FileFormatException(
+ key.getRegistryName(),
+ ("legacy format: unknown super-type " + basePath
+ + " of super-type with key " + sub.getName()));
+ default:
+ throw FileFormatException(
+ key.getRegistryName(),
+ ("legacy format: cannot open ucr sub-key " + basePath
+ + ": " + OUString::number(static_cast<int>(e))));
+ }
+ std::vector< char > buf2;
+ typereg::Reader reader2(getReader(key2, &buf2));
+ switch (reader2.getTypeClass()) {
+ case RT_TYPE_INTERFACE:
+ newStyle = true;
+ break;
+ case RT_TYPE_SERVICE:
+ newStyle = false;
+ break;
+ default:
+ throw FileFormatException(
+ key.getRegistryName(),
+ ("legacy format: unexpected type class "
+ + OUString::number(reader2.getTypeClass())
+ + " of super-type with key " + key2.getName()
+ + " of singleton with key " + sub.getName()));
+ }
+ }
+ return newStyle
+ ? rtl::Reference< Entity >(
+ new InterfaceBasedSingletonEntity(
+ reader.isPublished(), baseName,
+ translateAnnotations(reader.getDocumentation())))
+ : rtl::Reference< Entity >(
+ new ServiceBasedSingletonEntity(
+ reader.isPublished(), baseName,
+ translateAnnotations(reader.getDocumentation())));
+ }
+ case RT_TYPE_CONSTANTS:
+ {
+ std::vector< ConstantGroupEntity::Member > mems;
+ sal_uInt16 n = reader.getFieldCount();
+ for (sal_uInt16 j = 0; j != n; ++j) {
+ mems.emplace_back(
+ reader.getFieldName(j),
+ translateConstantValue(sub, reader.getFieldValue(j)),
+ translateAnnotations(reader.getFieldDocumentation(j)));
+ }
+ return new ConstantGroupEntity(
+ reader.isPublished(), std::move(mems),
+ translateAnnotations(reader.getDocumentation()));
+ }
+ default:
+ throw FileFormatException(
+ key.getRegistryName(),
+ ("legacy format: unexpected type class "
+ + OUString::number(reader.getTypeClass()) + " of key "
+ + sub.getName()));
+ }
+}
+
+}
+
+LegacyProvider::LegacyProvider(Manager & manager, OUString const & uri):
+ manager_(manager)
+{
+ Registry reg;
+ RegError e = reg.open(uri, RegAccessMode::READONLY);
+ switch (e) {
+ case RegError::NO_ERROR:
+ break;
+ case RegError::REGISTRY_NOT_EXISTS:
+ throw NoSuchFileException(uri);
+ default:
+ throw FileFormatException(
+ uri, "cannot open legacy file: " + OUString::number(static_cast<int>(e)));
+ }
+ RegistryKey root;
+ e = reg.openRootKey(root);
+ if (e != RegError::NO_ERROR) {
+ throw FileFormatException(
+ uri, "legacy format: cannot open root key: " + OUString::number(static_cast<int>(e)));
+ }
+ e = root.openKey("UCR", ucr_);
+ switch (e) {
+ case RegError::NO_ERROR:
+ case RegError::KEY_NOT_EXISTS: // such effectively empty files exist in the wild
+ break;
+ default:
+ throw FileFormatException(
+ uri, "legacy format: cannot open UCR key: " + OUString::number(static_cast<int>(e)));
+ }
+}
+
+rtl::Reference< MapCursor > LegacyProvider::createRootCursor() const {
+ return new Cursor(&manager_, ucr_, ucr_);
+}
+
+rtl::Reference< Entity > LegacyProvider::findEntity(OUString const & name)
+ const
+{
+ return ucr_.isValid()
+ ? readEntity(&manager_, ucr_, ucr_, name.replace('.', '/'), true)
+ : rtl::Reference< Entity >();
+}
+
+LegacyProvider::~LegacyProvider() noexcept {}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unoidl/source/legacyprovider.hxx b/unoidl/source/legacyprovider.hxx
new file mode 100644
index 000000000..caa1b38c8
--- /dev/null
+++ b/unoidl/source/legacyprovider.hxx
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <registry/registry.hxx>
+#include <rtl/ref.hxx>
+#include <unoidl/unoidl.hxx>
+
+namespace unoidl::detail {
+
+class LegacyProvider: public Provider {
+public:
+ // throws FileFormatException, NoSuchFileException:
+ LegacyProvider(Manager & manager, OUString const & uri);
+
+ // throws FileFormatException:
+ virtual rtl::Reference< MapCursor > createRootCursor() const override;
+
+ // throws FileFormatException:
+ virtual rtl::Reference< Entity > findEntity(OUString const & name)
+ const override;
+
+private:
+ virtual ~LegacyProvider() noexcept override;
+
+ Manager & manager_;
+ mutable RegistryKey ucr_;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unoidl/source/sourcefileprovider.cxx b/unoidl/source/sourcefileprovider.cxx
new file mode 100644
index 000000000..983b7d3c3
--- /dev/null
+++ b/unoidl/source/sourcefileprovider.cxx
@@ -0,0 +1,135 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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 <map>
+#include <utility>
+#include <vector>
+
+#include "sourcefileprovider.hxx"
+#include "sourceprovider-scanner.hxx"
+
+namespace unoidl::detail {
+
+namespace {
+
+class Cursor: public MapCursor {
+public:
+ explicit Cursor(std::map< OUString, rtl::Reference<Entity> > const & map):
+ map_(map), iterator_(map_.begin())
+ {}
+
+private:
+ virtual ~Cursor() noexcept override {}
+
+ virtual rtl::Reference< Entity > getNext(OUString * name) override;
+
+ std::map< OUString, rtl::Reference<Entity> > const & map_; //TODO: extent
+ std::map< OUString, rtl::Reference<Entity> >::const_iterator iterator_;
+};
+
+rtl::Reference< Entity > Cursor::getNext(OUString * name) {
+ assert(name != nullptr);
+ rtl::Reference< Entity > ent;
+ if (iterator_ != map_.end()) {
+ *name = iterator_->first;
+ ent = iterator_->second;
+ ++iterator_;
+ }
+ return ent;
+}
+
+class Module: public ModuleEntity {
+public:
+ Module() {}
+
+ std::map< OUString, rtl::Reference<Entity> > map;
+
+private:
+ virtual ~Module() noexcept override {}
+
+ virtual std::vector<OUString> getMemberNames() const override;
+
+ virtual rtl::Reference<MapCursor> createCursor() const override
+ { return new Cursor(map); }
+};
+
+std::vector<OUString> Module::getMemberNames() const {
+ std::vector<OUString> names;
+ for (auto & i: map) {
+ names.push_back(i.first);
+ }
+ return names;
+}
+
+}
+
+SourceFileProvider::SourceFileProvider(
+ rtl::Reference<Manager> const & manager, OUString const & uri)
+{
+ SourceProviderScannerData data(manager);
+ if (!parse(uri, &data)) {
+ throw NoSuchFileException(uri);
+ }
+ for (auto & i: data.entities) {
+ if (i.second.kind == SourceProviderEntity::KIND_LOCAL) {
+ assert(i.second.entity.is());
+ assert(i.second.entity->getSort() != Entity::SORT_MODULE);
+ std::map< OUString, rtl::Reference<Entity> > * map = &rootMap_;
+ for (sal_Int32 j = 0;;) {
+ OUString id(i.first.getToken(0, '.', j));
+ if (j == -1) {
+ map->insert(std::make_pair(id, i.second.entity));
+ break;
+ }
+ std::map< OUString, rtl::Reference<Entity> >::const_iterator k(
+ map->find(id));
+ if (k == map->end()) {
+ k = map->insert(std::make_pair(id, new Module)).first;
+ }
+ Module& mod = dynamic_cast<Module&>(*k->second);
+ map = &mod.map;
+ }
+ }
+ }
+}
+
+rtl::Reference<MapCursor> SourceFileProvider::createRootCursor() const {
+ return new Cursor(rootMap_);
+}
+
+rtl::Reference<Entity> SourceFileProvider::findEntity(OUString const & name)
+ const
+{
+ std::map< OUString, rtl::Reference<Entity> > const * map = &rootMap_;
+ for (sal_Int32 i = 0;;) {
+ OUString id(name.getToken(0, '.', i));
+ std::map< OUString, rtl::Reference<Entity> >::const_iterator j(
+ map->find(id));
+ if (j == map->end()) {
+ return rtl::Reference<Entity>();
+ }
+ if (i == -1) {
+ return j->second;
+ }
+ if (j->second->getSort() != Entity::SORT_MODULE) {
+ return rtl::Reference<Entity>();
+ }
+ Module * mod = dynamic_cast< Module * >(j->second.get());
+ assert(mod != nullptr);
+ map = &mod->map;
+ }
+}
+
+SourceFileProvider::~SourceFileProvider() noexcept {}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unoidl/source/sourcefileprovider.hxx b/unoidl/source/sourcefileprovider.hxx
new file mode 100644
index 000000000..52fd32f3c
--- /dev/null
+++ b/unoidl/source/sourcefileprovider.hxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <map>
+
+#include <rtl/ref.hxx>
+#include <unoidl/unoidl.hxx>
+
+namespace unoidl::detail
+{
+class SourceFileProvider : public Provider
+{
+public:
+ // throws FileFormatException, NoSuchFileException:
+ SourceFileProvider(rtl::Reference<Manager> const& manager, OUString const& uri);
+
+ // throws FileFormatException:
+ virtual rtl::Reference<MapCursor> createRootCursor() const override;
+
+ // throws FileFormatException:
+ virtual rtl::Reference<Entity> findEntity(OUString const& name) const override;
+
+private:
+ virtual ~SourceFileProvider() noexcept override;
+
+ std::map<OUString, rtl::Reference<Entity>> rootMap_;
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unoidl/source/sourceprovider-parser-requires.hxx b/unoidl/source/sourceprovider-parser-requires.hxx
new file mode 100644
index 000000000..5d9e5f6e3
--- /dev/null
+++ b/unoidl/source/sourceprovider-parser-requires.hxx
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <utility>
+#include <vector>
+
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+
+#define YYLTYPE int
+
+typedef void * yyscan_t;
+
+namespace unoidl::detail {
+
+struct SourceProviderEntity;
+
+enum SourceProviderAccessDecls { ACCESS_DECL_GET = 0x1, ACCESS_DECL_SET = 0x2 };
+
+enum SourceProviderFlags {
+ FLAG_ATTRIBUTE = 0x001, FLAG_BOUND = 0x002, FLAG_CONSTRAINED = 0x004,
+ FLAG_MAYBEAMBIGUOUS = 0x008, FLAG_MAYBEDEFAULT = 0x010,
+ FLAG_MAYBEVOID = 0x020, FLAG_OPTIONAL = 0x040, FLAG_PROPERTY = 0x080,
+ FLAG_READONLY = 0x100, FLAG_REMOVABLE = 0x200, FLAG_TRANSIENT = 0x400
+};
+
+struct SourceProviderExpr {
+ static SourceProviderExpr Bool(bool v) {
+ SourceProviderExpr e;
+ e.type = TYPE_BOOL;
+ e.bval = v;
+ return e;
+ }
+
+ static SourceProviderExpr Int(sal_Int64 v) {
+ SourceProviderExpr e;
+ e.type = TYPE_INT;
+ e.ival = v;
+ return e;
+ }
+
+ static SourceProviderExpr Uint(sal_uInt64 v) {
+ SourceProviderExpr e;
+ e.type = TYPE_UINT;
+ e.uval = v;
+ return e;
+ }
+
+ static SourceProviderExpr Float(double v) {
+ SourceProviderExpr e;
+ e.type = TYPE_FLOAT;
+ e.fval = v;
+ return e;
+ }
+
+ enum Type { TYPE_BOOL, TYPE_INT, TYPE_UINT, TYPE_FLOAT };
+
+ Type type;
+ union {
+ bool bval;
+ sal_Int64 ival;
+ sal_uInt64 uval;
+ double fval;
+ };
+};
+
+struct SourceProviderType {
+ enum Type {
+ TYPE_VOID, TYPE_BOOLEAN, TYPE_BYTE, TYPE_SHORT, TYPE_UNSIGNED_SHORT,
+ TYPE_LONG, TYPE_UNSIGNED_LONG, TYPE_HYPER, TYPE_UNSIGNED_HYPER,
+ TYPE_FLOAT, TYPE_DOUBLE, TYPE_CHAR, TYPE_STRING, TYPE_TYPE, TYPE_ANY,
+ TYPE_SEQUENCE, TYPE_ENUM, TYPE_PLAIN_STRUCT, TYPE_EXCEPTION,
+ TYPE_INTERFACE, TYPE_INSTANTIATED_POLYMORPHIC_STRUCT, TYPE_PARAMETER
+ };
+
+ SourceProviderType():
+ type(), entity() // avoid false warnings about uninitialized members
+ {}
+
+ explicit SourceProviderType(Type theType):
+ type(theType),
+ entity() // avoid false warnings about uninitialized member
+ { assert(theType <= TYPE_ANY); }
+
+ explicit SourceProviderType(SourceProviderType const * componentType):
+ type(TYPE_SEQUENCE),
+ entity() // avoid false warnings about uninitialized member
+ { assert(componentType != nullptr); subtypes.push_back(*componentType); }
+
+ SourceProviderType(
+ Type theType, OUString theName,
+ SourceProviderEntity const * theEntity):
+ type(theType), name(std::move(theName)), entity(theEntity)
+ {
+ assert(theType >= TYPE_ENUM && theType <= TYPE_INTERFACE);
+ assert(theEntity != nullptr);
+ }
+
+ SourceProviderType(
+ OUString polymorphicStructTypeTemplateName,
+ SourceProviderEntity const * theEntity,
+ std::vector<SourceProviderType>&& typeArguments):
+ type(TYPE_INSTANTIATED_POLYMORPHIC_STRUCT),
+ name(std::move(polymorphicStructTypeTemplateName)), entity(theEntity),
+ subtypes(std::move(typeArguments))
+ { assert(theEntity != nullptr); }
+
+ explicit SourceProviderType(OUString identifier):
+ type(TYPE_PARAMETER), name(std::move(identifier)),
+ entity() // avoid false warnings about uninitialized member
+ {}
+
+ OUString getName() const;
+
+ bool equals(SourceProviderType const & other) const;
+
+ Type type;
+ OUString name; // TYPE_ENUM ... TYPE_PARAMETER
+ SourceProviderEntity const * entity;
+ // TYPE_ENUM ... TYPE_INSTANTIATED_POLYMOPRHIC_STRUCT
+ std::vector<SourceProviderType> subtypes;
+ // TYPE_SEQUENCE, TYPE_INSTANTIATED_POLYMOPRHIC_STRUCT
+ OUString typedefName;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unoidl/source/sourceprovider-parser.y b/unoidl/source/sourceprovider-parser.y
new file mode 100644
index 000000000..abe17e355
--- /dev/null
+++ b/unoidl/source/sourceprovider-parser.y
@@ -0,0 +1,4476 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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/.
+ */
+
+/*TODO: check Exception, RuntimeException, XInterface defns */
+
+%locations
+%pure-parser
+
+%{
+
+#include <sal/config.h>
+
+#include <o3tl/unreachable.hxx>
+#include <o3tl/string_view.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <unoidl/unoidl.hxx>
+
+#include <algorithm>
+#include <cassert>
+#include <cerrno>
+#include <cstddef>
+#include <cstdlib>
+#include <limits>
+#include <new>
+#include <utility>
+#include <vector>
+
+#include "sourceprovider-parser-requires.hxx"
+
+%}
+
+%union {
+ sal_uInt64 ival;
+ double fval;
+ OString * sval;
+
+ bool bval;
+ std::vector<OUString> * excns;
+ unoidl::detail::SourceProviderAccessDecls decls;
+ unoidl::InterfaceTypeEntity::Method::Parameter::Direction dir;
+ unoidl::detail::SourceProviderFlags flags;
+ unoidl::detail::SourceProviderExpr expr;
+ unoidl::detail::SourceProviderType * type;
+ std::vector<unoidl::detail::SourceProviderType> * types;
+}
+
+/* TODO: %destructor { delete $$; } <sval> <excns> <type> <types> */
+
+%lex-param {yyscan_t yyscanner}
+%parse-param {yyscan_t yyscanner}
+
+%{
+
+#include <osl/file.h>
+#include <osl/thread.h>
+#include <sal/log.hxx>
+
+#include "sourceprovider-scanner.hxx"
+
+#define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do { (Current) = YYRHSLOC((Rhs), (N) ? 1 : 0); } while (0)
+
+static void yyerror(YYLTYPE * locp, yyscan_t yyscanner, char const * msg) {
+ assert(locp != nullptr);
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ data->errorLine = *locp;
+ data->parserError = OString(msg);
+}
+
+namespace {
+
+void error(YYLTYPE location, yyscan_t yyscanner, OUString const & message) {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ data->errorLine = location;
+ data->errorMessage = message;
+}
+
+OUString flagName(unoidl::detail::SourceProviderFlags flag) {
+ switch (flag) {
+ case unoidl::detail::FLAG_ATTRIBUTE:
+ return "attribute";
+ case unoidl::detail::FLAG_BOUND:
+ return "bound";
+ case unoidl::detail::FLAG_CONSTRAINED:
+ return "constrained";
+ case unoidl::detail::FLAG_MAYBEAMBIGUOUS:
+ return "maybeambiguous";
+ case unoidl::detail::FLAG_MAYBEDEFAULT:
+ return "maybedefault";
+ case unoidl::detail::FLAG_MAYBEVOID:
+ return "maybevoid";
+ case unoidl::detail::FLAG_OPTIONAL:
+ return "optional";
+ case unoidl::detail::FLAG_PROPERTY:
+ return "property";
+ case unoidl::detail::FLAG_READONLY:
+ return "readonly";
+ case unoidl::detail::FLAG_REMOVABLE:
+ return "removable";
+ case unoidl::detail::FLAG_TRANSIENT:
+ return "transient";
+ default:
+ assert(false && "this cannot happen"); for (;;) { std::abort(); }
+ }
+}
+
+OUString convertName(OString const * name) {
+ assert(name != nullptr);
+ OUString s(OStringToOUString(*name, RTL_TEXTENCODING_ASCII_US));
+ delete name;
+ return s;
+}
+
+OUString convertToFullName(
+ unoidl::detail::SourceProviderScannerData const * data,
+ OString const * identifier)
+{
+ assert(data != nullptr);
+ OUString pref;
+ if (!data->modules.empty()) {
+ pref = data->modules.back() + ".";
+ }
+ return pref + convertName(identifier);
+}
+
+void convertToCurrentName(
+ unoidl::detail::SourceProviderScannerData * data,
+ OString const * identifier)
+{
+ assert(data != nullptr);
+ assert(data->currentName.isEmpty());
+ data->currentName = convertToFullName(data, identifier);
+ assert(!data->currentName.isEmpty());
+}
+
+void clearCurrentState(unoidl::detail::SourceProviderScannerData * data) {
+ assert(data != nullptr);
+ data->currentName.clear();
+ data->publishedContext = false;
+}
+
+unoidl::detail::SourceProviderEntity * getCurrentEntity(
+ unoidl::detail::SourceProviderScannerData * data)
+{
+ assert(data != nullptr);
+ assert(!data->currentName.isEmpty());
+ std::map<OUString, unoidl::detail::SourceProviderEntity>::iterator i(
+ data->entities.find(data->currentName));
+ assert(i != data->entities.end());
+ assert(i->second.kind == unoidl::detail::SourceProviderEntity::KIND_LOCAL);
+ assert(i->second.pad.is());
+ return &i->second;
+}
+
+template<typename T> rtl::Reference<T> getCurrentPad(
+ unoidl::detail::SourceProviderScannerData * data)
+{
+ rtl::Reference<T> pad(dynamic_cast<T *>(getCurrentEntity(data)->pad.get()));
+ assert(pad.is());
+ return pad;
+}
+
+bool nameHasSameIdentifierAs(std::u16string_view name, std::u16string_view identifier)
+{
+ size_t i = name.rfind('.') + 1;
+ return identifier.size() == name.size() - i
+ && o3tl::starts_with(name.substr(i), identifier);
+}
+
+bool coerce(
+ YYLTYPE location, yyscan_t yyscanner,
+ unoidl::detail::SourceProviderExpr * lhs,
+ unoidl::detail::SourceProviderExpr * rhs)
+{
+ assert(lhs != nullptr);
+ assert(rhs != nullptr);
+ bool ok = bool(); // avoid warnings
+ switch (lhs->type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_BOOL:
+ ok = rhs->type != unoidl::detail::SourceProviderExpr::TYPE_BOOL;
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ switch (rhs->type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_BOOL:
+ ok = false;
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ ok = true;
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ if (lhs->ival >= 0) {
+ lhs->type = unoidl::detail::SourceProviderExpr::TYPE_UINT;
+ ok = true;
+ } else if (rhs->uval <= SAL_MAX_INT64) {
+ rhs->type = unoidl::detail::SourceProviderExpr::TYPE_INT;
+ ok = true;
+ } else {
+ ok = false;
+ }
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_FLOAT:
+ {
+ auto tmp = lhs->ival;
+ lhs->fval = tmp;
+ ok = true;
+ }
+ break;
+ }
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ switch (rhs->type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_BOOL:
+ ok = false;
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ if (rhs->ival >= 0) {
+ rhs->type = unoidl::detail::SourceProviderExpr::TYPE_UINT;
+ ok = true;
+ } else if (lhs->uval <= SAL_MAX_INT64) {
+ lhs->type = unoidl::detail::SourceProviderExpr::TYPE_INT;
+ ok = true;
+ } else {
+ ok = false;
+ }
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ ok = true;
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_FLOAT:
+ {
+ auto nTmp = lhs->uval;
+ lhs->fval = nTmp;
+ ok = true;
+ }
+ break;
+ }
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_FLOAT:
+ switch (rhs->type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_BOOL:
+ ok = false;
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ {
+ auto tmp = rhs->ival;
+ rhs->fval = tmp;
+ ok = true;
+ }
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ {
+ auto tmp = rhs->uval;
+ rhs->fval = tmp;
+ ok = true;
+ }
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_FLOAT:
+ ok = true;
+ break;
+ }
+ break;
+ }
+ if (!ok) {
+ error(location, yyscanner, "cannot coerce binary expression arguments");
+ }
+ return ok;
+}
+
+unoidl::detail::SourceProviderEntity * findEntity_(
+ unoidl::detail::SourceProviderScannerData * data, OUString * name)
+{
+ assert(data != nullptr);
+ assert(name != nullptr);
+ OUString n;
+ if (!name->startsWith(".", &n)) {
+ for (auto i(data->modules.rbegin()); i != data->modules.rend(); ++i) {
+ n = *i + "." + *name;
+ std::map<OUString, unoidl::detail::SourceProviderEntity>::iterator j(
+ data->entities.find(n));
+ if (j != data->entities.end()) {
+ *name = n;
+ return &j->second;
+ }
+ rtl::Reference<unoidl::Entity> ent(data->manager->findEntity(n));
+ if (ent.is()) {
+ std::map<OUString, unoidl::detail::SourceProviderEntity>::iterator
+ k(data->entities.emplace(
+ n,
+ unoidl::detail::SourceProviderEntity(
+ unoidl::detail::SourceProviderEntity::KIND_EXTERNAL,
+ ent)).
+ first);
+ *name = n;
+ return &k->second;
+ }
+ }
+ n = *name;
+ }
+ std::map<OUString, unoidl::detail::SourceProviderEntity>::iterator i(
+ data->entities.find(n));
+ if (i != data->entities.end()) {
+ *name = n;
+ return &i->second;
+ }
+ rtl::Reference<unoidl::Entity> ent(data->manager->findEntity(n));
+ if (ent.is()) {
+ std::map<OUString, unoidl::detail::SourceProviderEntity>::iterator
+ j(data->entities.emplace(
+ n,
+ unoidl::detail::SourceProviderEntity(
+ unoidl::detail::SourceProviderEntity::KIND_EXTERNAL,
+ ent)).
+ first);
+ *name = n;
+ return &j->second;
+ }
+ return nullptr;
+}
+
+enum Found { FOUND_ERROR, FOUND_TYPE, FOUND_ENTITY };
+
+Found findEntity(
+ YYLTYPE location, yyscan_t yyscanner,
+ unoidl::detail::SourceProviderScannerData * data,
+ bool resolveInterfaceDefinitions, OUString * name,
+ unoidl::detail::SourceProviderEntity const ** entity, bool * typedefed,
+ unoidl::detail::SourceProviderType * typedefedType)
+{
+ //TODO: avoid recursion
+ assert(data != nullptr);
+ assert(name != nullptr);
+ assert(entity != nullptr);
+ unoidl::detail::SourceProviderEntity * e = findEntity_(data, name);
+ OUString n(*name);
+ OUString typeNucleus;
+ std::size_t rank = 0;
+ std::vector<unoidl::detail::SourceProviderType> args;
+ for (;;) {
+ if (e != nullptr) {
+ switch (e->kind) {
+ case unoidl::detail::SourceProviderEntity::KIND_LOCAL:
+ if (e->pad.is()) {
+ break;
+ }
+ assert(e->entity.is());
+ [[fallthrough]];
+ case unoidl::detail::SourceProviderEntity::KIND_EXTERNAL:
+ if (e->entity->getSort() == unoidl::Entity::SORT_TYPEDEF) {
+ if (typedefed != nullptr) {
+ *typedefed = true;
+ }
+ if (data->publishedContext
+ && !static_cast<unoidl::TypedefEntity *>(
+ e->entity.get())->isPublished())
+ {
+ error(
+ location, yyscanner,
+ ("type " + *name + " based on unpublished typedef "
+ + n + " used in published context"));
+ return FOUND_ERROR;
+ }
+ OUString t(
+ static_cast<unoidl::TypedefEntity *>(e->entity.get())
+ ->getType());
+ typeNucleus = t;
+ while (typeNucleus.startsWith("[]", &typeNucleus)) {
+ if (!args.empty()) {
+ error(
+ location, yyscanner,
+ ("inconsistent type manager: bad type " + *name
+ + (" based on instantiated polymorphic struct"
+ " type based on sequence type named ")
+ + t));
+ return FOUND_ERROR;
+ }
+ if (rank == std::numeric_limits<std::size_t>::max()) {
+ error(
+ location, yyscanner,
+ ("bad type " + *name
+ + " based on sequence type of too high rank"));
+ return FOUND_ERROR;
+ }
+ ++rank;
+ }
+ sal_Int32 i = typeNucleus.indexOf('<');
+ if (i != -1) {
+ if (!args.empty()) {
+ error(
+ location, yyscanner,
+ ("inconsistent type manager: bad type " + *name
+ + (" based on instantiated polymorphic struct"
+ " type based on instantiated polymorphic"
+ " struct type named ")
+ + t));
+ return FOUND_ERROR;
+ }
+ std::u16string_view tmpl(typeNucleus.subView(0, i));
+ do {
+ ++i; // skip '<' or ','
+ sal_Int32 j = i;
+ for (sal_Int32 level = 0;
+ j != typeNucleus.getLength(); ++j)
+ {
+ sal_Unicode c = typeNucleus[j];
+ if (c == ',') {
+ if (level == 0) {
+ break;
+ }
+ } else if (c == '<') {
+ ++level;
+ } else if (c == '>') {
+ if (level == 0) {
+ break;
+ }
+ --level;
+ }
+ }
+ if (j != typeNucleus.getLength()) {
+ OUString argName(typeNucleus.copy(i, j - i));
+ unoidl::detail::SourceProviderEntity const *
+ argEnt;
+ unoidl::detail::SourceProviderType argType;
+ switch (
+ findEntity(
+ location, yyscanner, data, false,
+ &argName, &argEnt, nullptr, &argType))
+ {
+ case FOUND_ERROR:
+ return FOUND_ERROR;
+ case FOUND_TYPE:
+ break;
+ case FOUND_ENTITY:
+ if (argEnt == nullptr) {
+ error(
+ location, yyscanner,
+ (("inconsistent type manager: bad"
+ " instantiated polymorphic struct"
+ " type template type argument ")
+ + argName));
+ return FOUND_ERROR;
+ } else {
+ unoidl::detail::SourceProviderType::Type
+ argT
+ = unoidl::detail::SourceProviderType::Type();
+ // avoid warnings
+ switch (argEnt->kind) {
+ case unoidl::detail::SourceProviderEntity::KIND_LOCAL:
+ if (e->pad.is()) {
+ error(
+ location, yyscanner,
+ (("inconsistent type"
+ " manager: bad"
+ " instantiated"
+ " polymorphic struct type"
+ " template type"
+ " argument ")
+ + argName));
+ return FOUND_ERROR;
+ }
+ assert(e->entity.is());
+ [[fallthrough]];
+ case unoidl::detail::SourceProviderEntity::KIND_EXTERNAL:
+ switch (e->entity->getSort()) {
+ case unoidl::Entity::SORT_ENUM_TYPE:
+ argT = unoidl::detail::SourceProviderType::TYPE_ENUM;
+ break;
+ case unoidl::Entity::SORT_PLAIN_STRUCT_TYPE:
+ argT = unoidl::detail::SourceProviderType::TYPE_PLAIN_STRUCT;
+ break;
+ case unoidl::Entity::SORT_INTERFACE_TYPE:
+ argT = unoidl::detail::SourceProviderType::TYPE_INTERFACE;
+ break;
+ default:
+ error(
+ location, yyscanner,
+ (("inconsistent type"
+ "manager: bad"
+ " instantiated"
+ " polymorphic struct type"
+ " template type"
+ " argument ")
+ + argName));
+ return FOUND_ERROR;
+ }
+ break;
+ case unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL:
+ case unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL:
+ argT = unoidl::detail::SourceProviderType::TYPE_INTERFACE;
+ break;
+ case unoidl::detail::SourceProviderEntity::KIND_MODULE:
+ assert(false && "this cannot happen");
+ }
+ argType
+ = unoidl::detail::SourceProviderType(
+ argT, argName, argEnt);
+ }
+ break;
+ }
+ args.push_back(argType);
+ }
+ i = j;
+ } while (i != typeNucleus.getLength()
+ && typeNucleus[i] != '>');
+ if (i != typeNucleus.getLength() - 1
+ || typeNucleus[i] != '>')
+ {
+ error(
+ location, yyscanner,
+ ("inconsistent type manager: bad type name \""
+ + t + "\""));
+ return FOUND_ERROR;
+ }
+ assert(!args.empty());
+ typeNucleus = tmpl;
+ }
+ if (typeNucleus.isEmpty()) {
+ error(
+ location, yyscanner,
+ ("inconsistent type manager: bad type name \"" + t
+ + "\""));
+ return FOUND_ERROR;
+ }
+ if (typeNucleus == "void") {
+ error(
+ location, yyscanner,
+ ("inconsistent type manager: bad type " + *name
+ + " based on void"));
+ return FOUND_ERROR;
+ }
+ if (typeNucleus == "boolean" || typeNucleus == "byte"
+ || typeNucleus == "short"
+ || typeNucleus == "unsigned short"
+ || typeNucleus == "long"
+ || typeNucleus == "unsigned long"
+ || typeNucleus == "hyper"
+ || typeNucleus == "unsigned hyper"
+ || typeNucleus == "float" || typeNucleus == "double"
+ || typeNucleus == "char" || typeNucleus == "string"
+ || typeNucleus == "type" || typeNucleus == "any")
+ {
+ if (!args.empty()) {
+ error(
+ location, yyscanner,
+ ("inconsistent type manager: bad type " + *name
+ + (" based on instantiated polymorphic struct"
+ " type based on ")
+ + typeNucleus));
+ return FOUND_ERROR;
+ }
+ break;
+ }
+ n = "." + typeNucleus;
+ typeNucleus.clear();
+ e = findEntity_(data, &n);
+ continue;
+ }
+ break;
+ case unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL:
+ case unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL:
+ if (resolveInterfaceDefinitions) {
+ rtl::Reference<unoidl::Entity> ent(
+ data->manager->findEntity(n));
+ // Do not allow ent to be of SORT_TYPEDEF:
+ if (!ent.is()
+ || (ent->getSort()
+ != unoidl::Entity::SORT_INTERFACE_TYPE))
+ {
+ error(
+ location, yyscanner,
+ (*name + " is based on interface declaration " + n
+ + " that is not an interface type entity"));
+ return FOUND_ERROR;
+ }
+ e->kind
+ = unoidl::detail::SourceProviderEntity::KIND_EXTERNAL;
+ e->entity = ent;
+ }
+ break;
+ case unoidl::detail::SourceProviderEntity::KIND_MODULE:
+ error(
+ location, yyscanner,
+ *name + " is based on module entity " + n);
+ return FOUND_ERROR;
+ }
+ }
+ if (!typeNucleus.isEmpty() || rank != 0 || !args.empty()) {
+ if (typeNucleus.isEmpty() && e == nullptr) {
+ // Found a type name based on an unknown entity:
+ *entity = nullptr;
+ return FOUND_ENTITY;
+ }
+ unoidl::detail::SourceProviderType t;
+ if (args.empty()) {
+ if (typeNucleus == "boolean") {
+ t = unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_BOOLEAN);
+ } else if (typeNucleus == "byte") {
+ t = unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_BYTE);
+ } else if (typeNucleus == "short") {
+ t = unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_SHORT);
+ } else if (typeNucleus == "unsigned short") {
+ t = unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_UNSIGNED_SHORT);
+ } else if (typeNucleus == "long") {
+ t = unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_LONG);
+ } else if (typeNucleus == "unsigned long") {
+ t = unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_UNSIGNED_LONG);
+ } else if (typeNucleus == "hyper") {
+ t = unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_HYPER);
+ } else if (typeNucleus == "unsigned hyper") {
+ t = unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_UNSIGNED_HYPER);
+ } else if (typeNucleus == "float") {
+ t = unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_FLOAT);
+ } else if (typeNucleus == "double") {
+ t = unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_DOUBLE);
+ } else if (typeNucleus == "char") {
+ t = unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_CHAR);
+ } else if (typeNucleus == "string") {
+ t = unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_STRING);
+ } else if (typeNucleus == "type") {
+ t = unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_TYPE);
+ } else if (typeNucleus == "any") {
+ t = unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_ANY);
+ } else {
+ assert(typeNucleus.isEmpty());
+ assert(e != nullptr);
+ switch (e->kind) {
+ case unoidl::detail::SourceProviderEntity::KIND_LOCAL:
+ if (e->pad.is()) {
+ if (dynamic_cast<unoidl::detail::SourceProviderEnumTypeEntityPad *>(
+ e->pad.get())
+ != nullptr)
+ {
+ t = unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_ENUM,
+ n, e);
+ } else if (dynamic_cast<unoidl::detail::SourceProviderPlainStructTypeEntityPad *>(
+ e->pad.get())
+ != nullptr)
+ {
+ t = unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_PLAIN_STRUCT,
+ n, e);
+ } else if (dynamic_cast<unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad *>(
+ e->pad.get())
+ != nullptr)
+ {
+ error(
+ location, yyscanner,
+ ("bad type " + *name
+ + (" based on recursive reference to"
+ " polymorphic struct type template ")
+ + n));
+ return FOUND_ERROR;
+ } else if (dynamic_cast<unoidl::detail::SourceProviderExceptionTypeEntityPad *>(
+ e->pad.get())
+ != nullptr)
+ {
+ t = unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_EXCEPTION,
+ n, e);
+ } else if (dynamic_cast<unoidl::detail::SourceProviderInterfaceTypeEntityPad *>(
+ e->pad.get())
+ != nullptr)
+ {
+ t = unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_INTERFACE,
+ n, e);
+ } else {
+ error(
+ location, yyscanner,
+ ("bad type " + *name
+ + " based on non-type entity " + n));
+ return FOUND_ERROR;
+ }
+ break;
+ }
+ assert(e->entity.is());
+ [[fallthrough]];
+ case unoidl::detail::SourceProviderEntity::KIND_EXTERNAL:
+ switch (e->entity->getSort()) {
+ case unoidl::Entity::SORT_ENUM_TYPE:
+ t = unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_ENUM,
+ n, e);
+ break;
+ case unoidl::Entity::SORT_PLAIN_STRUCT_TYPE:
+ t = unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_PLAIN_STRUCT,
+ n, e);
+ break;
+ case unoidl::Entity::SORT_POLYMORPHIC_STRUCT_TYPE_TEMPLATE:
+ error(
+ location, yyscanner,
+ ("bad type " + *name
+ + " based on polymorphic struct type template "
+ + n + " without type arguments"));
+ return FOUND_ERROR;
+ case unoidl::Entity::SORT_EXCEPTION_TYPE:
+ t = unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_EXCEPTION,
+ n, e);
+ break;
+ case unoidl::Entity::SORT_INTERFACE_TYPE:
+ t = unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_INTERFACE,
+ n, e);
+ break;
+ default:
+ error(
+ location, yyscanner,
+ ("bad type " + *name
+ + " based on non-type entity " + n));
+ return FOUND_ERROR;
+ }
+ break;
+ case unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL:
+ case unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL:
+ t = unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_INTERFACE,
+ n, e);
+ break;
+ case unoidl::detail::SourceProviderEntity::KIND_MODULE:
+ assert(false && "this cannot happen");
+ }
+ }
+ } else {
+ assert(typeNucleus.isEmpty());
+ assert(e != nullptr);
+ switch (e->kind) {
+ case unoidl::detail::SourceProviderEntity::KIND_LOCAL:
+ if (e->pad.is()) {
+ error(
+ location, yyscanner,
+ ("bad type " + *name
+ + (" based on instantiated polymorphic struct type"
+ " based on ")
+ + n
+ + (" that is either not a polymorphic struct type"
+ " template or a recursive reference to a"
+ " polymorphic struct type template")));
+ return FOUND_ERROR;
+ }
+ assert(e->entity.is());
+ [[fallthrough]];
+ case unoidl::detail::SourceProviderEntity::KIND_EXTERNAL:
+ if (e->entity->getSort()
+ == unoidl::Entity::SORT_POLYMORPHIC_STRUCT_TYPE_TEMPLATE)
+ {
+ if (args.size()
+ != (static_cast<
+ unoidl::PolymorphicStructTypeTemplateEntity *>(
+ e->entity.get())
+ ->getTypeParameters().size()))
+ {
+ error(
+ location, yyscanner,
+ ("bad type " + *name
+ + (" based on instantiated polymorphic struct"
+ " type with ")
+ + OUString::number(args.size())
+ + (" type arguments based on polymorphic"
+ " struct type template ")
+ + n + " with "
+ + OUString::number(
+ static_cast<
+ unoidl::PolymorphicStructTypeTemplateEntity *>(
+ e->entity.get())
+ ->getTypeParameters().size())
+ + " type parameters"));
+ return FOUND_ERROR;
+ }
+ t = unoidl::detail::SourceProviderType(n, e, std::move(args));
+ break;
+ }
+ [[fallthrough]];
+ case unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL:
+ case unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL:
+ error(
+ location, yyscanner,
+ ("bad type " + *name
+ + (" based on instantiated polymorphic struct type"
+ " based on ")
+ + n
+ + " that is not a polymorphic struct type template"));
+ return FOUND_ERROR;
+ case unoidl::detail::SourceProviderEntity::KIND_MODULE:
+ assert(false && "this cannot happen");
+ }
+ }
+ if (typedefedType != nullptr) {
+ for (std::size_t i = 0; i != rank; ++i) {
+ t = unoidl::detail::SourceProviderType(&t);
+ }
+ *typedefedType = t;
+ typedefedType->typedefName = *name;
+ }
+ *entity = nullptr;
+ return FOUND_TYPE;
+ }
+ *entity = e;
+ return FOUND_ENTITY;
+ }
+}
+
+
+bool checkTypeArgument(
+ YYLTYPE location, yyscan_t yyscanner,
+ unoidl::detail::SourceProviderType const & type)
+{
+ switch (type.type) {
+ case unoidl::detail::SourceProviderType::TYPE_VOID:
+ case unoidl::detail::SourceProviderType::TYPE_UNSIGNED_SHORT:
+ case unoidl::detail::SourceProviderType::TYPE_UNSIGNED_LONG:
+ case unoidl::detail::SourceProviderType::TYPE_UNSIGNED_HYPER:
+ case unoidl::detail::SourceProviderType::TYPE_EXCEPTION:
+ case unoidl::detail::SourceProviderType::TYPE_PARAMETER: //TODO?
+ error(
+ location, yyscanner,
+ "bad instantiated polymorphic struct type argument");
+ return false;
+ case unoidl::detail::SourceProviderType::TYPE_SEQUENCE:
+ return checkTypeArgument(location, yyscanner, type.subtypes.front());
+ default:
+ return true;
+ }
+}
+
+bool checkInstantiatedPolymorphicStructTypeArgument(
+ unoidl::detail::SourceProviderType const & type, OUString const & name)
+{
+ if (type.type
+ == unoidl::detail::SourceProviderType::TYPE_INSTANTIATED_POLYMORPHIC_STRUCT)
+ {
+ for (auto & i: type.subtypes) {
+ if (checkInstantiatedPolymorphicStructTypeArgument(i, name)
+ || i.getName() == name) // no need to worry about typedef
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+std::vector<OUString> annotations(bool deprecated) {
+ std::vector<OUString> ann;
+ if (deprecated) {
+ ann.push_back("deprecated");
+ }
+ return ann;
+}
+
+}
+
+%}
+
+%token TOK_ELLIPSIS
+%token TOK_COLONS
+%token TOK_LEFTSHIFT
+%token TOK_RIGHTSHIFT
+
+%token TOK_FALSE
+%token TOK_TRUE
+%token TOK_ANY
+%token TOK_ATTRIBUTE
+%token TOK_BOOLEAN
+%token TOK_BOUND
+%token TOK_BYTE
+%token TOK_CHAR
+%token TOK_CONST
+%token TOK_CONSTANTS
+%token TOK_CONSTRAINED
+%token TOK_DOUBLE
+%token TOK_ENUM
+%token TOK_EXCEPTION
+%token TOK_FLOAT
+%token TOK_GET
+%token TOK_HYPER
+%token TOK_IN
+%token TOK_INOUT
+%token TOK_INTERFACE
+%token TOK_LONG
+%token TOK_MAYBEAMBIGUOUS
+%token TOK_MAYBEDEFAULT
+%token TOK_MAYBEVOID
+%token TOK_MODULE
+%token TOK_OPTIONAL
+%token TOK_OUT
+%token TOK_PROPERTY
+%token TOK_PUBLISHED
+%token TOK_RAISES
+%token TOK_READONLY
+%token TOK_REMOVABLE
+%token TOK_SEQUENCE
+%token TOK_SERVICE
+%token TOK_SET
+%token TOK_SHORT
+%token TOK_SINGLETON
+%token TOK_STRING
+%token TOK_STRUCT
+%token TOK_TRANSIENT
+%token TOK_TYPE
+%token TOK_TYPEDEF
+%token TOK_UNSIGNED
+%token TOK_VOID
+
+%token<sval> TOK_IDENTIFIER
+%token<ival> TOK_INTEGER
+%token<fval> TOK_FLOATING
+
+%token TOK_DEPRECATED
+
+%token TOK_ERROR
+
+%type<sval> identifier name singleInheritance singleInheritance_opt
+%type<bval> ctors_opt deprecated_opt ellipsis_opt published_opt
+%type<decls> attributeAccessDecl attributeAccessDecls
+%type<dir> direction
+%type<excns> exceptionSpec exceptionSpec_opt exceptions
+%type<flags> flag flagSection flagSection_opt flags
+%type<expr> addExpr andExpr expr multExpr orExpr primaryExpr shiftExpr unaryExpr
+ xorExpr
+%type<type> type
+%type<types> typeArguments
+
+%initial-action { yylloc = 1; }
+
+%%
+
+definitions:
+ definitions definition
+| /* empty */
+;
+
+definition:
+ moduleDecl
+| enumDefn
+| plainStructDefn
+| polymorphicStructTemplateDefn
+| exceptionDefn
+| interfaceDefn
+| typedefDefn
+| constantGroupDefn
+| singleInterfaceBasedServiceDefn
+| accumulationBasedServiceDefn
+| interfaceBasedSingletonDefn
+| serviceBasedSingletonDefn
+| interfaceDecl
+;
+
+moduleDecl:
+ TOK_MODULE identifier
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ OUString name(convertToFullName(data, $2));
+ data->modules.push_back(name);
+ std::pair<std::map<OUString, unoidl::detail::SourceProviderEntity>::iterator, bool> p(
+ data->entities.emplace(
+ name,
+ unoidl::detail::SourceProviderEntity(
+ unoidl::detail::SourceProviderEntity::KIND_MODULE)));
+ if (!p.second
+ && (p.first->second.kind
+ != unoidl::detail::SourceProviderEntity::KIND_MODULE))
+ {
+ error(@2, yyscanner, "multiple entities named " + name);
+ YYERROR;
+ }
+ }
+ '{' definitions '}' ';' { yyget_extra(yyscanner)->modules.pop_back(); }
+;
+
+enumDefn:
+ deprecated_opt published_opt TOK_ENUM identifier
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ data->publishedContext = $2;
+ convertToCurrentName(data, $4);
+ if (!data->entities.emplace(
+ data->currentName,
+ unoidl::detail::SourceProviderEntity(
+ new unoidl::detail::SourceProviderEnumTypeEntityPad(
+ $2))).
+ second)
+ {
+ error(@4, yyscanner, "multiple entities named " + data->currentName);
+ YYERROR;
+ }
+ }
+ '{' enumMembers '}' ';'
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ unoidl::detail::SourceProviderEntity * ent = getCurrentEntity(data);
+ unoidl::detail::SourceProviderEnumTypeEntityPad * pad =
+ dynamic_cast<unoidl::detail::SourceProviderEnumTypeEntityPad *>(
+ ent->pad.get());
+ assert(pad != nullptr);
+ ent->entity = new unoidl::EnumTypeEntity(
+ pad->isPublished(), std::move(pad->members), annotations($1));
+ ent->pad.clear();
+ clearCurrentState(data);
+ }
+;
+
+enumMembers:
+| enumMembers ',' enumMember
+| enumMember
+;
+
+enumMember:
+ deprecated_opt identifier
+ {
+ OUString id(convertName($2));
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ rtl::Reference<unoidl::detail::SourceProviderEnumTypeEntityPad> pad(
+ getCurrentPad<unoidl::detail::SourceProviderEnumTypeEntityPad>(data));
+ sal_Int32 v;
+ if (pad->members.empty()) {
+ v = 0;
+ } else {
+ v = pad->members.back().value;
+ if (v == SAL_MAX_INT32) {
+ error(
+ @2, yyscanner,
+ ("enum " + data->currentName + " member " + id
+ + " would have out-of-range value 2^31"));
+ YYERROR;
+ }
+ ++v;
+ }
+ pad->members.emplace_back(id, v, annotations($1));
+ }
+| deprecated_opt identifier '=' expr
+ {
+ OUString id(convertName($2));
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ rtl::Reference<unoidl::detail::SourceProviderEnumTypeEntityPad> pad(
+ getCurrentPad<unoidl::detail::SourceProviderEnumTypeEntityPad>(data));
+ sal_Int32 v;
+ switch ($4.type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ if ($4.ival < SAL_MIN_INT32 || $4.ival > SAL_MAX_INT32) {
+ error(
+ @4, yyscanner,
+ ("out-of-range enum " + data->currentName + " member " + id
+ + " value " + OUString::number($4.ival)));
+ YYERROR;
+ }
+ v = static_cast<sal_Int32>($4.ival);
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ if ($4.uval > SAL_MAX_INT32) {
+ error(
+ @4, yyscanner,
+ ("out-of-range enum " + data->currentName + " member " + id
+ + " value " + OUString::number($4.uval)));
+ YYERROR;
+ }
+ v = static_cast<sal_Int32>($4.uval);
+ break;
+ default:
+ error(
+ @4, yyscanner,
+ ("non-integer enum " + data->currentName + " member " + id
+ + " value"));
+ YYERROR;
+ break;
+ }
+ pad->members.emplace_back(id, v, annotations($1));
+ }
+;
+
+plainStructDefn:
+ deprecated_opt published_opt TOK_STRUCT identifier singleInheritance_opt
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ data->publishedContext = $2;
+ convertToCurrentName(data, $4);
+ OUString baseName;
+ rtl::Reference<unoidl::PlainStructTypeEntity> baseEnt;
+ if ($5 != nullptr) {
+ baseName = convertName($5);
+ unoidl::detail::SourceProviderEntity const * p;
+ if (findEntity(
+ @5, yyscanner, data, false, &baseName, &p, nullptr, nullptr)
+ == FOUND_ERROR)
+ {
+ YYERROR;
+ }
+ if (p == nullptr || !p->entity.is()
+ || p->entity->getSort() != unoidl::Entity::SORT_PLAIN_STRUCT_TYPE)
+ {
+ error(
+ @5, yyscanner,
+ ("plain struct type " + data->currentName + " base "
+ + baseName
+ + " does not resolve to an existing plain struct type"));
+ YYERROR;
+ }
+ baseEnt = static_cast<unoidl::PlainStructTypeEntity *>(
+ p->entity.get());
+ if ($2 && !baseEnt->isPublished()) {
+ error(
+ @5, yyscanner,
+ ("published plain struct type " + data->currentName + " base "
+ + baseName + " is unpublished"));
+ YYERROR;
+ }
+ }
+ if (!data->entities.emplace(
+ data->currentName,
+ unoidl::detail::SourceProviderEntity(
+ new unoidl::detail::SourceProviderPlainStructTypeEntityPad(
+ $2, baseName, baseEnt))).
+ second)
+ {
+ error(@4, yyscanner, "multiple entities named " + data->currentName);
+ YYERROR;
+ }
+ }
+ '{' structMembers '}' ';'
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ unoidl::detail::SourceProviderEntity * ent = getCurrentEntity(data);
+ unoidl::detail::SourceProviderPlainStructTypeEntityPad * pad =
+ dynamic_cast<
+ unoidl::detail::SourceProviderPlainStructTypeEntityPad *>(
+ ent->pad.get());
+ assert(pad != nullptr);
+ ent->entity = new unoidl::PlainStructTypeEntity(
+ pad->isPublished(), pad->baseName, std::move(pad->members), annotations($1));
+ ent->pad.clear();
+ clearCurrentState(data);
+ }
+;
+
+polymorphicStructTemplateDefn:
+ deprecated_opt published_opt TOK_STRUCT identifier '<'
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ data->publishedContext = $2;
+ convertToCurrentName(data, $4);
+ if (!data->entities.emplace(
+ data->currentName,
+ unoidl::detail::SourceProviderEntity(
+ new unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad(
+ $2))).
+ second)
+ {
+ error(@4, yyscanner, "multiple entities named " + data->currentName);
+ YYERROR;
+ }
+ }
+ typeParameters '>' '{' structMembers '}' ';'
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ unoidl::detail::SourceProviderEntity * ent = getCurrentEntity(data);
+ unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad *
+ pad = dynamic_cast<
+ unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad *>(
+ ent->pad.get());
+ assert(pad != nullptr);
+ ent->entity = new unoidl::PolymorphicStructTypeTemplateEntity(
+ pad->isPublished(), std::move(pad->typeParameters), std::move(pad->members),
+ annotations($1));
+ ent->pad.clear();
+ clearCurrentState(data);
+ }
+;
+
+typeParameters:
+ typeParameters ',' identifier
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ rtl::Reference<unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad>
+ pad(getCurrentPad<unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad>(
+ data));
+ OUString id(convertName($3));
+ if (std::find(pad->typeParameters.begin(), pad->typeParameters.end(), id)
+ != pad->typeParameters.end())
+ {
+ error(
+ @3, yyscanner,
+ ("polymorphic struct type template " + data->currentName
+ + " type parameter " + id
+ + " has same identifier as another type parameter"));
+ YYERROR;
+ }
+ pad->typeParameters.push_back(id);
+ }
+| identifier
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ rtl::Reference<unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad>
+ pad(getCurrentPad<unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad>(
+ data));
+ OUString id(convertName($1));
+ assert(pad->typeParameters.empty());
+ pad->typeParameters.push_back(id);
+ }
+;
+
+exceptionDefn:
+ deprecated_opt published_opt TOK_EXCEPTION identifier singleInheritance_opt
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ data->publishedContext = $2;
+ convertToCurrentName(data, $4);
+ OUString baseName;
+ rtl::Reference<unoidl::ExceptionTypeEntity> baseEnt;
+ if ($5 != nullptr) {
+ baseName = convertName($5);
+ unoidl::detail::SourceProviderEntity const * p;
+ if (findEntity(
+ @5, yyscanner, data, false, &baseName, &p, nullptr, nullptr)
+ == FOUND_ERROR)
+ {
+ YYERROR;
+ }
+ if (p == nullptr || !p->entity.is()
+ || p->entity->getSort() != unoidl::Entity::SORT_EXCEPTION_TYPE)
+ {
+ error(
+ @5, yyscanner,
+ ("exception type " + data->currentName + " base " + baseName
+ + " does not resolve to an existing exception type"));
+ YYERROR;
+ }
+ baseEnt = static_cast<unoidl::ExceptionTypeEntity *>(
+ p->entity.get());
+ if ($2 && !baseEnt->isPublished()) {
+ error(
+ @5, yyscanner,
+ ("published exception type " + data->currentName + " base "
+ + baseName + " is unpublished"));
+ YYERROR;
+ }
+ }
+ if (!data->entities.emplace(
+ data->currentName,
+ unoidl::detail::SourceProviderEntity(
+ new unoidl::detail::SourceProviderExceptionTypeEntityPad(
+ $2, baseName, baseEnt))).
+ second)
+ {
+ error(@4, yyscanner, "multiple entities named " + data->currentName);
+ YYERROR;
+ }
+ }
+ '{' structMembers '}' ';'
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ unoidl::detail::SourceProviderEntity * ent = getCurrentEntity(data);
+ unoidl::detail::SourceProviderExceptionTypeEntityPad * pad =
+ dynamic_cast<unoidl::detail::SourceProviderExceptionTypeEntityPad *>(
+ ent->pad.get());
+ assert(pad != nullptr);
+ ent->entity = new unoidl::ExceptionTypeEntity(
+ pad->isPublished(), pad->baseName, std::move(pad->members), annotations($1));
+ ent->pad.clear();
+ clearCurrentState(data);
+ }
+;
+
+structMembers:
+ structMembers structMember
+| /* empty */
+;
+
+structMember:
+ deprecated_opt type identifier ';'
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ unoidl::detail::SourceProviderType t(*$2);
+ delete $2;
+ OUString id(convertName($3));
+ switch (t.type) {
+ case unoidl::detail::SourceProviderType::TYPE_VOID:
+ case unoidl::detail::SourceProviderType::TYPE_EXCEPTION:
+ error(
+ @2, yyscanner,
+ ("illegal struct/exception type " + data->currentName
+ + " direct member " + id + " type"));
+ YYERROR;
+ break;
+ default:
+ break;
+ }
+ if (t.type != unoidl::detail::SourceProviderType::TYPE_PARAMETER
+ && t.getName() == data->currentName) // no need to worry about typedef
+ {
+ error(
+ @2, yyscanner,
+ ("struct/exception type " + data->currentName + " direct member "
+ + id + " has same type as the type itself"));
+ YYERROR;
+ }
+ if (checkInstantiatedPolymorphicStructTypeArgument(t, data->currentName))
+ {
+ error(
+ @2, yyscanner,
+ ("struct/exception type " + data->currentName + " direct member "
+ + id
+ + (" has instantiated polymorphic struct type that uses the type"
+ " itself as an argument")));
+ YYERROR;
+ }
+ if (nameHasSameIdentifierAs(data->currentName, id)) {
+ error(
+ @3, yyscanner,
+ ("struct/exception type " + data->currentName + " direct member "
+ + id + " has same unqualified identifier as the type itself"));
+ YYERROR;
+ }
+ unoidl::detail::SourceProviderEntity * ent = getCurrentEntity(data);
+ unoidl::detail::SourceProviderPlainStructTypeEntityPad * p1 =
+ dynamic_cast<unoidl::detail::SourceProviderPlainStructTypeEntityPad *>(
+ ent->pad.get());
+ if (p1 != nullptr) {
+ for (const auto & i: p1->members) {
+ if (id == i.name) {
+ error(
+ @3, yyscanner,
+ ("plain struct type " + data->currentName
+ + " direct member " + id
+ + " has same identifier as another direct member"));
+ YYERROR;
+ }
+ }
+ if (p1->baseEntity.is()) {
+ OUString baseName(p1->baseName);
+ for (auto baseEnt(p1->baseEntity);;) {
+ if (nameHasSameIdentifierAs(baseName, id)) {
+ error(
+ @3, yyscanner,
+ ("plain struct type " + data->currentName
+ + " direct member " + id
+ + " has same unqalified identifier as base "
+ + baseName));
+ YYERROR;
+ }
+ for (auto & i: baseEnt->getDirectMembers()) {
+ if (id == i.name) {
+ error(
+ @3, yyscanner,
+ ("plain struct type " + data->currentName
+ + " direct member " + id
+ + " has same identifier as a member of base "
+ + baseName));
+ YYERROR;
+ }
+ }
+ baseName = baseEnt->getDirectBase();
+ if (baseName.isEmpty()) {
+ break;
+ }
+ unoidl::detail::SourceProviderEntity const * p;
+ if (findEntity(
+ @2, yyscanner, data, false, &baseName, &p, nullptr,
+ nullptr)
+ == FOUND_ERROR)
+ {
+ YYERROR;
+ }
+ if (p == nullptr || !p->entity.is()
+ || (p->entity->getSort()
+ != unoidl::Entity::SORT_PLAIN_STRUCT_TYPE))
+ {
+ error(
+ @2, yyscanner,
+ ("inconsistent type manager: plain struct type "
+ + data->currentName + " base " + baseName
+ + (" does not resolve to an existing plain struct"
+ " type")));
+ YYERROR;
+ }
+ baseEnt = static_cast<unoidl::PlainStructTypeEntity *>(
+ p->entity.get());
+ }
+ }
+ p1->members.emplace_back(id, t.getName(), annotations($1));
+ } else {
+ unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad *
+ p2 = dynamic_cast<unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad *>(
+ ent->pad.get());
+ if (p2 != nullptr) {
+ for (const auto & i: p2->members) {
+ if (id == i.name) {
+ error(
+ @3, yyscanner,
+ ("polymorphic struct type template "
+ + data->currentName + " direct member " + id
+ + " has same identifier as another direct member"));
+ YYERROR;
+ }
+ }
+ p2->members.emplace_back(
+ id, t.getName(),
+ t.type == unoidl::detail::SourceProviderType::TYPE_PARAMETER,
+ annotations($1));
+ } else {
+ unoidl::detail::SourceProviderExceptionTypeEntityPad * p3
+ = dynamic_cast<unoidl::detail::SourceProviderExceptionTypeEntityPad *>(
+ ent->pad.get());
+ assert(p3 != nullptr);
+ for (const auto & i: p3->members) {
+ if (id == i.name) {
+ error(
+ @3, yyscanner,
+ ("exception type " + data->currentName
+ + " direct member " + id
+ + " has same identifier as another direct member"));
+ YYERROR;
+ }
+ }
+ if (p3->baseEntity.is()) {
+ OUString baseName(p3->baseName);
+ for (auto baseEnt(p3->baseEntity);;) {
+ if (nameHasSameIdentifierAs(baseName, id)) {
+ error(
+ @3, yyscanner,
+ ("exception type " + data->currentName
+ + " direct member " + id
+ + " has same unqalified identifier as base "
+ + baseName));
+ YYERROR;
+ }
+ for (auto & i: baseEnt->getDirectMembers()) {
+ if (id == i.name) {
+ error(
+ @3, yyscanner,
+ ("exception type " + data->currentName
+ + " direct member " + id
+ + " has same identifier as a member of base "
+ + baseName));
+ YYERROR;
+ }
+ }
+ baseName = baseEnt->getDirectBase();
+ if (baseName.isEmpty()) {
+ break;
+ }
+ unoidl::detail::SourceProviderEntity const * p;
+ if (findEntity(
+ @2, yyscanner, data, false, &baseName, &p,
+ nullptr, nullptr)
+ == FOUND_ERROR)
+ {
+ YYERROR;
+ }
+ if (p == nullptr || !p->entity.is()
+ || (p->entity->getSort()
+ != unoidl::Entity::SORT_EXCEPTION_TYPE))
+ {
+ error(
+ @2, yyscanner,
+ ("inconsistent type manager: exception type "
+ + data->currentName + " base " + baseName
+ + (" does not resolve to an existing exception"
+ " type")));
+ YYERROR;
+ }
+ baseEnt = static_cast<unoidl::ExceptionTypeEntity *>(
+ p->entity.get());
+ }
+ }
+ p3->members.emplace_back(id, t.getName(), annotations($1));
+ }
+ }
+ }
+;
+
+interfaceDefn:
+ deprecated_opt published_opt TOK_INTERFACE identifier singleInheritance_opt
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ data->publishedContext = $2;
+ convertToCurrentName(data, $4);
+ OUString baseName;
+ rtl::Reference<unoidl::InterfaceTypeEntity> baseEnt;
+ if ($5 != nullptr) {
+ baseName = convertName($5);
+ unoidl::detail::SourceProviderEntity const * p;
+ if (findEntity(
+ @5, yyscanner, data, true, &baseName, &p, nullptr, nullptr)
+ == FOUND_ERROR)
+ {
+ YYERROR;
+ }
+ if (p == nullptr || !p->entity.is()
+ || p->entity->getSort() != unoidl::Entity::SORT_INTERFACE_TYPE)
+ {
+ error(
+ @5, yyscanner,
+ ("interface type " + data->currentName + " direct base "
+ + baseName
+ + " does not resolve to an existing interface type"));
+ YYERROR;
+ }
+ baseEnt = static_cast<unoidl::InterfaceTypeEntity *>(p->entity.get());
+ if ($2 && !baseEnt->isPublished()) {
+ error(
+ @5, yyscanner,
+ ("published interface type " + data->currentName
+ + " direct base " + baseName + " is unpublished"));
+ YYERROR;
+ }
+ }
+ std::map<OUString, unoidl::detail::SourceProviderEntity>::iterator i(
+ data->entities.find(data->currentName));
+ if (i != data->entities.end()) {
+ switch (i->second.kind) {
+ case unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL:
+ break;
+ case unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL:
+ if (!$2) {
+ error(
+ @4, yyscanner,
+ ("unpublished interface type " + data->currentName
+ + " has been declared published"));
+ YYERROR;
+ }
+ break;
+ default:
+ error(
+ @4, yyscanner,
+ "multiple entities named " + data->currentName);
+ YYERROR;
+ break;
+ }
+ }
+ rtl::Reference<unoidl::detail::SourceProviderInterfaceTypeEntityPad> pad(
+ new unoidl::detail::SourceProviderInterfaceTypeEntityPad(
+ $2, baseEnt.is()));
+ if (baseEnt.is()
+ && !pad->addDirectBase(
+ @4, yyscanner, data,
+ unoidl::detail::SourceProviderInterfaceTypeEntityPad::DirectBase(
+ baseName, baseEnt, std::vector<OUString>()),
+ false))
+ {
+ YYERROR;
+ }
+ data->entities[data->currentName] = unoidl::detail::SourceProviderEntity(
+ pad);
+ }
+ '{' interfaceMembers '}' ';'
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ unoidl::detail::SourceProviderEntity * ent = getCurrentEntity(data);
+ unoidl::detail::SourceProviderInterfaceTypeEntityPad * pad =
+ dynamic_cast<unoidl::detail::SourceProviderInterfaceTypeEntityPad *>(
+ ent->pad.get());
+ assert(pad != nullptr);
+ if (pad->directMandatoryBases.empty()
+ && data->currentName != "com.sun.star.uno.XInterface")
+ {
+ OUString base(".com.sun.star.uno.XInterface");
+ unoidl::detail::SourceProviderEntity const * p;
+ if (findEntity(@4, yyscanner, data, true, &base, &p, nullptr, nullptr)
+ == FOUND_ERROR)
+ {
+ YYERROR;
+ }
+ if (p == nullptr || !p->entity.is()
+ || p->entity->getSort() != unoidl::Entity::SORT_INTERFACE_TYPE)
+ {
+ error(
+ @3, yyscanner,
+ ("interface type " + data->currentName
+ + " implicit direct base " + base
+ + " does not resolve to an existing interface type"));
+ YYERROR;
+ }
+ if (!pad->addDirectBase(
+ @3, yyscanner, data,
+ unoidl::detail::SourceProviderInterfaceTypeEntityPad::DirectBase(
+ base,
+ static_cast<unoidl::InterfaceTypeEntity *>(
+ p->entity.get()),
+ std::vector<OUString>()),
+ false))
+ {
+ YYERROR;
+ }
+ }
+ std::vector<unoidl::AnnotatedReference> mbases;
+ for (auto & i: pad->directMandatoryBases) {
+ mbases.emplace_back(i.name, std::move(i.annotations));
+ }
+ std::vector<unoidl::AnnotatedReference> obases;
+ for (auto & i: pad->directOptionalBases) {
+ obases.emplace_back(i.name, std::move(i.annotations));
+ }
+ ent->entity = new unoidl::InterfaceTypeEntity(
+ pad->isPublished(), std::move(mbases), std::move(obases), std::move(pad->directAttributes),
+ std::move(pad->directMethods), annotations($1));
+ ent->pad.clear();
+ clearCurrentState(data);
+ }
+;
+
+interfaceMembers:
+ interfaceMembers interfaceMember
+| /* empty */
+;
+
+interfaceMember:
+ interfaceBase
+| interfaceAttribute
+| interfaceMethod
+;
+
+interfaceBase:
+ deprecated_opt flagSection_opt TOK_INTERFACE name ';'
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ OUString name(convertName($4));
+ rtl::Reference<unoidl::detail::SourceProviderInterfaceTypeEntityPad> pad(
+ getCurrentPad<unoidl::detail::SourceProviderInterfaceTypeEntityPad>(
+ data));
+ if (pad->singleBase) {
+ error(
+ @3, yyscanner,
+ "single-inheritance interface cannot have additional bases");
+ YYERROR;
+ }
+ if (($2 & ~unoidl::detail::FLAG_OPTIONAL) != 0) {
+ error(
+ @2, yyscanner,
+ "interface base can only be flagged as [optional]");
+ YYERROR;
+ }
+ bool opt = ($2 & unoidl::detail::FLAG_OPTIONAL) != 0;
+ OUString orgName(name);
+ unoidl::detail::SourceProviderEntity const * p;
+ bool typedefed = false;
+ if (findEntity(@4, yyscanner, data, true, &name, &p, &typedefed, nullptr)
+ == FOUND_ERROR)
+ {
+ YYERROR;
+ }
+ if (p == nullptr || !p->entity.is()
+ || p->entity->getSort() != unoidl::Entity::SORT_INTERFACE_TYPE)
+ {
+ error(
+ @4, yyscanner,
+ ("interface type " + data->currentName + " direct base " + name
+ + " does not resolve to an existing interface type"));
+ YYERROR;
+ }
+ if (typedefed) {
+ error(
+ @4, yyscanner,
+ ("interface type " + data->currentName + " direct base " + orgName
+ + " is a typedef"));
+ YYERROR;
+ }
+ rtl::Reference<unoidl::InterfaceTypeEntity> ent(
+ static_cast<unoidl::InterfaceTypeEntity *>(p->entity.get()));
+ if (data->publishedContext && !ent->isPublished()) {
+ error(
+ @4, yyscanner,
+ ("published interface type " + data->currentName + " direct base "
+ + name + " is unpublished"));
+ YYERROR;
+ }
+ if (!pad->addDirectBase(
+ @4, yyscanner, data,
+ unoidl::detail::SourceProviderInterfaceTypeEntityPad::DirectBase(
+ name, ent, annotations($1)),
+ opt))
+ {
+ YYERROR;
+ }
+ }
+;
+
+interfaceAttribute:
+ deprecated_opt flagSection type identifier
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ unoidl::detail::SourceProviderType t(*$3);
+ delete $3;
+ OUString id(convertName($4));
+ if (($2 & unoidl::detail::FLAG_ATTRIBUTE) == 0) {
+ error(
+ @2, yyscanner,
+ "interface attribute must be flagged as [attribute]");
+ YYERROR;
+ }
+ if (($2
+ & ~(unoidl::detail::FLAG_ATTRIBUTE | unoidl::detail::FLAG_BOUND
+ | unoidl::detail::FLAG_READONLY))
+ != 0)
+ {
+ error(
+ @2, yyscanner,
+ ("interface attribute can only be flagged as [attribute,"
+ " bound, readonly]"));
+ YYERROR;
+ }
+ switch (t.type) {
+ case unoidl::detail::SourceProviderType::TYPE_VOID:
+ case unoidl::detail::SourceProviderType::TYPE_EXCEPTION:
+ error(
+ @3, yyscanner,
+ ("illegal interface type " + data->currentName
+ + " direct attribute " + id + " type"));
+ YYERROR;
+ break;
+ default:
+ break;
+ }
+ rtl::Reference<unoidl::detail::SourceProviderInterfaceTypeEntityPad> pad(
+ getCurrentPad<unoidl::detail::SourceProviderInterfaceTypeEntityPad>(
+ data));
+ if (!pad->addDirectMember(@4, yyscanner, data, id)) {
+ YYERROR;
+ }
+ pad->directAttributes.emplace_back(
+ id, t.getName(), ($2 & unoidl::detail::FLAG_BOUND) != 0,
+ ($2 & unoidl::detail::FLAG_READONLY) != 0,
+ std::vector<OUString>(), std::vector<OUString>(), annotations($1));
+ }
+ attributeAccessDecls_opt ';'
+;
+
+attributeAccessDecls_opt:
+ '{' attributeAccessDecls '}'
+| /* empty */
+;
+
+attributeAccessDecls:
+ attributeAccessDecls attributeAccessDecl
+ {
+ if (($1 & $2) != 0) {
+ error(
+ @2, yyscanner, "duplicate get/set attribute access declaration");
+ YYERROR;
+ }
+ $$ = unoidl::detail::SourceProviderAccessDecls($1 | $2);
+ }
+| /* empty */ { $$ = unoidl::detail::SourceProviderAccessDecls(0); }
+;
+
+attributeAccessDecl:
+ TOK_GET exceptionSpec ';'
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ rtl::Reference<unoidl::detail::SourceProviderInterfaceTypeEntityPad>
+ pad(getCurrentPad<unoidl::detail::SourceProviderInterfaceTypeEntityPad>(
+ data));
+ assert(!pad->directAttributes.empty());
+ pad->directAttributes.back().getExceptions = *$2;
+ delete $2;
+ $$ = unoidl::detail::ACCESS_DECL_GET;
+ }
+| TOK_SET exceptionSpec ';'
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ rtl::Reference<unoidl::detail::SourceProviderInterfaceTypeEntityPad>
+ pad(getCurrentPad<unoidl::detail::SourceProviderInterfaceTypeEntityPad>(
+ data));
+ assert(!pad->directAttributes.empty());
+ pad->directAttributes.back().setExceptions = *$2;
+ delete $2;
+ if (pad->directAttributes.back().readOnly) {
+ error(
+ @1, yyscanner,
+ ("interface type " + data->currentName
+ + " direct read-only attribute "
+ + pad->directAttributes.back().name
+ + " cannot have set access declaration"));
+ YYERROR;
+ }
+ $$ = unoidl::detail::ACCESS_DECL_SET;
+ }
+;
+
+interfaceMethod:
+ deprecated_opt type identifier
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ unoidl::detail::SourceProviderType t(*$2);
+ delete $2;
+ OUString id(convertName($3));
+ if (t.type == unoidl::detail::SourceProviderType::TYPE_EXCEPTION) {
+ error(
+ @3, yyscanner,
+ ("illegal interface type " + data->currentName
+ + " direct method " + id + " return type"));
+ YYERROR;
+ }
+ rtl::Reference<unoidl::detail::SourceProviderInterfaceTypeEntityPad> pad(
+ getCurrentPad<unoidl::detail::SourceProviderInterfaceTypeEntityPad>(
+ data));
+ if (!pad->addDirectMember(@3, yyscanner, data, id)) {
+ YYERROR;
+ }
+ pad->directMethods.emplace_back(
+ id, t.getName(),
+ std::vector<unoidl::InterfaceTypeEntity::Method::Parameter>(),
+ std::vector<OUString>(), annotations($1));
+ }
+ '(' methodParams_opt ')' exceptionSpec_opt ';'
+ {
+ if ($8 != nullptr) {
+ unoidl::detail::SourceProviderScannerData * data
+ = yyget_extra(yyscanner);
+ rtl::Reference<unoidl::detail::SourceProviderInterfaceTypeEntityPad>
+ pad(getCurrentPad<unoidl::detail::SourceProviderInterfaceTypeEntityPad>(
+ data));
+ assert(!pad->directMethods.empty());
+ pad->directMethods.back().exceptions = *$8;
+ delete $8;
+ }
+ }
+;
+
+methodParams_opt:
+ methodParams
+| /* empty */
+;
+
+methodParams:
+ methodParams ',' methodParam
+| methodParam
+;
+
+methodParam:
+ '[' direction ']' type identifier
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ unoidl::detail::SourceProviderType t(*$4);
+ delete $4;
+ OUString id(convertName($5));
+ rtl::Reference<unoidl::detail::SourceProviderInterfaceTypeEntityPad>
+ pad(getCurrentPad<unoidl::detail::SourceProviderInterfaceTypeEntityPad>(
+ data));
+ assert(!pad->directMethods.empty());
+ switch (t.type) {
+ case unoidl::detail::SourceProviderType::TYPE_VOID:
+ case unoidl::detail::SourceProviderType::TYPE_EXCEPTION:
+ error(
+ @4, yyscanner,
+ ("illegal interface type " + data->currentName
+ + " direct method " + pad->directMethods.back().name
+ + " parameter " + id + " type"));
+ YYERROR;
+ break;
+ default:
+ break;
+ }
+ for (const auto & i: pad->directMethods.back().parameters) {
+ if (id == i.name) {
+ error(
+ @5, yyscanner,
+ ("interface type " + data->currentName + " direct method "
+ + pad->directMethods.back().name + " parameter " + id
+ + " has same identifier as another parameter"));
+ YYERROR;
+ }
+ }
+ pad->directMethods.back().parameters.emplace_back(id, t.getName(), $2);
+ }
+;
+
+direction:
+ TOK_IN { $$ = unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN; }
+| TOK_OUT
+ { $$ = unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_OUT; }
+| TOK_INOUT
+ { $$ = unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN_OUT; }
+;
+
+typedefDefn:
+ deprecated_opt published_opt TOK_TYPEDEF type identifier ';'
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ data->publishedContext = $2;
+ unoidl::detail::SourceProviderType t(*$4);
+ delete $4;
+ OUString name(convertToFullName(data, $5));
+ // There is no good reason to forbid typedefs to VOID, to instantiated
+ // polymorphic struct types, and to exception types, but some old client
+ // code of registry data expects this typedef restriction (like the
+ // assert(false) default in handleTypedef in
+ // codemaker/source/javamaker/javatype.cxx), so forbid them for now:
+ switch (t.type) {
+ case unoidl::detail::SourceProviderType::TYPE_VOID:
+ case unoidl::detail::SourceProviderType::TYPE_EXCEPTION:
+ case unoidl::detail::SourceProviderType::TYPE_INSTANTIATED_POLYMORPHIC_STRUCT:
+ error(@4, yyscanner, "bad typedef type");
+ YYERROR;
+ break;
+ case unoidl::detail::SourceProviderType::TYPE_ENUM:
+ case unoidl::detail::SourceProviderType::TYPE_PLAIN_STRUCT:
+ case unoidl::detail::SourceProviderType::TYPE_INTERFACE:
+ if ($2) {
+ bool unpub = false;
+ switch (t.entity->kind) {
+ case unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL:
+ unpub = true;
+ break;
+ case unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL:
+ break;
+ case unoidl::detail::SourceProviderEntity::KIND_MODULE:
+ O3TL_UNREACHABLE;
+ default:
+ assert(t.entity->entity.is() || t.entity->pad.is());
+ unpub
+ = !(t.entity->entity.is()
+ ? static_cast<unoidl::PublishableEntity *>(
+ t.entity->entity.get())->isPublished()
+ : t.entity->pad->isPublished());
+ break;
+ }
+ if (unpub) {
+ error(
+ @4, yyscanner,
+ "published typedef " + name + " type is unpublished");
+ YYERROR;
+ }
+ }
+ break;
+ case unoidl::detail::SourceProviderType::TYPE_PARAMETER:
+ O3TL_UNREACHABLE;
+ default:
+ break;
+ }
+ if (!data->entities.emplace(
+ name,
+ unoidl::detail::SourceProviderEntity(
+ unoidl::detail::SourceProviderEntity::KIND_LOCAL,
+ new unoidl::TypedefEntity(
+ $2, t.getName(), annotations($1)))).
+ second)
+ {
+ error(@5, yyscanner, "multiple entities named " + name);
+ YYERROR;
+ }
+ clearCurrentState(data);
+ }
+;
+
+constantGroupDefn:
+ deprecated_opt published_opt TOK_CONSTANTS identifier
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ data->publishedContext = $2;
+ convertToCurrentName(data, $4);
+ if (!data->entities.emplace(
+ data->currentName,
+ unoidl::detail::SourceProviderEntity(
+ new unoidl::detail::SourceProviderConstantGroupEntityPad(
+ $2))).
+ second)
+ {
+ error(@4, yyscanner, "multiple entities named " + data->currentName);
+ YYERROR;
+ }
+ }
+ '{' constants '}' ';'
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ unoidl::detail::SourceProviderEntity * ent = getCurrentEntity(data);
+ unoidl::detail::SourceProviderConstantGroupEntityPad * pad =
+ dynamic_cast<unoidl::detail::SourceProviderConstantGroupEntityPad *>(
+ ent->pad.get());
+ assert(pad != nullptr);
+ ent->entity = new unoidl::ConstantGroupEntity(
+ pad->isPublished(), std::move(pad->members), annotations($1));
+ ent->pad.clear();
+ clearCurrentState(data);
+ }
+;
+
+constants:
+ constants constant
+| /* empty */
+;
+
+constant:
+ deprecated_opt TOK_CONST type identifier '=' expr ';'
+ {
+ OUString id(convertName($4));
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ rtl::Reference<unoidl::detail::SourceProviderConstantGroupEntityPad> pad(
+ getCurrentPad<unoidl::detail::SourceProviderConstantGroupEntityPad>(
+ data));
+ unoidl::detail::SourceProviderType t(*$3);
+ delete $3;
+ unoidl::ConstantValue v(false); // dummy value
+ switch (t.type) {
+ case unoidl::detail::SourceProviderType::TYPE_BOOLEAN:
+ if ($6.type != unoidl::detail::SourceProviderExpr::TYPE_BOOL) {
+ error(
+ @6, yyscanner,
+ ("bad value of boolean-typed constant " + data->currentName
+ + "." + id));
+ YYERROR;
+ }
+ v = unoidl::ConstantValue($6.bval);
+ break;
+ case unoidl::detail::SourceProviderType::TYPE_BYTE:
+ switch ($6.type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ if ($6.ival < SAL_MIN_INT8 || $6.ival > SAL_MAX_INT8) {
+ error(
+ @6, yyscanner,
+ ("out-of-range byte-typed constant " + data->currentName
+ + "." + id + " value " + OUString::number($6.ival)));
+ YYERROR;
+ }
+ v = unoidl::ConstantValue(static_cast<sal_Int8>($6.ival));
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ if ($6.uval > SAL_MAX_INT8) {
+ error(
+ @6, yyscanner,
+ ("out-of-range byte-typed constant " + data->currentName
+ + "." + id + " value " + OUString::number($6.uval)));
+ YYERROR;
+ }
+ v = unoidl::ConstantValue(static_cast<sal_Int8>($6.uval));
+ break;
+ default:
+ error(
+ @6, yyscanner,
+ ("bad value of byte-typed constant " + data->currentName + "."
+ + id));
+ YYERROR;
+ break;
+ }
+ break;
+ case unoidl::detail::SourceProviderType::TYPE_SHORT:
+ switch ($6.type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ if ($6.ival < SAL_MIN_INT16 || $6.ival > SAL_MAX_INT16) {
+ error(
+ @6, yyscanner,
+ ("out-of-range short-typed constant " + data->currentName
+ + "." + id + " value " + OUString::number($6.ival)));
+ YYERROR;
+ }
+ v = unoidl::ConstantValue(static_cast<sal_Int16>($6.ival));
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ if ($6.uval > SAL_MAX_INT16) {
+ error(
+ @6, yyscanner,
+ ("out-of-range short-typed constant " + data->currentName
+ + "." + id + " value " + OUString::number($6.uval)));
+ YYERROR;
+ }
+ v = unoidl::ConstantValue(static_cast<sal_Int16>($6.uval));
+ break;
+ default:
+ error(
+ @6, yyscanner,
+ ("bad value of short-typed constant " + data->currentName
+ + "." + id));
+ YYERROR;
+ break;
+ }
+ break;
+ case unoidl::detail::SourceProviderType::TYPE_UNSIGNED_SHORT:
+ switch ($6.type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ if ($6.ival < 0 || $6.ival > SAL_MAX_UINT16) {
+ error(
+ @6, yyscanner,
+ ("out-of-range unsigned-short-typed constant "
+ + data->currentName + "." + id + " value "
+ + OUString::number($6.ival)));
+ YYERROR;
+ }
+ v = unoidl::ConstantValue(static_cast<sal_uInt16>($6.ival));
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ if ($6.uval > SAL_MAX_UINT16) {
+ error(
+ @6, yyscanner,
+ ("out-of-range unsigned-short-typed constant "
+ + data->currentName + "." + id + " value "
+ + OUString::number($6.uval)));
+ YYERROR;
+ }
+ v = unoidl::ConstantValue(static_cast<sal_uInt16>($6.uval));
+ break;
+ default:
+ error(
+ @6, yyscanner,
+ ("bad value of unsigned-short-typed constant "
+ + data->currentName + "." + id));
+ YYERROR;
+ break;
+ }
+ break;
+ case unoidl::detail::SourceProviderType::TYPE_LONG:
+ switch ($6.type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ if ($6.ival < SAL_MIN_INT32 || $6.ival > SAL_MAX_INT32) {
+ error(
+ @6, yyscanner,
+ ("out-of-range long-typed constant " + data->currentName
+ + "." + id + " value " + OUString::number($6.ival)));
+ YYERROR;
+ }
+ v = unoidl::ConstantValue(static_cast<sal_Int32>($6.ival));
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ if ($6.uval > SAL_MAX_INT32) {
+ error(
+ @6, yyscanner,
+ ("out-of-range long-typed constant " + data->currentName
+ + "." + id + " value " + OUString::number($6.uval)));
+ YYERROR;
+ }
+ v = unoidl::ConstantValue(static_cast<sal_Int32>($6.uval));
+ break;
+ default:
+ error(
+ @6, yyscanner,
+ ("bad value of long-typed constant " + data->currentName
+ + "." + id));
+ YYERROR;
+ break;
+ }
+ break;
+ case unoidl::detail::SourceProviderType::TYPE_UNSIGNED_LONG:
+ switch ($6.type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ if ($6.ival < 0 || $6.ival > SAL_MAX_UINT32) {
+ error(
+ @6, yyscanner,
+ ("out-of-range unsigned-long-typed constant "
+ + data->currentName + "." + id + " value "
+ + OUString::number($6.ival)));
+ YYERROR;
+ }
+ v = unoidl::ConstantValue(static_cast<sal_uInt32>($6.ival));
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ if ($6.uval > SAL_MAX_UINT32) {
+ error(
+ @6, yyscanner,
+ ("out-of-range unsigned-long-typed constant "
+ + data->currentName + "." + id + " value "
+ + OUString::number($6.uval)));
+ YYERROR;
+ }
+ v = unoidl::ConstantValue(static_cast<sal_uInt32>($6.uval));
+ break;
+ default:
+ error(
+ @6, yyscanner,
+ ("bad value of unsigned-long-typed constant "
+ + data->currentName + "." + id));
+ YYERROR;
+ break;
+ }
+ break;
+ case unoidl::detail::SourceProviderType::TYPE_HYPER:
+ switch ($6.type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ v = unoidl::ConstantValue($6.ival);
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ if ($6.uval > SAL_MAX_INT64) {
+ error(
+ @6, yyscanner,
+ ("out-of-range hyper-typed constant " + data->currentName
+ + "." + id + " value " + OUString::number($6.uval)));
+ YYERROR;
+ }
+ v = unoidl::ConstantValue(static_cast<sal_Int64>($6.uval));
+ break;
+ default:
+ error(
+ @6, yyscanner,
+ ("bad value of hyper-typed constant " + data->currentName
+ + "." + id));
+ YYERROR;
+ break;
+ }
+ break;
+ case unoidl::detail::SourceProviderType::TYPE_UNSIGNED_HYPER:
+ switch ($6.type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ if ($6.ival < 0) {
+ error(
+ @6, yyscanner,
+ ("out-of-range unsigned-hyper-typed constant "
+ + data->currentName + "." + id + " value "
+ + OUString::number($6.ival)));
+ YYERROR;
+ }
+ v = unoidl::ConstantValue(static_cast<sal_uInt64>($6.ival));
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ v = unoidl::ConstantValue($6.uval);
+ break;
+ default:
+ error(
+ @6, yyscanner,
+ ("bad value of unsigned-hyper-typed constant "
+ + data->currentName + "." + id));
+ YYERROR;
+ break;
+ }
+ break;
+ case unoidl::detail::SourceProviderType::TYPE_FLOAT:
+ switch ($6.type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_BOOL:
+ error(
+ @6, yyscanner,
+ ("bad boolean value of float-typed constant "
+ + data->currentName + "." + id));
+ YYERROR;
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ v = unoidl::ConstantValue(static_cast<float>($6.ival));
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ v = unoidl::ConstantValue(static_cast<float>($6.uval));
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_FLOAT:
+ v = unoidl::ConstantValue(static_cast<float>($6.fval));
+ break;
+ }
+ break;
+ case unoidl::detail::SourceProviderType::TYPE_DOUBLE:
+ switch ($6.type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_BOOL:
+ error(
+ @6, yyscanner,
+ ("bad boolean value of double-typed constant "
+ + data->currentName + "." + id));
+ YYERROR;
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ v = unoidl::ConstantValue(static_cast<double>($6.ival));
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ v = unoidl::ConstantValue(static_cast<double>($6.uval));
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_FLOAT:
+ v = unoidl::ConstantValue($6.fval);
+ break;
+ }
+ break;
+ default:
+ error(
+ @3, yyscanner,
+ "bad type for constant " + data->currentName + "." + id);
+ YYERROR;
+ break;
+ }
+ pad->members.emplace_back(id, v, annotations($1));
+ }
+;
+
+singleInterfaceBasedServiceDefn:
+ deprecated_opt published_opt TOK_SERVICE identifier singleInheritance
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ data->publishedContext = $2;
+ convertToCurrentName(data, $4);
+ OUString base(convertName($5));
+ unoidl::detail::SourceProviderEntity const * p;
+ if (findEntity(@5, yyscanner, data, false, &base, &p, nullptr, nullptr)
+ == FOUND_ERROR)
+ {
+ YYERROR;
+ }
+ bool ifcBase = false;
+ bool pubBase = false;
+ if (p != nullptr) {
+ switch (p->kind) {
+ case unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL:
+ ifcBase = true;
+ pubBase = false;
+ break;
+ case unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL:
+ ifcBase = true;
+ pubBase = true;
+ break;
+ default:
+ if (p->entity.is()
+ && (p->entity->getSort()
+ == unoidl::Entity::SORT_INTERFACE_TYPE))
+ {
+ ifcBase = true;
+ pubBase = static_cast<unoidl::InterfaceTypeEntity *>(
+ p->entity.get())->isPublished();
+ }
+ break;
+ }
+ }
+ if (!ifcBase) {
+ error(
+ @5, yyscanner,
+ ("single-interface--based service " + data->currentName + " base "
+ + base + " does not resolve to an interface type"));
+ YYERROR;
+ }
+ if ($2 && !pubBase) {
+ error(
+ @5, yyscanner,
+ ("published single-interface--based service " + data->currentName
+ + " base " + base + " is unpublished"));
+ YYERROR;
+ }
+ if (!data->entities.emplace(
+ data->currentName,
+ unoidl::detail::SourceProviderEntity(
+ new unoidl::detail::SourceProviderSingleInterfaceBasedServiceEntityPad(
+ $2, base))).
+ second)
+ {
+ error(@4, yyscanner, "multiple entities named " + data->currentName);
+ YYERROR;
+ }
+ }
+ ctors_opt ';'
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ unoidl::detail::SourceProviderEntity * ent = getCurrentEntity(data);
+ unoidl::detail::SourceProviderSingleInterfaceBasedServiceEntityPad * pad =
+ dynamic_cast<unoidl::detail::SourceProviderSingleInterfaceBasedServiceEntityPad *>(
+ ent->pad.get());
+ assert(pad != nullptr);
+ std::vector<unoidl::SingleInterfaceBasedServiceEntity::Constructor> ctors;
+ if ($7) {
+ for (const auto & i: pad->constructors) {
+ std::vector<unoidl::SingleInterfaceBasedServiceEntity::Constructor::Parameter> parms;
+ for (auto & j: i.parameters) {
+ parms.emplace_back(j.name, j.type.getName(), j.rest);
+ }
+ ctors.push_back(
+ unoidl::SingleInterfaceBasedServiceEntity::Constructor(
+ i.name, std::vector(parms), std::vector(i.exceptions), std::vector(i.annotations)));
+ }
+ } else {
+ assert(pad->constructors.empty());
+ ctors.push_back(
+ unoidl::SingleInterfaceBasedServiceEntity::Constructor());
+ }
+ ent->entity = new unoidl::SingleInterfaceBasedServiceEntity(
+ pad->isPublished(), pad->base, std::move(ctors), annotations($1));
+ ent->pad.clear();
+ clearCurrentState(data);
+ }
+;
+
+ctors_opt:
+ '{' ctors '}' { $$ = true; }
+| /* empty */ { $$ = false; }
+;
+
+ctors:
+ ctors ctor
+| /* empty */
+;
+
+ctor:
+ deprecated_opt identifier
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ OUString id(convertName($2));
+ rtl::Reference<unoidl::detail::SourceProviderSingleInterfaceBasedServiceEntityPad>
+ pad(getCurrentPad<unoidl::detail::SourceProviderSingleInterfaceBasedServiceEntityPad>(
+ data));
+ for (const auto & i: pad->constructors) {
+ if (id == i.name) {
+ error(
+ @2, yyscanner,
+ ("single-interface--based service " + data->currentName
+ + " constructor " + id
+ + " has same identifier as another constructor"));
+ YYERROR;
+ }
+ }
+ pad->constructors.push_back(
+ unoidl::detail::SourceProviderSingleInterfaceBasedServiceEntityPad::Constructor(
+ id, annotations($1)));
+ }
+ '(' ctorParams_opt ')' exceptionSpec_opt ';'
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ rtl::Reference<unoidl::detail::SourceProviderSingleInterfaceBasedServiceEntityPad>
+ pad(getCurrentPad<unoidl::detail::SourceProviderSingleInterfaceBasedServiceEntityPad>(
+ data));
+ assert(!pad->constructors.empty());
+ if ($7 != nullptr) {
+ pad->constructors.back().exceptions = *$7;
+ delete $7;
+ }
+ for (auto i(pad->constructors.begin()); i != pad->constructors.end() - 1;
+ ++i)
+ {
+ if (i->parameters.size()
+ == pad->constructors.back().parameters.size())
+ {
+ bool same = true;
+ for (auto
+ j(i->parameters.begin()),
+ k(pad->constructors.back().parameters.begin());
+ j != i->parameters.end(); ++j, ++k)
+ {
+ if (!j->type.equals(k->type) || j->rest != k->rest) {
+ same = false;
+ break;
+ }
+ }
+ if (same) {
+ error(
+ @2, yyscanner,
+ ("single-interface--based service " + data->currentName
+ + " constructor " + pad->constructors.back().name
+ + " has similar paramete list to constructor "
+ + i->name));
+ YYERROR;
+ }
+ }
+ }
+ }
+;
+
+ctorParams_opt:
+ ctorParams
+| /* empty */
+;
+
+ctorParams:
+ ctorParams ',' ctorParam
+| ctorParam
+;
+
+ctorParam:
+ '[' direction ']' type ellipsis_opt identifier
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ unoidl::detail::SourceProviderType t(*$4);
+ delete $4;
+ OUString id(convertName($6));
+ rtl::Reference<unoidl::detail::SourceProviderSingleInterfaceBasedServiceEntityPad>
+ pad(getCurrentPad<unoidl::detail::SourceProviderSingleInterfaceBasedServiceEntityPad>(
+ data));
+ assert(!pad->constructors.empty());
+ if ($2 != unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN) {
+ error(
+ @4, yyscanner,
+ ("single-interface--based service " + data->currentName
+ + " constructor " + pad->constructors.back().name + " parameter "
+ + id + " direction must be [in]"));
+ YYERROR;
+ }
+ switch (t.type) {
+ case unoidl::detail::SourceProviderType::TYPE_VOID:
+ case unoidl::detail::SourceProviderType::TYPE_EXCEPTION:
+ error(
+ @4, yyscanner,
+ ("illegal single-interface--based service " + data->currentName
+ + " constructor " + pad->constructors.back().name + " parameter "
+ + id + " type"));
+ YYERROR;
+ break;
+ default:
+ break;
+ }
+ if ($5) {
+ if (t.type != unoidl::detail::SourceProviderType::TYPE_ANY) {
+ error(
+ @4, yyscanner,
+ ("illegal single-interface--based service "
+ + data->currentName + " constructor "
+ + pad->constructors.back().name + " rest parameter " + id
+ + " non-any type"));
+ YYERROR;
+ }
+ if (!pad->constructors.back().parameters.empty()) {
+ error(
+ @5, yyscanner,
+ ("single-interface--based service " + data->currentName
+ + " constructor " + pad->constructors.back().name
+ + " rest parameter " + id + " must be first parameter"));
+ YYERROR;
+ }
+ } else if (!pad->constructors.back().parameters.empty()
+ && pad->constructors.back().parameters.back().rest)
+ {
+ error(
+ @1, yyscanner,
+ ("single-interface--based service " + data->currentName
+ + " constructor " + pad->constructors.back().name
+ + " rest parameter must be last parameter"));
+ YYERROR;
+ }
+ for (const auto & i: pad->constructors.back().parameters) {
+ if (id == i.name) {
+ error(
+ @6, yyscanner,
+ ("single-interface--based service " + data->currentName
+ + " constructor " + pad->constructors.back().name
+ + " parameter " + id
+ + " has same identifier as another parameter"));
+ YYERROR;
+ }
+ }
+ pad->constructors.back().parameters.push_back(
+ unoidl::detail::SourceProviderSingleInterfaceBasedServiceEntityPad::Constructor::Parameter(
+ id, std::move(t), $5));
+ }
+;
+
+ellipsis_opt:
+ TOK_ELLIPSIS { $$ = true; }
+| /* empty */ { $$ = false; }
+
+accumulationBasedServiceDefn:
+ deprecated_opt published_opt TOK_SERVICE identifier
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ data->publishedContext = $2;
+ convertToCurrentName(data, $4);
+ if (!data->entities.emplace(
+ data->currentName,
+ unoidl::detail::SourceProviderEntity(
+ new unoidl::detail::SourceProviderAccumulationBasedServiceEntityPad(
+ $2))).
+ second)
+ {
+ error(@4, yyscanner, "multiple entities named " + data->currentName);
+ YYERROR;
+ }
+ }
+ '{' serviceMembers '}' ';'
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ unoidl::detail::SourceProviderEntity * ent = getCurrentEntity(data);
+ unoidl::detail::SourceProviderAccumulationBasedServiceEntityPad * pad =
+ dynamic_cast<unoidl::detail::SourceProviderAccumulationBasedServiceEntityPad *>(
+ ent->pad.get());
+ assert(pad != nullptr);
+ ent->entity = new unoidl::AccumulationBasedServiceEntity(
+ pad->isPublished(), std::move(pad->directMandatoryBaseServices),
+ std::move(pad->directOptionalBaseServices), std::move(pad->directMandatoryBaseInterfaces),
+ std::move(pad->directOptionalBaseInterfaces), std::move(pad->directProperties),
+ annotations($1));
+ ent->pad.clear();
+ clearCurrentState(data);
+ }
+;
+
+serviceMembers:
+ serviceMembers serviceMember
+| /* empty */
+;
+
+serviceMember:
+ serviceBase
+| serviceInterfaceBase
+| serviceProperty
+;
+
+serviceBase:
+ deprecated_opt flagSection_opt TOK_SERVICE name ';'
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ OUString name(convertName($4));
+ rtl::Reference<unoidl::detail::SourceProviderAccumulationBasedServiceEntityPad> pad(
+ getCurrentPad<unoidl::detail::SourceProviderAccumulationBasedServiceEntityPad>(
+ data));
+ if (($2 & ~unoidl::detail::FLAG_OPTIONAL) != 0) {
+ error(
+ @2, yyscanner,
+ "service base can only be flagged as [optional]");
+ YYERROR;
+ }
+ bool opt = ($2 & unoidl::detail::FLAG_OPTIONAL) != 0;
+ unoidl::detail::SourceProviderEntity const * p;
+ if (findEntity(@4, yyscanner, data, false, &name, &p, nullptr, nullptr)
+ == FOUND_ERROR)
+ {
+ YYERROR;
+ }
+ if (p == nullptr || !p->entity.is()
+ || (p->entity->getSort()
+ != unoidl::Entity::SORT_ACCUMULATION_BASED_SERVICE))
+ {
+ error(
+ @4, yyscanner,
+ ("accumulation-based service " + data->currentName
+ + " direct base service " + name
+ + " does not resolve to an accumulation-based service"));
+ YYERROR;
+ }
+ if (data->publishedContext
+ && !static_cast<unoidl::AccumulationBasedServiceEntity *>(
+ p->entity.get())->isPublished())
+ {
+ error(
+ @4, yyscanner,
+ ("published accumulation-based service " + data->currentName
+ + " direct base service " + name + " is unpublished"));
+ YYERROR;
+ }
+ std::vector<unoidl::AnnotatedReference> & v(
+ opt
+ ? pad->directOptionalBaseServices : pad->directMandatoryBaseServices);
+ for (const auto & i: v) {
+ if (name == i.name) {
+ error(
+ @4, yyscanner,
+ ("accumulation-based service " + data->currentName
+ + " duplicate direct base service " + name));
+ YYERROR;
+ }
+ }
+ v.emplace_back(name, annotations($1));
+ }
+;
+
+serviceInterfaceBase:
+ deprecated_opt flagSection_opt TOK_INTERFACE name ';'
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ OUString name(convertName($4));
+ rtl::Reference<unoidl::detail::SourceProviderAccumulationBasedServiceEntityPad> pad(
+ getCurrentPad<unoidl::detail::SourceProviderAccumulationBasedServiceEntityPad>(
+ data));
+ if (($2 & ~unoidl::detail::FLAG_OPTIONAL) != 0) {
+ error(
+ @2, yyscanner,
+ "interface base can only be flagged as [optional]");
+ YYERROR;
+ }
+ bool opt = ($2 & unoidl::detail::FLAG_OPTIONAL) != 0;
+ unoidl::detail::SourceProviderEntity const * p;
+ if (findEntity(@4, yyscanner, data, false, &name, &p, nullptr, nullptr)
+ == FOUND_ERROR)
+ {
+ YYERROR;
+ }
+ bool ifcBase = false;
+ bool pubBase = false;
+ if (p != nullptr) {
+ switch (p->kind) {
+ case unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL:
+ ifcBase = true;
+ pubBase = false;
+ break;
+ case unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL:
+ ifcBase = true;
+ pubBase = true;
+ break;
+ default:
+ if (p->entity.is()
+ && (p->entity->getSort()
+ == unoidl::Entity::SORT_INTERFACE_TYPE))
+ {
+ ifcBase = true;
+ pubBase = static_cast<unoidl::InterfaceTypeEntity *>(
+ p->entity.get())->isPublished();
+ }
+ break;
+ }
+ }
+ if (!ifcBase) {
+ error(
+ @4, yyscanner,
+ ("accumulation-based service " + data->currentName
+ + " direct base interface " + name
+ + " does not resolve to an interface type"));
+ YYERROR;
+ }
+ if (data->publishedContext && !opt && !pubBase) {
+ error(
+ @4, yyscanner,
+ ("published accumulation-based service " + data->currentName
+ + " direct base interface " + name + " is unpublished"));
+ YYERROR;
+ }
+ std::vector<unoidl::AnnotatedReference> & v(
+ opt
+ ? pad->directOptionalBaseInterfaces
+ : pad->directMandatoryBaseInterfaces);
+ for (const auto & i: v) {
+ if (name == i.name) {
+ error(
+ @4, yyscanner,
+ ("accumulation-based service " + data->currentName
+ + " duplicate direct base interface " + name));
+ YYERROR;
+ }
+ }
+ v.emplace_back(name, annotations($1));
+ }
+;
+
+serviceProperty:
+ deprecated_opt flagSection type identifier ';'
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ unoidl::detail::SourceProviderType t(*$3);
+ delete $3;
+ OUString id(convertName($4));
+ if (($2 & unoidl::detail::FLAG_PROPERTY) == 0) {
+ error(
+ @2, yyscanner,
+ ("accumulation-based service property must be flagged as"
+ " [property]"));
+ YYERROR;
+ }
+ if (($2
+ & ~(unoidl::detail::FLAG_BOUND | unoidl::detail::FLAG_CONSTRAINED
+ | unoidl::detail::FLAG_MAYBEAMBIGUOUS
+ | unoidl::detail::FLAG_MAYBEDEFAULT
+ | unoidl::detail::FLAG_MAYBEVOID | unoidl::detail::FLAG_OPTIONAL
+ | unoidl::detail::FLAG_PROPERTY | unoidl::detail::FLAG_READONLY
+ | unoidl::detail::FLAG_REMOVABLE
+ | unoidl::detail::FLAG_TRANSIENT))
+ != 0)
+ {
+ error(
+ @2, yyscanner,
+ ("accumulation-based service property can only be flagged as"
+ " [property, bound, constrained, maybeambiguous, maybedefault,"
+ " maybevoid, optional, readonly, removable, transient]"));
+ YYERROR;
+ }
+ int att = 0;
+ if (($2 & unoidl::detail::FLAG_BOUND) != 0) {
+ att |= unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_BOUND;
+ }
+ if (($2 & unoidl::detail::FLAG_CONSTRAINED) != 0) {
+ att |= unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_CONSTRAINED;
+ }
+ if (($2 & unoidl::detail::FLAG_MAYBEAMBIGUOUS) != 0) {
+ att |= unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_MAYBE_AMBIGUOUS;
+ }
+ if (($2 & unoidl::detail::FLAG_MAYBEDEFAULT) != 0) {
+ att |= unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_MAYBE_DEFAULT;
+ }
+ if (($2 & unoidl::detail::FLAG_MAYBEVOID) != 0) {
+ att |= unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_MAYBE_VOID;
+ }
+ if (($2 & unoidl::detail::FLAG_OPTIONAL) != 0) {
+ att |= unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_OPTIONAL;
+ }
+ if (($2 & unoidl::detail::FLAG_READONLY) != 0) {
+ att |= unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_READ_ONLY;
+ }
+ if (($2 & unoidl::detail::FLAG_REMOVABLE) != 0) {
+ att |= unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_REMOVABLE;
+ }
+ if (($2 & unoidl::detail::FLAG_TRANSIENT) != 0) {
+ att |= unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_TRANSIENT;
+ }
+ switch (t.type) {
+ case unoidl::detail::SourceProviderType::TYPE_VOID:
+ case unoidl::detail::SourceProviderType::TYPE_EXCEPTION:
+ error(
+ @3, yyscanner,
+ ("illegal accumulation-based service " + data->currentName
+ + " direct property " + id + " type"));
+ YYERROR;
+ break;
+ default:
+ break;
+ }
+ rtl::Reference<unoidl::detail::SourceProviderAccumulationBasedServiceEntityPad>
+ pad(getCurrentPad<unoidl::detail::SourceProviderAccumulationBasedServiceEntityPad>(
+ data));
+ for (const auto & i: pad->directProperties) {
+ if (id == i.name) {
+ error(
+ @4, yyscanner,
+ ("accumulation-based service " + data->currentName
+ + " duplicate direct property " + id));
+ YYERROR;
+ }
+ }
+ pad->directProperties.emplace_back(
+ id, t.getName(),
+ unoidl::AccumulationBasedServiceEntity::Property::Attributes(att),
+ annotations($1));
+ }
+;
+
+interfaceBasedSingletonDefn:
+ deprecated_opt published_opt TOK_SINGLETON identifier singleInheritance ';'
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ data->publishedContext = $2;
+ OUString name(convertToFullName(data, $4));
+ OUString base(convertName($5));
+ unoidl::detail::SourceProviderEntity const * p;
+ if (findEntity(@5, yyscanner, data, false, &base, &p, nullptr, nullptr)
+ == FOUND_ERROR)
+ {
+ YYERROR;
+ }
+ bool ifcBase = false;
+ bool pubBase = false;
+ if (p != nullptr) {
+ switch (p->kind) {
+ case unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL:
+ ifcBase = true;
+ pubBase = false;
+ break;
+ case unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL:
+ ifcBase = true;
+ pubBase = true;
+ break;
+ default:
+ if (p->entity.is()
+ && (p->entity->getSort()
+ == unoidl::Entity::SORT_INTERFACE_TYPE))
+ {
+ ifcBase = true;
+ pubBase = static_cast<unoidl::InterfaceTypeEntity *>(
+ p->entity.get())->isPublished();
+ }
+ break;
+ }
+ }
+ if (!ifcBase) {
+ error(
+ @5, yyscanner,
+ ("interface-based singleton " + name + " base " + base
+ + " does not resolve to an interface type"));
+ YYERROR;
+ }
+ if ($2 && !pubBase) {
+ error(
+ @5, yyscanner,
+ ("published interface-based singleton " + name + " base " + base
+ + " is unpublished"));
+ YYERROR;
+ }
+ if (!data->entities.emplace(
+ name,
+ unoidl::detail::SourceProviderEntity(
+ unoidl::detail::SourceProviderEntity::KIND_LOCAL,
+ new unoidl::InterfaceBasedSingletonEntity(
+ $2, base, annotations($1)))).
+ second)
+ {
+ error(@4, yyscanner, "multiple entities named " + name);
+ YYERROR;
+ }
+ clearCurrentState(data);
+ }
+;
+
+serviceBasedSingletonDefn:
+ deprecated_opt published_opt TOK_SINGLETON identifier '{' TOK_SERVICE name ';'
+ '}' ';'
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ data->publishedContext = $2;
+ OUString name(convertToFullName(data, $4));
+ OUString base(convertName($7));
+ unoidl::detail::SourceProviderEntity const * p;
+ if (findEntity(@7, yyscanner, data, false, &base, &p, nullptr, nullptr)
+ == FOUND_ERROR)
+ {
+ YYERROR;
+ }
+ if (p == nullptr
+ || !p->entity.is()
+ || (p->entity->getSort()
+ != unoidl::Entity::SORT_ACCUMULATION_BASED_SERVICE))
+ {
+ error(
+ @7, yyscanner,
+ ("service-based singleton " + name + " base " + base
+ + " does not resolve to an accumulation-based service"));
+ YYERROR;
+ }
+ if ($2
+ && !static_cast<unoidl::AccumulationBasedServiceEntity *>(
+ p->entity.get())->isPublished())
+ {
+ error(
+ @7, yyscanner,
+ ("published service-based singleton " + name + " base " + base
+ + " is unpublished"));
+ YYERROR;
+ }
+ if (!data->entities.emplace(
+ name,
+ unoidl::detail::SourceProviderEntity(
+ unoidl::detail::SourceProviderEntity::KIND_LOCAL,
+ new unoidl::ServiceBasedSingletonEntity(
+ $2, base, annotations($1)))).
+ second)
+ {
+ error(@4, yyscanner, "multiple entities named " + name);
+ YYERROR;
+ }
+ clearCurrentState(data);
+ }
+;
+
+singleInheritance_opt:
+ singleInheritance
+| /* empty */ { $$ = nullptr; }
+;
+
+singleInheritance: ':' name { $$ = $2; }
+;
+
+exceptionSpec_opt:
+ exceptionSpec
+| /* empty */ { $$ = nullptr; }
+;
+
+exceptionSpec: TOK_RAISES '(' exceptions ')' { $$ = $3; }
+;
+
+exceptions:
+ exceptions ',' name
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ OUString name(convertName($3));
+ unoidl::detail::SourceProviderEntity const * p;
+ if (findEntity(@3, yyscanner, data, false, &name, &p, nullptr, nullptr)
+ == FOUND_ERROR)
+ {
+ delete $1; /* see commented-out %destructor above */
+ YYERROR;
+ }
+ if (p == nullptr
+ || !p->entity.is()
+ || (p->entity->getSort() != unoidl::Entity::SORT_EXCEPTION_TYPE))
+ {
+ delete $1; /* see commented-out %destructor above */
+ error(
+ @3, yyscanner,
+ ("exception " + name + " does not resolve to an exception type"));
+ YYERROR;
+ }
+ if (data->publishedContext
+ && !(static_cast<unoidl::ExceptionTypeEntity *>(p->entity.get())
+ ->isPublished()))
+ {
+ delete $1; /* see commented-out %destructor above */
+ error(
+ @3, yyscanner,
+ ("unpublished exception " + name + " used in published context"));
+ YYERROR;
+ }
+ if (std::find($1->begin(), $1->end(), name) != $1->end()) {
+ delete $1; /* see commented-out %destructor above */
+ error(
+ @3, yyscanner, ("exception " + name + " listed more than once"));
+ YYERROR;
+ }
+ $1->push_back(name);
+ $$ = $1;
+ }
+| name
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ OUString name(convertName($1));
+ unoidl::detail::SourceProviderEntity const * p;
+ if (findEntity(@1, yyscanner, data, false, &name, &p, nullptr, nullptr)
+ == FOUND_ERROR)
+ {
+ YYERROR;
+ }
+ if (p == nullptr
+ || !p->entity.is()
+ || (p->entity->getSort() != unoidl::Entity::SORT_EXCEPTION_TYPE))
+ {
+ error(
+ @1, yyscanner,
+ ("exception " + name + " does not resolve to an exception type"));
+ YYERROR;
+ }
+ if (data->publishedContext
+ && !(static_cast<unoidl::ExceptionTypeEntity *>(p->entity.get())
+ ->isPublished()))
+ {
+ error(
+ @1, yyscanner,
+ ("unpublished exception " + name + " used in published context"));
+ YYERROR;
+ }
+ $$ = new std::vector<OUString>; $$->push_back(name);
+ }
+;
+
+interfaceDecl:
+ deprecated_opt/*ignored*/ published_opt TOK_INTERFACE identifier ';'
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ data->publishedContext = $2;
+ OUString name(convertToFullName(data, $4));
+ std::pair<std::map<OUString, unoidl::detail::SourceProviderEntity>::iterator, bool> p(
+ data->entities.emplace(
+ name,
+ unoidl::detail::SourceProviderEntity(
+ $2
+ ? unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL
+ : unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL)));
+ if (!p.second) {
+ switch (p.first->second.kind) {
+ case unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL:
+ if ($2) {
+ p.first->second.kind
+ = unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL;
+ }
+ break;
+ case unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL:
+ break;
+ default:
+ assert(p.first->second.entity.is());
+ if (p.first->second.entity->getSort()
+ != unoidl::Entity::SORT_INTERFACE_TYPE)
+ {
+ error(
+ @4, yyscanner,
+ "multiple entities named " + data->currentName);
+ YYERROR;
+ }
+ if ($2
+ && !static_cast<unoidl::InterfaceTypeEntity *>(
+ p.first->second.entity.get())->isPublished())
+ {
+ error(
+ @4, yyscanner,
+ ("published interface type declaration "
+ + data->currentName + " has been defined unpublished"));
+ YYERROR;
+ }
+ }
+ }
+ clearCurrentState(data);
+ }
+;
+
+published_opt:
+ TOK_PUBLISHED { $$ = true; }
+| /* empty */ { $$ = false; }
+;
+
+flagSection_opt:
+ flagSection
+| /* empty */ { $$ = unoidl::detail::SourceProviderFlags(0); }
+;
+
+flagSection: '[' flags ']' { $$ = $2; }
+;
+
+flags:
+ flags ',' flag
+ {
+ if (($1 & $3) != 0) {
+ error(@3, yyscanner, "duplicate flag " + flagName($3));
+ YYERROR;
+ }
+ $$ = unoidl::detail::SourceProviderFlags($1 | $3);
+ }
+| flag
+;
+
+flag:
+ TOK_ATTRIBUTE { $$ = unoidl::detail::FLAG_ATTRIBUTE; }
+| TOK_BOUND { $$ = unoidl::detail::FLAG_BOUND; }
+| TOK_CONSTRAINED { $$ = unoidl::detail::FLAG_CONSTRAINED; }
+| TOK_MAYBEAMBIGUOUS { $$ = unoidl::detail::FLAG_MAYBEAMBIGUOUS; }
+| TOK_MAYBEDEFAULT { $$ = unoidl::detail::FLAG_MAYBEDEFAULT; }
+| TOK_MAYBEVOID { $$ = unoidl::detail::FLAG_MAYBEVOID; }
+| TOK_OPTIONAL { $$ = unoidl::detail::FLAG_OPTIONAL; }
+| TOK_PROPERTY { $$ = unoidl::detail::FLAG_PROPERTY; }
+| TOK_READONLY { $$ = unoidl::detail::FLAG_READONLY; }
+| TOK_REMOVABLE { $$ = unoidl::detail::FLAG_REMOVABLE; }
+| TOK_TRANSIENT { $$ = unoidl::detail::FLAG_TRANSIENT; }
+;
+
+expr: orExpr
+;
+
+orExpr:
+ orExpr '|' xorExpr
+ {
+ if (!coerce(@1, yyscanner, &$1, &$3)) {
+ YYERROR;
+ }
+ switch ($1.type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ $$ = unoidl::detail::SourceProviderExpr::Int($1.ival | $3.ival);
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ $$ = unoidl::detail::SourceProviderExpr::Uint($1.uval | $3.uval);
+ break;
+ default:
+ error(@1, yyscanner, "arguments of non-integer type to \"|\"");
+ YYERROR;
+ break;
+ }
+ }
+| xorExpr
+;
+
+xorExpr:
+ xorExpr '^' andExpr
+ {
+ if (!coerce(@1, yyscanner, &$1, &$3)) {
+ YYERROR;
+ }
+ switch ($1.type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ $$ = unoidl::detail::SourceProviderExpr::Int($1.ival ^ $3.ival);
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ $$ = unoidl::detail::SourceProviderExpr::Uint($1.uval ^ $3.uval);
+ break;
+ default:
+ error(@1, yyscanner, "arguments of non-integer type to \"^\"");
+ YYERROR;
+ break;
+ }
+ }
+| andExpr
+;
+
+andExpr:
+ andExpr '&' shiftExpr
+ {
+ if (!coerce(@1, yyscanner, &$1, &$3)) {
+ YYERROR;
+ }
+ switch ($1.type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ $$ = unoidl::detail::SourceProviderExpr::Int($1.ival & $3.ival);
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ $$ = unoidl::detail::SourceProviderExpr::Uint($1.uval & $3.uval);
+ break;
+ default:
+ error(@1, yyscanner, "arguments of non-integer type to \"&\"");
+ YYERROR;
+ break;
+ }
+ }
+| shiftExpr
+;
+
+shiftExpr:
+ shiftExpr TOK_LEFTSHIFT addExpr
+ {
+ int n;
+ switch ($3.type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ if ($3.ival < 0 || $3.ival > 63) {
+ error(
+ @3, yyscanner,
+ ("out-of-range shift argument " + OUString::number($3.ival)
+ + " to \"<<\" "));
+ YYERROR;
+ }
+ n = static_cast<int>($3.ival);
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ if ($3.uval > 63) {
+ error(
+ @3, yyscanner,
+ ("out-of-range shift argument " + OUString::number($3.uval)
+ + " to \"<<\" "));
+ YYERROR;
+ }
+ n = static_cast<int>($3.uval);
+ break;
+ default:
+ error(@3, yyscanner, "right argument of non-integer type to \"<<\"");
+ YYERROR;
+ }
+ switch ($1.type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ if ($1.ival < 0) {
+ error(
+ @1, yyscanner,
+ ("cannot left-shift negative argument "
+ + OUString::number($1.ival)));
+ YYERROR;
+ }
+ $$ = unoidl::detail::SourceProviderExpr::Int($1.ival << n);
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ $$ = unoidl::detail::SourceProviderExpr::Uint($1.uval << n);
+ break;
+ default:
+ error(@1, yyscanner, "left argument of non-integer type to \"<<\"");
+ YYERROR;
+ break;
+ }
+ }
+| shiftExpr TOK_RIGHTSHIFT addExpr
+ {
+ int n;
+ switch ($3.type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ if ($3.ival < 0 || $3.ival > 63) {
+ error(
+ @3, yyscanner,
+ ("out-of-range shift argument " + OUString::number($3.ival)
+ + " to \">>\" "));
+ YYERROR;
+ }
+ n = static_cast<int>($3.ival);
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ if ($3.uval > 63) {
+ error(
+ @3, yyscanner,
+ ("out-of-range shift argument " + OUString::number($3.uval)
+ + " to \">>\" "));
+ YYERROR;
+ }
+ n = static_cast<int>($3.uval);
+ break;
+ default:
+ error(@3, yyscanner, "right argument of non-integer type to \">>\"");
+ YYERROR;
+ break;
+ }
+ switch ($1.type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ $$ = unoidl::detail::SourceProviderExpr::Int($1.ival >> n);
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ $$ = unoidl::detail::SourceProviderExpr::Uint($1.uval >> n);
+ break;
+ default:
+ error(@1, yyscanner, "left argument of non-integer type to \">>\"");
+ YYERROR;
+ break;
+ }
+ }
+| addExpr
+;
+
+addExpr:
+ addExpr '+' multExpr
+ {
+ if (!coerce(@1, yyscanner, &$1, &$3)) {
+ YYERROR;
+ }
+ switch ($1.type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_BOOL:
+ error(@1, yyscanner, "arguments of boolean type to binary \"+\"");
+ YYERROR;
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ $$ = unoidl::detail::SourceProviderExpr::Int($1.ival + $3.ival); //TODO: overflow
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ $$ = unoidl::detail::SourceProviderExpr::Uint($1.uval + $3.uval); //TODO: overflow
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_FLOAT:
+ $$ = unoidl::detail::SourceProviderExpr::Float($1.fval + $3.fval);
+ break;
+ }
+ }
+| addExpr '-' multExpr
+ {
+ if (!coerce(@1, yyscanner, &$1, &$3)) {
+ YYERROR;
+ }
+ switch ($1.type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_BOOL:
+ error(@1, yyscanner, "arguments of boolean type to binary \"-\"");
+ YYERROR;
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ $$ = unoidl::detail::SourceProviderExpr::Int($1.ival - $3.ival); //TODO: overflow
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ $$ = unoidl::detail::SourceProviderExpr::Uint($1.uval - $3.uval); //TODO: overflow
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_FLOAT:
+ $$ = unoidl::detail::SourceProviderExpr::Float($1.fval - $3.fval);
+ break;
+ }
+ }
+| multExpr
+;
+
+multExpr:
+ multExpr '*' unaryExpr
+ {
+ if (!coerce(@1, yyscanner, &$1, &$3)) {
+ YYERROR;
+ }
+ switch ($1.type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_BOOL:
+ error(@1, yyscanner, "arguments of boolean type to \"*\"");
+ YYERROR;
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ $$ = unoidl::detail::SourceProviderExpr::Int($1.ival * $3.ival); //TODO: overflow
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ $$ = unoidl::detail::SourceProviderExpr::Uint($1.uval * $3.uval); //TODO: overflow
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_FLOAT:
+ $$ = unoidl::detail::SourceProviderExpr::Float($1.fval * $3.fval);
+ break;
+ }
+ }
+| multExpr '/' unaryExpr
+ {
+ if (!coerce(@1, yyscanner, &$1, &$3)) {
+ YYERROR;
+ }
+ switch ($1.type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_BOOL:
+ error(@1, yyscanner, "arguments of boolean type to \"/\"");
+ YYERROR;
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ if ($3.ival == 0) {
+ error(@3, yyscanner, "cannot divide by zero");
+ YYERROR;
+ }
+ $$ = unoidl::detail::SourceProviderExpr::Int($1.ival / $3.ival);
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ if ($3.uval == 0) {
+ error(@3, yyscanner, "cannot divide by zero");
+ YYERROR;
+ }
+ $$ = unoidl::detail::SourceProviderExpr::Uint($1.uval / $3.uval);
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_FLOAT:
+ if ($3.fval == 0) {
+ error(@3, yyscanner, "cannot divide by zero");
+ YYERROR;
+ }
+ $$ = unoidl::detail::SourceProviderExpr::Float($1.fval - $3.fval);
+ break;
+ }
+ }
+| multExpr '%' unaryExpr
+ {
+ if (!coerce(@1, yyscanner, &$1, &$3)) {
+ YYERROR;
+ }
+ switch ($1.type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ if ($3.ival == 0) {
+ error(@3, yyscanner, "cannot divide by zero");
+ YYERROR;
+ }
+ $$ = unoidl::detail::SourceProviderExpr::Int($1.ival % $3.ival);
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ if ($3.uval == 0) {
+ error(@3, yyscanner, "cannot divide by zero");
+ YYERROR;
+ }
+ $$ = unoidl::detail::SourceProviderExpr::Uint($1.uval % $3.uval);
+ break;
+ default:
+ error(@1, yyscanner, "arguments of non-integer type to \"%\"");
+ YYERROR;
+ break;
+ }
+ }
+| unaryExpr
+;
+
+unaryExpr:
+ '+' primaryExpr
+ {
+ if ($2.type == unoidl::detail::SourceProviderExpr::TYPE_BOOL) {
+ error(@2, yyscanner, "argument of boolean type to unary \"+\"");
+ YYERROR;
+ }
+ $$ = $2;
+ }
+| '-' primaryExpr
+ {
+ switch ($2.type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_BOOL:
+ error(@2, yyscanner, "argument of boolean type to unary \"-\"");
+ YYERROR;
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ if ($2.ival == SAL_MIN_INT64) {
+ error(@2, yyscanner, "cannot negate -2^63");
+ YYERROR;
+ }
+ $$ = unoidl::detail::SourceProviderExpr::Int(-$2.ival);
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ if ($2.uval == SAL_CONST_UINT64(0x8000000000000000)) {
+ $$ = unoidl::detail::SourceProviderExpr::Int(SAL_MIN_INT64);
+ } else {
+ if ($2.uval > SAL_MAX_INT64) {
+ error(
+ @2, yyscanner,
+ ("cannot negate out-of-range value "
+ + OUString::number($2.uval)));
+ YYERROR;
+ }
+ $$ = unoidl::detail::SourceProviderExpr::Int(
+ -static_cast<sal_Int64>($2.uval));
+ }
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_FLOAT:
+ $$ = unoidl::detail::SourceProviderExpr::Float(-$2.fval);
+ break;
+ }
+ }
+| '~' primaryExpr
+ {
+ switch ($2.type) {
+ case unoidl::detail::SourceProviderExpr::TYPE_INT:
+ $$ = unoidl::detail::SourceProviderExpr::Int(~$2.ival);
+ break;
+ case unoidl::detail::SourceProviderExpr::TYPE_UINT:
+ $$ = unoidl::detail::SourceProviderExpr::Uint(~$2.uval);
+ break;
+ default:
+ error(@2, yyscanner, "argument of non-integer type to \"~\"");
+ YYERROR;
+ break;
+ }
+ }
+| primaryExpr
+;
+
+primaryExpr:
+ '(' expr ')' { $$ = $2; }
+| TOK_FALSE { $$ = unoidl::detail::SourceProviderExpr::Bool(false); }
+| TOK_TRUE { $$ = unoidl::detail::SourceProviderExpr::Bool(true); }
+| TOK_INTEGER { $$ = unoidl::detail::SourceProviderExpr::Uint($1); }
+| TOK_FLOATING { $$ = unoidl::detail::SourceProviderExpr::Float($1); }
+| name
+ {
+ OUString name(convertName($1));
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ unoidl::ConstantValue v(false); // dummy value
+ bool found = false;
+ bool unpub = false;
+ sal_Int32 i = name.lastIndexOf('.');
+ if (i == -1) {
+ rtl::Reference<unoidl::detail::SourceProviderEntityPad> pad(
+ getCurrentEntity(data)->pad);
+ unoidl::detail::SourceProviderEnumTypeEntityPad * p1 = dynamic_cast<
+ unoidl::detail::SourceProviderEnumTypeEntityPad *>(pad.get());
+ if (p1 != nullptr) {
+ for (const auto & j: p1->members) {
+ if (j.name == name) {
+ v = unoidl::ConstantValue(j.value);
+ found = true;
+ break;
+ }
+ }
+ } else {
+ unoidl::detail::SourceProviderConstantGroupEntityPad * p2
+ = dynamic_cast<
+ unoidl::detail::SourceProviderConstantGroupEntityPad *>(
+ pad.get());
+ if (p2 != nullptr) {
+ for (const auto & j: p2->members) {
+ if (j.name == name) {
+ v = j.value;
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ OUString scope(name.copy(0, i));
+ unoidl::detail::SourceProviderEntity const * ent;
+ if (findEntity(
+ @1, yyscanner, data, false, &scope, &ent, nullptr, nullptr)
+ == FOUND_ERROR)
+ {
+ YYERROR;
+ }
+ if (ent != nullptr) {
+ std::u16string_view id(name.subView(i + 1));
+ // No need to check for enum members here, as they cannot be
+ // referenced in expressions by qualified name (TODO: is that true?):
+ if (ent->entity.is()) {
+ if (ent->entity->getSort()
+ == unoidl::Entity::SORT_CONSTANT_GROUP)
+ {
+ std::vector<unoidl::ConstantGroupEntity::Member> const &
+ mems(
+ static_cast<unoidl::ConstantGroupEntity *>(
+ ent->entity.get())->
+ getMembers());
+ for (auto & j: mems) {
+ if (j.name == id) {
+ v = j.value;
+ found = true;
+ unpub
+ = !static_cast<unoidl::ConstantGroupEntity *>(
+ ent->entity.get())->isPublished();
+ break;
+ }
+ }
+ }
+ } else if (ent->pad.is()) {
+ unoidl::detail::SourceProviderConstantGroupEntityPad * pad
+ = dynamic_cast<
+ unoidl::detail::SourceProviderConstantGroupEntityPad *>(
+ ent->pad.get());
+ if (pad != nullptr) {
+ for (const auto & j: pad->members) {
+ if (j.name == id) {
+ v = j.value;
+ found = true;
+ unpub = !ent->pad->isPublished();
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (!found) {
+ error(
+ @1, yyscanner,
+ (name
+ + (" does not resolve to neither a constant nor an unqualified"
+ " enum member")));
+ YYERROR;
+ }
+ if (data->publishedContext && unpub) {
+ error(
+ @1, yyscanner,
+ "unpublished value " + name + " used in published context");
+ YYERROR;
+ }
+ switch (v.type) {
+ case unoidl::ConstantValue::TYPE_BOOLEAN:
+ $$ = unoidl::detail::SourceProviderExpr::Bool(v.booleanValue);
+ break;
+ case unoidl::ConstantValue::TYPE_BYTE:
+ $$ = unoidl::detail::SourceProviderExpr::Int(v.byteValue);
+ break;
+ case unoidl::ConstantValue::TYPE_SHORT:
+ $$ = unoidl::detail::SourceProviderExpr::Int(v.shortValue);
+ break;
+ case unoidl::ConstantValue::TYPE_UNSIGNED_SHORT:
+ $$ = unoidl::detail::SourceProviderExpr::Uint(v.unsignedShortValue);
+ break;
+ case unoidl::ConstantValue::TYPE_LONG:
+ $$ = unoidl::detail::SourceProviderExpr::Int(v.longValue);
+ break;
+ case unoidl::ConstantValue::TYPE_UNSIGNED_LONG:
+ $$ = unoidl::detail::SourceProviderExpr::Uint(v.unsignedLongValue);
+ break;
+ case unoidl::ConstantValue::TYPE_HYPER:
+ $$ = unoidl::detail::SourceProviderExpr::Int(v.hyperValue);
+ break;
+ case unoidl::ConstantValue::TYPE_UNSIGNED_HYPER:
+ $$ = unoidl::detail::SourceProviderExpr::Uint(v.unsignedHyperValue);
+ break;
+ case unoidl::ConstantValue::TYPE_FLOAT:
+ $$ = unoidl::detail::SourceProviderExpr::Float(v.floatValue);
+ break;
+ case unoidl::ConstantValue::TYPE_DOUBLE:
+ $$ = unoidl::detail::SourceProviderExpr::Float(v.doubleValue);
+ break;
+ }
+ }
+;
+
+typeArguments:
+ typeArguments ',' type
+ {
+ unoidl::detail::SourceProviderType t(*$3);
+ delete $3;
+ if (!checkTypeArgument(@3, yyscanner, t)) {
+ delete $1; /* see commented-out %destructor above */
+ YYERROR;
+ }
+ $1->push_back(t);
+ $$ = $1;
+ }
+| type
+ {
+ unoidl::detail::SourceProviderType t(*$1);
+ delete $1;
+ if (!checkTypeArgument(@1, yyscanner, t)) {
+ YYERROR;
+ }
+ $$ = new std::vector<unoidl::detail::SourceProviderType>;
+ $$->push_back(t);
+ }
+;
+
+type:
+ TOK_VOID
+ {
+ $$ = new unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_VOID);
+ }
+| TOK_BOOLEAN
+ {
+ $$ = new unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_BOOLEAN);
+ }
+| TOK_BYTE
+ {
+ $$ = new unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_BYTE);
+ }
+| TOK_SHORT
+ {
+ $$ = new unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_SHORT);
+ }
+| TOK_UNSIGNED TOK_SHORT
+ {
+ $$ = new unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_UNSIGNED_SHORT);
+ }
+| TOK_LONG
+ {
+ $$ = new unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_LONG);
+ }
+| TOK_UNSIGNED TOK_LONG
+ {
+ $$ = new unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_UNSIGNED_LONG);
+ }
+| TOK_HYPER
+ {
+ $$ = new unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_HYPER);
+ }
+| TOK_UNSIGNED TOK_HYPER
+ {
+ $$ = new unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_UNSIGNED_HYPER);
+ }
+| TOK_FLOAT
+ {
+ $$ = new unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_FLOAT);
+ }
+| TOK_DOUBLE
+ {
+ $$ = new unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_DOUBLE);
+ }
+| TOK_CHAR
+ {
+ $$ = new unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_CHAR);
+ }
+| TOK_STRING
+ {
+ $$ = new unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_STRING);
+ }
+| TOK_TYPE
+ {
+ $$ = new unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_TYPE);
+ }
+| TOK_ANY
+ {
+ $$ = new unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_ANY);
+ }
+| TOK_SEQUENCE '<' type '>'
+ {
+ switch ($3->type) {
+ case unoidl::detail::SourceProviderType::TYPE_VOID:
+ case unoidl::detail::SourceProviderType::TYPE_EXCEPTION:
+ case unoidl::detail::SourceProviderType::TYPE_PARAMETER: //TODO?
+ error(@3, yyscanner, "illegal sequence type component type");
+ YYERROR;
+ break;
+ default:
+ break;
+ }
+ $$ = new unoidl::detail::SourceProviderType($3);
+ delete $3;
+ }
+| name
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ OUString name(convertName($1));
+ bool done = false;
+ if (name.indexOf('.') == -1 && !data->currentName.isEmpty()) {
+ unoidl::detail::SourceProviderEntity * ent = getCurrentEntity(data);
+ unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad *
+ pad = dynamic_cast<
+ unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad *>(
+ ent->pad.get());
+ if (pad != nullptr
+ && (std::find(
+ pad->typeParameters.begin(), pad->typeParameters.end(),
+ name)
+ != pad->typeParameters.end()))
+ {
+ $$ = new unoidl::detail::SourceProviderType(name);
+ done = true;
+ }
+ }
+ if (!done) {
+ unoidl::detail::SourceProviderEntity const * ent;
+ unoidl::detail::SourceProviderType t;
+ switch (findEntity(
+ @1, yyscanner, data, false, &name, &ent, nullptr, &t))
+ {
+ case FOUND_ERROR:
+ YYERROR;
+ break;
+ case FOUND_TYPE:
+ $$ = new unoidl::detail::SourceProviderType(t);
+ break;
+ case FOUND_ENTITY:
+ if (ent == nullptr) {
+ error(@1, yyscanner, "unknown entity " + name);
+ YYERROR;
+ }
+ bool ok = false;
+ switch (ent->kind) {
+ case unoidl::detail::SourceProviderEntity::KIND_LOCAL:
+ if (ent->pad.is()) {
+ if (data->publishedContext && !ent->pad->isPublished()) {
+ error(
+ @1, yyscanner,
+ ("unpublished entity " + name
+ + " used in published context"));
+ YYERROR;
+ }
+ if (dynamic_cast<unoidl::detail::SourceProviderEnumTypeEntityPad *>(
+ ent->pad.get())
+ != nullptr)
+ {
+ $$ = new unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_ENUM,
+ name, ent);
+ ok = true;
+ } else if (dynamic_cast<unoidl::detail::SourceProviderPlainStructTypeEntityPad *>(
+ ent->pad.get())
+ != nullptr)
+ {
+ $$ = new unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_PLAIN_STRUCT,
+ name, ent);
+ ok = true;
+ } else if (dynamic_cast<unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad *>(
+ ent->pad.get())
+ != nullptr)
+ {
+ error(
+ @1, yyscanner,
+ (("recursive reference to polymorphic struct type"
+ " template ")
+ + name));
+ YYERROR;
+ } else if (dynamic_cast<unoidl::detail::SourceProviderExceptionTypeEntityPad *>(
+ ent->pad.get())
+ != nullptr)
+ {
+ $$ = new unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_EXCEPTION,
+ name, ent);
+ ok = true;
+ } else if (dynamic_cast<unoidl::detail::SourceProviderInterfaceTypeEntityPad *>(
+ ent->pad.get())
+ != nullptr)
+ {
+ $$ = new unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_INTERFACE,
+ name, ent);
+ ok = true;
+ }
+ break;
+ }
+ assert(ent->entity.is());
+ [[fallthrough]];
+ case unoidl::detail::SourceProviderEntity::KIND_EXTERNAL:
+ if (data->publishedContext
+ && ent->entity->getSort() != unoidl::Entity::SORT_MODULE
+ && !static_cast<unoidl::PublishableEntity *>(
+ ent->entity.get())->isPublished())
+ {
+ error(
+ @1, yyscanner,
+ ("unpublished entity " + name
+ + " used in published context"));
+ YYERROR;
+ }
+ switch (ent->entity->getSort()) {
+ case unoidl::Entity::SORT_ENUM_TYPE:
+ $$ = new unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_ENUM, name,
+ ent);
+ ok = true;
+ break;
+ case unoidl::Entity::SORT_PLAIN_STRUCT_TYPE:
+ $$ = new unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_PLAIN_STRUCT,
+ name, ent);
+ ok = true;
+ break;
+ case unoidl::Entity::SORT_POLYMORPHIC_STRUCT_TYPE_TEMPLATE:
+ error(
+ @1, yyscanner,
+ ("polymorphic struct type template " + name
+ + " without type arguments"));
+ YYERROR;
+ break;
+ case unoidl::Entity::SORT_EXCEPTION_TYPE:
+ $$ = new unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_EXCEPTION,
+ name, ent);
+ ok = true;
+ break;
+ case unoidl::Entity::SORT_INTERFACE_TYPE:
+ $$ = new unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_INTERFACE,
+ name, ent);
+ ok = true;
+ break;
+ case unoidl::Entity::SORT_TYPEDEF:
+ O3TL_UNREACHABLE;
+ default:
+ break;
+ }
+ break;
+ case unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL:
+ if (data->publishedContext) {
+ error(
+ @1, yyscanner,
+ ("unpublished entity " + name
+ + " used in published context"));
+ YYERROR;
+ }
+ [[fallthrough]];
+ case unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL:
+ $$ = new unoidl::detail::SourceProviderType(
+ unoidl::detail::SourceProviderType::TYPE_INTERFACE, name,
+ ent);
+ ok = true;
+ break;
+ case unoidl::detail::SourceProviderEntity::KIND_MODULE:
+ assert(false && "this cannot happen");
+ }
+ if (!ok) {
+ error(@1, yyscanner, "non-type entity " + name);
+ YYERROR;
+ }
+ break;
+ }
+ }
+ }
+| name '<' typeArguments '>'
+ {
+ unoidl::detail::SourceProviderScannerData * data = yyget_extra(yyscanner);
+ OUString name(convertName($1));
+ std::vector<unoidl::detail::SourceProviderType> args(*$3);
+ delete $3;
+ unoidl::detail::SourceProviderEntity const * ent;
+ if (findEntity(@1, yyscanner, data, false, &name, &ent, nullptr, nullptr)
+ == FOUND_ERROR)
+ {
+ YYERROR;
+ }
+ if (ent == nullptr) {
+ error(@1, yyscanner, "unknown entity " + name);
+ YYERROR;
+ }
+ bool ok = false;
+ switch (ent->kind) {
+ case unoidl::detail::SourceProviderEntity::KIND_LOCAL:
+ if (ent->pad.is()) {
+ if (dynamic_cast<unoidl::detail::SourceProviderPolymorphicStructTypeTemplateEntityPad *>(
+ ent->pad.get())
+ != nullptr)
+ {
+ error(
+ @1, yyscanner,
+ (("recursive reference to polymorphic struct type"
+ " template ")
+ + name));
+ YYERROR;
+ }
+ break;
+ }
+ assert(ent->entity.is());
+ [[fallthrough]];
+ case unoidl::detail::SourceProviderEntity::KIND_EXTERNAL:
+ if (ent->entity->getSort()
+ == unoidl::Entity::SORT_POLYMORPHIC_STRUCT_TYPE_TEMPLATE)
+ {
+ rtl::Reference<unoidl::PolymorphicStructTypeTemplateEntity> e(
+ static_cast<unoidl::PolymorphicStructTypeTemplateEntity *>(
+ ent->entity.get()));
+ if (args.size() != e->getTypeParameters().size()) {
+ error(
+ @1, yyscanner,
+ ("bad number of polymorphic struct type template " + name
+ + " type arguments"));
+ YYERROR;
+ }
+ if (data->publishedContext && !e->isPublished()) {
+ error(
+ @1, yyscanner,
+ ("unpublished polymorphic struct type template " + name
+ + " used in published context"));
+ YYERROR;
+ }
+ $$ = new unoidl::detail::SourceProviderType(name, ent, std::move(args));
+ ok = true;
+ }
+ break;
+ case unoidl::detail::SourceProviderEntity::KIND_INTERFACE_DECL:
+ case unoidl::detail::SourceProviderEntity::KIND_PUBLISHED_INTERFACE_DECL:
+ break;
+ case unoidl::detail::SourceProviderEntity::KIND_MODULE:
+ assert(false && "this cannot happen");
+ }
+ if (!ok) {
+ error(@1, yyscanner, "non-type entity " + name);
+ YYERROR;
+ }
+ }
+;
+
+name:
+ name TOK_COLONS identifier { *$1 += "." + *$3; delete $3; $$ = $1; }
+| TOK_COLONS identifier { *$2 = "." + *$2; $$ = $2; }
+| identifier
+;
+
+identifier:
+ TOK_IDENTIFIER
+| TOK_GET { $$ = new OString("get"); }
+| TOK_PUBLISHED { $$ = new OString("published"); }
+| TOK_SET { $$ = new OString("set"); }
+;
+
+deprecated_opt:
+ TOK_DEPRECATED { $$ = true; }
+| /* empty */ { $$ = false; }
+;
+
+%%
+
+namespace unoidl::detail {
+
+OUString SourceProviderType::getName() const {
+ if (!typedefName.isEmpty()) {
+ return typedefName;
+ }
+ switch (type) {
+ case unoidl::detail::SourceProviderType::TYPE_VOID:
+ return "void";
+ case unoidl::detail::SourceProviderType::TYPE_BOOLEAN:
+ return "boolean";
+ case unoidl::detail::SourceProviderType::TYPE_BYTE:
+ return "byte";
+ case unoidl::detail::SourceProviderType::TYPE_SHORT:
+ return "short";
+ case unoidl::detail::SourceProviderType::TYPE_UNSIGNED_SHORT:
+ return "unsigned short";
+ case unoidl::detail::SourceProviderType::TYPE_LONG:
+ return "long";
+ case unoidl::detail::SourceProviderType::TYPE_UNSIGNED_LONG:
+ return "unsigned long";
+ case unoidl::detail::SourceProviderType::TYPE_HYPER:
+ return "hyper";
+ case unoidl::detail::SourceProviderType::TYPE_UNSIGNED_HYPER:
+ return "unsigned hyper";
+ case unoidl::detail::SourceProviderType::TYPE_FLOAT:
+ return "float";
+ case unoidl::detail::SourceProviderType::TYPE_DOUBLE:
+ return "double";
+ case unoidl::detail::SourceProviderType::TYPE_CHAR:
+ return "char";
+ case unoidl::detail::SourceProviderType::TYPE_STRING:
+ return "string";
+ case unoidl::detail::SourceProviderType::TYPE_TYPE:
+ return "type";
+ case unoidl::detail::SourceProviderType::TYPE_ANY:
+ return "any";
+ case unoidl::detail::SourceProviderType::TYPE_SEQUENCE:
+ assert(subtypes.size() == 1);
+ return "[]" + subtypes.front().getName();
+ case unoidl::detail::SourceProviderType::TYPE_ENUM:
+ case unoidl::detail::SourceProviderType::TYPE_PLAIN_STRUCT:
+ case unoidl::detail::SourceProviderType::TYPE_EXCEPTION:
+ case unoidl::detail::SourceProviderType::TYPE_INTERFACE:
+ case unoidl::detail::SourceProviderType::TYPE_PARAMETER:
+ return name;
+ case unoidl::detail::SourceProviderType::TYPE_INSTANTIATED_POLYMORPHIC_STRUCT:
+ {
+ OUStringBuffer n(512);
+ n.append(name + "<");
+ for (auto i(subtypes.begin()); i != subtypes.end(); ++i) {
+ if (i != subtypes.begin()) {
+ n.append(",");
+ }
+ n.append(i->getName());
+ }
+ return n.append(">").makeStringAndClear();
+ }
+ default:
+ assert(false && "this cannot happen"); for (;;) { std::abort(); }
+ }
+}
+
+bool SourceProviderType::equals(SourceProviderType const & other) const {
+ if (type != other.type || name != other.name
+ || subtypes.size() != other.subtypes.size())
+ {
+ return false;
+ }
+ for (auto i(subtypes.begin()), j(other.subtypes.begin());
+ i != subtypes.end(); ++i, ++j)
+ {
+ if (!i->equals(*j)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool SourceProviderInterfaceTypeEntityPad::addDirectBase(
+ YYLTYPE location, yyscan_t yyscanner, SourceProviderScannerData * data,
+ DirectBase const & base, bool optional)
+{
+ std::set<OUString> seen;
+ if (!(checkBaseClashes(
+ location, yyscanner, data, base.name, base.entity, true, optional,
+ optional, &seen)
+ && addBase(
+ location, yyscanner, data, base.name, base.name, base.entity,
+ true, optional)))
+ {
+ return false;
+ }
+ if (optional) {
+ addOptionalBaseMembers(
+ location, yyscanner, data, base.name, base.entity);
+ }
+ (optional ? directOptionalBases : directMandatoryBases).push_back(base);
+ return true;
+}
+
+bool SourceProviderInterfaceTypeEntityPad::addDirectMember(
+ YYLTYPE location, yyscan_t yyscanner, SourceProviderScannerData * data,
+ OUString const & name)
+{
+ assert(data != nullptr);
+ if (!checkMemberClashes(location, yyscanner, data, u"", name, true)) {
+ return false;
+ }
+ allMembers.emplace(name, Member(data->currentName));
+ return true;
+}
+
+bool SourceProviderInterfaceTypeEntityPad::checkBaseClashes(
+ YYLTYPE location, yyscan_t yyscanner, SourceProviderScannerData * data,
+ OUString const & name,
+ rtl::Reference<unoidl::InterfaceTypeEntity> const & entity, bool direct,
+ bool optional, bool outerOptional, std::set<OUString> * seen) const
+{
+ assert(data != nullptr);
+ assert(entity.is());
+ assert(seen != nullptr);
+ if (direct || optional || seen->insert(name).second) {
+ std::map<OUString, BaseKind>::const_iterator i(allBases.find(name));
+ if (i != allBases.end()) {
+ switch (i->second) {
+ case BASE_INDIRECT_OPTIONAL:
+ if (direct && optional) {
+ error(
+ location, yyscanner,
+ ("interface type " + data->currentName
+ + " duplicate base " + name));
+ return false;
+ }
+ break;
+ case BASE_DIRECT_OPTIONAL:
+ if (direct || !outerOptional) {
+ error(
+ location, yyscanner,
+ ("interface type " + data->currentName
+ + " duplicate base " + name));
+ return false;
+ }
+ return true;
+ case BASE_INDIRECT_MANDATORY:
+ if (direct) {
+ error(
+ location, yyscanner,
+ ("interface type " + data->currentName
+ + " duplicate base " + name));
+ return false;
+ }
+ return true;
+ case BASE_DIRECT_MANDATORY:
+ if (direct || (!optional && !outerOptional)) {
+ error(
+ location, yyscanner,
+ ("interface type " + data->currentName
+ + " duplicate base " + name));
+ return false;
+ }
+ return true;
+ }
+ }
+ if (direct || !optional) {
+ for (auto & j: entity->getDirectMandatoryBases()) {
+ OUString n("." + j.name);
+ unoidl::detail::SourceProviderEntity const * p;
+ if (findEntity(
+ location, yyscanner, data, true, &n, &p, nullptr,
+ nullptr)
+ == FOUND_ERROR)
+ {
+ return false;
+ }
+ if (p == nullptr || !p->entity.is()
+ || (p->entity->getSort()
+ != unoidl::Entity::SORT_INTERFACE_TYPE))
+ {
+ error(
+ location, yyscanner,
+ ("inconsistent type manager: interface type "
+ + data->currentName + " base " + n
+ + " does not resolve to an existing interface type"));
+ return false;
+ }
+ if (!checkBaseClashes(
+ location, yyscanner, data, n,
+ static_cast<unoidl::InterfaceTypeEntity *>(
+ p->entity.get()),
+ false, false, outerOptional, seen))
+ {
+ return false;
+ }
+ }
+ for (auto & j: entity->getDirectOptionalBases()) {
+ OUString n("." + j.name);
+ unoidl::detail::SourceProviderEntity const * p;
+ if (findEntity(
+ location, yyscanner, data, true, &n, &p, nullptr,
+ nullptr)
+ == FOUND_ERROR)
+ {
+ return false;
+ }
+ if (p == nullptr || !p->entity.is()
+ || (p->entity->getSort()
+ != unoidl::Entity::SORT_INTERFACE_TYPE))
+ {
+ error(
+ location, yyscanner,
+ ("inconsistent type manager: interface type "
+ + data->currentName + " base " + n
+ + " does not resolve to an existing interface type"));
+ return false;
+ }
+ if (!checkBaseClashes(
+ location, yyscanner, data, n,
+ static_cast<unoidl::InterfaceTypeEntity *>(
+ p->entity.get()),
+ false, true, outerOptional, seen))
+ {
+ return false;
+ }
+ }
+ for (auto & j: entity->getDirectAttributes()) {
+ if (!checkMemberClashes(
+ location, yyscanner, data, name, j.name,
+ !outerOptional))
+ {
+ return false;
+ }
+ }
+ for (auto & j: entity->getDirectMethods()) {
+ if (!checkMemberClashes(
+ location, yyscanner, data, name, j.name,
+ !outerOptional))
+ {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+bool SourceProviderInterfaceTypeEntityPad::checkMemberClashes(
+ YYLTYPE location, yyscan_t yyscanner, SourceProviderScannerData * data,
+ std::u16string_view interfaceName, OUString const & memberName,
+ bool checkOptional) const
+{
+ std::map<OUString, Member>::const_iterator i(allMembers.find(memberName));
+ if (i != allMembers.end()) {
+ if (!i->second.mandatory.isEmpty()) {
+ // For a direct member, interfaceName will be empty, so this will
+ // catch two direct members with the same name:
+ if (i->second.mandatory != interfaceName) {
+ error(
+ location, yyscanner,
+ ("interface type " + data->currentName
+ + " duplicate member " + memberName));
+ return false;
+ }
+ } else if (checkOptional) {
+ for (auto & j: i->second.optional) {
+ if (j != interfaceName) {
+ error(
+ location, yyscanner,
+ ("interface type " + data->currentName
+ + " duplicate member " + memberName));
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+bool SourceProviderInterfaceTypeEntityPad::addBase(
+ YYLTYPE location, yyscan_t yyscanner, SourceProviderScannerData * data,
+ OUString const & directBaseName, OUString const & name,
+ rtl::Reference<unoidl::InterfaceTypeEntity> const & entity, bool direct,
+ bool optional)
+{
+ assert(data != nullptr);
+ assert(entity.is());
+ BaseKind kind = optional
+ ? direct ? BASE_DIRECT_OPTIONAL : BASE_INDIRECT_OPTIONAL
+ : direct ? BASE_DIRECT_MANDATORY : BASE_INDIRECT_MANDATORY;
+ std::pair<std::map<OUString, BaseKind>::iterator, bool> p(
+ allBases.emplace(name, kind));
+ bool seen = !p.second && p.first->second >= BASE_INDIRECT_MANDATORY;
+ if (!p.second && kind > p.first->second) {
+ p.first->second = kind;
+ }
+ if (!optional && !seen) {
+ for (auto & i: entity->getDirectMandatoryBases()) {
+ OUString n("." + i.name);
+ unoidl::detail::SourceProviderEntity const * q;
+ if (findEntity(
+ location, yyscanner, data, true, &n, &q, nullptr, nullptr)
+ == FOUND_ERROR)
+ {
+ return false;
+ }
+ if (q == nullptr || !q->entity.is()
+ || q->entity->getSort() != unoidl::Entity::SORT_INTERFACE_TYPE)
+ {
+ error(
+ location, yyscanner,
+ ("inconsistent type manager: interface type "
+ + data->currentName + " base " + n
+ + " does not resolve to an existing interface type"));
+ return false;
+ }
+ if (!addBase(
+ location, yyscanner, data, directBaseName, n,
+ static_cast<unoidl::InterfaceTypeEntity *>(q->entity.get()),
+ false, false))
+ {
+ return false;
+ }
+ }
+ for (auto & i: entity->getDirectOptionalBases())
+ {
+ OUString n("." + i.name);
+ unoidl::detail::SourceProviderEntity const * q;
+ if (findEntity(
+ location, yyscanner, data, true, &n, &q, nullptr, nullptr)
+ == FOUND_ERROR)
+ {
+ return false;
+ }
+ if (q == nullptr || !q->entity.is()
+ || q->entity->getSort() != unoidl::Entity::SORT_INTERFACE_TYPE)
+ {
+ error(
+ location, yyscanner,
+ ("inconsistent type manager: interface type "
+ + data->currentName + " base " + n
+ + " does not resolve to an existing interface type"));
+ return false;
+ }
+ if (!addBase(
+ location, yyscanner, data, directBaseName, n,
+ static_cast<unoidl::InterfaceTypeEntity *>(q->entity.get()),
+ false, true))
+ {
+ return false;
+ }
+ }
+ for (auto & i: entity->getDirectAttributes()) {
+ allMembers.emplace(i.name, Member(name));
+ }
+ for (auto & i: entity->getDirectMethods()) {
+ allMembers.emplace(i.name, Member(name));
+ }
+ }
+ return true;
+}
+
+bool SourceProviderInterfaceTypeEntityPad::addOptionalBaseMembers(
+ YYLTYPE location, yyscan_t yyscanner, SourceProviderScannerData * data,
+ OUString const & name,
+ rtl::Reference<unoidl::InterfaceTypeEntity> const & entity)
+{
+ assert(entity.is());
+ for (auto & i: entity->getDirectMandatoryBases()) {
+ OUString n("." + i.name);
+ unoidl::detail::SourceProviderEntity const * p;
+ if (findEntity(
+ location, yyscanner, data, true, &n, &p, nullptr, nullptr)
+ == FOUND_ERROR)
+ {
+ return false;
+ }
+ if (p == nullptr || !p->entity.is()
+ || p->entity->getSort() != unoidl::Entity::SORT_INTERFACE_TYPE)
+ {
+ error(
+ location, yyscanner,
+ ("inconsistent type manager: interface type "
+ + data->currentName + " base " + n
+ + " does not resolve to an existing interface type"));
+ return false;
+ }
+ if (!addOptionalBaseMembers(
+ location, yyscanner, data, n,
+ static_cast<unoidl::InterfaceTypeEntity *>(p->entity.get())))
+ {
+ return false;
+ }
+ }
+ for (auto & i: entity->getDirectAttributes()) {
+ Member & m(
+ allMembers.emplace(i.name, Member(""))
+ .first->second);
+ if (m.mandatory.isEmpty()) {
+ m.optional.insert(name);
+ }
+ }
+ for (auto & i: entity->getDirectMethods()) {
+ Member & m(
+ allMembers.emplace(i.name, Member(""))
+ .first->second);
+ if (m.mandatory.isEmpty()) {
+ m.optional.insert(name);
+ }
+ }
+ return true;
+}
+
+bool parse(OUString const & uri, SourceProviderScannerData * data) {
+ assert(data != nullptr);
+ oslFileHandle handle;
+ oslFileError e = osl_openFile(uri.pData, &handle, osl_File_OpenFlag_Read);
+ switch (e) {
+ case osl_File_E_None:
+ break;
+ case osl_File_E_NOENT:
+ return false;
+ default:
+ throw FileFormatException(uri, "cannot open: " + OUString::number(e));
+ }
+ sal_uInt64 size;
+ e = osl_getFileSize(handle, &size);
+ if (e != osl_File_E_None) {
+ oslFileError e2 = osl_closeFile(handle);
+ SAL_WARN_IF(
+ e2 != osl_File_E_None, "unoidl",
+ "cannot close " << uri << ": " << +e2);
+ throw FileFormatException(
+ uri, "cannot get size: " + OUString::number(e));
+ }
+ void * address;
+ e = osl_mapFile(handle, &address, size, 0, osl_File_MapFlag_RandomAccess);
+ if (e != osl_File_E_None) {
+ oslFileError e2 = osl_closeFile(handle);
+ SAL_WARN_IF(
+ e2 != osl_File_E_None, "unoidl",
+ "cannot close " << uri << ": " << +e2);
+ throw FileFormatException(uri, "cannot mmap: " + OUString::number(e));
+ }
+ try {
+ data->setSource(address, size);
+ yyscan_t yyscanner;
+ if (yylex_init_extra(data, &yyscanner) != 0) {
+ // Checking errno for the specific EINVAL, ENOMEM documented for
+ // yylex_init_extra would not work as those values are not defined
+ // by the C++ Standard:
+ int e2 = errno;
+ throw FileFormatException(
+ uri,
+ "yylex_init_extra failed with errno " + OUString::number(e2));
+ }
+ int e2 = yyparse(yyscanner);
+ yylex_destroy(yyscanner);
+ switch (e2) {
+ case 0:
+ break;
+ default:
+ O3TL_UNREACHABLE;
+ case 1:
+ throw FileFormatException(
+ uri,
+ ("cannot parse"
+ + (data->errorLine == 0
+ ? OUString() : " line " + OUString::number(data->errorLine))
+ + (data->parserError.isEmpty()
+ ? OUString()
+ : (", "
+ + OStringToOUString(
+ data->parserError, osl_getThreadTextEncoding())))
+ + (data->errorMessage.isEmpty()
+ ? OUString() : ": \"" + data->errorMessage + "\"")));
+ case 2:
+ throw std::bad_alloc();
+ }
+ } catch (...) {
+ e = osl_unmapMappedFile(handle, address, size);
+ SAL_WARN_IF(e != osl_File_E_None, "unoidl", "cannot unmap: " << +e);
+ e = osl_closeFile(handle);
+ SAL_WARN_IF(e != osl_File_E_None, "unoidl", "cannot close: " << +e);
+ throw;
+ }
+ e = osl_unmapMappedFile(handle, address, size);
+ SAL_WARN_IF(e != osl_File_E_None, "unoidl", "cannot unmap: " << +e);
+ e = osl_closeFile(handle);
+ SAL_WARN_IF(e != osl_File_E_None, "unoidl", "cannot close: " << +e);
+ return true;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unoidl/source/sourceprovider-scanner.hxx b/unoidl/source/sourceprovider-scanner.hxx
new file mode 100644
index 000000000..08ed189f9
--- /dev/null
+++ b/unoidl/source/sourceprovider-scanner.hxx
@@ -0,0 +1,328 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <cassert>
+#include <map>
+#include <set>
+#include <utility>
+#include <vector>
+
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/types.h>
+#include <salhelper/simplereferenceobject.hxx>
+#include <unoidl/unoidl.hxx>
+
+#include "sourceprovider-parser-requires.hxx"
+#include <sourceprovider-parser.hxx>
+
+namespace unoidl::detail {
+
+struct SourceProviderScannerData;
+
+class SourceProviderEntityPad: public salhelper::SimpleReferenceObject {
+public:
+ bool isPublished() const { return published_; }
+
+protected:
+ explicit SourceProviderEntityPad(bool published): published_(published) {}
+
+ virtual ~SourceProviderEntityPad() override {}
+
+private:
+ bool const published_;
+};
+
+class SourceProviderEnumTypeEntityPad: public SourceProviderEntityPad {
+public:
+ explicit SourceProviderEnumTypeEntityPad(bool published):
+ SourceProviderEntityPad(published)
+ {}
+
+ std::vector<unoidl::EnumTypeEntity::Member> members;
+
+private:
+ virtual ~SourceProviderEnumTypeEntityPad() noexcept override {}
+};
+
+class SourceProviderPlainStructTypeEntityPad: public SourceProviderEntityPad {
+public:
+ SourceProviderPlainStructTypeEntityPad(
+ bool published, const OUString & theBaseName,
+ rtl::Reference<unoidl::PlainStructTypeEntity> const & theBaseEntity):
+ SourceProviderEntityPad(published), baseName(theBaseName),
+ baseEntity(theBaseEntity)
+ { assert(theBaseName.isEmpty() != theBaseEntity.is()); }
+
+ OUString const baseName;
+ rtl::Reference<unoidl::PlainStructTypeEntity> const baseEntity;
+ std::vector<unoidl::PlainStructTypeEntity::Member> members;
+
+private:
+ virtual ~SourceProviderPlainStructTypeEntityPad() noexcept override {}
+};
+
+class SourceProviderPolymorphicStructTypeTemplateEntityPad:
+ public SourceProviderEntityPad
+{
+public:
+ explicit SourceProviderPolymorphicStructTypeTemplateEntityPad(bool published)
+ : SourceProviderEntityPad(published)
+ {}
+
+ std::vector<OUString> typeParameters;
+ std::vector<unoidl::PolymorphicStructTypeTemplateEntity::Member> members;
+
+private:
+ virtual ~SourceProviderPolymorphicStructTypeTemplateEntityPad() noexcept override {}
+};
+
+class SourceProviderExceptionTypeEntityPad: public SourceProviderEntityPad {
+public:
+ SourceProviderExceptionTypeEntityPad(
+ bool published, const OUString & theBaseName,
+ rtl::Reference<unoidl::ExceptionTypeEntity> const & theBaseEntity):
+ SourceProviderEntityPad(published), baseName(theBaseName),
+ baseEntity(theBaseEntity)
+ { assert(theBaseName.isEmpty() != theBaseEntity.is()); }
+
+ OUString const baseName;
+ rtl::Reference<unoidl::ExceptionTypeEntity> const baseEntity;
+ std::vector<unoidl::ExceptionTypeEntity::Member> members;
+
+private:
+ virtual ~SourceProviderExceptionTypeEntityPad() noexcept override {}
+};
+
+class SourceProviderInterfaceTypeEntityPad: public SourceProviderEntityPad {
+public:
+ struct DirectBase {
+ DirectBase(
+ OUString theName,
+ rtl::Reference<unoidl::InterfaceTypeEntity> const & theEntity,
+ std::vector<OUString>&& theAnnotations):
+ name(std::move(theName)), entity(theEntity), annotations(std::move(theAnnotations))
+ { assert(theEntity.is()); }
+
+ OUString name;
+ rtl::Reference<unoidl::InterfaceTypeEntity> entity;
+ std::vector<OUString> annotations;
+ };
+
+ enum BaseKind {
+ BASE_INDIRECT_OPTIONAL, BASE_DIRECT_OPTIONAL, BASE_INDIRECT_MANDATORY,
+ BASE_DIRECT_MANDATORY
+ };
+
+ struct Member {
+ OUString mandatory;
+ std::set<OUString> optional;
+
+ explicit Member(OUString theMandatory): mandatory(std::move(theMandatory)) {}
+ };
+
+ SourceProviderInterfaceTypeEntityPad(bool published, bool theSingleBase):
+ SourceProviderEntityPad(published), singleBase(theSingleBase)
+ {}
+
+ bool addDirectBase(
+ YYLTYPE location, yyscan_t yyscanner, SourceProviderScannerData * data,
+ DirectBase const & base, bool optional);
+
+ bool addDirectMember(
+ YYLTYPE location, yyscan_t yyscanner, SourceProviderScannerData * data,
+ OUString const & name);
+
+ bool singleBase;
+ std::vector<DirectBase> directMandatoryBases;
+ std::vector<DirectBase> directOptionalBases;
+ std::vector<unoidl::InterfaceTypeEntity::Attribute> directAttributes;
+ std::vector<unoidl::InterfaceTypeEntity::Method> directMethods;
+ std::map<OUString, BaseKind> allBases;
+ std::map<OUString, Member> allMembers;
+
+private:
+ virtual ~SourceProviderInterfaceTypeEntityPad() noexcept override {}
+
+ bool checkBaseClashes(
+ YYLTYPE location, yyscan_t yyscanner, SourceProviderScannerData * data,
+ OUString const & name,
+ rtl::Reference<unoidl::InterfaceTypeEntity> const & entity,
+ bool direct, bool optional, bool outerOptional,
+ std::set<OUString> * seen) const;
+
+ bool checkMemberClashes(
+ YYLTYPE location, yyscan_t yyscanner, SourceProviderScannerData * data,
+ std::u16string_view interfaceName, OUString const & memberName,
+ bool checkOptional) const;
+
+ bool addBase(
+ YYLTYPE location, yyscan_t yyscanner, SourceProviderScannerData * data,
+ OUString const & directBaseName, OUString const & name,
+ rtl::Reference<unoidl::InterfaceTypeEntity> const & entity, bool direct,
+ bool optional);
+
+ bool addOptionalBaseMembers(
+ YYLTYPE location, yyscan_t yyscanner, SourceProviderScannerData * data,
+ OUString const & name,
+ rtl::Reference<unoidl::InterfaceTypeEntity> const & entity);
+};
+
+class SourceProviderConstantGroupEntityPad: public SourceProviderEntityPad {
+public:
+ explicit SourceProviderConstantGroupEntityPad(bool published):
+ SourceProviderEntityPad(published)
+ {}
+
+ std::vector<unoidl::ConstantGroupEntity::Member> members;
+
+private:
+ virtual ~SourceProviderConstantGroupEntityPad() noexcept override {}
+};
+
+class SourceProviderSingleInterfaceBasedServiceEntityPad:
+ public SourceProviderEntityPad
+{
+public:
+ struct Constructor {
+ struct Parameter {
+ Parameter(
+ OUString theName,
+ SourceProviderType theType, bool theRest):
+ name(std::move(theName)), type(std::move(theType)), rest(theRest)
+ {}
+
+ OUString name;
+
+ SourceProviderType type;
+
+ bool rest;
+ };
+
+ Constructor(
+ OUString theName,
+ std::vector< OUString >&& theAnnotations):
+ name(std::move(theName)), annotations(std::move(theAnnotations))
+ {}
+
+ OUString name;
+
+ std::vector< Parameter > parameters;
+
+ std::vector< OUString > exceptions;
+
+ std::vector< OUString > annotations;
+ };
+
+ explicit SourceProviderSingleInterfaceBasedServiceEntityPad(
+ bool published, OUString theBase):
+ SourceProviderEntityPad(published), base(std::move(theBase))
+ {}
+
+ OUString const base;
+ std::vector<Constructor> constructors;
+
+private:
+ virtual ~SourceProviderSingleInterfaceBasedServiceEntityPad() noexcept override {}
+};
+
+class SourceProviderAccumulationBasedServiceEntityPad:
+ public SourceProviderEntityPad
+{
+public:
+ explicit SourceProviderAccumulationBasedServiceEntityPad(bool published):
+ SourceProviderEntityPad(published)
+ {}
+
+ std::vector<unoidl::AnnotatedReference> directMandatoryBaseServices;
+ std::vector<unoidl::AnnotatedReference> directOptionalBaseServices;
+ std::vector<unoidl::AnnotatedReference> directMandatoryBaseInterfaces;
+ std::vector<unoidl::AnnotatedReference> directOptionalBaseInterfaces;
+ std::vector<unoidl::AccumulationBasedServiceEntity::Property>
+ directProperties;
+
+private:
+ virtual ~SourceProviderAccumulationBasedServiceEntityPad() noexcept override {}
+};
+
+struct SourceProviderEntity {
+ enum Kind {
+ KIND_EXTERNAL, KIND_LOCAL, KIND_INTERFACE_DECL,
+ KIND_PUBLISHED_INTERFACE_DECL, KIND_MODULE
+ };
+
+ explicit SourceProviderEntity(
+ Kind theKind, rtl::Reference<unoidl::Entity> const & externalEntity):
+ kind(theKind), entity(externalEntity)
+ { assert(theKind <= KIND_LOCAL); assert(externalEntity.is()); }
+
+ explicit SourceProviderEntity(
+ rtl::Reference<SourceProviderEntityPad> const & localPad):
+ kind(KIND_LOCAL), pad(localPad)
+ { assert(localPad.is()); }
+
+ explicit SourceProviderEntity(Kind theKind): kind(theKind)
+ { assert(theKind >= KIND_INTERFACE_DECL); }
+
+ SourceProviderEntity(): // needed for std::map::operator []
+ kind() // avoid false warnings about uninitialized members
+ {}
+
+ Kind kind;
+ rtl::Reference<unoidl::Entity> entity;
+ rtl::Reference<SourceProviderEntityPad> pad;
+};
+
+struct SourceProviderScannerData {
+ explicit SourceProviderScannerData(
+ rtl::Reference<unoidl::Manager> theManager):
+ manager(std::move(theManager)),
+ sourcePosition(), sourceEnd(),
+ // avoid false warnings about uninitialized members
+ errorLine(0), publishedContext(false)
+ { assert(manager.is()); }
+
+ void setSource(void const * address, sal_uInt64 size) {
+ sourcePosition = static_cast<char const *>(address);
+ sourceEnd = sourcePosition + size;
+ }
+
+ rtl::Reference<unoidl::Manager> manager;
+
+ char const * sourcePosition;
+ char const * sourceEnd;
+ YYLTYPE errorLine;
+ OString parserError;
+ OUString errorMessage;
+
+ std::map<OUString, SourceProviderEntity> entities;
+ std::vector<OUString> modules;
+ OUString currentName;
+ bool publishedContext;
+};
+
+bool parse(OUString const & uri, SourceProviderScannerData * data);
+
+}
+
+int yylex_init_extra(
+ unoidl::detail::SourceProviderScannerData * user_defined,
+ yyscan_t * yyscanner);
+
+int yylex_destroy(yyscan_t yyscanner);
+
+int yylex(YYSTYPE * yylval_param, YYLTYPE * yylloc_param, yyscan_t yyscanner);
+
+unoidl::detail::SourceProviderScannerData * yyget_extra(yyscan_t yyscanner);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unoidl/source/sourceprovider-scanner.l b/unoidl/source/sourceprovider-scanner.l
new file mode 100644
index 000000000..bdedfcffc
--- /dev/null
+++ b/unoidl/source/sourceprovider-scanner.l
@@ -0,0 +1,253 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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/.
+ */
+
+%option bison-bridge
+%option bison-locations
+%option extra-type="unoidl::detail::SourceProviderScannerData *"
+%option never-interactive
+%option nounistd
+%option noyywrap
+%option noinput
+%option nounput
+%option reentrant
+%option warn
+%option yylineno
+
+%top {
+
+#include "sal/config.h"
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstring>
+
+}
+
+%{
+
+#include <rtl/math.h>
+#include <rtl/string.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/textenc.h>
+#include <sal/types.h>
+#include <unoidl/unoidl.hxx>
+
+#include "sourceprovider-parser-requires.hxx"
+#include <sourceprovider-parser.hxx>
+#include "sourceprovider-scanner.hxx"
+
+namespace unoidl::detail {
+
+static std::size_t sourceProviderScannerInput(
+ SourceProviderScannerData * data, char * buffer, std::size_t size)
+{
+ assert(data != nullptr);
+ if (data->sourcePosition == data->sourceEnd) {
+ return YY_NULL;
+ }
+ assert(data->sourcePosition < data->sourceEnd);
+ size = std::min<std::size_t>(size, data->sourceEnd - data->sourcePosition);
+ std::memcpy(buffer, data->sourcePosition, size);
+ data->sourcePosition += size;
+ return size;
+}
+
+}
+
+#define YY_INPUT(buf, result, max_size) ((result) = \
+ ::unoidl::detail::sourceProviderScannerInput(yyextra, (buf), (max_size)))
+
+namespace {
+
+int nonZeroIntegerLiteral(
+ char const * text, std::size_t length, sal_Int16 radix, sal_uInt64 * value,
+ unoidl::detail::SourceProviderScannerData * data)
+{
+ assert(text != nullptr);
+ assert(length != 0);
+ assert(value != nullptr);
+ assert(data != nullptr);
+ std::size_t n = length;
+ switch (text[length - 1]) {
+ case 'L':
+ case 'U':
+ case 'l':
+ case 'u':
+ --n;
+ break;
+ default:
+ break;
+ }
+ *value = OString(text, n).toUInt64(radix);
+ if (*value == 0) {
+ data->errorMessage = "out-of-range integer literal "
+ + OUString(text, length, RTL_TEXTENCODING_ASCII_US);
+ return TOK_ERROR;
+ }
+ return TOK_INTEGER;
+}
+
+}
+
+%}
+
+%x comment1 comment2 doc docdepr
+
+DIGIT [0-9]
+UPPER [A-Z]
+LOWER [a-z]
+ALPHA {UPPER}|{LOWER}
+ALNUM {DIGIT}|{ALPHA}
+
+%%
+
+[ \t\r]
+\n *yylloc = yylineno;
+
+"//" BEGIN comment1;
+"#" BEGIN comment1; //TODO: only at start of line
+<comment1>.
+<comment1>\n *yylloc = yylineno; BEGIN INITIAL;
+
+"/*" BEGIN comment2;
+"/**" BEGIN doc;
+"/***" BEGIN comment2;
+
+<comment2,doc>"*/" BEGIN INITIAL;
+<docdepr>"*/" BEGIN INITIAL; return TOK_DEPRECATED;
+
+<comment2,docdepr>.
+<comment2,doc,docdepr>\n *yylloc = yylineno;
+<comment2,doc,docdepr><<EOF>> {
+ yyextra->errorMessage = "unterminated comment";
+ return TOK_ERROR;
+}
+
+<doc>[ \t\r]
+<doc>"@deprecated" BEGIN docdepr;
+<doc>"*"
+<doc>[^ \t\r\n*]+
+
+[%&()*+,\-/:;<=>[\]^{|}~] return yytext[0];
+
+"..." return TOK_ELLIPSIS;
+"::" return TOK_COLONS;
+"<<" return TOK_LEFTSHIFT;
+">>" return TOK_RIGHTSHIFT;
+
+"FALSE" return TOK_FALSE;
+"False" return TOK_FALSE;
+"TRUE" return TOK_TRUE;
+"True" return TOK_TRUE;
+"any" return TOK_ANY;
+"attribute" return TOK_ATTRIBUTE;
+"boolean" return TOK_BOOLEAN;
+"bound" return TOK_BOUND;
+"byte" return TOK_BYTE;
+"char" return TOK_CHAR;
+"const" return TOK_CONST;
+"constants" return TOK_CONSTANTS;
+"constrained" return TOK_CONSTRAINED;
+"double" return TOK_DOUBLE;
+"enum" return TOK_ENUM;
+"exception" return TOK_EXCEPTION;
+"float" return TOK_FLOAT;
+"get" return TOK_GET;
+"hyper" return TOK_HYPER;
+"in" return TOK_IN;
+"inout" return TOK_INOUT;
+"interface" return TOK_INTERFACE;
+"long" return TOK_LONG;
+"maybeambiguous" return TOK_MAYBEAMBIGUOUS;
+"maybedefault" return TOK_MAYBEDEFAULT;
+"maybevoid" return TOK_MAYBEVOID;
+"module" return TOK_MODULE;
+"optional" return TOK_OPTIONAL;
+"out" return TOK_OUT;
+"property" return TOK_PROPERTY;
+"published" return TOK_PUBLISHED;
+"raises" return TOK_RAISES;
+"readonly" return TOK_READONLY;
+"removable" return TOK_REMOVABLE;
+"sequence" return TOK_SEQUENCE;
+"service" return TOK_SERVICE;
+"set" return TOK_SET;
+"short" return TOK_SHORT;
+"singleton" return TOK_SINGLETON;
+"string" return TOK_STRING;
+"struct" return TOK_STRUCT;
+"transient" return TOK_TRANSIENT;
+"type" return TOK_TYPE;
+"typedef" return TOK_TYPEDEF;
+"unsigned" return TOK_UNSIGNED;
+"void" return TOK_VOID;
+
+{UPPER}("_"?{ALNUM})*|{LOWER}{ALNUM}* {
+ yylval->sval = new OString(yytext);
+ return TOK_IDENTIFIER;
+}
+
+({ALPHA}|"_")({ALNUM}|"_")* {
+ yyextra->errorMessage = "illegal identifier "
+ + OUString(yytext, yyleng, RTL_TEXTENCODING_ASCII_US);
+ return TOK_ERROR;
+}
+
+0+[LUlu]? |
+0[Xx]0+[LUlu]? {
+ yylval->ival = 0;
+ return TOK_INTEGER;
+}
+
+0[0-7]+[LUlu]? {
+ return nonZeroIntegerLiteral(yytext, yyleng, 8, &yylval->ival, yyextra);
+}
+
+[1-9]{DIGIT}*[LUlu]? {
+ return nonZeroIntegerLiteral(yytext, yyleng, 10, &yylval->ival, yyextra);
+}
+
+0[Xx][0-9A-Fa-f]+[LUlu]? {
+ return nonZeroIntegerLiteral(
+ yytext + 2, yyleng - 2, 16, &yylval->ival, yyextra);
+}
+
+{DIGIT}+[Ee][+\-]?{DIGIT}+[Ff]? |
+{DIGIT}*"."{DIGIT}+([Ee][+\-]?{DIGIT}+)?[Ff]? {
+ rtl_math_ConversionStatus s;
+ yylval->fval = rtl_math_stringToDouble(
+ yytext, yytext + yyleng, '.', 0, &s, nullptr);
+ if (s == rtl_math_ConversionStatus_OutOfRange) {
+ yyextra->errorMessage = "out-of-range floating-point literal "
+ + OUString(yytext, yyleng, RTL_TEXTENCODING_ASCII_US);
+ return TOK_ERROR;
+ }
+ return TOK_FLOATING;
+}
+
+{DIGIT}({ALNUM}|"_")* {
+ yyextra->errorMessage = "illegal numeric literal "
+ + OUString(yytext, yyleng, RTL_TEXTENCODING_ASCII_US);
+ return TOK_ERROR;
+}
+
+. {
+ char c = yytext[0];
+ yyextra->errorMessage = c >= ' ' && c <= '~'
+ ? OUString("invalid character \"" + OUStringChar(c) + "\"")
+ : OUString(
+ "invalid byte x"
+ + OUString::number(static_cast<unsigned char>(c), 16).toAsciiUpperCase());
+ return TOK_ERROR;
+}
+
+%%
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unoidl/source/sourcetreeprovider.cxx b/unoidl/source/sourcetreeprovider.cxx
new file mode 100644
index 000000000..399329f30
--- /dev/null
+++ b/unoidl/source/sourcetreeprovider.cxx
@@ -0,0 +1,315 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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 <sal/log.hxx>
+
+#include <map>
+#include <utility>
+#include <vector>
+
+#include <osl/file.h>
+#include <osl/file.hxx>
+#include <rtl/character.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.hxx>
+#include <unoidl/unoidl.hxx>
+
+#include "sourceprovider-scanner.hxx"
+#include "sourcetreeprovider.hxx"
+
+#if defined MACOSX
+#include <dirent.h>
+#include <osl/thread.h>
+#endif
+
+namespace unoidl::detail {
+
+namespace {
+
+//TODO: Bad hack to work around osl::FileStatus::getFileName not determining the
+// original spelling of a file name (not even with
+// osl_FileStatus_Mask_Validate):
+OUString getFileName(OUString const & uri, osl::FileStatus const & status) {
+#if defined MACOSX
+ sal_Int32 i = uri.lastIndexOf('/') + 1;
+ OUString path;
+ if (osl::FileBase::getSystemPathFromFileURL(uri.copy(0, i), path)
+ != osl::FileBase::E_None)
+ {
+ SAL_WARN(
+ "unoidl",
+ "cannot getSystemPathFromFileURL(" << uri.copy(0, i) << ")");
+ return status.getFileName();
+ }
+ OString dir(OUStringToOString(path, osl_getThreadTextEncoding()));
+ OString name(OUStringToOString(uri.subView(i), osl_getThreadTextEncoding()));
+ DIR * d = opendir(dir.getStr());
+ if (d == nullptr) {
+ SAL_WARN("unoidl", "cannot opendir(" << dir << ")");
+ return status.getFileName();
+ }
+ for (;;) {
+ dirent ent;
+ dirent * p;
+ int e = readdir_r(d, &ent, &p);
+ if (e != 0) {
+ SAL_WARN("unoidl", "cannot readdir_r");
+ closedir(d);
+ return status.getFileName();
+ }
+ if (p == nullptr) {
+ SAL_WARN(
+ "unoidl", "cannot find " << name << " via readdir of " << dir);
+ closedir(d);
+ return status.getFileName();
+ }
+ if (name.equalsIgnoreAsciiCase(p->d_name)) {
+ closedir(d);
+ return OUString(
+ p->d_name, std::strlen(p->d_name), osl_getThreadTextEncoding());
+ }
+ }
+#else
+ (void) uri;
+ return status.getFileName();
+#endif
+}
+
+bool exists(OUString const & uri, bool directory) {
+ osl::DirectoryItem item;
+ osl::FileStatus status(
+ osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName);
+ return osl::DirectoryItem::get(uri, item) == osl::FileBase::E_None
+ && item.getFileStatus(status) == osl::FileBase::E_None
+ && (status.getFileType() == osl::FileStatus::Directory) == directory
+ && getFileName(uri, status) == uri.subView(uri.lastIndexOf('/') + 1);
+}
+
+class Cursor: public MapCursor {
+public:
+ Cursor(Manager& manager, OUString const & uri): manager_(manager), directory_(uri) {
+ auto const rc = directory_.open();
+ SAL_WARN_IF(
+ rc != osl::FileBase::E_None, "unoidl", "open(" << uri << ") failed with " << +rc);
+ }
+
+private:
+ virtual ~Cursor() noexcept override {}
+
+ virtual rtl::Reference<Entity> getNext(OUString *) override;
+
+ Manager& manager_;
+ osl::Directory directory_;
+};
+
+class SourceModuleEntity: public ModuleEntity {
+public:
+ SourceModuleEntity(Manager& manager, OUString uri): manager_(manager), uri_(std::move(uri)) {}
+
+private:
+ virtual ~SourceModuleEntity() noexcept override {}
+
+ virtual std::vector<OUString> getMemberNames() const override
+ { return std::vector<OUString>(); } //TODO
+
+ virtual rtl::Reference< MapCursor > createCursor() const override
+ { return new Cursor(manager_, uri_); }
+
+ Manager& manager_;
+ OUString uri_;
+};
+
+bool isValidFileName(std::u16string_view name, bool directory) {
+ for (size_t i = 0;; ++i) {
+ if (i == name.size()) {
+ if (i == 0) {
+ return false;
+ }
+ return directory;
+ }
+ auto const c = name[i];
+ if (c == '.') {
+ if (i == 0 || name[i - 1] == '_') {
+ return false;
+ }
+ return !directory && name.substr(i + 1) == u"idl";
+ } else if (c == '_') {
+ //TODO: Ignore case of name[0] only for case-insensitive file systems:
+ if (i == 0 || name[i - 1] == '_') {
+ return false;
+ }
+ } else if (rtl::isAsciiDigit(c)) {
+ if (i == 0) {
+ return false;
+ }
+ } else if (!rtl::isAsciiAlpha(c)) {
+ return false;
+ }
+ }
+}
+
+}
+
+rtl::Reference<Entity> Cursor::getNext(OUString * name) {
+ assert(name != nullptr);
+ for (;;) {
+ osl::DirectoryItem i;
+ auto rc = directory_.getNextItem(i);
+ switch (rc) {
+ case osl::FileBase::E_None:
+ {
+ osl::FileStatus stat(
+ osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileName |
+ osl_FileStatus_Mask_FileURL);
+ rc = i.getFileStatus(stat);
+ if (rc != osl::FileBase::E_None) {
+ SAL_WARN(
+ "unoidl",
+ "getFileSatus in <" << directory_.getURL() << "> failed with " << +rc);
+ continue;
+ }
+ auto const dir = stat.getFileType() == osl::FileStatus::Directory;
+ if (!isValidFileName(stat.getFileName(), dir)) {
+ continue;
+ }
+ if (dir) {
+ //TODO: Using osl::FileStatus::getFileName can likely cause issues on case-
+ // insensitive/preserving file systems, see the free getFileName function above
+ // (which likely goes unnoticed if module identifiers follow the convention of
+ // being all-lowercase):
+ *name = stat.getFileName();
+ return new SourceModuleEntity(manager_, stat.getFileURL());
+ } else {
+ SourceProviderScannerData data(&manager_);
+ if (!parse(stat.getFileURL(), &data)) {
+ SAL_WARN("unoidl", "cannot parse <" << stat.getFileURL() << ">");
+ continue;
+ }
+ auto ent = data.entities.end();
+ for (auto j = data.entities.begin(); j != data.entities.end(); ++j) {
+ if (j->second.kind == SourceProviderEntity::KIND_EXTERNAL
+ || j->second.kind == SourceProviderEntity::KIND_MODULE)
+ {
+ continue;
+ }
+ if (ent != data.entities.end()) {
+ throw FileFormatException(
+ stat.getFileURL(), "source file defines more than one entity");
+ }
+ ent = j;
+ }
+ if (ent == data.entities.end()) {
+ throw FileFormatException(
+ stat.getFileURL(), "source file defines no entity");
+ }
+ //TODO: Check that the entity's name matches the suffix of stat.getFileURL():
+ *name = ent->first.copy(ent->first.lastIndexOf('.') + 1);
+ return ent->second.entity;
+ }
+ }
+ default:
+ SAL_WARN( "unoidl", "getNext from <" << directory_.getURL() << "> failed with " << +rc);
+ [[fallthrough]];
+ case osl::FileBase::E_NOENT:
+ return {};
+ }
+ }
+}
+
+SourceTreeProvider::SourceTreeProvider(Manager & manager, OUString const & uri):
+ manager_(manager), uri_(uri.endsWith("/") ? uri : uri + "/")
+{}
+
+rtl::Reference<MapCursor> SourceTreeProvider::createRootCursor() const {
+ return new Cursor(manager_, uri_);
+}
+
+rtl::Reference<Entity> SourceTreeProvider::findEntity(OUString const & name)
+ const
+{
+ std::map< OUString, rtl::Reference<Entity> >::iterator ci(
+ cache_.find(name));
+ if (ci != cache_.end()) {
+ return ci->second;
+ }
+ // Match name against
+ // name ::= identifier ("." identifier)*
+ // identifier ::= upper-blocks | lower-block
+ // upper-blocks ::= upper ("_"? alnum)*
+ // lower-block :== lower alnum*
+ // alnum ::= digit | upper | lower
+ // digit ::= "0"--"9"
+ // upper ::= "A"--"Z"
+ // lower ::= "a"--"z"
+ OUStringBuffer buf(name);
+ sal_Int32 start = 0;
+ sal_Int32 i = 0;
+ for (; i != name.getLength(); ++i) {
+ sal_Unicode c = name[i];
+ if (c == '.') {
+ assert(i == start || i != 0);
+ if (i == start || name[i - 1] == '_') {
+ throw FileFormatException( //TODO
+ "", "Illegal UNOIDL identifier \"" + name + "\"");
+ }
+ buf[i] = '/';
+ start = i + 1;
+ } else if (c == '_') {
+ assert(i == start || i != 0);
+ if (i == start || name[i - 1] == '_'
+ || !rtl::isAsciiUpperCase(name[start]))
+ {
+ throw FileFormatException( //TODO
+ "", "Illegal UNOIDL identifier \"" + name + "\"");
+ }
+ } else if (rtl::isAsciiDigit(c)) {
+ if (i == start) {
+ throw FileFormatException( //TODO
+ "", "Illegal UNOIDL identifier \"" + name + "\"");
+ }
+ } else if (!rtl::isAsciiAlpha(c)) {
+ throw FileFormatException( //TODO
+ "", "Illegal UNOIDL identifier \"" + name + "\"");
+ }
+ }
+ if (i == start) {
+ throw FileFormatException( //TODO
+ "", "Illegal UNOIDL identifier \"" + name + "\"");
+ }
+ OUString uri(uri_ + buf.makeStringAndClear());
+ rtl::Reference<Entity> ent;
+ // Prevent conflicts between foo/ and Foo.idl on case-preserving file
+ // systems:
+ if (exists(uri, true) && !exists(uri + ".idl", false)) {
+ ent = new SourceModuleEntity(manager_, uri);
+ } else {
+ uri += ".idl";
+ SourceProviderScannerData data(&manager_);
+ if (parse(uri, &data)) {
+ std::map<OUString, SourceProviderEntity>::const_iterator j(
+ data.entities.find(name));
+ if (j != data.entities.end()) {
+ ent = j->second.entity;
+ }
+ SAL_WARN_IF(
+ !ent.is(), "unoidl",
+ "<" << uri << "> does not define entity " << name);
+ }
+ }
+ cache_.emplace(name, ent);
+ return ent;
+}
+
+SourceTreeProvider::~SourceTreeProvider() noexcept {}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unoidl/source/sourcetreeprovider.hxx b/unoidl/source/sourcetreeprovider.hxx
new file mode 100644
index 000000000..37bd6baa1
--- /dev/null
+++ b/unoidl/source/sourcetreeprovider.hxx
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <map>
+
+#include <rtl/ref.hxx>
+#include <unoidl/unoidl.hxx>
+
+namespace unoidl::detail
+{
+class SourceTreeProvider : public Provider
+{
+public:
+ // throws FileFormatException, NoSuchFileException:
+ SourceTreeProvider(Manager& manager, OUString const& uri);
+
+ // throws FileFormatException:
+ virtual rtl::Reference<MapCursor> createRootCursor() const override;
+
+ // throws FileFormatException:
+ virtual rtl::Reference<Entity> findEntity(OUString const& name) const override;
+
+private:
+ virtual ~SourceTreeProvider() noexcept override;
+
+ Manager& manager_;
+ OUString uri_;
+ mutable std::map<OUString, rtl::Reference<Entity>> cache_; //TODO: at manager
+};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unoidl/source/unoidl-check.cxx b/unoidl/source/unoidl-check.cxx
new file mode 100644
index 000000000..892793eab
--- /dev/null
+++ b/unoidl/source/unoidl-check.cxx
@@ -0,0 +1,1167 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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 <algorithm>
+#include <cassert>
+#include <cstdlib>
+#include <iostream>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include <osl/file.hxx>
+#include <osl/process.h>
+#include <rtl/character.hxx>
+#include <rtl/process.h>
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/main.h>
+#include <sal/types.h>
+#include <unoidl/unoidl.hxx>
+
+namespace unoidl {
+
+static bool operator ==(ConstantValue const & lhs, ConstantValue const & rhs) {
+ if (lhs.type == rhs.type) {
+ switch (lhs.type) {
+ case ConstantValue::TYPE_BOOLEAN:
+ return lhs.booleanValue == rhs.booleanValue;
+ case ConstantValue::TYPE_BYTE:
+ return lhs.byteValue == rhs.byteValue;
+ case ConstantValue::TYPE_SHORT:
+ return lhs.shortValue == rhs.shortValue;
+ case ConstantValue::TYPE_UNSIGNED_SHORT:
+ return lhs.unsignedShortValue == rhs.unsignedShortValue;
+ case ConstantValue::TYPE_LONG:
+ return lhs.longValue == rhs.longValue;
+ case ConstantValue::TYPE_UNSIGNED_LONG:
+ return lhs.unsignedLongValue == rhs.unsignedLongValue;
+ case ConstantValue::TYPE_HYPER:
+ return lhs.hyperValue == rhs.hyperValue;
+ case ConstantValue::TYPE_UNSIGNED_HYPER:
+ return lhs.unsignedHyperValue == rhs.unsignedHyperValue;
+ case ConstantValue::TYPE_FLOAT:
+ return lhs.floatValue == rhs.floatValue;
+ case ConstantValue::TYPE_DOUBLE:
+ return lhs.doubleValue == rhs.doubleValue;
+ }
+ }
+ return false;
+}
+
+static bool operator !=(ConstantValue const & lhs, ConstantValue const & rhs) {
+ return !(lhs == rhs);
+}
+
+static bool operator ==(
+ SingleInterfaceBasedServiceEntity::Constructor::Parameter const & lhs,
+ SingleInterfaceBasedServiceEntity::Constructor::Parameter const & rhs)
+{
+ return lhs.name == rhs.name && lhs.type == rhs.type && lhs.rest == rhs.rest;
+}
+
+}
+
+namespace {
+
+void badUsage() {
+ std::cerr
+ << "Usage:" << std::endl << std::endl
+ << (" unoidl-check [--ignore-unpublished] [<extra registries A>]"
+ " <registry A> --")
+ << std::endl << " [<extra registries B>] <registry B>" << std::endl
+ << std::endl
+ << ("where each <registry> is either a new- or legacy-format .rdb file,"
+ " a single .idl")
+ << std::endl
+ << ("file, or a root directory of an .idl file tree. Check that each"
+ " entity from")
+ << std::endl
+ << "<registry A> is also present in <registry B> in a compatible form."
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+}
+
+bool getArgument(
+ sal_uInt32 argument, bool * ignoreUnpublished, bool * delimiter,
+ OUString * uri)
+{
+ assert(ignoreUnpublished != nullptr);
+ assert(uri != nullptr);
+ OUString arg;
+ rtl_getAppCommandArg(argument, &arg.pData);
+ if (argument == 0 && arg == "--ignore-unpublished") {
+ *ignoreUnpublished = true;
+ return false;
+ }
+ if (arg == "--") {
+ if (delimiter == nullptr) {
+ badUsage();
+ }
+ *delimiter = true;
+ return false;
+ }
+ OUString url;
+ osl::FileBase::RC e1 = osl::FileBase::getFileURLFromSystemPath(arg, url);
+ if (e1 != osl::FileBase::E_None) {
+ std::cerr
+ << "Cannot convert \"" << arg << "\" to file URL, error code "
+ << +e1 << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ OUString cwd;
+ oslProcessError e2 = osl_getProcessWorkingDir(&cwd.pData);
+ if (e2 != osl_Process_E_None) {
+ std::cerr
+ << "Cannot obtain working directory, error code " << +e2
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ e1 = osl::FileBase::getAbsoluteFileURL(cwd, url, *uri);
+ if (e1 != osl::FileBase::E_None) {
+ std::cerr
+ << "Cannot make \"" << url
+ << "\" into an absolute file URL, error code " << +e1 << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ return true;
+}
+
+OUString showDirection(
+ unoidl::InterfaceTypeEntity::Method::Parameter::Direction direction)
+{
+ switch (direction) {
+ case unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN:
+ return "[in]";
+ case unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_OUT:
+ return "[out]";
+ case unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN_OUT:
+ return "[inout]";
+ default:
+ assert(false && "this cannot happen"); for (;;) { std::abort(); }
+ }
+}
+
+struct EqualsAnnotation {
+ explicit EqualsAnnotation(OUString name): name_(std::move(name)) {}
+
+ bool operator ()(unoidl::AnnotatedReference const & ref)
+ { return ref.name == name_; }
+
+private:
+ OUString name_;
+};
+
+void checkMap(
+ rtl::Reference<unoidl::Provider> const & providerB, std::u16string_view prefix,
+ rtl::Reference<unoidl::MapCursor> const & cursor, bool ignoreUnpublished)
+{
+ assert(providerB.is());
+ assert(cursor.is());
+ for (;;) {
+ OUString id;
+ rtl::Reference<unoidl::Entity> entA(cursor->getNext(&id));
+ if (!entA.is()) {
+ break;
+ }
+ OUString name(prefix + id);
+ if (entA->getSort() == unoidl::Entity::SORT_MODULE) {
+ checkMap(
+ providerB, OUStringConcatenation(name + "."),
+ (static_cast<unoidl::ModuleEntity *>(entA.get())
+ ->createCursor()),
+ ignoreUnpublished);
+ } else {
+ bool pubA = dynamic_cast<unoidl::PublishableEntity&>(*entA).isPublished();
+ if (!pubA && ignoreUnpublished) {
+ continue;
+ }
+ rtl::Reference<unoidl::Entity> entB(providerB->findEntity(name));
+ if (!entB.is()) {
+ std::cerr
+ << "A entity " << name << " is not present in B"
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ if (entA->getSort() != entB->getSort()) {
+ std::cerr
+ << "A entity " << name << " is of different sort in B"
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ if (pubA && (!dynamic_cast<unoidl::PublishableEntity&>(*entB).isPublished()))
+ {
+ std::cerr
+ << "A published entity " << name << " is not published in B"
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ switch (entA->getSort()) {
+ case unoidl::Entity::SORT_ENUM_TYPE:
+ {
+ rtl::Reference<unoidl::EnumTypeEntity> ent2A(
+ static_cast<unoidl::EnumTypeEntity *>(entA.get()));
+ rtl::Reference<unoidl::EnumTypeEntity> ent2B(
+ static_cast<unoidl::EnumTypeEntity *>(entB.get()));
+ if (ent2A->getMembers().size()
+ != ent2B->getMembers().size())
+ {
+ std::cerr
+ << "enum type " << name
+ << " number of members changed from "
+ << ent2A->getMembers().size() << " to "
+ << ent2B->getMembers().size() << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ for (auto
+ i(ent2A->getMembers().begin()),
+ j(ent2B->getMembers().begin());
+ i != ent2A->getMembers().end(); ++i, ++j)
+ {
+ if (i->name != j->name || i->value != j->value) {
+ std::cerr
+ << "enum type " << name << " member #"
+ << i - ent2A->getMembers().begin() + 1
+ << " changed from " << i->name << " = "
+ << i->value << " to " << j->name << " = "
+ << j->value << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ break;
+ }
+ case unoidl::Entity::SORT_PLAIN_STRUCT_TYPE:
+ {
+ rtl::Reference<unoidl::PlainStructTypeEntity> ent2A(
+ static_cast<unoidl::PlainStructTypeEntity *>(
+ entA.get()));
+ rtl::Reference<unoidl::PlainStructTypeEntity> ent2B(
+ static_cast<unoidl::PlainStructTypeEntity *>(
+ entB.get()));
+ if (ent2A->getDirectBase() != ent2B->getDirectBase()) {
+ std::cerr
+ << "plain struct type " << name
+ << " direct base changed from "
+ << (ent2A->getDirectBase().isEmpty()
+ ? OUString("none") : ent2A->getDirectBase())
+ << " to "
+ << (ent2B->getDirectBase().isEmpty()
+ ? OUString("none") : ent2B->getDirectBase())
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ if (ent2A->getDirectMembers().size()
+ != ent2B->getDirectMembers().size())
+ {
+ std::cerr
+ << "plain struct type " << name
+ << " number of direct members changed from "
+ << ent2A->getDirectMembers().size() << " to "
+ << ent2B->getDirectMembers().size() << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ for (auto
+ i(ent2A->getDirectMembers().begin()),
+ j(ent2B->getDirectMembers().begin());
+ i != ent2A->getDirectMembers().end(); ++i, ++j)
+ {
+ if (i->name != j->name || i->type != j->type) {
+ std::cerr
+ << "plain struct type " << name
+ << " direct member #"
+ << i - ent2A->getDirectMembers().begin() + 1
+ << " changed from " << i->type << " " << i->name
+ << " to " << j->type << " " << j->name
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ break;
+ }
+ case unoidl::Entity::SORT_POLYMORPHIC_STRUCT_TYPE_TEMPLATE:
+ {
+ rtl::Reference<unoidl::PolymorphicStructTypeTemplateEntity>
+ ent2A(
+ static_cast<unoidl::PolymorphicStructTypeTemplateEntity *>(
+ entA.get()));
+ rtl::Reference<unoidl::PolymorphicStructTypeTemplateEntity>
+ ent2B(
+ static_cast<unoidl::PolymorphicStructTypeTemplateEntity *>(
+ entB.get()));
+ if (ent2A->getTypeParameters().size()
+ != ent2B->getTypeParameters().size())
+ {
+ std::cerr
+ << "polymorphic struct type template " << name
+ << " number of type parameters changed from "
+ << ent2A->getTypeParameters().size() << " to "
+ << ent2B->getTypeParameters().size() << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ for (auto
+ i(ent2A->getTypeParameters().begin()),
+ j(ent2B->getTypeParameters().begin());
+ i != ent2A->getTypeParameters().end(); ++i, ++j)
+ {
+ if (*i != *j) {
+ std::cerr
+ << "polymorphic struct type template " << name
+ << " type parameter #"
+ << i - ent2A->getTypeParameters().begin() + 1
+ << " changed from " << *i << " to " << *j
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ if (ent2A->getMembers().size()
+ != ent2B->getMembers().size())
+ {
+ std::cerr
+ << "polymorphic struct type template " << name
+ << " number of members changed from "
+ << ent2A->getMembers().size() << " to "
+ << ent2B->getMembers().size() << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ for (auto
+ i(ent2A->getMembers().begin()),
+ j(ent2B->getMembers().begin());
+ i != ent2A->getMembers().end(); ++i, ++j)
+ {
+ if (i->name != j->name || i->type != j->type
+ || i->parameterized != j->parameterized)
+ {
+ std::cerr
+ << "polymorphic struct type template " << name
+ << " member #"
+ << i - ent2A->getMembers().begin() + 1
+ << " changed from "
+ << (i->parameterized
+ ? OUString("parameterized ") : OUString())
+ << i->type << " " << i->name
+ << " to "
+ << (j->parameterized
+ ? OUString("parameterized ") : OUString())
+ << j->type << " " << j->name
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ break;
+ }
+ case unoidl::Entity::SORT_EXCEPTION_TYPE:
+ {
+ rtl::Reference<unoidl::ExceptionTypeEntity> ent2A(
+ static_cast<unoidl::ExceptionTypeEntity *>(entA.get()));
+ rtl::Reference<unoidl::ExceptionTypeEntity> ent2B(
+ static_cast<unoidl::ExceptionTypeEntity *>(entB.get()));
+ if (ent2A->getDirectBase() != ent2B->getDirectBase()) {
+ std::cerr
+ << "exception type " << name
+ << " direct base changed from "
+ << (ent2A->getDirectBase().isEmpty()
+ ? OUString("none") : ent2A->getDirectBase())
+ << " to "
+ << (ent2B->getDirectBase().isEmpty()
+ ? OUString("none") : ent2B->getDirectBase())
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ if (ent2A->getDirectMembers().size()
+ != ent2B->getDirectMembers().size())
+ {
+ std::cerr
+ << "exception type " << name
+ << " number of direct members changed from "
+ << ent2A->getDirectMembers().size() << " to "
+ << ent2B->getDirectMembers().size() << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ for (auto
+ i(ent2A->getDirectMembers().begin()),
+ j(ent2B->getDirectMembers().begin());
+ i != ent2A->getDirectMembers().end(); ++i, ++j)
+ {
+ if (i->name != j->name || i->type != j->type) {
+ std::cerr
+ << "exception type " << name
+ << " direct member #"
+ << i - ent2A->getDirectMembers().begin() + 1
+ << " changed from " << i->type << " " << i->name
+ << " to " << j->type << " " << j->name
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ break;
+ }
+ case unoidl::Entity::SORT_INTERFACE_TYPE:
+ {
+ rtl::Reference<unoidl::InterfaceTypeEntity> ent2A(
+ static_cast<unoidl::InterfaceTypeEntity *>(entA.get()));
+ rtl::Reference<unoidl::InterfaceTypeEntity> ent2B(
+ static_cast<unoidl::InterfaceTypeEntity *>(entB.get()));
+ if (ent2A->getDirectMandatoryBases().size()
+ != ent2B->getDirectMandatoryBases().size())
+ {
+ std::cerr
+ << "interface type " << name
+ << " number of direct mandatory bases changed from "
+ << ent2A->getDirectMandatoryBases().size() << " to "
+ << ent2B->getDirectMandatoryBases().size()
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ for (auto
+ i(ent2A->getDirectMandatoryBases().begin()),
+ j(ent2B->getDirectMandatoryBases().begin());
+ i != ent2A->getDirectMandatoryBases().end(); ++i, ++j)
+ {
+ if (i->name != j->name) {
+ std::cerr
+ << "interface type " << name
+ << " direct mandatory base #"
+ << (i - ent2A->getDirectMandatoryBases().begin()
+ + 1)
+ << " changed from " << i->name << " to "
+ << j->name << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ if (ent2A->getDirectOptionalBases().size()
+ != ent2B->getDirectOptionalBases().size())
+ {
+ std::cerr
+ << "interface type " << name
+ << " number of direct optional bases changed from "
+ << ent2A->getDirectOptionalBases().size() << " to "
+ << ent2B->getDirectOptionalBases().size()
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ for (auto
+ i(ent2A->getDirectOptionalBases().begin()),
+ j(ent2B->getDirectOptionalBases().begin());
+ i != ent2A->getDirectOptionalBases().end(); ++i, ++j)
+ {
+ if (i->name != j->name) {
+ std::cerr
+ << "interface type " << name
+ << " direct optional base #"
+ << (i - ent2A->getDirectOptionalBases().begin()
+ + 1)
+ << " changed from " << i->name << " to "
+ << j->name << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ if (ent2A->getDirectAttributes().size()
+ != ent2B->getDirectAttributes().size())
+ {
+ std::cerr
+ << "interface type " << name
+ << " number of direct attributes changed from "
+ << ent2A->getDirectAttributes().size() << " to "
+ << ent2B->getDirectAttributes().size() << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ for (auto
+ i(ent2A->getDirectAttributes().begin()),
+ j(ent2B->getDirectAttributes().begin());
+ i != ent2A->getDirectAttributes().end(); ++i, ++j)
+ {
+ if (i->name != j->name || i->type != j->type
+ || i->bound != j->bound
+ || i->readOnly != j->readOnly
+ || i->getExceptions != j->getExceptions
+ || i->setExceptions != j->setExceptions)
+ {
+ std::cerr
+ << "interface type " << name
+ << " direct attribute #"
+ << i - ent2A->getDirectAttributes().begin() + 1
+ << " changed from "
+ << (i->bound ? OUString("bound ") : OUString())
+ << (i->readOnly
+ ? OUString("read-only ") : OUString())
+ << i->type << " " << i->name //TODO: exceptions
+ << " to "
+ << (j->bound ? OUString("bound ") : OUString())
+ << (j->readOnly
+ ? OUString("read-only ") : OUString())
+ << j->type << " " << j->name //TODO: exceptions
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ if (ent2A->getDirectMethods().size()
+ != ent2B->getDirectMethods().size())
+ {
+ std::cerr
+ << "interface type " << name
+ << " number of direct methods changed from "
+ << ent2A->getDirectMethods().size() << " to "
+ << ent2B->getDirectMethods().size() << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ for (auto
+ i(ent2A->getDirectMethods().begin()),
+ j(ent2B->getDirectMethods().begin());
+ i != ent2A->getDirectMethods().end(); ++i, ++j)
+ {
+ if (i->name != j->name || i->returnType != j->returnType
+ || i->exceptions != j->exceptions)
+ {
+ std::cerr
+ << "interface type " << name
+ << " direct method #"
+ << i - ent2A->getDirectMethods().begin() + 1
+ << " changed from "
+ << i->returnType << " " << i->name //TODO: exceptions
+ << " to " << j->returnType << " " << j->name //TODO: exceptions
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ if (i->parameters.size() != j->parameters.size()) {
+ std::cerr
+ << "interface type " << name
+ << " direct method " << i->name
+ << " number of parameters changed from "
+ << i->parameters.size() << " to "
+ << j->parameters.size() << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ for (auto
+ k(i->parameters.begin()),
+ l(j->parameters.begin());
+ k != i->parameters.end(); ++k, ++l)
+ {
+ if (k->type != l->type || k->direction != l->direction)
+ {
+ std::cerr
+ << "interface type " << name
+ << " direct method " << i->name
+ << " parameter #"
+ << k - i->parameters.begin() + 1
+ << " changed from "
+ << showDirection(k->direction) << " "
+ << k->type << " to "
+ << showDirection(l->direction) << " "
+ << l->type << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ if (k->name != l->name) {
+ std::cerr
+ << "interface type " << name
+ << " direct method " << i->name
+ << " parameter #"
+ << k - i->parameters.begin() + 1
+ << " changed name from " << k->name
+ << " to " << l->name << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ }
+ break;
+ }
+ case unoidl::Entity::SORT_TYPEDEF:
+ {
+ rtl::Reference<unoidl::TypedefEntity> ent2A(
+ static_cast<unoidl::TypedefEntity *>(entA.get()));
+ rtl::Reference<unoidl::TypedefEntity> ent2B(
+ static_cast<unoidl::TypedefEntity *>(entB.get()));
+ if (ent2A->getType() != ent2B->getType()) {
+ std::cerr
+ << "typedef " << name << " type changed from "
+ << ent2A->getType() << " to " << ent2B->getType()
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ break;
+ }
+ case unoidl::Entity::SORT_CONSTANT_GROUP:
+ {
+ rtl::Reference<unoidl::ConstantGroupEntity> ent2A(
+ static_cast<unoidl::ConstantGroupEntity *>(entA.get()));
+ rtl::Reference<unoidl::ConstantGroupEntity> ent2B(
+ static_cast<unoidl::ConstantGroupEntity *>(entB.get()));
+ for (auto & i: ent2A->getMembers()) {
+ bool found = false;
+ for (auto & j: ent2B->getMembers()) {
+ if (i.name == j.name) {
+ if (i.value != j.value) {
+ std::cerr
+ << "constant group " << name
+ << " member " << i.name
+ << " changed value" << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ std::cerr
+ << "A constant group " << name << " member "
+ << i.name << " is not present in B"
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ break;
+ }
+ case unoidl::Entity::SORT_SINGLE_INTERFACE_BASED_SERVICE:
+ {
+ rtl::Reference<unoidl::SingleInterfaceBasedServiceEntity>
+ ent2A(
+ static_cast<unoidl::SingleInterfaceBasedServiceEntity *>(
+ entA.get()));
+ rtl::Reference<unoidl::SingleInterfaceBasedServiceEntity>
+ ent2B(
+ static_cast<unoidl::SingleInterfaceBasedServiceEntity *>(
+ entB.get()));
+ if (ent2A->getBase() != ent2B->getBase()) {
+ std::cerr
+ << "single-interface--based service " << name
+ << " base changed from " << ent2A->getBase()
+ << " to " << ent2B->getBase()
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ if (ent2A->getConstructors().size()
+ != ent2B->getConstructors().size())
+ {
+ std::cerr
+ << "single-interface--based service " << name
+ << " number of constructors changed from "
+ << ent2A->getConstructors().size() << " to "
+ << ent2B->getConstructors().size() << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ for (auto
+ i(ent2A->getConstructors().begin()),
+ j(ent2B->getConstructors().begin());
+ i != ent2A->getConstructors().end(); ++i, ++j)
+ {
+ if (i->name != j->name || i->parameters != j->parameters
+ || i->exceptions != j->exceptions
+ || i->defaultConstructor != j->defaultConstructor)
+ {
+ std::cerr
+ << "single-interface--based service " << name
+ << " constructor #"
+ << i - ent2A->getConstructors().begin() + 1
+ << " changed from "
+ << (i->defaultConstructor
+ ? OUString("default ") : i->name) //TODO: parameters, exceptions
+ << " to "
+ << (j->defaultConstructor
+ ? OUString("default ") : j->name) //TODO: parameters, exceptions
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ break;
+ }
+ case unoidl::Entity::SORT_ACCUMULATION_BASED_SERVICE:
+ {
+ rtl::Reference<unoidl::AccumulationBasedServiceEntity>
+ ent2A(
+ static_cast<unoidl::AccumulationBasedServiceEntity *>(
+ entA.get()));
+ rtl::Reference<unoidl::AccumulationBasedServiceEntity>
+ ent2B(
+ static_cast<unoidl::AccumulationBasedServiceEntity *>(
+ entB.get()));
+ if (ent2A->getDirectMandatoryBaseServices().size()
+ != ent2B->getDirectMandatoryBaseServices().size())
+ {
+ std::cerr
+ << "accumulation-based service " << name
+ << (" number of direct mandatory base services"
+ " changed from ")
+ << ent2A->getDirectMandatoryBaseServices().size()
+ << " to "
+ << ent2B->getDirectMandatoryBaseServices().size()
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ for (auto
+ i(ent2A->getDirectMandatoryBaseServices().begin()),
+ j(ent2B->getDirectMandatoryBaseServices().begin());
+ i != ent2A->getDirectMandatoryBaseServices().end();
+ ++i, ++j)
+ {
+ if (i->name != j->name) {
+ std::cerr
+ << "accumulation-based service " << name
+ << " direct mandatory base service #"
+ << (i
+ - (ent2A->getDirectMandatoryBaseServices()
+ .begin())
+ + 1)
+ << " changed from " << i->name << " to "
+ << j->name << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ if (ent2A->getDirectOptionalBaseServices().size()
+ > ent2B->getDirectOptionalBaseServices().size())
+ {
+ std::cerr
+ << "accumulation-based service " << name
+ << (" number of direct optional base services"
+ " shrank from ")
+ << ent2A->getDirectOptionalBaseServices().size()
+ << " to "
+ << ent2B->getDirectOptionalBaseServices().size()
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ for (auto & i: ent2A->getDirectOptionalBaseServices()) {
+ if (std::none_of(
+ ent2B->getDirectOptionalBaseServices().begin(),
+ ent2B->getDirectOptionalBaseServices().end(),
+ EqualsAnnotation(i.name)))
+ {
+ std::cerr
+ << "accumulation-based service " << name
+ << " direct optional base service " << i.name
+ << " was removed" << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ if (ent2A->getDirectMandatoryBaseInterfaces().size()
+ != ent2B->getDirectMandatoryBaseInterfaces().size())
+ {
+ std::cerr
+ << "accumulation-based service " << name
+ << (" number of direct mandatory base interfaces"
+ " changed from ")
+ << ent2A->getDirectMandatoryBaseInterfaces().size()
+ << " to "
+ << ent2B->getDirectMandatoryBaseInterfaces().size()
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ for (auto
+ i(ent2A->getDirectMandatoryBaseInterfaces()
+ .begin()),
+ j(ent2B->getDirectMandatoryBaseInterfaces()
+ .begin());
+ i != ent2A->getDirectMandatoryBaseInterfaces().end();
+ ++i, ++j)
+ {
+ if (i->name != j->name) {
+ std::cerr
+ << "accumulation-based service " << name
+ << " direct mandatory base interface #"
+ << (i
+ - (ent2A->getDirectMandatoryBaseInterfaces()
+ .begin())
+ + 1)
+ << " changed from " << i->name << " to "
+ << j->name << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ if (ent2A->getDirectOptionalBaseInterfaces().size()
+ > ent2B->getDirectOptionalBaseInterfaces().size())
+ {
+ std::cerr
+ << "accumulation-based service " << name
+ << (" number of direct optional base interfaces"
+ " shrank from ")
+ << ent2A->getDirectOptionalBaseInterfaces().size()
+ << " to "
+ << ent2B->getDirectOptionalBaseInterfaces().size()
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ for (auto & i: ent2A->getDirectOptionalBaseInterfaces()) {
+ if (std::none_of(
+ (ent2B->getDirectOptionalBaseInterfaces()
+ .begin()),
+ ent2B->getDirectOptionalBaseInterfaces().end(),
+ EqualsAnnotation(i.name)))
+ {
+ std::cerr
+ << "accumulation-based service " << name
+ << " direct optional base interface " << i.name
+ << " was removed" << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ if (ent2A->getDirectProperties().size()
+ > ent2B->getDirectProperties().size())
+ {
+ std::cerr
+ << "accumulation-based service " << name
+ << " number of direct properties changed from "
+ << ent2A->getDirectProperties().size() << " to "
+ << ent2B->getDirectProperties().size() << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ for (auto
+ i(ent2A->getDirectProperties().begin()),
+ j(ent2B->getDirectProperties().begin());
+ i != ent2A->getDirectProperties().end(); ++i, ++j)
+ {
+ if (i->name != j->name || i->type != j->type
+ || i->attributes != j->attributes)
+ {
+ std::cerr
+ << "accumulation-based service " << name
+ << " direct property #"
+ << i - ent2A->getDirectProperties().begin() + 1
+ << " changed from "
+ << i->type << " " << i->name //TODO: attributes
+ << " to "
+ << j->type << " " << j->name //TODO: attributes
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ for (auto
+ i(ent2B->getDirectProperties().begin()
+ + ent2A->getDirectProperties().size());
+ i != ent2B->getDirectProperties().end(); ++i)
+ {
+ if ((i->attributes & unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_OPTIONAL) == 0)
+ {
+ std::cerr
+ << "B accumulation-based service " << name
+ << " additional direct property " << i->name
+ << " is not optional" << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ break;
+ }
+ case unoidl::Entity::SORT_INTERFACE_BASED_SINGLETON:
+ {
+ rtl::Reference<unoidl::InterfaceBasedSingletonEntity> ent2A(
+ static_cast<unoidl::InterfaceBasedSingletonEntity *>(
+ entA.get()));
+ rtl::Reference<unoidl::InterfaceBasedSingletonEntity> ent2B(
+ static_cast<unoidl::InterfaceBasedSingletonEntity *>(
+ entB.get()));
+ if (ent2A->getBase() != ent2B->getBase()) {
+ std::cerr
+ << "interface-based singleton " << name
+ << " base changed from " << ent2A->getBase()
+ << " to " << ent2B->getBase() << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ break;
+ }
+ case unoidl::Entity::SORT_SERVICE_BASED_SINGLETON:
+ {
+ rtl::Reference<unoidl::ServiceBasedSingletonEntity> ent2A(
+ static_cast<unoidl::ServiceBasedSingletonEntity *>(
+ entA.get()));
+ rtl::Reference<unoidl::ServiceBasedSingletonEntity> ent2B(
+ static_cast<unoidl::ServiceBasedSingletonEntity *>(
+ entB.get()));
+ if (ent2A->getBase() != ent2B->getBase()) {
+ std::cerr
+ << "service-based singleton " << name
+ << " base changed from " << ent2A->getBase()
+ << " to " << ent2B->getBase() << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ break;
+ }
+ case unoidl::Entity::SORT_MODULE:
+ assert(false && "this cannot happen");
+ }
+ }
+ }
+}
+
+bool valid(std::u16string_view identifier) {
+ for (size_t i = 0;; ++i) {
+ i = identifier.find('_', i);
+ if (i == std::u16string_view::npos) {
+ return true;
+ }
+ if (!rtl::isAsciiUpperCase(identifier[0]) || identifier[i - 1] == '_') {
+ return false;
+ }
+ }
+}
+
+void checkIds(
+ rtl::Reference<unoidl::Provider> const & providerA, std::u16string_view prefix,
+ rtl::Reference<unoidl::MapCursor> const & cursor)
+{
+ assert(cursor.is());
+ for (;;) {
+ OUString id;
+ rtl::Reference<unoidl::Entity> entB(cursor->getNext(&id));
+ if (!entB.is()) {
+ break;
+ }
+ OUString name(prefix + id);
+ rtl::Reference<unoidl::Entity> entA(providerA->findEntity(name));
+ if (!(entA.is() || valid(id))) {
+ std::cerr
+ << "entity name " << name << " uses an invalid identifier"
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ switch (entB->getSort()) {
+ case unoidl::Entity::SORT_MODULE:
+ checkIds(
+ providerA, OUStringConcatenation(name + "."),
+ (static_cast<unoidl::ModuleEntity *>(entB.get())
+ ->createCursor()));
+ break;
+ case unoidl::Entity::SORT_ENUM_TYPE:
+ if (!entA.is()) {
+ rtl::Reference<unoidl::EnumTypeEntity> ent2B(
+ static_cast<unoidl::EnumTypeEntity *>(entB.get()));
+ for (auto & i: ent2B->getMembers()) {
+ if (!valid(i.name)) {
+ std::cerr
+ << "enum type " << name << " member " << i.name
+ << " uses an invalid identifier" << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ }
+ break;
+ case unoidl::Entity::SORT_PLAIN_STRUCT_TYPE:
+ if (!entA.is()) {
+ rtl::Reference<unoidl::PlainStructTypeEntity> ent2B(
+ static_cast<unoidl::PlainStructTypeEntity *>(
+ entB.get()));
+ for (auto & i: ent2B->getDirectMembers()) {
+ if (!valid(i.name)) {
+ std::cerr
+ << "plain struct type " << name << " direct member "
+ << i.name << " uses an invalid identifier"
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ }
+ break;
+ case unoidl::Entity::SORT_POLYMORPHIC_STRUCT_TYPE_TEMPLATE:
+ if (!entA.is()) {
+ rtl::Reference<unoidl::PolymorphicStructTypeTemplateEntity>
+ ent2B(
+ static_cast<
+ unoidl::PolymorphicStructTypeTemplateEntity *>(
+ entB.get()));
+ for (auto & i: ent2B->getTypeParameters()) {
+ if (!valid(i)) {
+ std::cerr
+ << "polymorphic struct type template " << name
+ << " type parameter " << i
+ << " uses an invalid identifier" << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ for (auto & i: ent2B->getMembers()) {
+ if (!valid(i.name)) {
+ std::cerr
+ << "polymorphic struct type template " << name
+ << " member " << i.name
+ << " uses an invalid identifier" << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ }
+ break;
+ case unoidl::Entity::SORT_EXCEPTION_TYPE:
+ if (!entA.is()) {
+ rtl::Reference<unoidl::ExceptionTypeEntity> ent2B(
+ static_cast<unoidl::ExceptionTypeEntity *>(entB.get()));
+ for (auto & i: ent2B->getDirectMembers()) {
+ if (!valid(i.name)) {
+ std::cerr
+ << "exception type " << name << " direct member "
+ << i.name << " uses an invalid identifier"
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ }
+ break;
+ case unoidl::Entity::SORT_INTERFACE_TYPE:
+ if (!entA.is()) {
+ rtl::Reference<unoidl::InterfaceTypeEntity> ent2B(
+ static_cast<unoidl::InterfaceTypeEntity *>(entB.get()));
+ for (auto & i: ent2B->getDirectAttributes()) {
+ if (!valid(i.name)) {
+ std::cerr
+ << "interface type " << name << " direct attribute "
+ << i.name << " uses an invalid identifier"
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ for (auto & i: ent2B->getDirectMethods()) {
+ if (!valid(i.name)) {
+ std::cerr
+ << "interface type " << name << " direct method "
+ << i.name << " uses an invalid identifier"
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ for (auto & j: i.parameters) {
+ if (!valid(j.name)) {
+ std::cerr
+ << "interface type " << name
+ << " direct method " << i.name << " parameter "
+ << j.name << " uses an invalid identifier"
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ }
+ }
+ break;
+ case unoidl::Entity::SORT_TYPEDEF:
+ case unoidl::Entity::SORT_INTERFACE_BASED_SINGLETON:
+ case unoidl::Entity::SORT_SERVICE_BASED_SINGLETON:
+ break;
+ case unoidl::Entity::SORT_CONSTANT_GROUP:
+ {
+ rtl::Reference<unoidl::ConstantGroupEntity> ent2B(
+ static_cast<unoidl::ConstantGroupEntity *>(entB.get()));
+ for (auto & i: ent2B->getMembers()) {
+ bool found = false;
+ if (entA.is()) {
+ rtl::Reference<unoidl::ConstantGroupEntity> ent2A(
+ static_cast<unoidl::ConstantGroupEntity *>(
+ entA.get()));
+ for (auto & j: ent2A->getMembers()) {
+ if (i.name == j.name) {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!(found || valid(i.name))) {
+ std::cerr
+ << "Constant group " << name << " member "
+ << i.name << " uses an invalid identifier"
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ break;
+ }
+ case unoidl::Entity::SORT_SINGLE_INTERFACE_BASED_SERVICE:
+ if (!entA.is()) {
+ rtl::Reference<unoidl::SingleInterfaceBasedServiceEntity>
+ ent2B(
+ static_cast<unoidl::SingleInterfaceBasedServiceEntity *>(
+ entB.get()));
+ for (auto & i: ent2B->getConstructors()) {
+ if (!valid(i.name)) {
+ std::cerr
+ << "single-interface--based service " << name
+ << " constructor " << i.name
+ << " uses an invalid identifier" << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ for (auto & j: i.parameters) {
+ if (!valid(j.name)) {
+ std::cerr
+ << "single-interface--based service " << name
+ << " constructor " << i.name << " parameter "
+ << j.name << " uses an invalid identifier"
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ }
+ }
+ break;
+ case unoidl::Entity::SORT_ACCUMULATION_BASED_SERVICE:
+ {
+ rtl::Reference<unoidl::AccumulationBasedServiceEntity> ent2B(
+ static_cast<unoidl::AccumulationBasedServiceEntity *>(
+ entB.get()));
+ std::vector<unoidl::AccumulationBasedServiceEntity::Property>::size_type
+ n(entA.is()
+ ? (static_cast<unoidl::AccumulationBasedServiceEntity *>(
+ entA.get())
+ ->getDirectProperties().size())
+ : 0);
+ assert(n <= ent2B->getDirectProperties().size());
+ for (auto i(ent2B->getDirectProperties().begin() +n);
+ i != ent2B->getDirectProperties().end(); ++i)
+ {
+ if (!valid(i->name)) {
+ std::cerr
+ << "accumulation-based service " << name
+ << " direct property " << i->name
+ << " uses an invalid identifier" << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ break;
+ }
+ }
+ }
+}
+
+}
+
+SAL_IMPLEMENT_MAIN() {
+ try {
+ sal_uInt32 args = rtl_getAppCommandArgCount();
+ rtl::Reference<unoidl::Manager> mgr[2];
+ mgr[0] = new unoidl::Manager;
+ mgr[1] = new unoidl::Manager;
+ rtl::Reference<unoidl::Provider> prov[2];
+ int side = 0;
+ bool ignoreUnpublished = false;
+ for (sal_uInt32 i = 0; i != args; ++i) {
+ bool delimiter = false;
+ OUString uri;
+ if (getArgument(
+ i, &ignoreUnpublished, side == 0 ? &delimiter : nullptr,
+ &uri))
+ {
+ try {
+ prov[side] = mgr[side]->addProvider(uri);
+ } catch (unoidl::NoSuchFileException &) {
+ std::cerr
+ << "Input <" << uri << "> does not exist" << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ } else if (delimiter) {
+ side = 1;
+ }
+ }
+ if (side == 0 || !(prov[0].is() && prov[1].is())) {
+ badUsage();
+ }
+ checkMap(prov[1], u"", prov[0]->createRootCursor(), ignoreUnpublished);
+ checkIds(prov[0], u"", prov[1]->createRootCursor());
+ return EXIT_SUCCESS;
+ } catch (unoidl::FileFormatException & e1) {
+ std::cerr
+ << "Bad input <" << e1.getUri() << ">: " << e1.getDetail()
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ } catch (std::exception & e1) {
+ std::cerr << "Failure: " << e1.what() << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unoidl/source/unoidl-read.cxx b/unoidl/source/unoidl-read.cxx
new file mode 100644
index 000000000..283c660ce
--- /dev/null
+++ b/unoidl/source/unoidl-read.cxx
@@ -0,0 +1,1139 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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 <cstdlib>
+#include <iostream>
+#include <map>
+#include <set>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include <osl/file.hxx>
+#include <osl/process.h>
+#include <rtl/process.h>
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/main.h>
+#include <sal/types.h>
+#include <unoidl/unoidl.hxx>
+#include <o3tl/string_view.hxx>
+
+namespace {
+
+void badUsage() {
+ std::cerr
+ << "Usage:" << std::endl << std::endl
+ << " unoidl-read [--published] [--summary] [<extra registries>] <registry>"
+ << std::endl << std::endl
+ << ("where each <registry> is either a new- or legacy-format .rdb file,"
+ " a single .idl")
+ << std::endl
+ << ("file, or a root directory of an .idl file tree. The complete"
+ " content of the")
+ << std::endl
+ << ("last <registry> is written to stdout; if --published is specified,"
+ " only the")
+ << std::endl
+ << ("published entities (plus any non-published entities referenced"
+ " from published")
+ << std::endl
+ << "via any unpublished optional bases) are written out. If --summary is specified,"
+ << std::endl
+ << "only a short summary is written, with the type and name of one entity per line."
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+}
+
+OUString getArgumentUri(sal_uInt32 argument) {
+ OUString arg;
+ rtl_getAppCommandArg(argument, &arg.pData);
+ OUString url;
+ osl::FileBase::RC e1 = osl::FileBase::getFileURLFromSystemPath(arg, url);
+ if (e1 != osl::FileBase::E_None) {
+ std::cerr
+ << "Cannot convert \"" << arg << "\" to file URL, error code "
+ << +e1 << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ OUString cwd;
+ oslProcessError e2 = osl_getProcessWorkingDir(&cwd.pData);
+ if (e2 != osl_Process_E_None) {
+ std::cerr
+ << "Cannot obtain working directory, error code " << +e2
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ OUString abs;
+ e1 = osl::FileBase::getAbsoluteFileURL(cwd, url, abs);
+ if (e1 != osl::FileBase::E_None) {
+ std::cerr
+ << "Cannot make \"" << url
+ << "\" into an absolute file URL, error code " << +e1 << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ return abs;
+}
+
+std::u16string_view decomposeType(
+ std::u16string_view type, std::size_t * rank,
+ std::vector<OUString> * typeArguments, bool * entity)
+{
+ assert(rank != nullptr);
+ assert(typeArguments != nullptr);
+ assert(entity != nullptr);
+ std::u16string_view nucl(type);
+ *rank = 0;
+ typeArguments->clear();
+ while (o3tl::starts_with(nucl, u"[]", &nucl)) {
+ ++*rank;
+ }
+ size_t i = nucl.find('<');
+ if (i != std::u16string_view::npos) {
+ std::u16string_view tmpl(nucl.substr(0, i));
+ do {
+ ++i; // skip '<' or ','
+ size_t j = i;
+ for (size_t level = 0; j != nucl.size(); ++j) {
+ sal_Unicode c = nucl[j];
+ if (c == ',') {
+ if (level == 0) {
+ break;
+ }
+ } else if (c == '<') {
+ ++level;
+ } else if (c == '>') {
+ if (level == 0) {
+ break;
+ }
+ --level;
+ }
+ }
+ if (j != nucl.size()) {
+ typeArguments->push_back(OUString(nucl.substr(i, j - i)));
+ }
+ i = j;
+ } while (i != nucl.size() && nucl[i] != '>');
+ assert(i == nucl.size() - 1 && nucl[i] == '>');
+ assert(!typeArguments->empty());
+ nucl = tmpl;
+ }
+ assert(!nucl.empty());
+ *entity = nucl != u"void" && nucl != u"boolean" && nucl != u"byte"
+ && nucl != u"short" && nucl != u"unsigned short" && nucl != u"long"
+ && nucl != u"unsigned long" && nucl != u"hyper"
+ && nucl != u"unsigned hyper" && nucl != u"float" && nucl != u"double"
+ && nucl != u"char" && nucl != u"string" && nucl != u"type"
+ && nucl != u"any";
+ assert(*entity || typeArguments->empty());
+ return nucl;
+}
+
+struct Entity {
+ enum class Sorted { NO, ACTIVE, YES };
+ enum class Written { NO, DECLARATION, DEFINITION };
+
+ explicit Entity(
+ rtl::Reference<unoidl::Entity> theEntity, bool theRelevant, Entity * theParent):
+ entity(std::move(theEntity)), relevant(theRelevant), sorted(Sorted::NO),
+ written(Written::NO), parent(theParent)
+ {}
+
+ rtl::Reference<unoidl::Entity> const entity;
+ std::set<OUString> dependencies;
+ std::set<OUString> interfaceDependencies;
+ bool relevant;
+ Sorted sorted;
+ Written written;
+ Entity * parent;
+};
+
+void insertEntityDependency(
+ rtl::Reference<unoidl::Manager> const & manager,
+ std::map<OUString, Entity>::iterator const & iterator,
+ OUString const & name, bool weakInterfaceDependency = false)
+{
+ assert(manager.is());
+ if (name == iterator->first)
+ return;
+
+ bool ifc = false;
+ if (weakInterfaceDependency) {
+ rtl::Reference<unoidl::Entity> ent(manager->findEntity(name));
+ if (!ent.is()) {
+ std::cerr << "Unknown entity " << name << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ ifc = ent->getSort() == unoidl::Entity::SORT_INTERFACE_TYPE;
+ }
+ (ifc
+ ? iterator->second.interfaceDependencies
+ : iterator->second.dependencies)
+ .insert(name);
+}
+
+void insertEntityDependencies(
+ rtl::Reference<unoidl::Manager> const & manager,
+ std::map<OUString, Entity>::iterator const & iterator,
+ std::vector<OUString> const & names)
+{
+ for (auto & i: names) {
+ insertEntityDependency(manager, iterator, i);
+ }
+}
+
+void insertEntityDependencies(
+ rtl::Reference<unoidl::Manager> const & manager,
+ std::map<OUString, Entity>::iterator const & iterator,
+ std::vector<unoidl::AnnotatedReference> const & references)
+{
+ for (auto & i: references) {
+ insertEntityDependency(manager, iterator, i.name);
+ }
+}
+
+void insertTypeDependency(
+ rtl::Reference<unoidl::Manager> const & manager,
+ std::map<OUString, Entity>::iterator const & iterator,
+ std::u16string_view type)
+{
+ std::size_t rank;
+ std::vector<OUString> args;
+ bool entity;
+ OUString nucl(decomposeType(type, &rank, &args, &entity));
+ if (entity) {
+ insertEntityDependency(manager, iterator, nucl, true);
+ for (const auto & i: args) {
+ insertTypeDependency(manager, iterator, i);
+ }
+ }
+}
+
+void scanMap(
+ rtl::Reference<unoidl::Manager> const & manager,
+ rtl::Reference<unoidl::MapCursor> const & cursor, bool modules, bool published,
+ std::u16string_view prefix, Entity * parent, std::map<OUString, Entity> & entities)
+{
+ assert(cursor.is());
+ for (;;) {
+ OUString id;
+ rtl::Reference<unoidl::Entity> ent(cursor->getNext(&id));
+ if (!ent.is()) {
+ break;
+ }
+ OUString name(prefix + id);
+ if (ent->getSort() == unoidl::Entity::SORT_MODULE) {
+ Entity * p = nullptr;
+ if (modules) {
+ p = &entities.insert(std::make_pair(name, Entity(ent, !published, parent))).first
+ ->second;
+ }
+ scanMap(
+ manager,
+ static_cast<unoidl::ModuleEntity *>(ent.get())->createCursor(), modules,
+ published, OUStringConcatenation(name + "."), p, entities);
+ } else {
+ auto const pub = static_cast<unoidl::PublishableEntity *>(ent.get())->isPublished();
+ std::map<OUString, Entity>::iterator i(
+ entities.insert(
+ std::make_pair(
+ name,
+ Entity(
+ ent,
+ (!published
+ || pub),
+ parent)))
+ .first);
+ if (modules && published && pub) {
+ for (auto j = parent; j; j = j->parent) {
+ j->relevant = true;
+ }
+ }
+ switch (ent->getSort()) {
+ case unoidl::Entity::SORT_ENUM_TYPE:
+ case unoidl::Entity::SORT_CONSTANT_GROUP:
+ break;
+ case unoidl::Entity::SORT_PLAIN_STRUCT_TYPE:
+ {
+ rtl::Reference<unoidl::PlainStructTypeEntity> ent2(
+ static_cast<unoidl::PlainStructTypeEntity *>(
+ ent.get()));
+ if (!ent2->getDirectBase().isEmpty()) {
+ insertEntityDependency(
+ manager, i, ent2->getDirectBase());
+ }
+ for (auto & j: ent2->getDirectMembers()) {
+ insertTypeDependency(manager, i, j.type);
+ }
+ break;
+ }
+ case unoidl::Entity::SORT_POLYMORPHIC_STRUCT_TYPE_TEMPLATE:
+ {
+ rtl::Reference<unoidl::PolymorphicStructTypeTemplateEntity>
+ ent2(
+ static_cast<unoidl::PolymorphicStructTypeTemplateEntity *>(
+ ent.get()));
+ for (auto & j: ent2->getMembers()) {
+ if (!j.parameterized) {
+ insertTypeDependency(manager, i, j.type);
+ }
+ }
+ break;
+ }
+ case unoidl::Entity::SORT_EXCEPTION_TYPE:
+ {
+ rtl::Reference<unoidl::ExceptionTypeEntity> ent2(
+ static_cast<unoidl::ExceptionTypeEntity *>(ent.get()));
+ if (!ent2->getDirectBase().isEmpty()) {
+ insertEntityDependency(
+ manager, i, ent2->getDirectBase());
+ }
+ for (auto & j: ent2->getDirectMembers()) {
+ insertTypeDependency(manager, i, j.type);
+ }
+ break;
+ }
+ case unoidl::Entity::SORT_INTERFACE_TYPE:
+ {
+ rtl::Reference<unoidl::InterfaceTypeEntity> ent2(
+ static_cast<unoidl::InterfaceTypeEntity *>(
+ ent.get()));
+ insertEntityDependencies(
+ manager, i, ent2->getDirectMandatoryBases());
+ insertEntityDependencies(
+ manager, i, ent2->getDirectOptionalBases());
+ for (auto & j: ent2->getDirectAttributes()) {
+ insertTypeDependency(manager, i, j.type);
+ }
+ for (auto & j: ent2->getDirectMethods()) {
+ insertTypeDependency(manager, i, j.returnType);
+ for (auto & k: j.parameters) {
+ insertTypeDependency(manager, i, k.type);
+ }
+ insertEntityDependencies(manager, i, j.exceptions);
+ }
+ break;
+ }
+ case unoidl::Entity::SORT_TYPEDEF:
+ {
+ rtl::Reference<unoidl::TypedefEntity> ent2(
+ static_cast<unoidl::TypedefEntity *>(ent.get()));
+ insertTypeDependency(manager, i, ent2->getType());
+ break;
+ }
+ case unoidl::Entity::SORT_SINGLE_INTERFACE_BASED_SERVICE:
+ {
+ rtl::Reference<unoidl::SingleInterfaceBasedServiceEntity>
+ ent2(
+ static_cast<unoidl::SingleInterfaceBasedServiceEntity *>(
+ ent.get()));
+ insertEntityDependency(manager, i, ent2->getBase());
+ for (auto & j: ent2->getConstructors()) {
+ for (auto & k: j.parameters) {
+ insertTypeDependency(manager, i, k.type);
+ }
+ insertEntityDependencies(manager, i, j.exceptions);
+ }
+ break;
+ }
+ case unoidl::Entity::SORT_ACCUMULATION_BASED_SERVICE:
+ {
+ rtl::Reference<unoidl::AccumulationBasedServiceEntity> ent2(
+ static_cast<unoidl::AccumulationBasedServiceEntity *>(
+ ent.get()));
+ insertEntityDependencies(
+ manager, i, ent2->getDirectMandatoryBaseServices());
+ insertEntityDependencies(
+ manager, i, ent2->getDirectOptionalBaseServices());
+ insertEntityDependencies(
+ manager, i, ent2->getDirectMandatoryBaseInterfaces());
+ insertEntityDependencies(
+ manager, i, ent2->getDirectOptionalBaseInterfaces());
+ for (auto & j: ent2->getDirectProperties()) {
+ insertTypeDependency(manager, i, j.type);
+ }
+ break;
+ }
+ case unoidl::Entity::SORT_INTERFACE_BASED_SINGLETON:
+ {
+ rtl::Reference<unoidl::InterfaceBasedSingletonEntity> ent2(
+ static_cast<unoidl::InterfaceBasedSingletonEntity *>(
+ ent.get()));
+ insertEntityDependency(manager, i, ent2->getBase());
+ break;
+ }
+ case unoidl::Entity::SORT_SERVICE_BASED_SINGLETON:
+ {
+ rtl::Reference<unoidl::ServiceBasedSingletonEntity> ent2(
+ static_cast<unoidl::ServiceBasedSingletonEntity *>(
+ ent.get()));
+ insertEntityDependency(manager, i, ent2->getBase());
+ break;
+ }
+ case unoidl::Entity::SORT_MODULE:
+ assert(false && "this cannot happen");
+ }
+ }
+ }
+}
+
+void propagateRelevant(std::map<OUString, Entity> & entities, Entity & entity) {
+ if (!entity.relevant) {
+ entity.relevant = true;
+ if (entity.sorted != Entity::Sorted::YES) {
+ for (auto & i: entity.dependencies) {
+ std::map<OUString, Entity>::iterator j(entities.find(i));
+ if (j != entities.end()) {
+ propagateRelevant(entities, j->second);
+ }
+ }
+ }
+ }
+}
+
+void visit(
+ std::map<OUString, Entity> & entities,
+ std::map<OUString, Entity>::iterator const & iterator,
+ std::vector<OUString> & result)
+{
+ switch (iterator->second.sorted) {
+ case Entity::Sorted::NO:
+ iterator->second.sorted = Entity::Sorted::ACTIVE;
+ for (auto & i: iterator->second.dependencies) {
+ std::map<OUString, Entity>::iterator j(entities.find(i));
+ if (j != entities.end()) {
+ if (iterator->second.relevant) {
+ propagateRelevant(entities, j->second);
+ }
+ visit(entities, j, result);
+ }
+ }
+ iterator->second.sorted = Entity::Sorted::YES;
+ result.push_back(iterator->first);
+ break;
+ case Entity::Sorted::ACTIVE:
+ std::cerr
+ << "Entity " << iterator->first << " recursively depends on itself"
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ // fall-through avoids warnings
+ default:
+ break;
+ }
+}
+
+std::vector<OUString> sort(std::map<OUString, Entity> & entities) {
+ std::vector<OUString> res;
+ for (auto i(entities.begin()); i != entities.end(); ++i) {
+ visit(entities, i, res);
+ }
+ return res;
+}
+
+void indent(std::vector<OUString> const & modules, unsigned int extra = 0) {
+ for (std::vector<OUString>::size_type i = 0; i != modules.size(); ++i) {
+ std::cout << ' ';
+ }
+ for (unsigned int i = 0; i != extra; ++i) {
+ std::cout << ' ';
+ }
+}
+
+void closeModules(
+ std::vector<OUString> & modules, std::vector<OUString>::size_type n)
+{
+ for (std::vector<OUString>::size_type i = 0; i != n; ++i) {
+ assert(!modules.empty());
+ modules.pop_back();
+ indent(modules);
+ std::cout << "};\n";
+ }
+}
+
+OUString openModulesFor(std::vector<OUString> & modules, std::u16string_view name)
+{
+ std::vector<OUString>::iterator i(modules.begin());
+ for (sal_Int32 j = 0;;) {
+ OUString id(o3tl::getToken(name, 0, '.', j));
+ if (j == -1) {
+ closeModules(
+ modules,
+ static_cast< std::vector<OUString>::size_type >(
+ modules.end() - i));
+ indent(modules);
+ return id;
+ }
+ if (i != modules.end()) {
+ if (id == *i) {
+ ++i;
+ continue;
+ }
+ closeModules(
+ modules,
+ static_cast< std::vector<OUString>::size_type >(
+ modules.end() - i));
+ i = modules.end();
+ }
+ indent(modules);
+ std::cout << "module " << id << " {\n";
+ modules.push_back(id);
+ i = modules.end();
+ }
+}
+
+void writeName(OUString const & name) {
+ std::cout << "::" << name.replaceAll(".", "::");
+}
+
+void writeAnnotations(std::vector<OUString> const & annotations) {
+ if (!annotations.empty()) {
+ std::cout << "/**";
+ for (auto & i: annotations) {
+ //TODO: i.indexOf("*/") == -1
+ std::cout << " @" << i;
+ }
+ std::cout << " */ ";
+ }
+}
+
+void writePublished(rtl::Reference<unoidl::PublishableEntity> const & entity) {
+ assert(entity.is());
+ if (entity->isPublished()) {
+ std::cout << "published ";
+ }
+}
+
+void writeAnnotationsPublished(
+ rtl::Reference<unoidl::PublishableEntity> const & entity)
+{
+ assert(entity.is());
+ writeAnnotations(entity->getAnnotations());
+ writePublished(entity);
+}
+
+void writeType(std::u16string_view type) {
+ std::size_t rank;
+ std::vector<OUString> args;
+ bool entity;
+ OUString nucl(decomposeType(type, &rank, &args, &entity));
+ for (std::size_t i = 0; i != rank; ++i) {
+ std::cout << "sequence< ";
+ }
+ if (entity) {
+ writeName(nucl);
+ } else {
+ std::cout << nucl;
+ }
+ if (!args.empty()) {
+ std::cout << "< ";
+ for (auto i(args.begin()); i != args.end(); ++i) {
+ if (i != args.begin()) {
+ std::cout << ", ";
+ }
+ writeType(*i);
+ }
+ std::cout << " >";
+ }
+ for (std::size_t i = 0; i != rank; ++i) {
+ std::cout << " >";
+ }
+}
+
+void writeExceptionSpecification(std::vector<OUString> const & exceptions) {
+ if (!exceptions.empty()) {
+ std::cout << " raises (";
+ for (auto i(exceptions.begin()); i != exceptions.end(); ++i) {
+ if (i != exceptions.begin()) {
+ std::cout << ", ";
+ }
+ writeName(*i);
+ }
+ std::cout << ')';
+ }
+}
+
+void writeEntity(
+ std::map<OUString, Entity> & entities, std::vector<OUString> & modules,
+ OUString const & name)
+{
+ std::map<OUString, Entity>::iterator i(entities.find(name));
+ if (i == entities.end() || !i->second.relevant)
+ return;
+
+ assert(i->second.written != Entity::Written::DEFINITION);
+ i->second.written = Entity::Written::DEFINITION;
+ for (auto & j: i->second.interfaceDependencies) {
+ std::map<OUString, Entity>::iterator k(entities.find(j));
+ if (k != entities.end() && k->second.written == Entity::Written::NO) {
+ k->second.written = Entity::Written::DECLARATION;
+ OUString id(openModulesFor(modules, j));
+ if (k->second.entity->getSort()
+ != unoidl::Entity::SORT_INTERFACE_TYPE)
+ {
+ std::cerr
+ << "Entity " << j << " should be an interface type"
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ writePublished(
+ static_cast<unoidl::PublishableEntity *>(
+ k->second.entity.get()));
+ std::cout << "interface " << id << ";\n";
+ }
+ }
+ OUString id(openModulesFor(modules, name));
+ rtl::Reference<unoidl::PublishableEntity> ent(
+ static_cast<unoidl::PublishableEntity *>(i->second.entity.get()));
+ switch (ent->getSort()) {
+ case unoidl::Entity::SORT_ENUM_TYPE:
+ {
+ rtl::Reference<unoidl::EnumTypeEntity> ent2(
+ static_cast<unoidl::EnumTypeEntity *>(ent.get()));
+ writeAnnotationsPublished(ent);
+ std::cout << "enum " << id << " {\n";
+ for (auto j(ent2->getMembers().begin());
+ j != ent2->getMembers().end(); ++j)
+ {
+ indent(modules, 1);
+ writeAnnotations(j->annotations);
+ std::cout << j->name << " = " << j->value;
+ if (j + 1 != ent2->getMembers().end()) {
+ std::cout << ',';
+ }
+ std::cout << '\n';
+ }
+ indent(modules);
+ std::cout << "};\n";
+ break;
+ }
+ case unoidl::Entity::SORT_PLAIN_STRUCT_TYPE:
+ {
+ rtl::Reference<unoidl::PlainStructTypeEntity> ent2(
+ static_cast<unoidl::PlainStructTypeEntity *>(ent.get()));
+ writeAnnotationsPublished(ent);
+ std::cout << "struct " << id;
+ if (!ent2->getDirectBase().isEmpty()) {
+ std::cout << ": ";
+ writeName(ent2->getDirectBase());
+ }
+ std::cout << " {\n";
+ for (auto & j: ent2->getDirectMembers()) {
+ indent(modules, 1);
+ writeAnnotations(j.annotations);
+ writeType(j.type);
+ std::cout << ' ' << j.name << ";\n";
+ }
+ indent(modules);
+ std::cout << "};\n";
+ break;
+ }
+ case unoidl::Entity::SORT_POLYMORPHIC_STRUCT_TYPE_TEMPLATE:
+ {
+ rtl::Reference<unoidl::PolymorphicStructTypeTemplateEntity>
+ ent2(
+ static_cast<unoidl::PolymorphicStructTypeTemplateEntity *>(
+ ent.get()));
+ writeAnnotationsPublished(ent);
+ std::cout << "struct " << id << '<';
+ for (auto j(ent2->getTypeParameters().begin());
+ j != ent2->getTypeParameters().end(); ++j)
+ {
+ if (j != ent2->getTypeParameters().begin()) {
+ std::cout << ", ";
+ }
+ std::cout << *j;
+ }
+ std::cout << "> {\n";
+ for (auto & j: ent2->getMembers()) {
+ indent(modules, 1);
+ writeAnnotations(j.annotations);
+ if (j.parameterized) {
+ std::cout << j.type;
+ } else {
+ writeType(j.type);
+ }
+ std::cout << ' ' << j.name << ";\n";
+ }
+ indent(modules);
+ std::cout << "};\n";
+ break;
+ }
+ case unoidl::Entity::SORT_EXCEPTION_TYPE:
+ {
+ rtl::Reference<unoidl::ExceptionTypeEntity> ent2(
+ static_cast<unoidl::ExceptionTypeEntity *>(ent.get()));
+ writeAnnotationsPublished(ent);
+ std::cout << "exception " << id;
+ if (!ent2->getDirectBase().isEmpty()) {
+ std::cout << ": ";
+ writeName(ent2->getDirectBase());
+ }
+ std::cout << " {\n";
+ for (auto & j: ent2->getDirectMembers()) {
+ indent(modules, 1);
+ writeAnnotations(j.annotations);
+ writeType(j.type);
+ std::cout << ' ' << j.name << ";\n";
+ }
+ indent(modules);
+ std::cout << "};\n";
+ break;
+ }
+ case unoidl::Entity::SORT_INTERFACE_TYPE:
+ {
+ rtl::Reference<unoidl::InterfaceTypeEntity> ent2(
+ static_cast<unoidl::InterfaceTypeEntity *>(
+ ent.get()));
+ writeAnnotationsPublished(ent);
+ std::cout << "interface " << id << " {\n";
+ for (auto & j: ent2->getDirectMandatoryBases()) {
+ indent(modules, 1);
+ writeAnnotations(j.annotations);
+ std::cout << "interface ";
+ writeName(j.name);
+ std::cout << ";\n";
+ }
+ for (auto & j: ent2->getDirectOptionalBases()) {
+ indent(modules, 1);
+ writeAnnotations(j.annotations);
+ std::cout << "[optional] interface ";
+ writeName(j.name);
+ std::cout << ";\n";
+ }
+ for (auto & j: ent2->getDirectAttributes()) {
+ indent(modules, 1);
+ writeAnnotations(j.annotations);
+ std::cout << "[attribute";
+ if (j.bound) {
+ std::cout << ", bound";
+ }
+ if (j.readOnly) {
+ std::cout << ", readonly";
+ }
+ std::cout << "] ";
+ writeType(j.type);
+ std::cout << ' ' << j.name;
+ if (!(j.getExceptions.empty() && j.setExceptions.empty())) {
+ std::cout << " {\n";
+ if (!j.getExceptions.empty()) {
+ indent(modules, 2);
+ std::cout << "get";
+ writeExceptionSpecification(j.getExceptions);
+ std::cout << ";\n";
+ }
+ if (!j.setExceptions.empty()) {
+ indent(modules, 2);
+ std::cout << "set";
+ writeExceptionSpecification(j.setExceptions);
+ std::cout << ";\n";
+ }
+ std::cout << " }";
+ }
+ std::cout << ";\n";
+ }
+ for (auto & j: ent2->getDirectMethods()) {
+ indent(modules, 1);
+ writeAnnotations(j.annotations);
+ writeType(j.returnType);
+ std::cout << ' ' << j.name << '(';
+ for (auto k(j.parameters.begin()); k != j.parameters.end();
+ ++k)
+ {
+ if (k != j.parameters.begin()) {
+ std::cout << ", ";
+ }
+ switch (k->direction) {
+ case unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN:
+ std::cout << "[in] ";
+ break;
+ case unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_OUT:
+ std::cout << "[out] ";
+ break;
+ case unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN_OUT:
+ std::cout << "[inout] ";
+ break;
+ }
+ writeType(k->type);
+ std::cout << ' ' << k->name;
+ }
+ std::cout << ')';
+ writeExceptionSpecification(j.exceptions);
+ std::cout << ";\n";
+ }
+ indent(modules);
+ std::cout << "};\n";
+ break;
+ }
+ case unoidl::Entity::SORT_TYPEDEF:
+ {
+ rtl::Reference<unoidl::TypedefEntity> ent2(
+ static_cast<unoidl::TypedefEntity *>(ent.get()));
+ writeAnnotationsPublished(ent);
+ std::cout << "typedef ";
+ writeType(ent2->getType());
+ std::cout << ' ' << id << ";\n";
+ break;
+ }
+ case unoidl::Entity::SORT_CONSTANT_GROUP:
+ {
+ rtl::Reference<unoidl::ConstantGroupEntity> ent2(
+ static_cast<unoidl::ConstantGroupEntity *>(ent.get()));
+ writeAnnotationsPublished(ent);
+ std::cout << "constants " << id << " {\n";
+ for (auto & j: ent2->getMembers()) {
+ indent(modules, 1);
+ writeAnnotations(j.annotations);
+ std::cout << "const ";
+ switch (j.value.type) {
+ case unoidl::ConstantValue::TYPE_BOOLEAN:
+ std::cout << "boolean";
+ break;
+ case unoidl::ConstantValue::TYPE_BYTE:
+ std::cout << "byte";
+ break;
+ case unoidl::ConstantValue::TYPE_SHORT:
+ std::cout << "short";
+ break;
+ case unoidl::ConstantValue::TYPE_UNSIGNED_SHORT:
+ std::cout << "unsigned short";
+ break;
+ case unoidl::ConstantValue::TYPE_LONG:
+ std::cout << "long";
+ break;
+ case unoidl::ConstantValue::TYPE_UNSIGNED_LONG:
+ std::cout << "unsigned long";
+ break;
+ case unoidl::ConstantValue::TYPE_HYPER:
+ std::cout << "hyper";
+ break;
+ case unoidl::ConstantValue::TYPE_UNSIGNED_HYPER:
+ std::cout << "unsigned hyper";
+ break;
+ case unoidl::ConstantValue::TYPE_FLOAT:
+ std::cout << "float";
+ break;
+ case unoidl::ConstantValue::TYPE_DOUBLE:
+ std::cout << "double";
+ break;
+ }
+ std::cout << ' ' << j.name << " = ";
+ switch (j.value.type) {
+ case unoidl::ConstantValue::TYPE_BOOLEAN:
+ std::cout << (j.value.booleanValue ? "TRUE" : "FALSE");
+ break;
+ case unoidl::ConstantValue::TYPE_BYTE:
+ std::cout << int(j.value.byteValue);
+ break;
+ case unoidl::ConstantValue::TYPE_SHORT:
+ std::cout << j.value.shortValue;
+ break;
+ case unoidl::ConstantValue::TYPE_UNSIGNED_SHORT:
+ std::cout << j.value.unsignedShortValue;
+ break;
+ case unoidl::ConstantValue::TYPE_LONG:
+ std::cout << j.value.longValue;
+ break;
+ case unoidl::ConstantValue::TYPE_UNSIGNED_LONG:
+ std::cout << j.value.unsignedLongValue;
+ break;
+ case unoidl::ConstantValue::TYPE_HYPER:
+ std::cout << j.value.hyperValue;
+ break;
+ case unoidl::ConstantValue::TYPE_UNSIGNED_HYPER:
+ std::cout << j.value.unsignedHyperValue;
+ break;
+ case unoidl::ConstantValue::TYPE_FLOAT:
+ std::cout << j.value.floatValue;
+ break;
+ case unoidl::ConstantValue::TYPE_DOUBLE:
+ std::cout << j.value.doubleValue;
+ break;
+ }
+ std::cout << ";\n";
+ }
+ indent(modules);
+ std::cout << "};\n";
+ break;
+ }
+ case unoidl::Entity::SORT_SINGLE_INTERFACE_BASED_SERVICE:
+ {
+ rtl::Reference<unoidl::SingleInterfaceBasedServiceEntity> ent2(
+ static_cast<unoidl::SingleInterfaceBasedServiceEntity *>(
+ ent.get()));
+ writeAnnotationsPublished(ent);
+ std::cout << "service " << id << ": ";
+ writeName(ent2->getBase());
+ if (ent2->getConstructors().size() != 1
+ || !ent2->getConstructors().front().defaultConstructor)
+ {
+ std::cout << " {\n";
+ for (auto & j: ent2->getConstructors()) {
+ indent(modules, 1);
+ writeAnnotations(j.annotations);
+ std::cout << j.name << '(';
+ for (auto k(j.parameters.begin());
+ k != j.parameters.end(); ++k)
+ {
+ if (k != j.parameters.begin()) {
+ std::cout << ", ";
+ }
+ std::cout << "[in] ";
+ writeType(k->type);
+ if (k->rest) {
+ std::cout << "...";
+ }
+ std::cout << ' ' << k->name;
+ }
+ std::cout << ')';
+ writeExceptionSpecification(j.exceptions);
+ std::cout << ";\n";
+ }
+ indent(modules);
+ std::cout << '}';
+ }
+ std::cout << ";\n";
+ break;
+ }
+ case unoidl::Entity::SORT_ACCUMULATION_BASED_SERVICE:
+ {
+ rtl::Reference<unoidl::AccumulationBasedServiceEntity> ent2(
+ static_cast<unoidl::AccumulationBasedServiceEntity *>(
+ ent.get()));
+ writeAnnotationsPublished(ent);
+ std::cout << "service " << id << " {\n";
+ for (auto & j: ent2->getDirectMandatoryBaseServices()) {
+ indent(modules, 1);
+ writeAnnotations(j.annotations);
+ std::cout << "service ";
+ writeName(j.name);
+ std::cout << ";\n";
+ }
+ for (auto & j: ent2->getDirectOptionalBaseServices()) {
+ indent(modules, 1);
+ writeAnnotations(j.annotations);
+ std::cout << "[optional] service ";
+ writeName(j.name);
+ std::cout << ";\n";
+ }
+ for (auto & j: ent2->getDirectMandatoryBaseInterfaces()) {
+ indent(modules, 1);
+ writeAnnotations(j.annotations);
+ std::cout << "interface ";
+ writeName(j.name);
+ std::cout << ";\n";
+ }
+ for (auto & j: ent2->getDirectOptionalBaseInterfaces()) {
+ indent(modules, 1);
+ writeAnnotations(j.annotations);
+ std::cout << "[optional] interface ";
+ writeName(j.name);
+ std::cout << ";\n";
+ }
+ for (auto & j: ent2->getDirectProperties()) {
+ indent(modules, 1);
+ writeAnnotations(j.annotations);
+ std::cout << "[property";
+ if ((j.attributes
+ & unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_BOUND)
+ != 0)
+ {
+ std::cout << ", bound";
+ }
+ if ((j.attributes
+ & unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_CONSTRAINED)
+ != 0)
+ {
+ std::cout << ", constrained";
+ }
+ if ((j.attributes
+ & unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_MAYBE_AMBIGUOUS)
+ != 0)
+ {
+ std::cout << ", maybeambiguous";
+ }
+ if ((j.attributes
+ & unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_MAYBE_DEFAULT)
+ != 0)
+ {
+ std::cout << ", maybedefault";
+ }
+ if ((j.attributes
+ & unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_MAYBE_VOID)
+ != 0)
+ {
+ std::cout << ", maybevoid";
+ }
+ if ((j.attributes
+ & unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_OPTIONAL)
+ != 0)
+ {
+ std::cout << ", optional";
+ }
+ if ((j.attributes
+ & unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_READ_ONLY)
+ != 0)
+ {
+ std::cout << ", readonly";
+ }
+ if ((j.attributes
+ & unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_REMOVABLE)
+ != 0)
+ {
+ std::cout << ", removable";
+ }
+ if ((j.attributes
+ & unoidl::AccumulationBasedServiceEntity::Property::ATTRIBUTE_TRANSIENT)
+ != 0)
+ {
+ std::cout << ", transient";
+ }
+ std::cout << "] ";
+ writeType(j.type);
+ std::cout << ' ' << j.name << ";\n";
+ }
+ indent(modules);
+ std::cout << "};\n";
+ break;
+ }
+ case unoidl::Entity::SORT_INTERFACE_BASED_SINGLETON:
+ {
+ rtl::Reference<unoidl::InterfaceBasedSingletonEntity> ent2(
+ static_cast<unoidl::InterfaceBasedSingletonEntity *>(
+ ent.get()));
+ writeAnnotationsPublished(ent);
+ std::cout << "singleton " << id << ": ";
+ writeName(ent2->getBase());
+ std::cout << ";\n";
+ break;
+ }
+ case unoidl::Entity::SORT_SERVICE_BASED_SINGLETON:
+ {
+ rtl::Reference<unoidl::ServiceBasedSingletonEntity> ent2(
+ static_cast<unoidl::ServiceBasedSingletonEntity *>(
+ ent.get()));
+ writeAnnotationsPublished(ent);
+ std::cout << "singleton " << id << " { service ";
+ writeName(ent2->getBase());
+ std::cout << "; };";
+ break;
+ }
+ case unoidl::Entity::SORT_MODULE:
+ assert(false && "this cannot happen");
+ }
+}
+
+void writeSummary(OUString const & name, Entity const & entity) {
+ if (!entity.relevant) {
+ return;
+ }
+ switch (entity.entity->getSort()) {
+ case unoidl::Entity::SORT_ENUM_TYPE:
+ std::cout << "enum";
+ break;
+ case unoidl::Entity::SORT_PLAIN_STRUCT_TYPE:
+ case unoidl::Entity::SORT_POLYMORPHIC_STRUCT_TYPE_TEMPLATE:
+ std::cout << "struct";
+ break;
+ case unoidl::Entity::SORT_EXCEPTION_TYPE:
+ std::cout << "exception";
+ break;
+ case unoidl::Entity::SORT_INTERFACE_TYPE:
+ std::cout << "interface";
+ break;
+ case unoidl::Entity::SORT_TYPEDEF:
+ std::cout << "typedef";
+ break;
+ case unoidl::Entity::SORT_CONSTANT_GROUP:
+ std::cout << "constants";
+ break;
+ case unoidl::Entity::SORT_SINGLE_INTERFACE_BASED_SERVICE:
+ case unoidl::Entity::SORT_ACCUMULATION_BASED_SERVICE:
+ std::cout << "service";
+ break;
+ case unoidl::Entity::SORT_INTERFACE_BASED_SINGLETON:
+ case unoidl::Entity::SORT_SERVICE_BASED_SINGLETON:
+ std::cout << "singleton";
+ break;
+ case unoidl::Entity::SORT_MODULE:
+ std::cout << "module";
+ break;
+ }
+ std::cout << ' ' << name << '\n';
+}
+
+}
+
+SAL_IMPLEMENT_MAIN() {
+ try {
+ sal_uInt32 args = rtl_getAppCommandArgCount();
+ sal_uInt32 i = 0;
+ bool published = false;
+ bool summary = false;
+ for (;; ++i) {
+ if (i == args) {
+ badUsage();
+ }
+ OUString arg;
+ rtl_getAppCommandArg(i, &arg.pData);
+ if (arg == "--published") {
+ if (published) {
+ badUsage();
+ }
+ published = true;
+ } else if (arg == "--summary") {
+ if (summary) {
+ badUsage();
+ }
+ summary = true;
+ } else {
+ break;
+ }
+ }
+ rtl::Reference<unoidl::Manager> mgr(new unoidl::Manager);
+ rtl::Reference<unoidl::Provider> prov;
+ for (; i != args; ++i) {
+ OUString uri(getArgumentUri(i));
+ try {
+ prov = mgr->addProvider(uri);
+ } catch (unoidl::NoSuchFileException &) {
+ std::cerr
+ << "Input <" << uri << "> does not exist" << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ std::map<OUString, Entity> ents;
+ scanMap(mgr, prov->createRootCursor(), summary, published, u"", nullptr, ents);
+ if (summary) {
+ for (auto const & j: ents) {
+ writeSummary(j.first, j.second);
+ }
+ } else {
+ std::vector<OUString> sorted(sort(ents));
+ std::vector<OUString> mods;
+ for (const auto & j: sorted) {
+ writeEntity(ents, mods, j);
+ }
+ closeModules(mods, mods.size());
+ }
+ return EXIT_SUCCESS;
+ } catch (unoidl::FileFormatException & e1) {
+ std::cerr
+ << "Bad input <" << e1.getUri() << ">: " << e1.getDetail()
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ } catch (std::exception & e1) {
+ std::cerr << "Failure: " << e1.what() << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unoidl/source/unoidl-write.cxx b/unoidl/source/unoidl-write.cxx
new file mode 100644
index 000000000..84cad5bdd
--- /dev/null
+++ b/unoidl/source/unoidl-write.cxx
@@ -0,0 +1,951 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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 <algorithm>
+#include <cassert>
+#include <cstdlib>
+#include <iostream>
+#include <map>
+#include <utility>
+#include <vector>
+
+#include <config_version.h>
+#include <osl/endian.h>
+#include <osl/file.h>
+#include <osl/file.hxx>
+#include <osl/process.h>
+#include <rtl/byteseq.hxx>
+#include <rtl/process.h>
+#include <rtl/string.h>
+#include <rtl/string.hxx>
+#include <rtl/textenc.h>
+#include <rtl/textcvt.h>
+#include <rtl/ustring.hxx>
+#include <sal/macros.h>
+#include <sal/main.h>
+#include <unoidl/unoidl.hxx>
+
+namespace {
+
+void badUsage() {
+ std::cerr
+ << "Usage:" << std::endl << std::endl
+ << " unoidl-write [<registries>] [@<entities file>] <unoidl file>"
+ << std::endl << std::endl
+ << ("where each <registry> is either a new- or legacy-format .rdb file,"
+ " a single .idl")
+ << std::endl
+ << ("file, or a root directory of an .idl file tree; and the UTF-8"
+ " encoded <entities")
+ << std::endl
+ << ("file> contains zero or more space-separated names of (non-module)"
+ " entities to")
+ << std::endl
+ << ("include in the output, and, if omitted, defaults to the complete"
+ " content of the")
+ << std::endl << "last <registry>, if any." << std::endl;
+ std::exit(EXIT_FAILURE);
+}
+
+OUString getArgumentUri(sal_uInt32 argument, bool * entities) {
+ OUString arg;
+ rtl_getAppCommandArg(argument, &arg.pData);
+ if (arg.startsWith("@", &arg)) {
+ if (entities == nullptr) {
+ badUsage();
+ }
+ *entities = true;
+ } else if (entities != nullptr) {
+ *entities = false;
+ }
+ OUString url;
+ osl::FileBase::RC e1 = osl::FileBase::getFileURLFromSystemPath(arg, url);
+ if (e1 != osl::FileBase::E_None) {
+ std::cerr
+ << "Cannot convert \"" << arg << "\" to file URL, error code "
+ << +e1 << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ OUString cwd;
+ oslProcessError e2 = osl_getProcessWorkingDir(&cwd.pData);
+ if (e2 != osl_Process_E_None) {
+ std::cerr
+ << "Cannot obtain working directory, error code " << +e2
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ OUString abs;
+ e1 = osl::FileBase::getAbsoluteFileURL(cwd, url, abs);
+ if (e1 != osl::FileBase::E_None) {
+ std::cerr
+ << "Cannot make \"" << url
+ << "\" into an absolute file URL, error code " << +e1 << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ return abs;
+}
+
+sal_uInt64 getOffset(osl::File & file) {
+ sal_uInt64 off;
+ osl::FileBase::RC e = file.getPos(off);
+ if (e != osl::FileBase::E_None) {
+ std::cerr
+ << "Cannot determine current position in <" << file.getURL()
+ << ">, error code " << +e << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ return off;
+}
+
+void write(osl::File & file, void const * buffer, sal_uInt64 size) {
+ sal_uInt64 n;
+ osl::FileBase::RC e = file.write(buffer, size, n);
+ if (e != osl::FileBase::E_None) {
+ std::cerr
+ << "Cannot write to <" << file.getURL() << ">, error code " << +e
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ if (n != size) {
+ std::cerr
+ << "Bad write of " << n << " instead of " << size << " bytes to <"
+ << file.getURL() << '>' << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+}
+
+void write8(osl::File & file, sal_uInt64 value) {
+ if (value > 0xFF) {
+ std::cerr
+ << "Cannot write value >= 2^8; input is too large" << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ unsigned char buf[1];
+ buf[0] = value & 0xFF;
+ write(file, buf, SAL_N_ELEMENTS(buf));
+}
+
+void write16(osl::File & file, sal_uInt64 value) {
+ if (value > 0xFFFF) {
+ std::cerr
+ << "Cannot write value >= 2^16; input is too large" << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ unsigned char buf[2];
+ buf[0] = value & 0xFF;
+ buf[1] = (value >> 8) & 0xFF;
+ write(file, buf, SAL_N_ELEMENTS(buf));
+}
+
+void write32(osl::File & file, sal_uInt64 value) {
+ if (value > 0xFFFFFFFF) {
+ std::cerr
+ << "Cannot write value >= 2^32; input is too large" << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ unsigned char buf[4];
+ buf[0] = value & 0xFF;
+ buf[1] = (value >> 8) & 0xFF;
+ buf[2] = (value >> 16) & 0xFF;
+ buf[3] = (value >> 24) & 0xFF;
+ write(file, buf, SAL_N_ELEMENTS(buf));
+}
+
+void write64(osl::File & file, sal_uInt64 value) {
+ unsigned char buf[8];
+ buf[0] = value & 0xFF;
+ buf[1] = (value >> 8) & 0xFF;
+ buf[2] = (value >> 16) & 0xFF;
+ buf[3] = (value >> 24) & 0xFF;
+ buf[4] = (value >> 32) & 0xFF;
+ buf[5] = (value >> 40) & 0xFF;
+ buf[6] = (value >> 48) & 0xFF;
+ buf[7] = (value >> 56) & 0xFF;
+ write(file, buf, SAL_N_ELEMENTS(buf));
+}
+
+void writeIso60599Binary32(osl::File & file, float value) {
+ union {
+ unsigned char buf[4];
+ float f; // assuming float is ISO 60599 binary32
+ } sa;
+ sa.f = value;
+#if defined OSL_BIGENDIAN
+ std::swap(sa.buf[0], sa.buf[3]);
+ std::swap(sa.buf[1], sa.buf[2]);
+#endif
+ write(file, sa.buf, SAL_N_ELEMENTS(sa.buf));
+}
+
+void writeIso60599Binary64(osl::File & file, double value) {
+ union {
+ unsigned char buf[8];
+ float d; // assuming double is ISO 60599 binary64
+ } sa;
+ sa.d = value;
+#if defined OSL_BIGENDIAN
+ std::swap(sa.buf[0], sa.buf[7]);
+ std::swap(sa.buf[1], sa.buf[6]);
+ std::swap(sa.buf[2], sa.buf[5]);
+ std::swap(sa.buf[3], sa.buf[4]);
+#endif
+ write(file, sa.buf, SAL_N_ELEMENTS(sa.buf));
+}
+
+OString toAscii(OUString const & name) {
+ OString ascii;
+ if (!name.convertToString(
+ &ascii, RTL_TEXTENCODING_ASCII_US,
+ (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
+ | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
+ {
+ std::cerr
+ << "Cannot convert \"" << name << "\" to US ASCII" << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ return ascii;
+}
+
+OString toUtf8(OUString const & string) {
+ OString ascii;
+ if (!string.convertToString(
+ &ascii, RTL_TEXTENCODING_UTF8,
+ (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
+ | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)))
+ {
+ std::cerr
+ << "Cannot convert \"" << string << "\" to UTF-8" << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ return ascii;
+}
+
+sal_uInt64 writeNulName(osl::File & file, OUString const & name) {
+ OString ascii(toAscii(name));
+ if (ascii.indexOf('\0') != -1) {
+ std::cerr
+ << "Name \"" << ascii << "\" contains NUL characters" << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ sal_uInt64 off = getOffset(file);
+ write(file, ascii.getStr(), ascii.getLength() + 1);
+ return off;
+}
+
+void writeIdxString(osl::File & file, OString const & string) {
+ static std::map< OString, sal_uInt64 > reuse;
+ std::map< OString, sal_uInt64 >::iterator i(reuse.find(string));
+ if (i == reuse.end()) {
+ reuse.insert(std::make_pair(string, getOffset(file)));
+ assert(
+ (static_cast< sal_uInt64 >(string.getLength()) & 0x80000000) == 0);
+ write32(file, static_cast< sal_uInt64 >(string.getLength()));
+ write(file, string.getStr(), string.getLength());
+ } else {
+ if ((i->second & 0x80000000) != 0) {
+ std::cerr
+ << "Cannot write index 0x" << std::hex << i->second << std::dec
+ << " of \"" << string << "\"; input is too large" << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ write32(file, i->second | 0x80000000);
+ }
+}
+
+void writeIdxName(osl::File & file, OUString const & name) {
+ writeIdxString(file, toAscii(name));
+}
+
+void writeAnnotations(
+ osl::File & file, bool annotate,
+ std::vector< OUString > const & annotations)
+{
+ assert(annotate || annotations.empty());
+ if (annotate) {
+ write32(file, annotations.size());
+ // overflow from std::vector::size_type -> sal_uInt64 is unrealistic
+ for (auto & i: annotations) {
+ writeIdxString(file, toUtf8(i));
+ }
+ }
+}
+
+void writeKind(
+ osl::File & file,
+ rtl::Reference< unoidl::PublishableEntity > const & entity,
+ bool annotated, bool flag = false)
+{
+ assert(entity.is());
+ sal_uInt64 v = entity->getSort();
+ if (entity->isPublished()) {
+ v |= 0x80;
+ }
+ if (annotated) {
+ v |= 0x40;
+ }
+ if (flag) {
+ v |= 0x20;
+ }
+ write8(file, v);
+}
+
+struct Item {
+ explicit Item(rtl::Reference< unoidl::Entity > theEntity):
+ entity(std::move(theEntity)), nameOffset(0), dataOffset(0)
+ {}
+
+ rtl::Reference< unoidl::Entity > entity;
+ std::map< OUString, Item > module;
+ sal_uInt64 nameOffset;
+ sal_uInt64 dataOffset;
+};
+
+struct ConstItem {
+ ConstItem(
+ unoidl::ConstantValue const & theConstant,
+ std::vector< OUString >&& theAnnotations):
+ constant(theConstant), annotations(std::move(theAnnotations)), nameOffset(0),
+ dataOffset(0)
+ {}
+
+ unoidl::ConstantValue constant;
+ std::vector< OUString > annotations;
+ sal_uInt64 nameOffset;
+ sal_uInt64 dataOffset;
+};
+
+void mapEntities(
+ rtl::Reference< unoidl::Manager > const & manager, OUString const & uri,
+ std::map< OUString, Item > & map)
+{
+ assert(manager.is());
+ osl::File f(uri);
+ osl::FileBase::RC e = f.open(osl_File_OpenFlag_Read);
+ if (e != osl::FileBase::E_None) {
+ std::cerr
+ << "Cannot open <" << f.getURL() << "> for reading, error code "
+ << +e << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ for (;;) {
+ sal_Bool eof;
+ e = f.isEndOfFile(&eof);
+ if (e != osl::FileBase::E_None) {
+ std::cerr
+ << "Cannot check <" << f.getURL() << "> for EOF, error code "
+ << +e << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ if (eof) {
+ break;
+ }
+ rtl::ByteSequence s1;
+ e = f.readLine(s1);
+ if (e != osl::FileBase::E_None) {
+ std::cerr
+ << "Cannot read from <" << f.getURL() << ">, error code "
+ << +e << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ OUString s2;
+ if (!rtl_convertStringToUString(
+ &s2.pData, reinterpret_cast< char const * >(s1.getConstArray()),
+ s1.getLength(), RTL_TEXTENCODING_UTF8,
+ (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
+ | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
+ | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
+ {
+ std::cerr
+ << "Cannot interpret line read from <" << f.getURL()
+ << "> as UTF-8" << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ for (sal_Int32 i = 0; i != -1;) {
+ OUString t(s2.getToken(0, ' ', i));
+ if (!t.isEmpty()) {
+ rtl::Reference< unoidl::Entity > ent(manager->findEntity(t));
+ if (!ent.is()) {
+ std::cerr
+ << "Unknown entity \"" << t << "\" read from <"
+ << f.getURL() << ">" << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ if (ent->getSort() == unoidl::Entity::SORT_MODULE) {
+ std::cerr
+ << "Module entity \"" << t << "\" read from <"
+ << f.getURL() << ">" << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ std::map< OUString, Item > * map2 = &map;
+ for (sal_Int32 j = 0;;) {
+ OUString id(t.getToken(0, '.', j));
+ if (j == -1) {
+ map2->insert(std::make_pair(id, Item(ent)));
+ break;
+ }
+ std::map< OUString, Item >::iterator k(map2->find(id));
+ if (k == map2->end()) {
+ rtl::Reference< unoidl::Entity > ent2(
+ manager->findEntity(t.copy(0, j - 1)));
+ assert(ent2.is());
+ k = map2->insert(std::make_pair(id, Item(ent2))).first;
+ }
+ assert(
+ k->second.entity->getSort()
+ == unoidl::Entity::SORT_MODULE);
+ map2 = &k->second.module;
+ }
+ }
+ }
+ }
+ e = f.close();
+ if (e != osl::FileBase::E_None) {
+ std::cerr
+ << "Cannot close <" << f.getURL() << "> after reading, error code "
+ << +e << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+}
+
+void mapCursor(
+ rtl::Reference< unoidl::MapCursor > const & cursor,
+ std::map< OUString, Item > & map)
+{
+ if (!cursor.is())
+ return;
+
+ for (;;) {
+ OUString name;
+ rtl::Reference< unoidl::Entity > ent(cursor->getNext(&name));
+ if (!ent.is()) {
+ break;
+ }
+ std::pair< std::map< OUString, Item >::iterator, bool > i(
+ map.insert(std::make_pair(name, Item(ent))));
+ if (!i.second) {
+ std::cout << "Duplicate name \"" << name << '"' << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ if (i.first->second.entity->getSort()
+ == unoidl::Entity::SORT_MODULE)
+ {
+ mapCursor(
+ rtl::Reference< unoidl::ModuleEntity >(
+ static_cast< unoidl::ModuleEntity * >(
+ i.first->second.entity.get()))->createCursor(),
+ i.first->second.module);
+ }
+ }
+}
+
+template<typename T>
+bool hasNotEmptyAnnotations(const std::vector<T>& v)
+{
+ return std::any_of(v.begin(), v.end(), [](const T& rItem) { return !rItem.annotations.empty(); });
+}
+
+sal_uInt64 writeMap(
+ osl::File & file, std::map< OUString, Item > & map, std::size_t * rootSize)
+{
+ for (auto & i: map) {
+ switch (i.second.entity->getSort()) {
+ case unoidl::Entity::SORT_MODULE:
+ i.second.dataOffset = writeMap(file, i.second.module, nullptr);
+ break;
+ case unoidl::Entity::SORT_ENUM_TYPE:
+ {
+ rtl::Reference< unoidl::EnumTypeEntity > ent2(
+ static_cast< unoidl::EnumTypeEntity * >(
+ i.second.entity.get()));
+ bool ann = !ent2->getAnnotations().empty() ||
+ hasNotEmptyAnnotations(ent2->getMembers());
+ i.second.dataOffset = getOffset(file);
+ writeKind(file, ent2, ann);
+ write32(file, ent2->getMembers().size());
+ for (auto & j: ent2->getMembers()) {
+ writeIdxName(file, j.name);
+ write32(file, static_cast< sal_uInt32 >(j.value));
+ writeAnnotations(file, ann, j.annotations);
+ }
+ writeAnnotations(file, ann, ent2->getAnnotations());
+ break;
+ }
+ case unoidl::Entity::SORT_PLAIN_STRUCT_TYPE:
+ {
+ rtl::Reference< unoidl::PlainStructTypeEntity > ent2(
+ static_cast< unoidl::PlainStructTypeEntity * >(
+ i.second.entity.get()));
+ bool ann = !ent2->getAnnotations().empty() ||
+ hasNotEmptyAnnotations(ent2->getDirectMembers());
+ i.second.dataOffset = getOffset(file);
+ writeKind(
+ file, ent2, ann, !ent2->getDirectBase().isEmpty());
+ if (!ent2->getDirectBase().isEmpty()) {
+ writeIdxName(file, ent2->getDirectBase());
+ }
+ write32(file, ent2->getDirectMembers().size());
+ for (auto & j: ent2->getDirectMembers()) {
+ writeIdxName(file, j.name);
+ writeIdxName(file, j.type);
+ writeAnnotations(file, ann, j.annotations);
+ }
+ writeAnnotations(file, ann, ent2->getAnnotations());
+ break;
+ }
+ case unoidl::Entity::SORT_POLYMORPHIC_STRUCT_TYPE_TEMPLATE:
+ {
+ rtl::Reference< unoidl::PolymorphicStructTypeTemplateEntity >
+ ent2(
+ static_cast<
+ unoidl::PolymorphicStructTypeTemplateEntity * >(
+ i.second.entity.get()));
+ bool ann = !ent2->getAnnotations().empty() ||
+ hasNotEmptyAnnotations(ent2->getMembers());
+ i.second.dataOffset = getOffset(file);
+ writeKind(file, ent2, ann);
+ write32(file, ent2->getTypeParameters().size());
+ for (auto & j: ent2->getTypeParameters()) {
+ writeIdxName(file, j);
+ }
+ write32(file, ent2->getMembers().size());
+ for (auto & j: ent2->getMembers()) {
+ sal_uInt64 f = 0;
+ if (j.parameterized) {
+ f |= 0x01;
+ }
+ write8(file, f);
+ writeIdxName(file, j.name);
+ writeIdxName(file, j.type);
+ writeAnnotations(file, ann, j.annotations);
+ }
+ writeAnnotations(file, ann, ent2->getAnnotations());
+ break;
+ }
+ case unoidl::Entity::SORT_EXCEPTION_TYPE:
+ {
+ rtl::Reference< unoidl::ExceptionTypeEntity > ent2(
+ static_cast< unoidl::ExceptionTypeEntity * >(
+ i.second.entity.get()));
+ bool ann = !ent2->getAnnotations().empty() ||
+ hasNotEmptyAnnotations(ent2->getDirectMembers());
+ i.second.dataOffset = getOffset(file);
+ writeKind(
+ file, ent2, ann, !ent2->getDirectBase().isEmpty());
+ if (!ent2->getDirectBase().isEmpty()) {
+ writeIdxName(file, ent2->getDirectBase());
+ }
+ write32(file, ent2->getDirectMembers().size());
+ for (auto & j: ent2->getDirectMembers()) {
+ writeIdxName(file, j.name);
+ writeIdxName(file, j.type);
+ writeAnnotations(file, ann, j.annotations);
+ }
+ writeAnnotations(file, ann, ent2->getAnnotations());
+ break;
+ }
+ case unoidl::Entity::SORT_INTERFACE_TYPE:
+ {
+ rtl::Reference< unoidl::InterfaceTypeEntity > ent2(
+ static_cast< unoidl::InterfaceTypeEntity * >(
+ i.second.entity.get()));
+ bool ann = !ent2->getAnnotations().empty() ||
+ hasNotEmptyAnnotations(ent2->getDirectMandatoryBases()) ||
+ hasNotEmptyAnnotations(ent2->getDirectOptionalBases()) ||
+ hasNotEmptyAnnotations(ent2->getDirectAttributes()) ||
+ hasNotEmptyAnnotations(ent2->getDirectMethods());
+ i.second.dataOffset = getOffset(file);
+ writeKind(file, ent2, ann);
+ write32(file, ent2->getDirectMandatoryBases().size());
+ for (auto & j: ent2->getDirectMandatoryBases()) {
+ writeIdxName(file, j.name);
+ writeAnnotations(file, ann, j.annotations);
+ }
+ write32(file, ent2->getDirectOptionalBases().size());
+ for (auto & j: ent2->getDirectOptionalBases()) {
+ writeIdxName(file, j.name);
+ writeAnnotations(file, ann, j.annotations);
+ }
+ write32(file, ent2->getDirectAttributes().size());
+ for (auto & j: ent2->getDirectAttributes()) {
+ sal_uInt64 f = 0;
+ if (j.bound) {
+ f |= 0x01;
+ }
+ if (j.readOnly) {
+ f |= 0x02;
+ }
+ write8(file, f);
+ writeIdxName(file, j.name);
+ writeIdxName(file, j.type);
+ write32(file, j.getExceptions.size());
+ for (auto & k: j.getExceptions) {
+ writeIdxName(file, k);
+ }
+ if (!j.readOnly) {
+ write32(file, j.setExceptions.size());
+ for (auto & k: j.setExceptions) {
+ writeIdxName(file, k);
+ }
+ }
+ writeAnnotations(file, ann, j.annotations);
+ }
+ write32(file, ent2->getDirectMethods().size());
+ for (auto & j: ent2->getDirectMethods()) {
+ writeIdxName(file, j.name);
+ writeIdxName(file, j.returnType);
+ write32(file, j.parameters.size());
+ for (auto & k: j.parameters) {
+ write8(file, k.direction);
+ writeIdxName(file, k.name);
+ writeIdxName(file, k.type);
+ }
+ write32(file, j.exceptions.size());
+ for (auto & k: j.exceptions) {
+ writeIdxName(file, k);
+ }
+ writeAnnotations(file, ann, j.annotations);
+ }
+ writeAnnotations(file, ann, ent2->getAnnotations());
+ break;
+ }
+ case unoidl::Entity::SORT_TYPEDEF:
+ {
+ rtl::Reference< unoidl::TypedefEntity > ent2(
+ static_cast< unoidl::TypedefEntity * >(
+ i.second.entity.get()));
+ bool ann = !ent2->getAnnotations().empty();
+ i.second.dataOffset = getOffset(file);
+ writeKind(file, ent2, ann);
+ writeIdxName(file, ent2->getType());
+ writeAnnotations(file, ann, ent2->getAnnotations());
+ break;
+ }
+ case unoidl::Entity::SORT_CONSTANT_GROUP:
+ {
+ rtl::Reference< unoidl::ConstantGroupEntity > ent2(
+ static_cast< unoidl::ConstantGroupEntity * >(
+ i.second.entity.get()));
+ std::map< OUString, ConstItem > cmap;
+ for (auto & j: ent2->getMembers()) {
+ if (!cmap.insert(
+ std::make_pair(
+ j.name, ConstItem(j.value, std::vector(j.annotations)))).
+ second)
+ {
+ std::cout
+ << "Duplicate constant group member name \""
+ << j.name << '"' << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ for (auto & j: cmap) {
+ j.second.dataOffset = getOffset(file);
+ sal_uInt64 v = j.second.constant.type;
+ if (!j.second.annotations.empty()) {
+ v |= 0x80;
+ }
+ write8(file, v);
+ switch (j.second.constant.type) {
+ case unoidl::ConstantValue::TYPE_BOOLEAN:
+ write8(file, j.second.constant.booleanValue ? 1 : 0);
+ break;
+ case unoidl::ConstantValue::TYPE_BYTE:
+ write8(
+ file,
+ static_cast< sal_uInt8 >(
+ j.second.constant.byteValue));
+ break;
+ case unoidl::ConstantValue::TYPE_SHORT:
+ write16(
+ file,
+ static_cast< sal_uInt16 >(
+ j.second.constant.shortValue));
+ break;
+ case unoidl::ConstantValue::TYPE_UNSIGNED_SHORT:
+ write16(file, j.second.constant.unsignedShortValue);
+ break;
+ case unoidl::ConstantValue::TYPE_LONG:
+ write32(
+ file,
+ static_cast< sal_uInt32 >(
+ j.second.constant.longValue));
+ break;
+ case unoidl::ConstantValue::TYPE_UNSIGNED_LONG:
+ write32(file, j.second.constant.unsignedLongValue);
+ break;
+ case unoidl::ConstantValue::TYPE_HYPER:
+ write64(
+ file,
+ static_cast< sal_uInt64 >(
+ j.second.constant.hyperValue));
+ break;
+ case unoidl::ConstantValue::TYPE_UNSIGNED_HYPER:
+ write64(file, j.second.constant.unsignedHyperValue);
+ break;
+ case unoidl::ConstantValue::TYPE_FLOAT:
+ writeIso60599Binary32(
+ file, j.second.constant.floatValue);
+ break;
+ case unoidl::ConstantValue::TYPE_DOUBLE:
+ writeIso60599Binary64(
+ file, j.second.constant.doubleValue);
+ break;
+ default:
+ for (;;) { std::abort(); } // this cannot happen
+ }
+ writeAnnotations(
+ file, !j.second.annotations.empty(),
+ j.second.annotations);
+ }
+ for (auto & j: cmap) {
+ j.second.nameOffset = writeNulName(file, j.first);
+ }
+ bool ann = !ent2->getAnnotations().empty();
+ i.second.dataOffset = getOffset(file);
+ writeKind(file, ent2, ann);
+ write32(file, cmap.size());
+ // overflow from std::map::size_type -> sal_uInt64 is
+ // unrealistic
+ for (const auto & j: cmap) {
+ write32(file, j.second.nameOffset);
+ write32(file, j.second.dataOffset);
+ }
+ writeAnnotations(file, ann, ent2->getAnnotations());
+ break;
+ }
+ case unoidl::Entity::SORT_SINGLE_INTERFACE_BASED_SERVICE:
+ {
+ rtl::Reference< unoidl::SingleInterfaceBasedServiceEntity >
+ ent2(
+ static_cast<
+ unoidl::SingleInterfaceBasedServiceEntity * >(
+ i.second.entity.get()));
+ bool dfltCtor = ent2->getConstructors().size() == 1
+ && ent2->getConstructors()[0].defaultConstructor;
+ bool ann = !ent2->getAnnotations().empty();
+ if (!dfltCtor && !ann)
+ ann = hasNotEmptyAnnotations(ent2->getConstructors());
+ i.second.dataOffset = getOffset(file);
+ writeKind(file, ent2, ann, dfltCtor);
+ writeIdxName(file, ent2->getBase());
+ if (!dfltCtor) {
+ write32(file, ent2->getConstructors().size());
+ for (auto & j: ent2->getConstructors()) {
+ if (j.defaultConstructor) {
+ std::cout
+ << "Unexpected default constructor \""
+ << j.name << '"' << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ writeIdxName(file, j.name);
+ write32(file, j.parameters.size());
+ for (auto & k: j.parameters) {
+ sal_uInt64 f = 0;
+ if (k.rest) {
+ f |= 0x04;
+ }
+ write8(file, f);
+ writeIdxName(file, k.name);
+ writeIdxName(file, k.type);
+ }
+ write32(file, j.exceptions.size());
+ for (auto & k: j.exceptions) {
+ writeIdxName(file, k);
+ }
+ writeAnnotations(file, ann, j.annotations);
+ }
+ }
+ writeAnnotations(file, ann, ent2->getAnnotations());
+ break;
+ }
+ case unoidl::Entity::SORT_ACCUMULATION_BASED_SERVICE:
+ {
+ rtl::Reference< unoidl::AccumulationBasedServiceEntity > ent2(
+ static_cast< unoidl::AccumulationBasedServiceEntity * >(
+ i.second.entity.get()));
+ bool ann = !ent2->getAnnotations().empty() ||
+ hasNotEmptyAnnotations(ent2->getDirectMandatoryBaseServices()) ||
+ hasNotEmptyAnnotations(ent2->getDirectOptionalBaseServices()) ||
+ hasNotEmptyAnnotations(ent2->getDirectMandatoryBaseInterfaces()) ||
+ hasNotEmptyAnnotations(ent2->getDirectOptionalBaseInterfaces()) ||
+ hasNotEmptyAnnotations(ent2->getDirectProperties());
+ i.second.dataOffset = getOffset(file);
+ writeKind(file, ent2, ann);
+ write32(file, ent2->getDirectMandatoryBaseServices().size());
+ for (auto & j: ent2->getDirectMandatoryBaseServices()) {
+ writeIdxName(file, j.name);
+ writeAnnotations(file, ann, j.annotations);
+ }
+ write32(file, ent2->getDirectOptionalBaseServices().size());
+ for (auto & j: ent2->getDirectOptionalBaseServices()) {
+ writeIdxName(file, j.name);
+ writeAnnotations(file, ann, j.annotations);
+ }
+ write32(file, ent2->getDirectMandatoryBaseInterfaces().size());
+ for (auto & j: ent2->getDirectMandatoryBaseInterfaces()) {
+ writeIdxName(file, j.name);
+ writeAnnotations(file, ann, j.annotations);
+ }
+ write32(file, ent2->getDirectOptionalBaseInterfaces().size());
+ for (auto & j: ent2->getDirectOptionalBaseInterfaces()) {
+ writeIdxName(file, j.name);
+ writeAnnotations(file, ann, j.annotations);
+ }
+ write32(file, ent2->getDirectProperties().size());
+ for (auto & j: ent2->getDirectProperties()) {
+ write16(file, static_cast< sal_uInt16 >(j.attributes));
+ writeIdxName(file, j.name);
+ writeIdxName(file, j.type);
+ writeAnnotations(file, ann, j.annotations);
+ }
+ writeAnnotations(file, ann, ent2->getAnnotations());
+ break;
+ }
+ case unoidl::Entity::SORT_INTERFACE_BASED_SINGLETON:
+ {
+ rtl::Reference< unoidl::InterfaceBasedSingletonEntity > ent2(
+ static_cast< unoidl::InterfaceBasedSingletonEntity * >(
+ i.second.entity.get()));
+ bool ann = !ent2->getAnnotations().empty();
+ i.second.dataOffset = getOffset(file);
+ writeKind(file, ent2, ann);
+ writeIdxName(file, ent2->getBase());
+ writeAnnotations(file, ann, ent2->getAnnotations());
+ break;
+ }
+ case unoidl::Entity::SORT_SERVICE_BASED_SINGLETON:
+ {
+ rtl::Reference< unoidl::ServiceBasedSingletonEntity > ent2(
+ static_cast< unoidl::ServiceBasedSingletonEntity * >(
+ i.second.entity.get()));
+ bool ann = !ent2->getAnnotations().empty();
+ i.second.dataOffset = getOffset(file);
+ writeKind(file, ent2, ann);
+ writeIdxName(file, ent2->getBase());
+ writeAnnotations(file, ann, ent2->getAnnotations());
+ break;
+ }
+ }
+ }
+ for (auto & i: map) {
+ i.second.nameOffset = writeNulName(file, i.first);
+ }
+ sal_uInt64 off = getOffset(file);
+ if (rootSize == nullptr) {
+ write8(file, 0); // SORT_MODULE
+ write32(file, map.size());
+ // overflow from std::map::size_type -> sal_uInt64 is unrealistic
+ } else {
+ *rootSize = map.size();
+ // overflow from std::map::size_type -> std::size_t is unrealistic
+ }
+ for (const auto & i: map) {
+ write32(file, i.second.nameOffset);
+ write32(file, i.second.dataOffset);
+ }
+ return off;
+}
+
+}
+
+SAL_IMPLEMENT_MAIN() {
+ try {
+ sal_uInt32 args = rtl_getAppCommandArgCount();
+ if (args == 0) {
+ badUsage();
+ }
+ rtl::Reference< unoidl::Manager > mgr(new unoidl::Manager);
+ bool entities = false;
+ rtl::Reference< unoidl::Provider > prov;
+ std::map< OUString, Item > map;
+ for (sal_uInt32 i = 0; i != args - 1; ++i) {
+ assert(args > 1);
+ OUString uri(getArgumentUri(i, i == args - 2 ? &entities : nullptr));
+ if (entities) {
+ mapEntities(mgr, uri, map);
+ } else {
+ try {
+ prov = mgr->addProvider(uri);
+ } catch (unoidl::NoSuchFileException &) {
+ std::cerr
+ << "Input <" << uri << "> does not exist" << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ }
+ }
+ if (!entities) {
+ mapCursor(
+ (prov.is()
+ ? prov->createRootCursor()
+ : rtl::Reference< unoidl::MapCursor >()),
+ map);
+ }
+ osl::File f(getArgumentUri(args - 1, nullptr));
+ osl::FileBase::RC e = f.open(osl_File_OpenFlag_Write);
+ if (e == osl::FileBase::E_NOENT) {
+ e = f.open(osl_File_OpenFlag_Write | osl_File_OpenFlag_Create);
+ }
+ if (e != osl::FileBase::E_None) {
+ std::cerr
+ << "Cannot open <" << f.getURL() << "> for writing, error code "
+ << +e << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ write(f, "UNOIDL\xFF\0", 8);
+ write32(f, 0); // root map offset
+ write32(f, 0); // root map size
+ write(
+ f,
+ RTL_CONSTASCII_STRINGPARAM(
+ "\0** Created by LibreOffice " LIBO_VERSION_DOTTED
+ " unoidl-write **\0"));
+ std::size_t size;
+ sal_uInt64 off = writeMap(f, map, &size);
+ e = f.setSize(getOffset(f)); // truncate in case it already existed
+ if (e != osl::FileBase::E_None) {
+ std::cerr
+ << "Cannot set size of <" << f.getURL() << ">, error code "
+ << +e << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ e = f.setPos(osl_Pos_Absolut, 8);
+ if (e != osl::FileBase::E_None) {
+ std::cerr
+ << "Cannot rewind current position in <" << f.getURL()
+ << ">, error code " << +e << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ write32(f, off);
+ write32(f, size);
+ // overflow from std::map::size_type -> sal_uInt64 is unrealistic
+ e = f.close();
+ if (e != osl::FileBase::E_None) {
+ std::cerr
+ << "Cannot close <" << f.getURL()
+ << "> after writing, error code " << +e << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+ return EXIT_SUCCESS;
+ } catch (unoidl::FileFormatException & e1) {
+ std::cerr
+ << "Bad input <" << e1.getUri() << ">: " << e1.getDetail()
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ } catch (std::exception & e1) {
+ std::cerr
+ << "Failure: " << e1.what()
+ << std::endl;
+ std::exit(EXIT_FAILURE);
+ }
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unoidl/source/unoidl.cxx b/unoidl/source/unoidl.cxx
new file mode 100644
index 000000000..2022bcd05
--- /dev/null
+++ b/unoidl/source/unoidl.cxx
@@ -0,0 +1,224 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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 <sal/log.hxx>
+
+#include <set>
+#include <utility>
+#include <vector>
+
+#include <osl/file.h>
+#include <osl/file.hxx>
+#include <osl/mutex.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/ustring.hxx>
+#include <unoidl/unoidl.hxx>
+
+#include "legacyprovider.hxx"
+#include "sourcefileprovider.hxx"
+#include "sourcetreeprovider.hxx"
+#include "unoidlprovider.hxx"
+
+namespace unoidl {
+
+namespace {
+
+class AggregatingModule: public ModuleEntity {
+public:
+ AggregatingModule(
+ std::vector< rtl::Reference< Provider > >&& providers,
+ OUString name):
+ providers_(std::move(providers)), name_(std::move(name))
+ {}
+
+private:
+ virtual ~AggregatingModule() noexcept override {}
+
+ virtual std::vector< OUString > getMemberNames() const override;
+
+ virtual rtl::Reference< MapCursor > createCursor() const override;
+
+ std::vector< rtl::Reference< Provider > > providers_;
+ OUString name_;
+};
+
+std::vector< OUString > AggregatingModule::getMemberNames() const {
+ std::set< OUString > names;
+ for (auto & i: providers_) {
+ rtl::Reference< Entity > ent(i->findEntity(name_));
+ if (ent.is() && ent->getSort() == Entity::SORT_MODULE) {
+ std::vector< OUString > ns(
+ static_cast< ModuleEntity * >(ent.get())->getMemberNames());
+ names.insert(ns.begin(), ns.end());
+ }
+ }
+ return std::vector< OUString >(names.begin(), names.end());
+}
+
+class AggregatingCursor: public MapCursor {
+public:
+ AggregatingCursor(
+ std::vector< rtl::Reference< Provider > >&& providers,
+ OUString name):
+ providers_(std::move(providers)), name_(std::move(name)), iterator_(providers_.begin())
+ { findCursor(); }
+
+private:
+ virtual ~AggregatingCursor() noexcept override {}
+
+ virtual rtl::Reference< Entity > getNext(OUString * name) override;
+
+ void findCursor();
+
+ std::vector< rtl::Reference< Provider > > providers_;
+ OUString name_;
+ std::vector< rtl::Reference< Provider > >::iterator iterator_;
+ rtl::Reference< MapCursor > cursor_;
+ std::set< OUString > seen_;
+};
+
+rtl::Reference< Entity > AggregatingCursor::getNext(OUString * name) {
+ while (cursor_.is()) {
+ OUString n;
+ rtl::Reference< Entity > ent(cursor_->getNext(&n));
+ if (ent.is()) {
+ if (seen_.insert(n).second) {
+ if (name != nullptr) {
+ *name = n;
+ }
+ return ent->getSort() == Entity::SORT_MODULE
+ ? new AggregatingModule(
+ std::vector(providers_), (name_.isEmpty() ? name_ : name_ + ".") + n)
+ : ent;
+ }
+ } else {
+ cursor_.clear();
+ findCursor();
+ }
+ }
+ return rtl::Reference< Entity >();
+}
+
+void AggregatingCursor::findCursor() {
+ for (; !cursor_.is() && iterator_ != providers_.end(); ++iterator_) {
+ if (name_.isEmpty()) {
+ cursor_ = (*iterator_)->createRootCursor();
+ } else {
+ rtl::Reference< Entity > ent((*iterator_)->findEntity(name_));
+ if (ent.is() && ent->getSort() == Entity::SORT_MODULE) {
+ cursor_ = static_cast< ModuleEntity * >(ent.get())->
+ createCursor();
+ }
+ }
+ }
+}
+
+rtl::Reference< MapCursor > AggregatingModule::createCursor() const {
+ return new AggregatingCursor(std::vector(providers_), name_);
+}
+
+}
+
+NoSuchFileException::~NoSuchFileException() noexcept {}
+
+FileFormatException::~FileFormatException() noexcept {}
+
+Entity::~Entity() noexcept {}
+
+MapCursor::~MapCursor() noexcept {}
+
+ModuleEntity::~ModuleEntity() noexcept {}
+
+PublishableEntity::~PublishableEntity() noexcept {}
+
+EnumTypeEntity::~EnumTypeEntity() noexcept {}
+
+PlainStructTypeEntity::~PlainStructTypeEntity() noexcept {}
+
+PolymorphicStructTypeTemplateEntity::~PolymorphicStructTypeTemplateEntity()
+ noexcept
+{}
+
+ExceptionTypeEntity::~ExceptionTypeEntity() noexcept {}
+
+InterfaceTypeEntity::~InterfaceTypeEntity() noexcept {}
+
+TypedefEntity::~TypedefEntity() noexcept {}
+
+ConstantGroupEntity::~ConstantGroupEntity() noexcept {}
+
+SingleInterfaceBasedServiceEntity::~SingleInterfaceBasedServiceEntity() noexcept
+{}
+
+AccumulationBasedServiceEntity::~AccumulationBasedServiceEntity() noexcept {}
+
+InterfaceBasedSingletonEntity::~InterfaceBasedSingletonEntity() noexcept {}
+
+ServiceBasedSingletonEntity::~ServiceBasedSingletonEntity() noexcept {}
+
+Provider::~Provider() noexcept {}
+
+rtl::Reference< Provider > Manager::addProvider(OUString const & uri) {
+ rtl::Reference< Provider > p(loadProvider(uri));
+ assert(p.is());
+ {
+ osl::MutexGuard g(mutex_);
+ providers_.push_back(p);
+ }
+ return p;
+}
+
+rtl::Reference< Entity > Manager::findEntity(OUString const & name) const {
+ //TODO: caching? (here or in cppuhelper::TypeManager?)
+ osl::MutexGuard g(mutex_);
+ for (auto & i: providers_) {
+ rtl::Reference< Entity > ent(i->findEntity(name));
+ if (ent.is()) {
+ return ent;
+ }
+ }
+ return rtl::Reference< Entity >();
+}
+
+rtl::Reference< MapCursor > Manager::createCursor(OUString const & name)
+ const
+{
+ return new AggregatingCursor(std::vector(providers_), name);
+}
+
+Manager::~Manager() noexcept {}
+
+rtl::Reference< Provider > Manager::loadProvider(OUString const & uri) {
+ osl::DirectoryItem item;
+ if (osl::DirectoryItem::get(uri, item) == osl::FileBase::E_None) {
+ osl::FileStatus status(osl_FileStatus_Mask_Type);
+ if (item.getFileStatus(status) == osl::FileBase::E_None
+ && status.getFileType() == osl::FileStatus::Directory)
+ {
+ return new detail::SourceTreeProvider(*this, uri);
+ }
+ }
+ if (uri.endsWith(".idl")) {
+ return new detail::SourceFileProvider(this, uri);
+ }
+ try {
+ return new detail::UnoidlProvider(uri);
+ } catch (FileFormatException & e) {
+ SAL_INFO(
+ "unoidl",
+ "FileFormatException \"" << e.getDetail() << "\", retrying <" << uri
+ << "> as legacy format");
+ return new detail::LegacyProvider(*this, uri);
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unoidl/source/unoidlprovider.cxx b/unoidl/source/unoidlprovider.cxx
new file mode 100644
index 000000000..d6f4d4ddd
--- /dev/null
+++ b/unoidl/source/unoidlprovider.cxx
@@ -0,0 +1,1431 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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 <algorithm>
+#include <cassert>
+#include <cstring>
+#include <set>
+#include <string_view>
+#include <utility>
+#include <vector>
+
+#include <o3tl/string_view.hxx>
+#include <osl/endian.h>
+#include <osl/file.h>
+#include <rtl/character.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/textenc.h>
+#include <rtl/textcvt.h>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <sal/types.h>
+#include <salhelper/simplereferenceobject.hxx>
+#include <unoidl/unoidl.hxx>
+
+#include "unoidlprovider.hxx"
+
+namespace unoidl::detail {
+
+class MappedFile: public salhelper::SimpleReferenceObject {
+public:
+ explicit MappedFile(OUString fileUrl);
+
+ sal_uInt8 read8(sal_uInt32 offset) const;
+
+ sal_uInt16 read16(sal_uInt32 offset) const;
+
+ sal_uInt32 read32(sal_uInt32 offset) const;
+
+ sal_uInt64 read64(sal_uInt32 offset) const;
+
+ float readIso60599Binary32(sal_uInt32 offset) const;
+
+ double readIso60599Binary64(sal_uInt32 offset) const;
+
+ OUString readNulName(sal_uInt32 offset) /*const*/;
+
+ OUString readIdxName(sal_uInt32 * offset) const
+ { return readIdxString(offset, RTL_TEXTENCODING_ASCII_US); }
+
+ OUString readIdxString(sal_uInt32 * offset) const
+ { return readIdxString(offset, RTL_TEXTENCODING_UTF8); }
+
+ OUString uri;
+ oslFileHandle handle;
+ sal_uInt64 size;
+ void * address;
+
+private:
+ virtual ~MappedFile() override;
+
+ sal_uInt8 get8(sal_uInt32 offset) const;
+
+ sal_uInt16 get16(sal_uInt32 offset) const;
+
+ sal_uInt32 get32(sal_uInt32 offset) const;
+
+ sal_uInt64 get64(sal_uInt32 offset) const;
+
+ float getIso60599Binary32(sal_uInt32 offset) const;
+
+ double getIso60599Binary64(sal_uInt32 offset) const;
+
+ OUString readIdxString(sal_uInt32 * offset, rtl_TextEncoding encoding)
+ const;
+};
+
+namespace {
+
+// sizeof (Memory16) == 2
+struct Memory16 {
+ unsigned char byte[2];
+
+ sal_uInt16 getUnsigned16() const {
+ return static_cast< sal_uInt16 >(byte[0])
+ | (static_cast< sal_uInt16 >(byte[1]) << 8);
+ }
+};
+
+// sizeof (Memory32) == 4
+struct Memory32 {
+ unsigned char byte[4];
+
+ sal_uInt32 getUnsigned32() const {
+ return static_cast< sal_uInt32 >(byte[0])
+ | (static_cast< sal_uInt32 >(byte[1]) << 8)
+ | (static_cast< sal_uInt32 >(byte[2]) << 16)
+ | (static_cast< sal_uInt32 >(byte[3]) << 24);
+ }
+
+ float getIso60599Binary32() const {
+ union {
+ unsigned char buf[4];
+ float f; // assuming float is ISO 60599 binary32
+ } sa;
+#if defined OSL_LITENDIAN
+ sa.buf[0] = byte[0];
+ sa.buf[1] = byte[1];
+ sa.buf[2] = byte[2];
+ sa.buf[3] = byte[3];
+#else
+ sa.buf[0] = byte[3];
+ sa.buf[1] = byte[2];
+ sa.buf[2] = byte[1];
+ sa.buf[3] = byte[0];
+#endif
+ return sa.f;
+ }
+};
+
+// sizeof (Memory64) == 8
+struct Memory64 {
+ unsigned char byte[8];
+
+ sal_uInt64 getUnsigned64() const {
+ return static_cast< sal_uInt64 >(byte[0])
+ | (static_cast< sal_uInt64 >(byte[1]) << 8)
+ | (static_cast< sal_uInt64 >(byte[2]) << 16)
+ | (static_cast< sal_uInt64 >(byte[3]) << 24)
+ | (static_cast< sal_uInt64 >(byte[4]) << 32)
+ | (static_cast< sal_uInt64 >(byte[5]) << 40)
+ | (static_cast< sal_uInt64 >(byte[6]) << 48)
+ | (static_cast< sal_uInt64 >(byte[7]) << 56);
+ }
+
+ double getIso60599Binary64() const {
+ union {
+ unsigned char buf[8];
+ double d; // assuming double is ISO 60599 binary64
+ } sa;
+#if defined OSL_LITENDIAN
+ sa.buf[0] = byte[0];
+ sa.buf[1] = byte[1];
+ sa.buf[2] = byte[2];
+ sa.buf[3] = byte[3];
+ sa.buf[4] = byte[4];
+ sa.buf[5] = byte[5];
+ sa.buf[6] = byte[6];
+ sa.buf[7] = byte[7];
+#else
+ sa.buf[0] = byte[7];
+ sa.buf[1] = byte[6];
+ sa.buf[2] = byte[5];
+ sa.buf[3] = byte[4];
+ sa.buf[4] = byte[3];
+ sa.buf[5] = byte[2];
+ sa.buf[6] = byte[1];
+ sa.buf[7] = byte[0];
+#endif
+ return sa.d;
+ }
+};
+
+bool isSimpleType(std::u16string_view type) {
+ return type == u"void" || type == u"boolean" || type == u"byte"
+ || type == u"short" || type == u"unsigned short" || type == u"long"
+ || type == u"unsigned long" || type == u"hyper"
+ || type == u"unsigned hyper" || type == u"float" || type == u"double"
+ || type == u"char" || type == u"string" || type == u"type"
+ || type == u"any";
+}
+
+// For backwards compatibility, does not strictly check segments to match
+//
+// <segment> ::= <blocks> | <block>
+// <blocks> ::= <capital> <other>* ("_" <block>)*
+// <block> ::= <other>+
+// <other> ::= <capital> | "a"--"z" | "0"--"9"
+// <capital> ::= "A"--"Z"
+//
+bool isIdentifier(std::u16string_view type, bool scoped) {
+ if (type.empty()) {
+ return false;
+ }
+ for (size_t i = 0; i != type.size(); ++i) {
+ sal_Unicode c = type[i];
+ if (c == '.') {
+ if (!scoped || i == 0 || i == type.size() - 1
+ || type[i - 1] == '.')
+ {
+ return false;
+ }
+ } else if (!rtl::isAsciiAlphanumeric(c) && c != '_') {
+ return false;
+ }
+ }
+ return true;
+}
+
+void checkTypeName(
+ rtl::Reference< MappedFile > const & file, std::u16string_view type)
+{
+ std::u16string_view nucl(type);
+ bool args = false;
+ while (o3tl::starts_with(nucl, u"[]", &nucl)) {}
+ size_t i = nucl.find('<');
+ if (i != std::u16string_view::npos) {
+ std::u16string_view tmpl(nucl.substr(0, i));
+ do {
+ ++i; // skip '<' or ','
+ size_t j = i;
+ for (size_t level = 0; j != nucl.size(); ++j) {
+ sal_Unicode c = nucl[j];
+ if (c == ',') {
+ if (level == 0) {
+ break;
+ }
+ } else if (c == '<') {
+ ++level;
+ } else if (c == '>') {
+ if (level == 0) {
+ break;
+ }
+ --level;
+ }
+ }
+ if (j != nucl.size()) {
+ checkTypeName(file, nucl.substr(i, j - i));
+ args = true;
+ }
+ i = j;
+ } while (i != nucl.size() && nucl[i] != '>');
+ if (i != nucl.size() - 1 || nucl[i] != '>' || !args) {
+ tmpl = {}; // bad input
+ }
+ nucl = tmpl;
+ }
+ if (isSimpleType(nucl) ? args : !isIdentifier(nucl, true)) {
+ throw FileFormatException(
+ file->uri, OUString::Concat("UNOIDL format: bad type \"") + type + "\"");
+ }
+}
+
+void checkEntityName(
+ rtl::Reference< MappedFile > const & file, std::u16string_view name)
+{
+ if (isSimpleType(name) || !isIdentifier(name, false)) {
+ throw FileFormatException(
+ file->uri, OUString::Concat("UNOIDL format: bad entity name \"") + name + "\"");
+ }
+}
+
+}
+
+MappedFile::MappedFile(OUString fileUrl): uri(std::move(fileUrl)), handle(nullptr) {
+ oslFileError e = osl_openFile(uri.pData, &handle, osl_File_OpenFlag_Read);
+ switch (e) {
+ case osl_File_E_None:
+ break;
+ case osl_File_E_NOENT:
+ throw NoSuchFileException(uri);
+ default:
+ throw FileFormatException(uri, "cannot open: " + OUString::number(e));
+ }
+ e = osl_getFileSize(handle, &size);
+ if (e == osl_File_E_None) {
+ e = osl_mapFile(
+ handle, &address, size, 0, osl_File_MapFlag_RandomAccess);
+ }
+ if (e != osl_File_E_None) {
+ oslFileError e2 = osl_closeFile(handle);
+ SAL_WARN_IF(
+ e2 != osl_File_E_None, "unoidl",
+ "cannot close " << uri << ": " << +e2);
+ throw FileFormatException(uri, "cannot mmap: " + OUString::number(e));
+ }
+}
+
+sal_uInt8 MappedFile::read8(sal_uInt32 offset) const {
+ assert(size >= 8);
+ if (offset > size - 1) {
+ throw FileFormatException(
+ uri, "UNOIDL format: offset for 8-bit value too large");
+ }
+ return get8(offset);
+}
+
+sal_uInt16 MappedFile::read16(sal_uInt32 offset) const {
+ assert(size >= 8);
+ if (offset > size - 2) {
+ throw FileFormatException(
+ uri, "UNOIDL format: offset for 16-bit value too large");
+ }
+ return get16(offset);
+}
+
+sal_uInt32 MappedFile::read32(sal_uInt32 offset) const {
+ assert(size >= 8);
+ if (offset > size - 4) {
+ throw FileFormatException(
+ uri, "UNOIDL format: offset for 32-bit value too large");
+ }
+ return get32(offset);
+}
+
+sal_uInt64 MappedFile::read64(sal_uInt32 offset) const {
+ assert(size >= 8);
+ if (offset > size - 8) {
+ throw FileFormatException(
+ uri, "UNOIDL format: offset for 64-bit value too large");
+ }
+ return get64(offset);
+}
+
+float MappedFile::readIso60599Binary32(sal_uInt32 offset) const {
+ assert(size >= 8);
+ if (offset > size - 4) {
+ throw FileFormatException(
+ uri, "UNOIDL format: offset for 32-bit value too large");
+ }
+ return getIso60599Binary32(offset);
+}
+
+double MappedFile::readIso60599Binary64(sal_uInt32 offset) const {
+ assert(size >= 8);
+ if (offset > size - 8) {
+ throw FileFormatException(
+ uri, "UNOIDL format: offset for 64-bit value too large");
+ }
+ return getIso60599Binary64(offset);
+}
+
+OUString MappedFile::readNulName(sal_uInt32 offset) {
+ if (offset > size) {
+ throw FileFormatException(
+ uri, "UNOIDL format: offset for string too large");
+ }
+ sal_uInt64 end = offset;
+ for (;; ++end) {
+ if (end == size) {
+ throw FileFormatException(
+ uri, "UNOIDL format: string misses trailing NUL");
+ }
+ if (static_cast< char const * >(address)[end] == 0) {
+ break;
+ }
+ }
+ if (end - offset > SAL_MAX_INT32) {
+ throw FileFormatException(uri, "UNOIDL format: string too long");
+ }
+ OUString name;
+ if (!rtl_convertStringToUString(
+ &name.pData, static_cast< char const * >(address) + offset,
+ end - offset, RTL_TEXTENCODING_ASCII_US,
+ (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
+ | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
+ | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
+ {
+ throw FileFormatException(uri, "UNOIDL format: name is not ASCII");
+ }
+ checkEntityName(this, name);
+ return name;
+}
+
+MappedFile::~MappedFile() {
+ oslFileError e = osl_unmapMappedFile(handle, address, size);
+ SAL_WARN_IF(e != osl_File_E_None, "unoidl", "cannot unmap: " << +e);
+ e = osl_closeFile(handle);
+ SAL_WARN_IF(e != osl_File_E_None, "unoidl", "cannot close: " << +e);
+}
+
+sal_uInt8 MappedFile::get8(sal_uInt32 offset) const {
+ assert(size >= 8);
+ assert(offset <= size - 1);
+ return static_cast< char const * >(address)[offset];
+}
+
+sal_uInt16 MappedFile::get16(sal_uInt32 offset) const {
+ assert(size >= 8);
+ assert(offset <= size - 2);
+ return reinterpret_cast< Memory16 const * >(
+ static_cast< char const * >(address) + offset)->getUnsigned16();
+}
+
+sal_uInt32 MappedFile::get32(sal_uInt32 offset) const {
+ assert(size >= 8);
+ assert(offset <= size - 4);
+ return reinterpret_cast< Memory32 const * >(
+ static_cast< char const * >(address) + offset)->getUnsigned32();
+}
+
+sal_uInt64 MappedFile::get64(sal_uInt32 offset) const {
+ assert(size >= 8);
+ assert(offset <= size - 8);
+ return reinterpret_cast< Memory64 const * >(
+ static_cast< char const * >(address) + offset)->getUnsigned64();
+}
+
+float MappedFile::getIso60599Binary32(sal_uInt32 offset) const {
+ assert(size >= 8);
+ assert(offset <= size - 4);
+ return reinterpret_cast< Memory32 const * >(
+ static_cast< char const * >(address) + offset)->getIso60599Binary32();
+}
+
+double MappedFile::getIso60599Binary64(sal_uInt32 offset) const {
+ assert(size >= 8);
+ assert(offset <= size - 8);
+ return reinterpret_cast< Memory64 const * >(
+ static_cast< char const * >(address) + offset)->getIso60599Binary64();
+}
+
+OUString MappedFile::readIdxString(
+ sal_uInt32 * offset, rtl_TextEncoding encoding) const
+{
+ assert(offset != nullptr);
+ sal_uInt32 len = read32(*offset);
+ sal_uInt32 off;
+ if ((len & 0x80000000) == 0) {
+ off = *offset;
+ *offset += 4 + len;
+ } else {
+ *offset += 4;
+ off = len & ~0x80000000;
+ len = read32(off);
+ if ((len & 0x80000000) != 0) {
+ throw FileFormatException(
+ uri, "UNOIDL format: string length high bit set");
+ }
+ }
+ if (len > SAL_MAX_INT32 || len > size - off - 4) {
+ throw FileFormatException(
+ uri, "UNOIDL format: size of string is too large");
+ }
+ OUString name;
+ if (!rtl_convertStringToUString(
+ &name.pData, static_cast< char const * >(address) + off + 4, len,
+ encoding,
+ (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
+ | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
+ | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR)))
+ {
+ throw FileFormatException(
+ uri, "UNOIDL format: string bytes do not match encoding");
+ }
+ return name;
+}
+
+// sizeof (MapEntry) == 8
+struct MapEntry {
+ Memory32 name;
+ Memory32 data;
+};
+
+static bool operator <(const Map& map1, const Map& map2) {
+ return map1.begin < map2.begin
+ || (map1.begin == map2.begin && map1.size < map2.size);
+}
+
+namespace {
+
+enum Compare { COMPARE_LESS, COMPARE_GREATER, COMPARE_EQUAL };
+
+Compare compare(
+ rtl::Reference< MappedFile > const & file, std::u16string_view name,
+ sal_Int32 nameOffset, sal_Int32 nameLength, MapEntry const * entry)
+{
+ assert(file.is());
+ assert(entry != nullptr);
+ sal_uInt32 off = entry->name.getUnsigned32();
+ if (off > file->size - 1) { // at least a trailing NUL
+ throw FileFormatException(
+ file->uri, "UNOIDL format: string offset too large");
+ }
+ assert(nameLength >= 0);
+ sal_uInt64 min = std::min(
+ static_cast< sal_uInt64 >(nameLength), file->size - off);
+ for (sal_uInt64 i = 0; i != min; ++i) {
+ sal_Unicode c1 = name[nameOffset + i];
+ sal_Unicode c2 = static_cast< unsigned char const * >(file->address)[
+ off + i];
+ if (c1 < c2) {
+ return COMPARE_LESS;
+ } else if (c1 > c2 || c2 == 0) {
+ // ...the "|| c2 == 0" is for the odd case where name erroneously
+ // contains NUL characters
+ return COMPARE_GREATER;
+ }
+ }
+ if (static_cast< sal_uInt64 >(nameLength) == min) {
+ if (file->size - off == min) {
+ throw FileFormatException(
+ file->uri, "UNOIDL format: string misses trailing NUL");
+ }
+ return
+ static_cast< unsigned char const * >(file->address)[off + min] == 0
+ ? COMPARE_EQUAL : COMPARE_LESS;
+ } else {
+ return COMPARE_GREATER;
+ }
+}
+
+sal_uInt32 findInMap(
+ rtl::Reference< MappedFile > const & file, MapEntry const * mapBegin,
+ sal_uInt32 mapSize, OUString const & name, sal_Int32 nameOffset,
+ sal_Int32 nameLength)
+{
+ if (mapSize == 0) {
+ return 0;
+ }
+ sal_uInt32 n = mapSize / 2;
+ MapEntry const * p = mapBegin + n;
+ switch (compare(file, name, nameOffset, nameLength, p)) {
+ case COMPARE_LESS:
+ return findInMap(file, mapBegin, n, name, nameOffset, nameLength);
+ case COMPARE_GREATER:
+ return findInMap(
+ file, p + 1, mapSize - n - 1, name, nameOffset, nameLength);
+ default: // COMPARE_EQUAL
+ break;
+ }
+ sal_uInt32 off = mapBegin[n].data.getUnsigned32();
+ if (off == 0) {
+ throw FileFormatException(
+ file->uri, "UNOIDL format: map entry data offset is null");
+ }
+ return off;
+}
+
+#if defined(__COVERITY__)
+extern "C" void __coverity_tainted_data_sanitize__(void *);
+#endif
+
+std::vector< OUString > readAnnotations(
+ bool annotated, rtl::Reference< MappedFile > const & file,
+ sal_uInt32 offset, sal_uInt32 * newOffset = nullptr)
+{
+ std::vector< OUString > ans;
+ if (annotated) {
+ sal_uInt32 n = file->read32(offset);
+#if defined(__COVERITY__)
+ __coverity_tainted_data_sanitize__(&n);
+#endif
+ offset += 4;
+ for (sal_uInt32 i = 0; i != n; ++i) {
+ ans.push_back(file->readIdxString(&offset));
+ }
+ }
+ if (newOffset != nullptr) {
+ *newOffset = offset;
+ }
+ return ans;
+}
+
+ConstantValue readConstant(
+ rtl::Reference< MappedFile > const & file, sal_uInt32 offset,
+ sal_uInt32 * newOffset, bool * annotated)
+{
+ assert(file.is());
+ int v = file->read8(offset);
+ int type = v & 0x7F;
+ if (annotated != nullptr) {
+ *annotated = (v & 0x80) != 0;
+ }
+ switch (type) {
+ case 0: // BOOLEAN
+ v = file->read8(offset + 1);
+ if (newOffset != nullptr) {
+ *newOffset = offset + 2;
+ }
+ switch (v) {
+ case 0:
+ return ConstantValue(false);
+ case 1:
+ return ConstantValue(true);
+ default:
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: bad boolean constant value "
+ + OUString::number(v)));
+ }
+ case 1: // BYTE
+ if (newOffset != nullptr) {
+ *newOffset = offset + 2;
+ }
+ return ConstantValue(static_cast< sal_Int8 >(file->read8(offset + 1)));
+ //TODO: implementation-defined behavior of conversion from sal_uInt8
+ // to sal_Int8 relies on two's complement representation
+ case 2: // SHORT
+ if (newOffset != nullptr) {
+ *newOffset = offset + 3;
+ }
+ return ConstantValue(
+ static_cast< sal_Int16 >(file->read16(offset + 1)));
+ //TODO: implementation-defined behavior of conversion from
+ // sal_uInt16 to sal_Int16 relies on two's complement representation
+ case 3: // UNSIGNED SHORT
+ if (newOffset != nullptr) {
+ *newOffset = offset + 3;
+ }
+ return ConstantValue(file->read16(offset + 1));
+ case 4: // LONG
+ if (newOffset != nullptr) {
+ *newOffset = offset + 5;
+ }
+ return ConstantValue(
+ static_cast< sal_Int32 >(file->read32(offset + 1)));
+ //TODO: implementation-defined behavior of conversion from
+ // sal_uInt32 to sal_Int32 relies on two's complement representation
+ case 5: // UNSIGNED LONG
+ if (newOffset != nullptr) {
+ *newOffset = offset + 5;
+ }
+ return ConstantValue(file->read32(offset + 1));
+ case 6: // HYPER
+ if (newOffset != nullptr) {
+ *newOffset = offset + 9;
+ }
+ return ConstantValue(
+ static_cast< sal_Int64 >(file->read64(offset + 1)));
+ //TODO: implementation-defined behavior of conversion from
+ // sal_uInt64 to sal_Int64 relies on two's complement representation
+ case 7: // UNSIGNED HYPER
+ if (newOffset != nullptr) {
+ *newOffset = offset + 9;
+ }
+ return ConstantValue(file->read64(offset + 1));
+ case 8: // FLOAT
+ if (newOffset != nullptr) {
+ *newOffset = offset + 5;
+ }
+ return ConstantValue(file->readIso60599Binary32(offset + 1));
+ case 9: // DOUBLE
+ if (newOffset != nullptr) {
+ *newOffset = offset + 9;
+ }
+ return ConstantValue(file->readIso60599Binary64(offset + 1));
+ default:
+ throw FileFormatException(
+ file->uri,
+ "UNOIDL format: bad constant type byte " + OUString::number(v));
+ }
+}
+
+rtl::Reference< Entity > readEntity(
+ rtl::Reference< MappedFile > const & file, sal_uInt32 offset,
+ std::set<Map> && trace);
+
+class UnoidlModuleEntity;
+
+class UnoidlCursor: public MapCursor {
+public:
+ UnoidlCursor(
+ rtl::Reference< MappedFile > file,
+ rtl::Reference<UnoidlProvider> reference1,
+ rtl::Reference<UnoidlModuleEntity> reference2,
+ NestedMap const & map):
+ file_(std::move(file)), reference1_(std::move(reference1)), reference2_(std::move(reference2)),
+ map_(map), index_(0)
+ {}
+
+private:
+ virtual ~UnoidlCursor() noexcept override {}
+
+ virtual rtl::Reference< Entity > getNext(OUString * name) override;
+
+ rtl::Reference< MappedFile > file_;
+ rtl::Reference<UnoidlProvider> reference1_; // HACK to keep alive whatever
+ rtl::Reference<UnoidlModuleEntity> reference2_; // owner of map_
+ NestedMap const & map_;
+ sal_uInt32 index_;
+};
+
+rtl::Reference< Entity > UnoidlCursor::getNext(OUString * name) {
+ assert(name != nullptr);
+ rtl::Reference< Entity > ent;
+ if (index_ != map_.map.size) {
+ *name = file_->readNulName(map_.map.begin[index_].name.getUnsigned32());
+ ent = readEntity(
+ file_, map_.map.begin[index_].data.getUnsigned32(), std::set(map_.trace));
+ ++index_;
+ }
+ return ent;
+}
+
+class UnoidlModuleEntity: public ModuleEntity {
+public:
+ UnoidlModuleEntity(
+ rtl::Reference< MappedFile > const & file, sal_uInt32 mapOffset,
+ sal_uInt32 mapSize, std::set<Map> && trace):
+ file_(file)
+ {
+ assert(file.is());
+ map_.map.begin = reinterpret_cast<MapEntry const *>(
+ static_cast<char const *>(file_->address) + mapOffset);
+ map_.map.size = mapSize;
+ map_.trace = std::move(trace);
+ if (!map_.trace.insert(map_.map).second) {
+ throw FileFormatException(
+ file_->uri, "UNOIDL format: recursive map");
+ }
+ }
+
+private:
+ virtual ~UnoidlModuleEntity() noexcept override {}
+
+ virtual std::vector< OUString > getMemberNames() const override;
+
+ virtual rtl::Reference< MapCursor > createCursor() const override {
+ return new UnoidlCursor(
+ file_, rtl::Reference<UnoidlProvider>(),
+ const_cast<UnoidlModuleEntity *>(this), map_);
+ }
+
+ rtl::Reference< MappedFile > file_;
+ NestedMap map_;
+};
+
+std::vector< OUString > UnoidlModuleEntity::getMemberNames() const {
+ std::vector< OUString > names;
+ for (sal_uInt32 i = 0; i != map_.map.size; ++i) {
+ names.push_back(
+ file_->readNulName(map_.map.begin[i].name.getUnsigned32()));
+ }
+ return names;
+}
+
+rtl::Reference< Entity > readEntity(
+ rtl::Reference< MappedFile > const & file, sal_uInt32 offset,
+ std::set<Map> && trace)
+{
+ assert(file.is());
+ int v = file->read8(offset);
+ int type = v & 0x3F;
+ bool published = (v & 0x80) != 0;
+ bool annotated = (v & 0x40) != 0;
+ bool flag = (v & 0x20) != 0;
+ switch (type) {
+ case 0: // module
+ {
+ if (v != 0) {
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: bad module type byte "
+ + OUString::number(v)));
+ }
+ sal_uInt32 n = file->read32(offset + 1);
+ if (n > SAL_MAX_INT32) {
+ throw FileFormatException(
+ file->uri, "UNOIDL format: too many items in module");
+ }
+ if (sal_uInt64(offset) + 5 + 8 * sal_uInt64(n) > file->size)
+ // cannot overflow
+ {
+ throw FileFormatException(
+ file->uri,
+ "UNOIDL format: module map offset + size too large");
+ }
+ return new UnoidlModuleEntity(file, offset + 5, n, std::move(trace));
+ }
+ case 1: // enum type
+ {
+ sal_uInt32 n = file->read32(offset + 1);
+ if (n == 0) {
+ throw FileFormatException(
+ file->uri, "UNOIDL format: enum type with no members");
+ }
+ if (n > SAL_MAX_INT32) {
+ throw FileFormatException(
+ file->uri, "UNOIDL format: too many members of enum type");
+ }
+ offset += 5;
+ std::vector< EnumTypeEntity::Member > mems;
+ mems.reserve(n);
+ for (sal_uInt32 i = 0; i != n; ++i) {
+ OUString memName(file->readIdxName(&offset));
+ checkEntityName(file, memName);
+ sal_Int32 memValue = static_cast< sal_Int32 >(
+ file->read32(offset));
+ //TODO: implementation-defined behavior of conversion from
+ // sal_uInt32 to sal_Int32 relies on two's complement
+ // representation
+ offset += 4;
+ mems.emplace_back(
+ memName, memValue,
+ readAnnotations(annotated, file, offset, &offset));
+ }
+ return new EnumTypeEntity(
+ published, std::move(mems), readAnnotations(annotated, file, offset));
+ }
+ case 2: // plain struct type without base
+ case 2 | 0x20: // plain struct type with base
+ {
+ ++offset;
+ OUString base;
+ if (flag) {
+ base = file->readIdxName(&offset);
+ if (base.isEmpty()) {
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: empty base type name of plain struct"
+ " type"));
+ }
+ checkTypeName(file, base);
+ }
+ sal_uInt32 n = file->read32(offset);
+ if (n > SAL_MAX_INT32) {
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: too many direct members of plain struct"
+ " type"));
+ }
+ offset += 4;
+ std::vector< PlainStructTypeEntity::Member > mems;
+ mems.reserve(n);
+ for (sal_uInt32 i = 0; i != n; ++i) {
+ OUString memName(file->readIdxName(&offset));
+ checkEntityName(file, memName);
+ OUString memType(file->readIdxName(&offset));
+ checkTypeName(file, memType);
+ mems.emplace_back(
+ memName, memType,
+ readAnnotations(annotated, file, offset, &offset));
+ }
+ return new PlainStructTypeEntity(
+ published, base, std::move(mems),
+ readAnnotations(annotated, file, offset));
+ }
+ case 3: // polymorphic struct type template
+ {
+ sal_uInt32 n = file->read32(offset + 1);
+ if (n > SAL_MAX_INT32) {
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: too many type parameters of polymorphic"
+ " struct type template"));
+ }
+ offset += 5;
+ std::vector< OUString > params;
+ params.reserve(n);
+ for (sal_uInt32 i = 0; i != n; ++i) {
+ OUString param(file->readIdxName(&offset));
+ checkEntityName(file, param);
+ params.push_back(param);
+ }
+ n = file->read32(offset);
+ if (n > SAL_MAX_INT32) {
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: too many members of polymorphic struct"
+ " type template"));
+ }
+ offset += 4;
+ std::vector< PolymorphicStructTypeTemplateEntity::Member > mems;
+ mems.reserve(n);
+ for (sal_uInt32 i = 0; i != n; ++i) {
+ v = file->read8(offset);
+ ++offset;
+ OUString memName(file->readIdxName(&offset));
+ checkEntityName(file, memName);
+ OUString memType(file->readIdxName(&offset));
+ checkTypeName(file, memType);
+ if (v > 1) {
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: bad flags " + OUString::number(v)
+ + " for member " + memName
+ + " of polymorphic struct type template"));
+ }
+ mems.emplace_back(
+ memName, memType, v == 1,
+ readAnnotations(annotated, file, offset, &offset));
+ }
+ return new PolymorphicStructTypeTemplateEntity(
+ published, std::move(params), std::move(mems),
+ readAnnotations(annotated, file, offset));
+ }
+ case 4: // exception type without base
+ case 4 | 0x20: // exception type with base
+ {
+ ++offset;
+ OUString base;
+ if (flag) {
+ base = file->readIdxName(&offset);
+ if (base.isEmpty()) {
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: empty base type name of exception"
+ " type"));
+ }
+ checkTypeName(file, base);
+ }
+ sal_uInt32 n = file->read32(offset);
+ if (n > SAL_MAX_INT32) {
+ throw FileFormatException(
+ file->uri,
+ "UNOIDL format: too many direct members of exception type");
+ }
+ offset += 4;
+ std::vector< ExceptionTypeEntity::Member > mems;
+ mems.reserve(n);
+ for (sal_uInt32 i = 0; i != n; ++i) {
+ OUString memName(file->readIdxName(&offset));
+ checkEntityName(file, memName);
+ OUString memType(file->readIdxName(&offset));
+ checkTypeName(file, memType);
+ mems.emplace_back(
+ memName, memType,
+ readAnnotations(annotated, file, offset, &offset));
+ }
+ return new ExceptionTypeEntity(
+ published, base, std::move(mems),
+ readAnnotations(annotated, file, offset));
+ }
+ case 5: // interface type
+ {
+ sal_uInt32 n = file->read32(offset + 1);
+ if (n > SAL_MAX_INT32) {
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: too many direct mandatory bases of"
+ " interface type"));
+ }
+ offset += 5;
+ std::vector< AnnotatedReference > mandBases;
+ mandBases.reserve(n);
+ for (sal_uInt32 i = 0; i != n; ++i) {
+ OUString base(file->readIdxName(&offset));
+ checkTypeName(file, base);
+ mandBases.emplace_back(
+ base, readAnnotations(annotated, file, offset, &offset));
+ }
+ n = file->read32(offset);
+ if (n > SAL_MAX_INT32) {
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: too many direct optional bases of"
+ " interface type"));
+ }
+ offset += 4;
+ std::vector< AnnotatedReference > optBases;
+ optBases.reserve(n);
+ for (sal_uInt32 i = 0; i != n; ++i) {
+ OUString base(file->readIdxName(&offset));
+ checkTypeName(file, base);
+ optBases.emplace_back(
+ base, readAnnotations(annotated, file, offset, &offset));
+ }
+ sal_uInt32 nAttrs = file->read32(offset);
+ if (nAttrs > SAL_MAX_INT32) {
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: too many direct attributes of interface"
+ " type"));
+ }
+ offset += 4;
+ std::vector< InterfaceTypeEntity::Attribute > attrs;
+ attrs.reserve(nAttrs);
+ for (sal_uInt32 i = 0; i != nAttrs; ++i) {
+ v = file->read8(offset);
+ ++offset;
+ OUString attrName(file->readIdxName(&offset));
+ checkEntityName(file, attrName);
+ OUString attrType(file->readIdxName(&offset));
+ checkTypeName(file, attrType);
+ if (v > 0x03) {
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: bad flags for direct attribute "
+ + attrName + " of interface type"));
+ }
+ std::vector< OUString > getExcs;
+ sal_uInt32 m = file->read32(offset);
+ if (m > SAL_MAX_INT32) {
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: too many getter exceptions for direct"
+ " attribute " + attrName + " of interface type"));
+ }
+ offset += 4;
+ getExcs.reserve(m);
+ for (sal_uInt32 j = 0; j != m; ++j) {
+ OUString exc(file->readIdxName(&offset));
+ checkTypeName(file, exc);
+ getExcs.push_back(exc);
+ }
+ std::vector< OUString > setExcs;
+ if ((v & 0x02) == 0) {
+ m = file->read32(offset);
+ if (m > SAL_MAX_INT32) {
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: too many setter exceptions for"
+ " direct attribute " + attrName
+ + " of interface type"));
+ }
+ offset += 4;
+ setExcs.reserve(m);
+ for (sal_uInt32 j = 0; j != m; ++j) {
+ OUString exc(file->readIdxName(&offset));
+ checkTypeName(file, exc);
+ setExcs.push_back(exc);
+ }
+ }
+ attrs.emplace_back(
+ attrName, attrType, (v & 0x01) != 0, (v & 0x02) != 0,
+ std::move(getExcs), std::move(setExcs),
+ readAnnotations(annotated, file, offset, &offset));
+ }
+ sal_uInt32 nMeths = file->read32(offset);
+ if (nMeths > SAL_MAX_INT32 - nAttrs) {
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: too many direct attributes and methods of"
+ " interface type"));
+ }
+ offset += 4;
+ std::vector< InterfaceTypeEntity::Method > meths;
+ meths.reserve(nMeths);
+ for (sal_uInt32 i = 0; i != nMeths; ++i) {
+ OUString methName(file->readIdxName(&offset));
+ checkEntityName(file, methName);
+ OUString methType(file->readIdxName(&offset));
+ checkTypeName(file, methType);
+ sal_uInt32 m = file->read32(offset);
+ if (m > SAL_MAX_INT32) {
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: too many parameters for method "
+ + methName + " of interface type"));
+ }
+ offset += 4;
+ std::vector< InterfaceTypeEntity::Method::Parameter > params;
+ params.reserve(m);
+ for (sal_uInt32 j = 0; j != m; ++j) {
+ v = file->read8(offset);
+ ++offset;
+ OUString paramName(file->readIdxName(&offset));
+ checkEntityName(file, paramName);
+ OUString paramType(file->readIdxName(&offset));
+ checkTypeName(file, paramType);
+ InterfaceTypeEntity::Method::Parameter::Direction dir;
+ switch (v) {
+ case 0:
+ dir = InterfaceTypeEntity::Method::Parameter::
+ DIRECTION_IN;
+ break;
+ case 1:
+ dir = InterfaceTypeEntity::Method::Parameter::
+ DIRECTION_OUT;
+ break;
+ case 2:
+ dir = InterfaceTypeEntity::Method::Parameter::
+ DIRECTION_IN_OUT;
+ break;
+ default:
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: bad direction "
+ + OUString::number(v) + " of parameter "
+ + paramName + " for method " + methName
+ + " of interface type"));
+ }
+ params.emplace_back(paramName, paramType, dir);
+ }
+ std::vector< OUString > excs;
+ m = file->read32(offset);
+ if (m > SAL_MAX_INT32) {
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: too many exceptions for method "
+ + methName + " of interface type"));
+ }
+ offset += 4;
+ excs.reserve(m);
+ for (sal_uInt32 j = 0; j != m; ++j) {
+ OUString exc(file->readIdxName(&offset));
+ checkTypeName(file, exc);
+ excs.push_back(exc);
+ }
+ meths.emplace_back(
+ methName, methType, std::move(params), std::move(excs),
+ readAnnotations(annotated, file, offset, &offset));
+ }
+ return new InterfaceTypeEntity(
+ published, std::move(mandBases), std::move(optBases), std::move(attrs), std::move(meths),
+ readAnnotations(annotated, file, offset));
+ }
+ case 6: // typedef
+ {
+ ++offset;
+ OUString base(file->readIdxName(&offset));
+ checkTypeName(file, base);
+ return new TypedefEntity(
+ published, base, readAnnotations(annotated, file, offset));
+ }
+ case 7: // constant group
+ {
+ sal_uInt32 n = file->read32(offset + 1);
+ if (n > SAL_MAX_INT32) {
+ throw FileFormatException(
+ file->uri,
+ "UNOIDL format: too many constants in constant group");
+ }
+ if (sal_uInt64(offset) + 5 + 8 * sal_uInt64(n) > file->size)
+ // cannot overflow
+ {
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: constant group map offset + size too"
+ " large"));
+ }
+ MapEntry const * p = reinterpret_cast< MapEntry const * >(
+ static_cast< char const * >(file->address) + offset + 5);
+ std::vector< ConstantGroupEntity::Member > mems;
+ mems.reserve(n);
+ for (sal_uInt32 i = 0; i != n; ++i) {
+ sal_uInt32 off = p[i].data.getUnsigned32();
+ bool ann;
+ ConstantValue val(readConstant(file, off, &off, &ann));
+ mems.emplace_back(
+ file->readNulName(p[i].name.getUnsigned32()), val,
+ readAnnotations(ann, file, off));
+ }
+ return new ConstantGroupEntity(
+ published, std::move(mems),
+ readAnnotations(annotated, file, offset + 5 + 8 * n));
+ }
+ case 8: // single-interface--based service without default constructor
+ case 8 | 0x20: // single-interface--based service with default constructor
+ {
+ ++offset;
+ OUString base(file->readIdxName(&offset));
+ checkTypeName(file, base);
+ std::vector< SingleInterfaceBasedServiceEntity::Constructor > ctors;
+ if (flag) {
+ ctors.push_back(
+ SingleInterfaceBasedServiceEntity::Constructor());
+ } else {
+ sal_uInt32 n = file->read32(offset);
+ if (n > SAL_MAX_INT32) {
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: too many constructors of"
+ " single-interface--based service"));
+ }
+ offset += 4;
+ ctors.reserve(n);
+ for (sal_uInt32 i = 0; i != n; ++i) {
+ OUString ctorName(file->readIdxName(&offset));
+ checkEntityName(file, ctorName);
+ sal_uInt32 m = file->read32(offset);
+ if (m > SAL_MAX_INT32) {
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: too many parameters for"
+ " constructor " + ctorName
+ + " of single-interface--based service"));
+ }
+ offset += 4;
+ std::vector<
+ SingleInterfaceBasedServiceEntity::Constructor::
+ Parameter > params;
+ params.reserve(m);
+ for (sal_uInt32 j = 0; j != m; ++j) {
+ v = file->read8(offset);
+ ++offset;
+ OUString paramName(file->readIdxName(&offset));
+ checkEntityName(file, paramName);
+ OUString paramType(file->readIdxName(&offset));
+ checkTypeName(file, paramType);
+ bool rest;
+ switch (v) {
+ case 0:
+ rest = false;
+ break;
+ case 0x04:
+ rest = true;
+ break;
+ default:
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: bad mode "
+ + OUString::number(v) + " of parameter "
+ + paramName + " for constructor " + ctorName
+ + " of single-interface--based service"));
+ }
+ params.emplace_back(paramName, paramType, rest);
+ }
+ std::vector< OUString > excs;
+ m = file->read32(offset);
+ if (m > SAL_MAX_INT32) {
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: too many exceptions for"
+ " constructor " + ctorName
+ + " of single-interface--based service"));
+ }
+ offset += 4;
+ excs.reserve(m);
+ for (sal_uInt32 j = 0; j != m; ++j) {
+ OUString exc(file->readIdxName(&offset));
+ checkTypeName(file, exc);
+ excs.push_back(exc);
+ }
+ ctors.push_back(
+ SingleInterfaceBasedServiceEntity::Constructor(
+ ctorName, std::move(params), std::move(excs),
+ readAnnotations(annotated, file, offset, &offset)));
+ }
+ }
+ return new SingleInterfaceBasedServiceEntity(
+ published, base, std::move(ctors),
+ readAnnotations(annotated, file, offset));
+ }
+ case 9: // accumulation-based service
+ {
+ sal_uInt32 n = file->read32(offset + 1);
+ if (n > SAL_MAX_INT32) {
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: too many direct mandatory service bases of"
+ " accumulation-based service"));
+ }
+ offset += 5;
+ std::vector< AnnotatedReference > mandServs;
+ mandServs.reserve(n);
+ for (sal_uInt32 i = 0; i != n; ++i) {
+ OUString base(file->readIdxName(&offset));
+ checkTypeName(file, base);
+ mandServs.emplace_back(
+ base, readAnnotations(annotated, file, offset, &offset));
+ }
+ n = file->read32(offset);
+ if (n > SAL_MAX_INT32) {
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: too many direct optional service bases of"
+ " accumulation-based service"));
+ }
+ offset += 4;
+ std::vector< AnnotatedReference > optServs;
+ optServs.reserve(n);
+ for (sal_uInt32 i = 0; i != n; ++i) {
+ OUString base(file->readIdxName(&offset));
+ checkTypeName(file, base);
+ optServs.emplace_back(
+ base, readAnnotations(annotated, file, offset, &offset));
+ }
+ n = file->read32(offset);
+ if (n > SAL_MAX_INT32) {
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: too many direct mandatory interface bases"
+ " of accumulation-based service"));
+ }
+ offset += 4;
+ std::vector< AnnotatedReference > mandIfcs;
+ mandIfcs.reserve(n);
+ for (sal_uInt32 i = 0; i != n; ++i) {
+ OUString base(file->readIdxName(&offset));
+ checkTypeName(file, base);
+ mandIfcs.emplace_back(
+ base, readAnnotations(annotated, file, offset, &offset));
+ }
+ n = file->read32(offset);
+ if (n > SAL_MAX_INT32) {
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: too many direct optional interface bases"
+ " of accumulation-based service"));
+ }
+ offset += 4;
+ std::vector< AnnotatedReference > optIfcs;
+ optIfcs.reserve(n);
+ for (sal_uInt32 i = 0; i != n; ++i) {
+ OUString base(file->readIdxName(&offset));
+ checkTypeName(file, base);
+ optIfcs.emplace_back(
+ base, readAnnotations(annotated, file, offset, &offset));
+ }
+ n = file->read32(offset);
+ if (n > SAL_MAX_INT32) {
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: too many direct properties of"
+ " accumulation-based service"));
+ }
+ offset += 4;
+ std::vector< AccumulationBasedServiceEntity::Property > props;
+ props.reserve(n);
+ for (sal_uInt32 i = 0; i != n; ++i) {
+ sal_uInt16 attrs = file->read16(offset);
+ offset += 2;
+ OUString propName(file->readIdxName(&offset));
+ checkEntityName(file, propName);
+ OUString propType(file->readIdxName(&offset));
+ checkTypeName(file, propType);
+ if (attrs > 0x01FF) { // see css.beans.PropertyAttribute
+ throw FileFormatException(
+ file->uri,
+ ("UNOIDL format: bad mode " + OUString::number(v)
+ + " of property " + propName
+ + " for accumulation-based service"));
+ }
+ props.emplace_back(
+ propName, propType,
+ static_cast<
+ AccumulationBasedServiceEntity::Property::Attributes >(
+ attrs),
+ readAnnotations(annotated, file, offset, &offset));
+ }
+ return new AccumulationBasedServiceEntity(
+ published, std::move(mandServs), std::move(optServs), std::move(mandIfcs), std::move(optIfcs), std::move(props),
+ readAnnotations(annotated, file, offset));
+ }
+ case 10: // interface-based singleton
+ {
+ ++offset;
+ OUString base(file->readIdxName(&offset));
+ checkTypeName(file, base);
+ return new InterfaceBasedSingletonEntity(
+ published, base, readAnnotations(annotated, file, offset));
+ }
+ case 11: // service-based singleton
+ {
+ ++offset;
+ OUString base(file->readIdxName(&offset));
+ checkTypeName(file, base);
+ return new ServiceBasedSingletonEntity(
+ published, base, readAnnotations(annotated, file, offset));
+ }
+ default:
+ throw FileFormatException(
+ file->uri, "UNOIDL format: bad type byte " + OUString::number(v));
+ }
+}
+
+}
+
+UnoidlProvider::UnoidlProvider(OUString const & uri): file_(new MappedFile(uri))
+{
+ if (file_->size < 8 || std::memcmp(file_->address, "UNOIDL\xFF\0", 8) != 0)
+ {
+ throw FileFormatException(
+ file_->uri,
+ "UNOIDL format: does not begin with magic UNOIDL\\xFF and version"
+ " 0");
+ }
+ sal_uInt32 off = file_->read32(8);
+ map_.map.size = file_->read32(12);
+ if (off + 8 * sal_uInt64(map_.map.size) > file_->size) { // cannot overflow
+ throw FileFormatException(
+ file_->uri, "UNOIDL format: root map offset + size too large");
+ }
+ map_.map.begin = reinterpret_cast< MapEntry const * >(
+ static_cast< char const * >(file_->address) + off);
+ map_.trace.insert(map_.map);
+}
+
+rtl::Reference< MapCursor > UnoidlProvider::createRootCursor() const {
+ return new UnoidlCursor(
+ file_, const_cast<UnoidlProvider *>(this),
+ rtl::Reference<UnoidlModuleEntity>(), map_);
+}
+
+rtl::Reference< Entity > UnoidlProvider::findEntity(OUString const & name) const
+{
+ NestedMap map(map_);
+ bool cgroup = false;
+ for (sal_Int32 i = 0;;) {
+ sal_Int32 j = name.indexOf('.', i);
+ if (j == -1) {
+ j = name.getLength();
+ }
+ sal_Int32 off = findInMap(
+ file_, map.map.begin, map.map.size, name, i, j - i);
+ if (off == 0) {
+ return rtl::Reference< Entity >();
+ }
+ if (j == name.getLength()) {
+ return cgroup
+ ? rtl::Reference< Entity >()
+ : readEntity(file_, off, std::set(map.trace));
+ }
+ if (cgroup) {
+ return rtl::Reference< Entity >();
+ //TODO: throw an exception instead here, where the segments of a
+ // constant's name are a prefix of the requested name's
+ // segments?
+ }
+ int v = file_->read8(off);
+ if (v != 0) { // module
+ if ((v & 0x3F) == 7) { // constant group
+ cgroup = true;
+ } else {
+ return rtl::Reference< Entity >();
+ //TODO: throw an exception instead here, where the segments
+ // of a non-module, non-constant-group entity's name are a
+ // prefix of the requested name's segments?
+ }
+ }
+ map.map.size = file_->read32(off + 1);
+ if (sal_uInt64(off) + 5 + 8 * sal_uInt64(map.map.size) > file_->size)
+ // cannot overflow
+ {
+ throw FileFormatException(
+ file_->uri, "UNOIDL format: map offset + size too large");
+ }
+ map.map.begin = reinterpret_cast< MapEntry const * >(
+ static_cast< char const * >(file_->address) + off + 5);
+ if (!map.trace.insert(map.map).second) {
+ throw FileFormatException(
+ file_->uri, "UNOIDL format: recursive map");
+ }
+ i = j + 1;
+ }
+}
+
+UnoidlProvider::~UnoidlProvider() noexcept {}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unoidl/source/unoidlprovider.hxx b/unoidl/source/unoidlprovider.hxx
new file mode 100644
index 000000000..734f8592b
--- /dev/null
+++ b/unoidl/source/unoidlprovider.hxx
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <set>
+
+#include <rtl/ref.hxx>
+#include <sal/types.h>
+#include <unoidl/unoidl.hxx>
+
+namespace unoidl::detail {
+
+class MappedFile;
+struct MapEntry;
+
+struct Map {
+ MapEntry const * begin;
+ sal_uInt32 size;
+};
+
+struct NestedMap {
+ Map map;
+ std::set<Map> trace;
+};
+
+class UnoidlProvider: public Provider {
+public:
+ // throws FileFormatException, NoSuchFileException:
+ explicit UnoidlProvider(OUString const & uri);
+
+ // throws FileFormatException:
+ virtual rtl::Reference< MapCursor > createRootCursor() const override;
+
+ // throws FileFormatException:
+ virtual rtl::Reference< Entity > findEntity(OUString const & name)
+ const override;
+
+private:
+ virtual ~UnoidlProvider() noexcept override;
+
+ rtl::Reference< detail::MappedFile > file_;
+ NestedMap map_;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */