forked from adobe-flash/avmplus
-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathapiversioning.html
332 lines (269 loc) · 15.2 KB
/
apiversioning.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
<!-- 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/. -->
<h1>API Versioning in AVM</h1>
<hr>
<pre>
Revision History:
14-Sep-2009 [email protected] updated to reflect new bitmask mechanics
22-Jul-2009 [email protected] initial draft
</pre>
<hr>
<h2>OVERVIEW</h2>
<P><span class="pcounter"></span>
API versioning in AVM allows code compiled against one version of a built-in API
to see exactly the same set of bindings when run on a different, compatible
version of that API. A compatible version is one that has zero or more public
names added to it, and none removed from it. Thus, in AVM, APIs may add global
and class members without the risk of breaking existing ABC programs. The
versioning mechanism described here supports simultaneously executing ABCs of
different versions.
<h2>BACKGROUND</h2>
<P><span class="pcounter"></span>
Logically, a version can be thought of as a set of bindings. There is a partial
order of versions such that every version is a subset of itself and zero or more
other versions. A version is said to be compatible with every other version of
which it is a superset including itself.
<P><span class="pcounter"></span>
By way of example, imagine there exists a runtime called FR. It supports the
evolution of two coexisting profiles over time. One product supports program
execution in one of the the various browsers via a plugin (let's call it FP). The
other product supports the execution of standalone applications and is compatible
with the web product (let's refer to it as AR). At any point in time the latest
version of AR is larger than (includes a superset of names of) FP. And
over time each new release of each products is larger than all previous releases
of the same product.
<P><span class="pcounter"></span>
Now imagine that there exists versions of these two products as described
by the following relations:
<pre>
FP_9_0 < AR_1_0
FP_9_0 < FP_10_0
AR_1_0 < AR_1_5
FP_10_0 < FP_10_1
FP_10_0 < AR_1_5
AR_1_5 < AR_1_5_1
AR_1_5_1 < AR_2_0
FP_10_1 < AR_2_0
</pre>
<P><span class="pcounter"></span>From these relations we can see, for example, that a binding introduced in
FP_9_0 will be in every other version; a binding introduced in AR_1_0 is not in
FP_9_0 (or any other FP version, for that matter) but is in AR_1_5; and a binding
introduced in AR_2_0 is only in AR_2_0.
<P><span class="pcounter"></span>
In source code, each built-in (library code) definition may be annotated with a
metadata attribute that indicates the version or versions in which it was
introduced. If no metadata occurs on a definition, it is assumed to have been
introduced in the smallest version (FP_9_0). Library code reference names are
always of the largest version (in this case AR_2_0). This means that regardless
of the version they are added to library code, references can "see" all names
that are in scope of that reference.
<P><span class="pcounter"></span>
Non-builtin code (client code) definitions and references are of the version it
was built with, as indicated by the embedding host. There is no version
information encoded into the ABC format to indicate its API version. In fact, it
is up to the host embedding to allocate version numbers and compatibility
relations between those versions, and to provide that information to AvmCore on
startup.
<P><span class="pcounter"></span>
Only public names in AVM library code (that which is built into the AVM binary)
may be versioned. Furthermore, it is prohibited to version names in a private,
protected, internal, interface, or user defined namespace. Private
names are not visible outside of the builtin code and so versioning has no
effect on them; other names might be visible outside of the builtin code but
are not required by currently targeted use cases to be versioned and therefore
doing so unnecessarily complicates the implementation.
<P><span class="pcounter"></span>
We also limit the set of names being versioned to those whose namespaces have
one of the host defined URI strings.
<h2>USAGE</h2>
<h3>API Version Metadata</h3>
The metadata syntax for marking a builtin API with a version is this:
<pre>
VersionMetaData := '[' 'API' '(' VersionList ')' ']'
VersionList := VersionList ',' Version
Version := <b>Integer</b>
</pre>
<P><span class="pcounter"></span>
The value of <b><code>Integer</code></b> is determined by the host embedding. In our running
example of the hypothetical FR runtime, imagine that we assign the following version numbers
to the various versions:
<pre>
FP_9_0 660
AR_1_0 661
FP_10_0 662
AR_1_5 663
AR_1_5_1 664
FP_10_1 665
AR_2_0 666
</pre>
[TODO] ASC configuration variables are defined to alias these integers. Thus to
indicate that a particular method was introduced in both AR_1_0 and in FP_10_0
we would use the following annotation:
<pre>
[API(AR_1_0, FP_10_0)]
public function ...
</pre>
<P><span class="pcounter"></span>
Such dual versioning occurs when a name is introduced first in the larger
profile and later migrated to a smaller profile.
<P><span class="pcounter"></span>
It is an ASC compile error to use an invalid version number in API metadata.
<P><span class="pcounter"></span>
[NOTE: Embedders should be aware that the ASC released with the Flex SDK is
configured for targeting Flash Runtime versions. This is of no consequence to
embedders that do not version their library code and to users in general.
Embedders that want to version their library code and who use ASC to compile
that code will either need to repurpose the Flash versions and compatibility
relations or update the version information compiled into ASC. See the section
titled 'apivergen.abc' below)
<h3>Invoking ASC</h3>
<P><span class="pcounter"></span>
When using ASC to compile builtin code with ASC's ScriptCompiler, both the
'-builtin' and '-apiversioning' flags must be used. This causes names in a
versioned namespace to be marked either with the specified version or the
default version (FP_9_0, in our running example).
<P><span class="pcounter"></span>
When using ASC to compile client code (this includes all code that is not
treated as built-in code by AvmCore) as any version other than the largest
version (AR_2_0, in our example) it is necessary to use the '-api' flag to
specify the presumed version of that code. The given version is used to exclude
names of larger versions during ABC import. ASC does nothing version specific to
the generated ABCs of client code. Among other things this means that third
party tools that read ABC will continue to work as before.
<h3>Invoking AVM</h3>
<P><span class="pcounter"></span>
By default AVM is built with API versioning turned off. However, for the purpose
of testing versioning, the macro <code>VMCFG_TEST_API_VERSIONING</code> can be defined to
enable Flash Player like versioning in AVM.
<P><span class="pcounter"></span>
Since client version information is not stored in ABC files, the AVM has no way
to know what version client code is compiled against. For this reason there is
a command-line flag <code>-api</code> which allows an api version to be specified for all
user ABCs. [NOTE: this flag is only available with versioning is enabled as describe
in the previous paragraph.]
<h2>MECHANICS</h2>
<h3>AVM Mechanics</h3>
<P><span class="pcounter"></span>
Namespaces control the visibility of names in AS3 and AVM. We exploit this fact
by adding a bit mask to namespaces to indicate the version of the associated name,
and a bit mask to the binding (multiname hash) table entry to indicate all versions
in which a name is visible. A bitwise comparision between these two bitmasks is
done during name lookup to determine whether or not a name is a match.
<P><span class="pcounter"></span>
To cause versioned bindings to be visible to code of larger (and therefore different)
versions, we set the version bit of each larger (compatible) version in the binding
table. This is true for client, as well as library, code to allow SWFs of larger
versions to "see" bindings of SWFs of smaller versions.
<P><span class="pcounter"></span>
In order to avoid changing the ABC file format we encode the version of a namespace
in the URI of the namespace in the ABC file and strip it out during ABC parsing,
replacing it with the version bitmask in the instantiated namespace object. A version
mark is a single Unicode character from the Private Use Area (0xE000 to 0xF8FF). Such
a mark is appended to the normal (base) URI. The unicode character used to represent
a version has the code point equal to the sum of 0xE000 and the version number (in
our example, FP_9_0 namespaces would be marked with the character whose code point
is 0xE000+660).
<P><span class="pcounter"></span>
The important differences between builtin and client code are that:
<ul>
<li>built-ins can contain bindings of different versions, whereas client code
bindings within an ABC are always of the same version
<li>built-in versions are explicitly encoded in each trait name, whereas client
code versions are indicated by the host
<li>version numbers appear nowhere in client code. this is important because
because version numbers might change, intentionally or accidently, from release
to release causing content containing that information to be brittle
</ul>
<P><span class="pcounter"></span>
On AVM startup, the version compatibility information is passed to AvmCore via
the instance method <code>AvmCore::setAPIInfo()</code>. (See <code>./shell/ShellCore.cpp</code>
for an example).
<P><span class="pcounter"></span>
During ABC parsing, the version of the ABC being parsed is given to the parser
by the host embedding code. Again, builtin code is by default treated as if of
the largest version. The version number is cached in the PoolObject associated
with the ABC and versioned namespaces have their appropriate version bit set.
When traits (and scripts for toplevel bindings) are added to the various
multiname hash tables the version bit on the hash table entry is set for each
version that is compatible with the initial version(s). [NOTE: builtin traits
can be introduced in multiple versions and so the set of compatible versions
must be computed as the union of the compatibility sets of each of the initial
versions.]
<P><span class="pcounter"></span>
At runtime, dynamically computed names (e.g E4X names) that have a versioned URI
must also be versioned. AS3 and E4X namespaces share the same syntax and values
in the language. For example, the expression <code>obj.public::x</code> is a valid
reference to the an xml element or an ordinary object property. Therefore it is
necessary (or at least expedient) to also version E4X names.
<P><span class="pcounter"></span>
TESTING NOTE: Create an E4X value with elements with names in a namespace with a
versioned URI (e.g. the empty string) and passing that value to code of a smaller
version. Are the elements of that value visible to the receiver code? They
should not be, but is that a problem in practice. At least its something to document.
[NOTE: this is not a problem for ordinary object properties, since such
properties have no namespace.]
<P><span class="pcounter"></span>
At runtime, the PoolObject of the executing code must be derived from the dynamic
scope of the callee (which is builtin code). The innermost client MethodEnv
contains a reference to its PoolObject (through a MethodInfo). This pool contains
the current API version.
<P><span class="pcounter"></span>
Any code that reflects on the names of a value needs to reflect only those names
that are of the same version as the caller (e.g. describeType).
<P><span class="pcounter"></span>
Any code that formats a name needs to strip off the version marker of that
name's namespace. (For example, E4X methods that return the URI of a qname and
namespace, and the Multiname format method)
<h3>ASC Mechanics</h3>
<P><span class="pcounter"></span>
ASC only marks versioned namespaces of traits when both the '-builtin' and
'-apiversioning' are passed to ASC's ScriptCompiler (the driver used to compile
builtins in the AVM shell). All other uses of ASC will result in unversioned
traits. Builtin traits have names that are constant multinames. Non-builtin
names are single qualified name constants (as before) and so there is no need to
bump the abc version number.
<P><span class="pcounter"></span>
ASC marks all builtin trait names with a version by appending a version marker
to the uri of the names namespace. Definitions with no version metadata are
marked with the smallest version (e.g. FP_9_0=0xE000+660). All other definitions
are marked with the version(s) specified by metadata.
<P><span class="pcounter"></span>
ASC ABC import (AbcParser.java) ignores traits that are introduced in a larger
version than the version of the code being compiled. The version of the current
code is specified by the '-api' flag on the ASC command line. It is an error to
use '-api' and '-apiversioning' on the same command line. This is because
builtin code is always of the largest version and so the '-api' flag is
meaningless. [NOTE: the '-api' flag has no effect on the import of '.as' files.]
<P><span class="pcounter"></span>
It is an error to use an invalid version number with API metadata. The definition
of valid versions can be found found in the file ./shell/api-versions.xml (see
below).
<h2>UTILITIES</h2>
<h3>nativegen.py</h3>
<P><span class="pcounter"></span>nativegen.py reads the builtin ABC files and generates thunks for native
methods contained within. This tool ignores duplicates that result from
versioning, and it strips off the version marker when generating thunk names.
<h3>apivergen.abc</h3>
<P><span class="pcounter"></span>
Version numbers and compatibility information are specified in an domain
specific xml file that is processed by the utility named <code>apivergen.abc</code>.
(e.g. <code>./shell/api-versions.xml</code>). The XML file is used as input to
<code>./utils/apivergen.abc</code> to generated equivalent C++ and Java code. Copy and/or
rename these files as appropriate.
<P><span class="pcounter"></span>
To build apivergen.abc, execute
<pre>
java -jar asc.jar apivergen.as
</pre>
<P><span class="pcounter"></span>
To compile api-versions.xml, execute
<pre>
avmshell apivergen.abc -- api-versions.xml
</pre>
<P><span class="pcounter"></span>
This will result in the files <code>api-versions.xml.h</code> and <code>api-versions.xml.java</code>
being generated. These files must then be copied to the appropriately named files
(e.g. api-versions.h and APIVersions.java) in their respective locations in the
avm/player and ASC workspaces. [NOTE: This process should not be automated as it
is performed once per release.]