diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 05:54:39 +0000 |
commit | 267c6f2ac71f92999e969232431ba04678e7437e (patch) | |
tree | 358c9467650e1d0a1d7227a21dac2e3d08b622b2 /unoidl | |
parent | Initial commit. (diff) | |
download | libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip |
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'unoidl')
34 files changed, 12340 insertions, 0 deletions
diff --git a/unoidl/CustomTarget_unoidl-check_test.mk b/unoidl/CustomTarget_unoidl-check_test.mk new file mode 100644 index 0000000000..52ef4d6c30 --- /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 0000000000..fc80f516c5 --- /dev/null +++ b/unoidl/CustomTarget_unoidl-write_test.mk @@ -0,0 +1,102 @@ +# -*- 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/conversion.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/conversion.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 0000000000..cdf9f5aa28 --- /dev/null +++ b/unoidl/Executable_unoidl-check.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_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 \ +)) +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 0000000000..b134c30b1a --- /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 0000000000..e7ec3ba23c --- /dev/null +++ b/unoidl/Executable_unoidl-write.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_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 \ +)) +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 0000000000..506f964038 --- /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 0000000000..ff37972eec --- /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 0000000000..0997e62848 --- /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 0000000000..1553371774 --- /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 0000000000..d6421f8585 --- /dev/null +++ b/unoidl/README.md @@ -0,0 +1,282 @@ +# 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. + +`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 0000000000..75473015bc --- /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 0000000000..f6c94ef665 --- /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 0000000000..c597b29f04 --- /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 0000000000..b8079c9add --- /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 0000000000..03d02b302c --- /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 0000000000..0face3356b --- /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 0000000000..80fe2c2f4c --- /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 0000000000..08e7e9daa9 --- /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 0000000000..9299ae7ce0 --- /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 0000000000..caa1b38c84 --- /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 0000000000..983b7d3c34 --- /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 0000000000..52fd32f3c7 --- /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 0000000000..5d9e5f6e3f --- /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 0000000000..f3510cb17b --- /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"_ostr); } +| TOK_PUBLISHED { $$ = new OString("published"_ostr); } +| TOK_SET { $$ = new OString("set"_ostr); } +; + +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 0000000000..08ed189f9f --- /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 0000000000..bdedfcffc9 --- /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 0000000000..629e50fbf8 --- /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); + 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 0000000000..37bd6baa12 --- /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 0000000000..fdeb74441f --- /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, Concat2View(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, Concat2View(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 0000000000..721fdabe9c --- /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, Concat2View(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 0000000000..042b72c047 --- /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, std::size(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, std::size(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, std::size(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, std::size(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, std::size(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, std::size(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 = ↦ + 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 0000000000..2022bcd058 --- /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 0000000000..d6f4d4ddd5 --- /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 0000000000..734f8592b8 --- /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: */ |