Store, Registry and UNO-IDL

Store

This module implements a legacy binary UNO types format, still used by extensions and core code. Eventually the LibreOffice team want to migrate away from this format.
The module works on files and memory streams. Possibly the easiest way of understanding how the format works is to show an example of how to read and write a byte of data to a memory stream (writing to a file backed store works in almost the same way):
  • To open a file in memory, you do the following:
1
store::OStoreFile aFile;
2
aFile.createInMemory()
Copied!
  • To write to the file, you must first open a stream, then write to it. The following is an example of how to write a byte to the stream, and then read the byte back again.
1
if aFile.isValid();
2
{
3
store::OStoreStream aStream;
4
if (aStream.create(aFile,
5
"testnode", // normally the directory
6
"testname", // normally the file name
7
storeAccessMode::ReadWrite)
8
{
9
{
10
std::unique_ptr<sal_uInt8[]> pWriteBuffer(new sal_uInt8[1]);
11
pWriteBuffer[0] = 'a';
12
13
sal_uInt32 writtenBytes;
14
15
if (!(aStream.writeAt(
16
0, // start offset
17
pWriteBuffer.get(),
18
1, // number of bytes to write
19
writtenBytes) // record the bytes written
20
&& writtenBytes == 1))
21
{
22
assert(false);
23
}
24
}
25
26
{
27
std::unique_ptr<sal_uInt8[]> pReadBuffer;
28
sal_uInt32 readBytes;
29
30
if (!(aStream.readAt(
31
0, // start offset
32
pReadBuffer.get(),
33
1, // number of bytes to read
34
readBytes) // record the bytes read
35
&& readBytes == 1))
36
{
37
assert(false);
38
}
39
}
40
}
41
}
Copied!
As the store is deprecated, I will not go into how it actually stores the files.
Note: I have written some unit tests, please see https://gerrit.libreoffice.org/c/core/+/127674 - one day hopefully a developer will review.

Registry

The registry holds the system's UNO type information in a database. The following data types are stored in the registry:
  • Modules
  • Structs
  • Enums
  • Interfaces
  • Exceptions
  • Services
  • Value types (aka constants)

Reading the registry

All keys in the registry are part of a module. To read the registry, you must first open it. To do this, you first instantiate a Registry object, and call open(). You then need to open the root key (the registry is a hierarchical key/value store), and from the root key you can find your subkey.
For example, say you have a registry file with a single module called ModuleA, containing a constant key "test", you would do the following:
1
#include <registry/registry.hxx>
2
3
Registry reg;
4
reg.open("registryfile.reg", RegAccessMode::READONLY);
5
6
RegistryKey root;
7
reg.openRootKey(root);
8
9
RegistryKey modulekey;
10
root.openKey("ModuleA", modulekey);
11
12
RegistryKey testkey;
13
modulekey.openKey("test");
Copied!

UNO-IDL

UNOIDL (UNO Interface Definition Language) is a way of specifying types, services, and other entities used by UNO via a metalanguage of its own. UNOIDL should be seen as a specification language, and is the building block used by UNO to create UNO components, which consist of a variety of compiled libraries that interacts are are bound to the UNO infrastructure.
Starting in LibreOffice 7.5, developers will start to use the unoidl module to write and read UNO types. Changes made will mean that LibreOffice extensions are now incompatible with OpenOffice.org extensions, and any LibreOffice extensions developed before LibreOffice 4.1 will no longer work either. This has been a very necessary step in degunking extemely legacy code (the idlc module and regmerge utility are being removed).
The unoidl module actually handles more than just types, it also processes the UNO modules, services, singletons, etc. that make up actual object instances. These are managed via .idl (Interface Definition Language) files, and thus must be processed differently than the binary types.rdb file.
The first step in reading the registry is to load a provider, which does the hard work of actually reading from the binary types file. The provider is used to create the root cursor - this cursor holds the root location in the type registry. This is then used to navigate the registry.
Let's say you have a types.rdb file. To open the file for reading, you must first instantiate a provider manager, and have the manager produce the provider that does the work of parsing the rdb file:
1
rtl::Reference<unoidl::Manager> mgr(new unoidl::Manager);
2
rtl::Reference<unoidl::Provider> prov = mgr.addProvider("types.rdb");
Copied!
Each provider produces a rootMapCursor which is a simple forward iterator. Each type is returned as an Entity.
1
rtl::Reference<unoidl::MapCursor> cursor = prov->getRootCursor();
2
3
for (;;)
4
{
5
OUString id;
6
rtl::Reference<unoidl::Entity> ent(cursor->getNext(&id));
7
8
if (!ent.is()) {
9
break;
10
}
11
12
// process entity
13
}
Copied!

Interface Definition Language (IDL)

The BNF notation for IDL files that define services is as follows:
1
(1) <idl_specification> := <definition>+
2
3
(2) <definition> := <type_decl> ";"
4
| <module_decl> ";"
5
| <constant_decl> ";"
6
| <exception_decl> ";"
7
| <constants_decl> ";"
8
| <service_decl> ";"
9
10
(3) <type_decl> := <interface>
11
| <struct_decl>
12
| <enum_decl>
13
| <union_decl>
14
| "typedef" <type_spec> <declarator> {"," <declarator> }*
15
16
(4) <interface> := <interface_decl>
17
| <forward_decl>
18
19
(5) <forward_decl> := "interface" <identifier>
20
21
(6) <interface_decl> := <interface_header> "{" <interface_body> "}"
22
23
(7) <interface_header> := "interface" <identifier> [ <interface_inheritance> ]
24
25
(8) <interface_inheritance> := ":" <interface_name>
26
27
(9) <interface_name> := <scoped_name>
28
29
(10) <scoped_name> := <identifier>
30
| "::" <scoped_name>
31
| <scoped_name> "::" <identifier>
32
33
(11) <interface_body> := <export>+
34
35
(12) <export> := <attribute_decl> ";"
36
| <operation_decl> ";"
37
38
(13) <attribute_decl> := <attribute_head> <type_spec> <declarator> { "," <declarator> }*
39
40
(14) <attribute_head> := "[" ["readonly" ","] "attribute" "]"
41
| "[" "attribute" ["," "readonly"] "]"
42
43
44
(15) <declarator> := <identifier>
45
| <array_declarator>
46
47
(16) <array_declarator> := <identifier> <array_size>+
48
49
(17) <array_size> := "[" <positive_int> "]"
50
51
(18) <positive_int> := <const_expr>
52
53
(19) <type_spec> := <simple_type_spec>
54
| constr_type_spec>
55
56
(20) <simple_type_spec> := <base_type_spec>
57
| <template_type_spec>
58
| <scoped_name>
59
60
(21) <base_type_spec> := <integer_type>
61
| <floating_point_type>
62
| <char_type>
63
| <byte_type>
64
| <boolean_type>
65
| <string_type>
66
| <any_type>
67
| <type_type>
68
69
(22) <template_type> := <sequence_type>
70
| <array_type>
71
72
(23) <sequence_type> := "sequence" "<" <type_spec> ">"
73
74
(24) <array_type> := <type_spec> <array_size>+
75
76
(25) <floating_point_type> := "float"
77
| "double"
78
79
(26) <integer_type> := <signed_int>
80
| <unsinged_int>
81
82
(27) <signed_int> := "short"
83
| "long"
84
| "hyper"
85
86
87
(28) <unsigned_int> := "unsigned" "short"
88
| "unsigned" "long"
89
| "unsigned" "hyper"
90
91
(29) <char_type> := "char"
92
93
(30) <type_type> := "type"
94
95
(31) <string_type> := "string"
96
97
(32) <byte_type> := "byte"
98
99
(33) <any_type" := "any"
100
101
(34) <boolean_type> := "boolean"
102
103
(35) <constr_type_spec> := <struct_type>
104
| <enum_type>
105
| <union_type>
106
107
(36) <struct_type> := "struct" <identifier> [ <struct_inheritance> ] "{" <member>+ "}"
108
109
(37) <struct_inheritance> := ":" <scoped_name>
110
111
(38) <member> := <type_spec> <declarator> { "," <declarator> }*
112
113
(39) enum_type> := enum <identifier> "{" <enumerator> { "," <enumerator> }* "}"
114
115
(40) <enumerator> := <identifier> [ "=" <positive_int> ]
116
117
(41) <union_type> := "union" <identifier> "switch" "(" <switch_type_spec> ")"
118
"{" <switch_body> "}"
119
120
(42) <switch_type_spec> := <integer_type>
121
| <enum_type>
122
| <scoped_name>
123
124
(43) <switch_body> := <case>+
125
126
(44) <case> := <case_label> <element_spec> ";"
127
128
(45) <case_label> := "case" <const_expr> ":"
129
| "default" ":";
130
131
(46) <element_spec> := <type_spec> <declarator>
132
133
(47) <exception_decl> := "exception" <identifier> [ <exception_inheritance> ] "{" <member>* "}"
134
135
(48) <exception_inheritance> := ":" <scoped_name>
136
137
(49) <module_decl> := "module" <identifier> "{" <definition>+ "}"
138
139
(50) <constant_decl> := "const" <const_type> <identifier> "=" <const_expr>
140
141
(51) <const_type> := <integer_type>
142
| <char_type>
143
| <boolean_type>
144
| <floating_point_type>
145
| <string_type>
146
| <scoped_name>
147
148
(52) <const_expr> := <or_expr>
149
150
(53) <or_expr> := <xor_expr>
151
| <or_expr> "|" <xor_expr>
152
153
(54) <xor_expr> := <and_expr>
154
| <xor_expr> "^" <and_expr>
155
156
(55) <and_expr> := <shift_expr>
157
| <and_expr> "&" <shift_expr>
158
159
(56) <shift_expr> := <add_Expr>
160
| <shift_expr ">>" <add_expr>
161
| <shift_expr "<<" <add_expr>
162
163
(57) <add_expr> := <mult_expr>
164
| <add_expr> "+" <mult_expr>
165
| <add_expr> "-" <mult_expr>
166
167
(58) <mult_Expr> := <unary_expr>
168
| <mult_expr> "*" <unary_expr>
169
| <mult_expr> "/" <unary_expr>
170
| <mult_expr> "%" <unary_expr>
171
172
(59) <unary_expr> := <unary_operator><primary_expr>
173
| <primary_expr>
174
175
(60) <unary_operator> := "-" | "+" | "~"
176
177
(61) <primary_expr> := <scoped_name>
178
| <literal>
179
| "(" <const_expr> ")"
180
181
(62) <literal> := <integer_literal>
182
| <string_literal>
183
| <character_literal>
184
| <floating_point_literal>
185
| <boolean_literal>
186
187
(63) <boolean_literal> := "TRUE"
188
| "True"
189
| "FALSE"
190
| "False"
191
(64) <service_decl> := "service" <identifier> "{" <service_member>+ "}"
192
193
(65) <service_member> := <property_decl> ";"
194
| <support_decl> ";"
195
| <export_decl> ";"
196
| <observe_decl> ";"
197
| <needs_decl> ";"
198
199
(66) <property_decl> := <property_head> <type_spec> <declarator> { "," <declarator> }*
200
201
(67) <property_head> := "[" {<property_flags> ","}* "property" "]"
202
| "[" "property" {"," <property_flags>}* "]"
203
204
(68) <property_flags> := "readonly"
205
| "bound
206
| "constrained"
207
| "maybeambigious"
208
| "maybedefault"
209
| "maybevoid"
210
| "optional"
211
| "removable"
212
| "transient"
213
214
(69) <support_decl> := "interface" <declarator> { "," <declarator> }*
215
216
(70) <export_decl> := "service" <declarator> { "," <declarator> }*
217
218
(71) <observe_decl> := "observe" <declarator> { "," <declarator> }*
219
220
(72) <needs_decl> := "needs" <declarator> { "," <declarator> }*
221
222
(73) <constants_decl> := "constants" <identifier> "{" <constant_decl>+ "}"
Copied!

Examples

Some examples using IDL:

Defining types

1
interface Example : ::BaseExample
2
{
3
[readonly, attribute] short exampleArray[10];
4
long exampleVariable;
5
6
struct ExampleStruct {
7
unsigned hyper member;
8
}
9
}
Copied!
Copy link
Edit on GitHub