Merge remote-tracking branch 'redux/master' into sh4-pool
[tamarin-stm.git] / esc / src / abc-parse.es
blobba9584ab43227c3cf8f0fdc17acbca654ceba778
1 /* -*- mode: java; tab-width: 4; insert-tabs-mode: nil; indent-tabs-mode: nil -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  * http://www.mozilla.org/MPL/
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  *
15  * The Original Code is [Open Source Virtual Machine.].
16  *
17  * The Initial Developer of the Original Code is
18  * Adobe System Incorporated.
19  * Portions created by the Initial Developer are Copyright (C) 2004-2006
20  * the Initial Developer. All Rights Reserved.
21  *
22  * Contributor(s):
23  *   Adobe AS3 Team
24  *
25  * Alternatively, the contents of this file may be used under the terms of
26  * either the GNU General Public License Version 2 or later (the "GPL"), or
27  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28  * in which case the provisions of the GPL or the LGPL are applicable instead
29  * of those above. If you wish to allow use of your version of this file only
30  * under the terms of either the GPL or the LGPL, and not to allow others to
31  * use your version of this file under the terms of the MPL, indicate your
32  * decision by deleting the provisions above and replace them with the notice
33  * and other provisions required by the GPL or the LGPL. If you do not delete
34  * the provisions above, a recipient may use your version of this file under
35  * the terms of any one of the MPL, the GPL or the LGPL.
36  *
37  * ***** END LICENSE BLOCK ***** */
39 use default namespace Abc,
40     namespace Abc;
42 use namespace Asm;
44 // Construct an ABCFile instance from a bytestream representing an abc block.
45 function parseAbcFile(b : ABCByteStream) : ABCFile {
46     b.position = 0;
47     magic = b.readInt();
49     if (magic != (46<<16|16))
50         throw new Error("not an abc file.  magic=" + magic.toString(16));
51         
52     var abc : ABCFile = new ABCFile();
54     abc.constants = parseCpool(b);
55         
56     var i;
57     var n;
58     // MethodInfos
59     n = b.readU32();
60     for(i = 0; i < n; i++) {
61         abc.addMethod(parseMethodInfo(b));
62     }
64     // MetaDataInfos
65     n = b.readU32();
66     for(i = 0; i < n; i++) {
67         abc.addMetadata(parseMetadata(b));
68     }
70     // InstanceInfos
71     n = b.readU32();
72     for(i = 0; i < n; i++) {
73         abc.addInstance(parseInstanceInfo(b));
74     }
76     // ClassInfos
77     for(i = 0; i < n; i++) {
78         abc.addClass(parseClassInfo(b));
79     }
81     // ScriptInfos
82     n = b.readU32();
83     for(i = 0; i < n; i++) {
84         abc.addScript(parseScriptInfo(b));
85     }
87     // MethodBodies
88     n = b.readU32();
89     for(i = 0; i < n; i++) {
90         abc.addMethodBody(parseMethodBody(b));
91     }
93     return abc;            
96 function parseCpool(b : ABCByteStream) : ABCConstantPool {
97     var i:int;
98     var n:int;
99         
100     var pool : ABCConstantPool = new ABCConstantPool;
101         
102     // ints
103     n = b.readU30();
104     for (i=1; i < n; i++)
105         pool.int32(b.readS32());
106         
107     // uints
108     n = b.readU30();
109     for (i=1; i < n; i++)
110         pool.uint32(b.readU32());
111         
112     // doubles
113     n = b.readU30();
114     doubles = [NaN];
115     for (i=1; i < n; i++)
116         pool.float64(b.readDouble());
118     // strings
119     n = b.readU30();
120     for (i=1; i < n; i++)
121         pool.stringUtf8(b.readUTFBytes(b.readU32()));
122         
123     // namespaces
124     n = b.readU30()
125                 for (i=1; i < n; i++)
126             {
127                 var nskind = b.readByte();
128                 var uri = b.readU32();
129                 pool.namespace(nskind, uri);
130             }
131         
132     // namespace sets
133     n = b.readU30();
134     for (i=1; i < n; i++)
135                 {
136                         var count:int = b.readU30();
137                         var nsset = [];
138                         for (j=0; j < count; j++)
139                                 nsset[j] = b.readU30();
140             pool.namespaceset(nsset);
141                 }
142         
143     // multinames
144     n = b.readU30();
145     for (i=1; i < n; i++) {
146         var kind = b.readByte();
147         switch (kind) {
148         case CONSTANT_QName:
149         case CONSTANT_QNameA:
150             pool.QName(b.readU30(), b.readU30(), kind==CONSTANT_QNameA);
151             break;
152                         
153         case CONSTANT_RTQName:
154         case CONSTANT_RTQNameA:
155             pool.RTQName(b.readU30(), kind==CONSTANT_RTQNameA);
156             break;
157                         
158         case CONSTANT_RTQNameL:
159         case CONSTANT_RTQNameLA:
160             pool.RTQNameL(kind==CONSTANT_RTQNameLA);
161             names[i] = null;
162             break;
163                         
164         case CONSTANT_Multiname:
165         case CONSTANT_MultinameA:
166             var name = b.readU30();
167             pool.Multiname(b.readU30(), name, kind==CONSTANT_MultinameA);
168             break;
170         case CONSTANT_MultinameL:
171         case CONSTANT_MultinameLA:
172             pool.MultinameL(b.readU30(), kind==CONSTANT_MultinameLA);
173             break;
174         }
175     }
176         
177     return pool;
180 function parseMethodInfo(b : ABCByteStream) : ABCMethodInfo {
181         
182     var paramcount = b.readU32();
183     var returntype = b.readU32();
184     var params = [];
185     for(let i = 0; i < paramcount; ++i) {
186         params[i] = b.readU32();
187     }
188         
189     var name = b.readU32();
190     var flags = b.readByte();
191         
192     var optionalcount = 0;
193     var optionals = null;
194     if( flags & METHOD_HasOptional ) {
195         optionalcount = b.readU32();
196         optionals = [];
197         for(let i = 0; i < optionalcount; ++i ) {
198             optionals[i] = { val:b.readU32(), kind:b.readByte() };
199         }
200     }
201         
202     var paramnames = null;
203     if( flags & METHOD_HasParamNames )        {
204         paramnames=[];
205         for(let i = 0; i < paramcount; ++i)
206             paramnames[i] = b.readU32();
207     }    
208         
209     return new ABCMethodInfo(name, params, returntype, flags, optionals, paramnames);
211     
212 function parseMetadataInfo(b : ABCByteStream) : ABCMetadataInfo {
213     var name = b.readU32();
214     var itemcount = b.readU32();
215         
216     var items = [];
217     for( let i = 0; i < itemcount; i++ ) {
218         let key = b.readU32();
219         let value = b.readU32();
220         items[i] = { key:key, value:value };
221     }
222         
223     return new ABCMetadataInfo(name, items);
225     
226 function parseInstanceInfo(b : ABCByteStream) : ABCInstanceInfo {
227     var name = b.readU32();
228     var superclass = b.readU32();
229     var flags = b.readByte();
230     var protectedNS = 0;
231     if( flags & 8 ) 
232         protectedNS = b.readU32();
233         
234     var interfacecount = b.readU32();
235     var interfaces = [];
236     for(let i = 0; i < interfacecount; ++i) {
237         interfaces[i] = b.readU32();
238     }
239     var iinit = b.readU32();
240         
241     var instance_info = new ABCInstanceInfo(name, superclass, flags, protectedNS, interfaces);
242         
243     instance_info.setIInit(iinit);
244         
245     parseTraits(instance_info, b);
246         
247     return instance_info;
249     
250 function parseClassInfo(b : ABCByteStream) : ABCClassInfo {
251     var cinit = b.readU32();
253     var class_info = new ABCClassInfo();
254     class_info.setCInit(cinit);
255         
256     parseTraits(class_info, b);
257         
258     return class_info;
260     
261 function parseScriptInfo(b : ABCByteStream) : ABCScriptInfo {
262         
263     var script = new ABCScriptInfo(b.readU32());
264     parseTraits(script, b);
265     return script;
267     
268 function parseMethodBody(b : ABCByteStream) : ABCMethodBodyInfo {
269     var mb:ABCMethodBodyInfo = new ABCMethodBodyInfo(b.readU32());
270         
271     mb.setMaxStack(b.readU32());
272     mb.setLocalCount(b.readU32());
273     mb.setInitScopeDepth(b.readU32());
274     mb.setMaxScopeDepth(b.readU32());
275         
276     let code_len = b.readU32();
277     let code = new ABCByteStream;
278     mb.setCode(code);
279     for(let i = 0; i < code_len; ++i) {
280         code.uint8(b.readByte());
281     }
282         
283     var excount = b.readU32();
284     for( let i = 0; i < excount; ++i ) {
285         mb.addException(parseException(b));
286     }
287         
288     parseTraits(mb, b);
289         
290     return mb;
292     
293 function parseException(b : ABCByteStream) : ABCException {
294     var start = b.readU32();
295     var end = b.readU32();
296     var target = b.readU32();
297     var typename = b.readU32();
298     var name = b.readU32();
299         
300     // WTF is wrong with this????
301     var ex;
302     ex = new ABCException(start, end, target, typename, name);
303     return ex;
305     
306 function parseTraits(target, b : ABCByteStream) {
307     var traitcount = b.readU32();
308     for(let i =0 ; i < traitcount; ++i) {
309         target.addTrait(parseTrait(b));
310     }
313 function parseTrait(b : ABCByteStream) { //: ABCTrait should be ABCTrait once inheritance is supported
315     var name = b.readU32();
316         
317     var tag = b.readByte();
318     var kind = tag & 0x0F;
319     var attrs = (tag>>4) & 0x0F;
320         
321     var trait;
322         
323     switch(kind) {
324     case TRAIT_Slot:
325     case TRAIT_Const:
326         let slotid = b.readU32();
327         let typename = b.readU32();
328         let value = b.readU32();
329         let kind = null;
330         if( value != 0 )
331             kind = b.readByte();
332         trait = new ABCSlotTrait(name, attrs, kind==TRAIT_Const, slotid, typename, value, kind);
333         break;
334     case TRAIT_Method:
335     case TRAIT_Setter:
336     case TRAIT_Getter:
337         let dispid = b.readU32();
338         let methinfo = b.readU32();
339         trait = new ABCOtherTrait(name, attrs, kind, dispid, methinfo);
340         break;
341     case TRAIT_Class:
342         let slotid = b.readU32();
343         let classinfo = b.readU32();
344         trait = new ABCOtherTrait(name, attrs, kind, slotid, classinfo);
345         break;
346     case TRAIT_Function: // TODO:
347         b.readU32();
348         b.readU32();
349         break;
350     }
351         
352     if( attrs & ATTR_Metadata ) {
353         let metadatacount = b.readU32();
354         for(let i = 0; i < metadatacount; ++i) {
355             trait.addMetadata(b.readU32());
356         }
357     }
358         
359     return trait;
362 // This is a helper for parsing the ABCConstantPool from its
363 // bytes and storing the resulting values in arrays.  This allows
364 // easy access to the values referenced in the pools.
366 public class ABCConstantPoolParser {
367     // the pools themselves are public for people who need the
368     // raw data.
369     public var int_pool, uint_pool, double_pool;
370     public var utf8_pool, namespace_pool, namespaceset_pool, name_pool;
372     public function ABCConstantPoolParser(pool: ABCConstantPool) {
373         // load the constants from an existing abc object
374         // setup our arrays, reserving element 0 which remains undefined
375         int_pool = new Array(1);
376         uint_pool = new Array(1);
377         double_pool = new Array(1);
378         utf8_pool = new Array(1);
379         namespace_pool = new Array(1);
380         namespaceset_pool = new Array(1);
381         name_pool = new Array(1);
383         // and read the bytes from the streams.
384         intPool(pool.int_bytes);
385         uintPool(pool.uint_bytes);
386         doublePool(pool.double_bytes);
387         utf8Pool(pool.utf8_bytes);
388         namespacePool(pool.namespace_bytes);
389         namespacesetPool(pool.namespaceset_bytes);
390         namePool(pool.multiname_bytes);
391     }
393     // We also provide a couple of helper functions take an index/ices
394     // and indirects through the various pools returning the value.
395     public function getInt(offset: uint) : int {
396         return int_pool[offset];
397     }
398     public function getUint(offset: uint) : uint {
399         return uint_pool[offset];
400     }
401     public function getDouble(offset: uint) : double {
402         return double_pool[offset];
403     }
404     public function getUtf8(offset: uint) : string {
405         return utf8_pool[offset];
406     }
407     public function getNamespace(offset: uint) {
408         // returns [kind:uint, value:string]
409         var entry = namespace_pool[offset]
410             return [entry.kind, getUtf8(entry.utf8)]
411             }
412     public function getConstantName(offset: uint) {
413         // returns [namespace:string, name:string]
414         var entry = name_pool[offset]
415             Util::assert(entry.kind == CONSTANT_QName); // not a constant string.
416         var [ns_kind, ns_name] = getNamespace(entry.ns)
417             return [ns_name, getUtf8(entry.utf8)]
418             }
419     public function getConstantNameType(offset: uint) {
420         var entry = name_pool[offset]
421             Util::assert(entry.kind == CONSTANT_QName); // not a constant string.
422         var [ns_kind, ns_name] = getNamespace(entry.ns)
423             return ns_kind;
424     }
425     // any others?
427     // ******************************************************
428     // Loading/parsing of the pools
429     // prepare a pool's stream for re-reading
430     function preparePool (nd) : uint
431     {
432         var orig = nd.position;
433         var pos = 0;
434         nd.position = (pos);
435         return orig;
436     }
438     // load the various pools
439     function intPool (nd) : void {
440         var orig = preparePool(nd);
441         while (nd.bytesAvailable) 
442             int_pool.push(nd.readS32());
443         nd.position = orig;
444     }
446     function uintPool (nd) : void {
447         var orig = preparePool(nd);
448         while (nd.bytesAvailable) 
449             uint_pool.push(nd.readU32());
450         nd.position = orig;
451     }
453     function doublePool (nd) : void {
454         var orig = preparePool(nd);nd.position;
455         while (nd.bytesAvailable) 
456             double_pool.push(nd.readDouble());
457         nd.position = orig;
458     }
460     function utf8Pool (nd) : void {
461         var orig = preparePool(nd);
462         while (nd.bytesAvailable) {
463             var len = nd.readU32();
464             utf8_pool.push(nd.readUTFBytes(len));
465         }
466         nd.position = orig;
467     }
469     function namespacePool (nd) : void {
470         var orig = preparePool(nd);
471         while(nd.bytesAvailable)
472             namespace_pool.push( {kind: nd.readByte(), utf8: nd.readU32()} )
473                 nd.position = orig;
474     }
476     function namespacesetPool (nd) : void {
477         var orig = preparePool(nd);
478         while(nd.bytesAvailable) {
479             // read a const - each one is variable length.
480             var len = nd.readU32();
481             var new_val = new Array(len);
482             for (var i = 0 ; i < len; ++i) 
483                 new_val[i] = nd.readU32();
485             namespaceset_pool.push(new_val);
486         }
487         nd.position = orig;
488     }
490     function namePool (nd) : void {
491         var orig = preparePool(nd);
492         while(nd.bytesAvailable) {
493             // read a name-const
494             var kind = nd.readByte();
495             var new_val = {kind: kind}
497             switch (kind) {
498             case CONSTANT_QName:
499                 new_val.ns = nd.readU32(); // ns offset
500                 new_val.utf8 = nd.readU32(); // utf8 index
501                 break;
502             case CONSTANT_RTQName:
503                 new_val.utf8 = nd.readU32(); // utf8 index.
504                 break;
505             case CONSTANT_RTQNameL:
506                 break;
507             case CONSTANT_Multiname:
508                 new_val.utf8 = nd.readU32(); // utf8 offset
509                 new_val.nsset = nd.readU32(); // nsset offset
510                 break;
511             case CONSTANT_MultinameL:
512                 new_val.nss = nd.readU32() // nss
513                     break;
514             case CONSTANT_QNameA:
515             case CONSTANT_RTQNameA:
516             case CONSTANT_RTQNameLA:
517             case CONSTANT_MultinameA:
518             case CONSTANT_MultinameLA:
519             default:
520                 // we must fail here - we don't know how many bytes to
521                 // read, screwing up attempts to parse ones which follow
522                 throw("Unsupported Name constant");
523             }
524             name_pool.push(new_val);
525         }
526         nd.position = orig;
527     }