Merge remote-tracking branch 'redux/master' into sh4-pool
[tamarin-stm.git] / utils / nativegen.py
blobfc4e747897cf0fd8f7bc385de41cbeab9d368bd8
1 #!/usr/bin/env python
2 # -*- Mode: Python; indent-tabs-mode: nil -*-
3 # vi: set ts=4 sw=4 expandtab:
4 # ***** BEGIN LICENSE BLOCK *****
5 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 # The contents of this file are subject to the Mozilla Public License Version
8 # 1.1 (the "License"); you may not use this file except in compliance with
9 # the License. You may obtain a copy of the License at
10 # http://www.mozilla.org/MPL/
12 # Software distributed under the License is distributed on an "AS IS" basis,
13 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 # for the specific language governing rights and limitations under the
15 # License.
17 # The Original Code is [Open Source Virtual Machine].
19 # The Initial Developer of the Original Code is
20 # Adobe System Incorporated.
21 # Portions created by the Initial Developer are Copyright (C) 2007
22 # the Initial Developer. All Rights Reserved.
24 # Contributor(s):
25 # Adobe AS3 Team
27 # Alternatively, the contents of this file may be used under the terms of
28 # either the GNU General Public License Version 2 or later (the "GPL"), or
29 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 # in which case the provisions of the GPL or the LGPL are applicable instead
31 # of those above. If you wish to allow use of your version of this file only
32 # under the terms of either the GPL or the LGPL, and not to allow others to
33 # use your version of this file under the terms of the MPL, indicate your
34 # decision by deleting the provisions above and replace them with the notice
35 # and other provisions required by the GPL or the LGPL. If you do not delete
36 # the provisions above, a recipient may use your version of this file under
37 # the terms of any one of the MPL, the GPL or the LGPL.
39 # ***** END LICENSE BLOCK *****
41 # Documentation for native annotations.
43 # Classes can be annotated with 'native' metainformation:
45 # [native(cls="...", ...)]
46 # public class Fnord extends FnordBase { ... }
48 # or
50 # [native("...")]
51 # public class Fnord extends FnordBase { ... }
53 # The latter form is equivalent to the former with a single cls attribute (?).
55 # The named attributes are:
57 # cls the C++ name for the class object's class (required)
58 # instance the C++ name for the instance object's class (required)
59 # gc the GC method for both class and instance objects
60 # classgc the GC method for the class object
61 # instancegc the GC method for the instance object
62 # methods ?
63 # construct indicates that the class has a nonstandard construction pipeline:
65 # "none": the class cannot be instantiated, nor can subclasses; we will generate a stub
66 # that throws kCantInstantiateError. (Typically used for classes that provide only
67 # static members and/or static methods.)
69 # "check": the class is expected to provide a function of the form
71 # static void FASTCALL preCreateInstanceCheck(ClassClosure* cls)
73 # which will be called prior to creation of an instance; this function is
74 # allowed to throw an exception to prevent object creation. (Throwing
75 # an exception in the C++ ctor is discouraged, as our current longjmp implementation
76 # of exception handling can result in a half-constructed object, which could cause
77 # issues at object destruction time.)
79 # "restricted": subclasses must be defined in the same ABC chunk as the baseclass (otherwise they will
80 # throw kCantInstantiateError). This is an odd beast, used by Flash for classes
81 # which can't be subclassed by user code (only by builtin code.)
83 # "restricted-check": Like restricted, but with a preCreateInstanceCheck function required as well.
85 # "abstract": the class cannot be instantiated, but its subclasses can (ie, an Abstract Base Class);
86 # we will generate a stub that throws kCantInstantiateError when appropriate.
88 # "abstract-restricted": is the union of of abstract+restricted: the class can't be instantiated,
89 # and subclasses can only from from the same ABC chunk.
91 # * THE FOLLOWING VALUES FOR construct ARE ONLY LEGAL FOR USE IN THE BASE VM BUILTINS *
93 # "override": the Class must provide an override of the ScriptObject::construct() method.
94 # (We will autogenerate a createInstanceProc stub that asserts if called.)
96 # "instance": the Class *and* Instance must provide an override of the ScriptObject::construct() method.
97 # (We will autogenerate a createInstanceProc stub that asserts if called.)
99 # instancebase if the instance inherits from a C++ class that can't be inferred from the AS3 inheritance,
100 # it must be specified here. this is not supported for new code; it's provided for temporary support
101 # of existing code.
103 # The allowable values for "gc", "classgc", and "instancegc" are "exact"
104 # and "conservative"; the default is "conservative". If the value is "exact"
105 # then tracers are autogenerated based on annotations on the C++ class and
106 # on the pointer fields in the C++ class.
108 # For more information about GC see the documentation in utils/exactgc.as.
110 import optparse, struct, os, sys, StringIO
111 from optparse import OptionParser
112 from struct import *
113 from os import path
114 from math import floor
115 from sys import stderr
117 import sys
119 parser = OptionParser(usage="usage: %prog [importfile [, importfile]...] file...")
120 parser.add_option("-v", "--thunkvprof", action="store_true", default=False)
121 parser.add_option("-e", "--externmethodandclassetables", action="store_true", default=False, help="generate extern decls for method and class tables")
122 parser.add_option("--native-id-namespace", dest="nativeIDNS", default="avmplus::NativeID", help="Specifies the C++ namespace in which all generated should be in.")
123 parser.add_option("--root-implementation-namespace", dest="rootImplNS", default="avmplus", help="Specifies the C++ namespace in under which all implementation classes can be found.")
124 opts, args = parser.parse_args()
126 if not args:
127 parser.print_help()
128 exit(2)
130 NEED_ARGUMENTS = 0x01
131 NEED_ACTIVATION = 0x02
132 NEED_REST = 0x04
133 HAS_OPTIONAL = 0x08
134 IGNORE_REST = 0x10
135 NATIVE = 0x20
136 HAS_ParamNames = 0x80
138 CONSTANT_Utf8 = 0x01
139 CONSTANT_Int = 0x03
140 CONSTANT_UInt = 0x04
141 CONSTANT_PrivateNs = 0x05
142 CONSTANT_Double = 0x06
143 CONSTANT_Qname = 0x07
144 CONSTANT_Namespace = 0x08
145 CONSTANT_Multiname = 0x09
146 CONSTANT_False = 0x0A
147 CONSTANT_True = 0x0B
148 CONSTANT_Null = 0x0C
149 CONSTANT_QnameA = 0x0D
150 CONSTANT_MultinameA = 0x0E
151 CONSTANT_RTQname = 0x0F
152 CONSTANT_RTQnameA = 0x10
153 CONSTANT_RTQnameL = 0x11
154 CONSTANT_RTQnameLA = 0x12
155 CONSTANT_NameL = 0x13
156 CONSTANT_NameLA = 0x14
157 CONSTANT_NamespaceSet = 0x15
158 CONSTANT_PackageNs = 0x16
159 CONSTANT_PackageInternalNs = 0x17
160 CONSTANT_ProtectedNs = 0x18
161 CONSTANT_ExplicitNamespace = 0x19
162 CONSTANT_StaticProtectedNs = 0x1A
163 CONSTANT_MultinameL = 0x1B
164 CONSTANT_MultinameLA = 0x1C
165 CONSTANT_TypeName = 0x1D
167 TRAIT_Slot = 0x00
168 TRAIT_Method = 0x01
169 TRAIT_Getter = 0x02
170 TRAIT_Setter = 0x03
171 TRAIT_Class = 0x04
172 TRAIT_Const = 0x06
173 TRAIT_mask = 15
175 ATTR_final = 0x10
176 ATTR_override = 0x20
177 ATTR_metadata = 0x40
179 CTYPE_VOID = 0
180 CTYPE_ATOM = 1
181 CTYPE_BOOLEAN = 2
182 CTYPE_INT = 3
183 CTYPE_UINT = 4
184 CTYPE_DOUBLE = 5
185 CTYPE_STRING = 6
186 CTYPE_NAMESPACE = 7
187 CTYPE_OBJECT = 8
189 MIN_API_MARK = 0xE000
190 MAX_API_MARK = 0xF8FF
192 MPL_HEADER = "/* ***** BEGIN LICENSE BLOCK *****\n" \
193 " * Version: MPL 1.1/GPL 2.0/LGPL 2.1\n" \
194 " *\n" \
195 " * The contents of this file are subject to the Mozilla Public License Version\n" \
196 " * 1.1 (the \"License\"); you may not use this file except in compliance with\n" \
197 " * the License. You may obtain a copy of the License at\n" \
198 " * http://www.mozilla.org/MPL/\n" \
199 " *\n" \
200 " * Software distributed under the License is distributed on an \"AS IS\" basis,\n" \
201 " * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License\n" \
202 " * for the specific language governing rights and limitations under the\n" \
203 " * License.\n" \
204 " *\n" \
205 " * The Original Code is [Open Source Virtual Machine].\n" \
206 " *\n" \
207 " * The Initial Developer of the Original Code is\n" \
208 " * Adobe System Incorporated.\n" \
209 " * Portions created by the Initial Developer are Copyright (C) 2008\n" \
210 " * the Initial Developer. All Rights Reserved.\n" \
211 " *\n" \
212 " * Contributor(s):\n" \
213 " * Adobe AS3 Team\n" \
214 " *\n" \
215 " * Alternatively, the contents of this file may be used under the terms of\n" \
216 " * either the GNU General Public License Version 2 or later (the \"GPL\"), or\n" \
217 " * the GNU Lesser General Public License Version 2.1 or later (the \"LGPL\"),\n" \
218 " * in which case the provisions of the GPL or the LGPL are applicable instead\n" \
219 " * of those above. If you wish to allow use of your version of this file only\n" \
220 " * under the terms of either the GPL or the LGPL, and not to allow others to\n" \
221 " * use your version of this file under the terms of the MPL, indicate your\n" \
222 " * decision by deleting the provisions above and replace them with the notice\n" \
223 " * and other provisions required by the GPL or the LGPL. If you do not delete\n" \
224 " * the provisions above, a recipient may use your version of this file under\n" \
225 " * the terms of any one of the MPL, the GPL or the LGPL.\n" \
226 " *\n" \
227 " * ***** END LICENSE BLOCK ***** */"
229 # Python 2.5 and earlier didn't reliably handle float("nan") and friends uniformly
230 # across all platforms. This is a workaround that appears to be more reliable.
231 # if/when we require Python 2.6 or later we can use a less hack-prone approach
232 kPosInf = 1e300000
233 kNegInf = -1e300000
234 kNaN = kPosInf / kPosInf
236 def is_nan(val):
237 strValLower = str(val).lower()
238 return strValLower == "nan"
240 def is_pos_inf(val):
241 # [-]1.#INF on Windows in Python 2.5.2!
242 strValLower = str(val).lower()
243 return strValLower.endswith("inf") and not strValLower.startswith("-")
245 def is_neg_inf(val):
246 # [-]1.#INF on Windows in Python 2.5.2!
247 strValLower = str(val).lower()
248 return strValLower.endswith("inf") and strValLower.startswith("-")
250 class Error(Exception):
251 nm = ""
252 def __init__(self, n):
253 self.nm = n
254 def __str__(self):
255 return self.nm
257 BASE_CLASS_NAME = "avmplus::ClassClosure"
258 BASE_INSTANCE_NAME = "avmplus::ScriptObject"
260 TYPEMAP_RETTYPE = {
261 CTYPE_OBJECT: "%s*",
262 CTYPE_ATOM: "avmplus::Atom",
263 CTYPE_VOID: "void",
264 CTYPE_BOOLEAN: "bool",
265 CTYPE_INT: "int32_t",
266 CTYPE_UINT: "uint32_t",
267 CTYPE_DOUBLE: "double",
268 CTYPE_STRING: "avmplus::String*",
269 CTYPE_NAMESPACE: "avmplus::Namespace*",
272 TYPEMAP_RETTYPE_GCREF = {
273 CTYPE_OBJECT: lambda type: "GCRef<%s>" % type,
274 CTYPE_ATOM: lambda type: "avmplus::Atom",
275 CTYPE_VOID: lambda type: "void",
276 CTYPE_BOOLEAN: lambda type: "bool",
277 CTYPE_INT: lambda type: "int32_t",
278 CTYPE_UINT: lambda type: "uint32_t",
279 CTYPE_DOUBLE: lambda type: "double",
280 CTYPE_STRING: lambda type: "GCRef<avmplus::String>",
281 CTYPE_NAMESPACE: lambda type: "GCRef<avmplus::Namespace>",
284 TYPEMAP_THUNKRETTYPE = {
285 CTYPE_OBJECT: "avmplus::Atom(%s)",
286 CTYPE_ATOM: "avmplus::Atom(%s)",
287 CTYPE_VOID: "avmplus::Atom(%s)",
288 CTYPE_BOOLEAN: "avmplus::Atom(%s)",
289 CTYPE_INT: "avmplus::Atom(%s)",
290 CTYPE_UINT: "avmplus::Atom(%s)",
291 CTYPE_DOUBLE: "double(%s)",
292 CTYPE_STRING: "avmplus::Atom(%s)",
293 CTYPE_NAMESPACE: "avmplus::Atom(%s)",
296 TYPEMAP_MEMBERTYPE = {
297 CTYPE_OBJECT: "DRCWB(%s*)",
298 CTYPE_ATOM: "avmplus::AtomWB",
299 CTYPE_VOID: "#error",
300 CTYPE_BOOLEAN: "bool32",
301 CTYPE_INT: "int32_t",
302 CTYPE_UINT: "uint32_t",
303 CTYPE_DOUBLE: "double",
304 CTYPE_STRING: "DRCWB(avmplus::String*)",
305 CTYPE_NAMESPACE: "DRCWB(avmplus::Namespace*)",
308 TYPEMAP_ARGTYPE = {
309 CTYPE_OBJECT: "%s*",
310 CTYPE_ATOM: "avmplus::Atom",
311 CTYPE_VOID: "void",
312 CTYPE_BOOLEAN: "bool32",
313 CTYPE_INT: "int32_t",
314 CTYPE_UINT: "uint32_t",
315 CTYPE_DOUBLE: "double",
316 CTYPE_STRING: "avmplus::String*",
317 CTYPE_NAMESPACE: "avmplus::Namespace*",
320 TYPEMAP_ARGTYPE_SUFFIX = {
321 CTYPE_OBJECT: "OBJECT",
322 CTYPE_ATOM: "ATOM",
323 CTYPE_VOID: "VOID",
324 CTYPE_BOOLEAN: "BOOLEAN",
325 CTYPE_INT: "INT",
326 CTYPE_UINT: "UINT",
327 CTYPE_DOUBLE: "DOUBLE",
328 CTYPE_STRING: "STRING",
329 CTYPE_NAMESPACE: "NAMESPACE",
332 TYPEMAP_ARGTYPE_FOR_UNBOX = {
333 CTYPE_OBJECT: "%s*",
334 CTYPE_ATOM: "avmplus::Atom",
335 CTYPE_VOID: "#error",
336 CTYPE_BOOLEAN: "bool32",
337 CTYPE_INT: "int32_t",
338 CTYPE_UINT: "uint32_t",
339 CTYPE_DOUBLE: "double",
340 CTYPE_STRING: "avmplus::String*",
341 CTYPE_NAMESPACE: "avmplus::Namespace*",
344 TYPEMAP_TO_ATOM = {
345 # The explicit cast to ScriptObject is necessary because the type in
346 # question may have only been forward-declared, thus calling methods
347 # on it isn't legal; since we know it's a ScriptObject, we can force the issue.
348 # Note that, for this reason, we can't use staticCast, as the compiler
349 # will complain that they are apparently unrelated types.
350 CTYPE_OBJECT: lambda val: "%s.reinterpretCast<avmplus::ScriptObject>()->atom()" % val,
351 CTYPE_ATOM: lambda val: "%s" % val,
352 CTYPE_VOID: lambda val: "undefinedAtom",
353 CTYPE_BOOLEAN: lambda val: "((%s) ? trueAtom : falseAtom)" % val,
354 CTYPE_INT: lambda val: "core->intToAtom(%s)" % val,
355 CTYPE_UINT: lambda val: "core->uintToAtom(%s)" % val,
356 CTYPE_DOUBLE: lambda val: "core->doubleToAtom(%s)" % val,
357 CTYPE_STRING: lambda val: "%s->atom()" % val,
358 CTYPE_NAMESPACE: lambda val: "%s->atom()" % val,
361 TYPEMAP_TO_ATOM_NEEDS_CORE = {
362 CTYPE_OBJECT: False,
363 CTYPE_ATOM: False,
364 CTYPE_VOID: False,
365 CTYPE_BOOLEAN: False,
366 CTYPE_INT: True,
367 CTYPE_UINT: True,
368 CTYPE_DOUBLE: True,
369 CTYPE_STRING: False,
370 CTYPE_NAMESPACE: False,
373 TYPEMAP_FROM_ATOM = {
374 # We can't use static_cast<> because the subclass might be only forward-declared at this point;
375 # use good old brute-force cast instead.
376 CTYPE_OBJECT: lambda val,type: "(%s*)(AvmCore::atomToScriptObject(%s))" % (type,val),
377 CTYPE_ATOM: lambda val,type: "%s" % val,
378 CTYPE_VOID: lambda val,type: "undefinedAtom",
379 CTYPE_BOOLEAN: lambda val,type: "((%s) != falseAtom)" % val,
380 CTYPE_INT: lambda val,type: "AvmCore::integer(%s)" % val,
381 CTYPE_UINT: lambda val,type: "AvmCore::toUInt32(%s)" % val,
382 CTYPE_DOUBLE: lambda val,type: "AvmCore::number(%s)" % val,
383 CTYPE_STRING: lambda val,type: "AvmCore::atomToString(%s)" % val,
384 CTYPE_NAMESPACE: lambda val,type: "AvmCore::atomToNamespace(%s)" % val,
387 TYPEMAP_TO_GCREF = {
388 CTYPE_OBJECT: lambda val,type: "GCRef<%s>(%s)" % (type,val),
389 CTYPE_ATOM: lambda val,type: val,
390 CTYPE_VOID: lambda val,type: val,
391 CTYPE_BOOLEAN: lambda val,type: val,
392 CTYPE_INT: lambda val,type: val,
393 CTYPE_UINT: lambda val,type: val,
394 CTYPE_DOUBLE: lambda val,type: val,
395 CTYPE_STRING: lambda val,type: "GCRef<%s>(%s)" % (type,val),
396 CTYPE_NAMESPACE: lambda val,type: "GCRef<%s>(%s)" % (type,val),
399 def uint(i):
400 return int(i) & 0xffffffff
402 def c_argtype_from_enum(ct):
403 assert ct != CTYPE_OBJECT
404 r = TYPEMAP_ARGTYPE[ct]
405 return r
407 def to_cname(nm):
408 nm = str(nm)
409 nm = nm.replace("+", "_");
410 nm = nm.replace("-", "_");
411 nm = nm.replace("?", "_");
412 nm = nm.replace("!", "_");
413 nm = nm.replace("<", "_");
414 nm = nm.replace(">", "_");
415 nm = nm.replace("=", "_");
416 nm = nm.replace("(", "_");
417 nm = nm.replace(")", "_");
418 nm = nm.replace("\"", "_");
419 nm = nm.replace("'", "_");
420 nm = nm.replace("*", "_");
421 nm = nm.replace(" ", "_");
422 nm = nm.replace(".", "_");
423 nm = nm.replace("$", "_");
424 nm = nm.replace("::", "_");
425 nm = nm.replace(":", "_");
426 nm = nm.replace("/", "_");
427 return nm
430 GLUECLASSES_WITHOUT_NS = frozenset((
431 'bool',
432 'double',
433 'int32_t',
434 'uint32_t'))
436 def ns_prefix(ns, iscls):
437 if not ns.isPublic() and not ns.isInternal():
438 if ns.isPrivate() and not iscls:
439 return "private_";
440 if ns.isProtected():
441 return "protected_";
442 if ns.srcname != None:
443 return to_cname(str(ns.srcname)) + "_"
444 p = to_cname(ns.uri);
445 if len(p) > 0:
446 p += "_"
447 return p
449 def stripVersion(uri):
450 if len(uri) > 0:
451 uri16 = uri.decode('utf8')
452 cc = ord(uri16[-1])
453 if cc >= MIN_API_MARK and cc <= MAX_API_MARK:
454 return cc-MIN_API_MARK, uri16[0:len(uri16)-1].encode('utf8')
455 return -1,uri
457 class Namespace:
458 uri = ""
459 kind = 0
460 srcname = None
461 def __init__(self, uri, kind):
462 self.uri = uri
463 self.kind = kind
464 def __str__(self):
465 return self.uri
466 def isPublic(self):
467 return self.kind in [CONSTANT_Namespace, CONSTANT_PackageNs] and self.uri == ""
468 def isInternal(self):
469 return self.kind in [CONSTANT_PackageInternalNs]
470 def isPrivate(self):
471 return self.kind in [CONSTANT_PrivateNs]
472 def isProtected(self):
473 return self.kind in [CONSTANT_ProtectedNs, CONSTANT_StaticProtectedNs]
474 def stripVersion(self):
475 api, strippeduri = stripVersion(self.uri)
476 # it's important to return 'self' (and not an identical clone)
477 # if we are unversioned, otherwise native methods with custom namespaces
478 # may be emitted incorrectly
479 if api < 0:
480 return self
481 newself = Namespace(strippeduri, self.kind)
482 newself.srcname = self.srcname
483 return newself
485 class QName:
486 ns = None
487 name = ""
488 def __init__(self, ns, name):
489 self.ns = ns
490 self.name = name
491 def __str__(self):
492 if str(self.ns) == "":
493 return self.name
494 if self.ns == None:
495 return "*::" + self.name
496 return str(self.ns) + "::" + self.name
498 class Multiname:
499 nsset = None
500 name = ""
501 def __init__(self, nsset, name):
502 self.nsset = nsset
503 self.name = name
504 def __str__(self):
505 nsStrings = map(lambda ns: '"' + ns.decode("utf8") + '"', self.nsset)
506 stringForNSSet = '[' + ', '.join(nsStrings) + ']'
507 return stringForNSSet + '::' + unicode(self.name.decode("utf8"))
509 class TypeName:
510 name = ""
511 types = None
512 def __init__(self, name, types):
513 self.name = name
514 self.types = types
515 def __str__(self):
516 # @todo horrible special-casing, improve someday
517 s = str(self.name)
518 t = str(self.types[0])
519 if t == "int":
520 s += "$int"
521 elif t == "uint":
522 s += "$uint"
523 elif t == "Number":
524 s += "$double"
525 else:
526 s += "$object"
527 return s
529 class MetaData:
530 name = ""
531 attrs = {}
532 def __init__(self, name):
533 self.name = name
534 self.attrs = {}
536 class MemberInfo:
537 id = -1
538 kind = -1
539 name = ""
540 metadata = None
542 class MethodInfo(MemberInfo):
543 flags = 0
544 debugName = ""
545 paramTypes = None
546 paramNames = None
547 optional_count = 0
548 optionalValues = None
549 returnType = None
550 local_count = 0
551 max_scope = 0
552 max_stack = 0
553 code_length = 0
554 code = None
555 activation = None
556 native_id_name = None
557 native_method_name = None
558 final = False
559 override = False
560 receiver = None
561 unbox_this = -1 # -1 == undetermined, 0 = no, 1 = yes
563 def isNative(self):
564 return (self.flags & NATIVE) != 0
566 def needRest(self):
567 return (self.flags & NEED_REST) != 0
569 def hasOptional(self):
570 return (self.flags & HAS_OPTIONAL) != 0
572 def assign_names(self, traits, prefix):
573 self.receiver = traits
575 if not self.isNative():
576 return
578 if self == traits.init:
579 raise Error("ctors cannot be native")
581 assert(isinstance(self.name, QName))
582 self.native_id_name = prefix + ns_prefix(self.name.ns, False) + self.name.name
583 self.native_method_name = self.name.name
585 if self.kind == TRAIT_Getter:
586 self.native_id_name += "_get"
587 self.native_method_name = "get_" + self.native_method_name
588 elif self.kind == TRAIT_Setter:
589 self.native_id_name += "_set"
590 self.native_method_name = "set_" + self.native_method_name
592 if self.name.ns.srcname != None:
593 self.native_method_name = str(self.name.ns.srcname) + "_" + self.native_method_name
595 # if we are an override, prepend the classname to the C method name.
596 # (native method implementations must not be virtual, and some compilers
597 # will be unhappy if a subclass overrides a method with the same name and signature
598 # without it being virtual.) Note that we really only need to do this if the ancestor
599 # implementation is native, rather than pure AS3, but we currently do it regardless.
600 if self.override:
601 self.native_method_name = traits.name.name + "_" + self.native_method_name
603 self.native_method_name = to_cname(self.native_method_name)
605 class SlotInfo(MemberInfo):
606 type = ""
607 value = ""
608 fileOffset = -1
610 class NativeInfo:
611 traits = None
612 itraits = None
613 class_gc_exact = None
614 instancebase_name = None
615 instance_gc_exact = None
616 gen_method_map = False
617 method_map_name = None
618 has_const_setters = False
619 construct = None
621 def __init__(self, traits, itraits):
622 self.traits = traits
623 self.itraits = itraits
625 def parse_one_nativeinfo(self, attrs, is_vm_builtin):
627 for k in attrs.keys():
628 v = attrs[k]
629 if (k == "script"):
630 raise Error("native scripts are no longer supported; please use a native class instead and wrap with AS3 code as necessary.")
632 elif (k == "cls"):
633 self.set_class(v)
634 elif (k == "instance"):
635 self.set_instance(v)
636 elif (k == "instancebase"):
637 self.set_instancebase(v)
638 elif (k == "gc"):
639 self.set_classGC(v)
640 self.set_instanceGC(v)
641 elif (k == "classgc"):
642 self.set_classGC(v)
643 elif (k == "instancegc"):
644 self.set_instanceGC(v)
645 elif (k == "methods"):
646 self.gen_method_map = True
647 if v != "auto":
648 self.method_map_name = v
649 elif (k == "constsetters"):
650 if (v == "true"):
651 self.has_const_setters = True
652 elif (v != "false"):
653 raise Error('native metadata specified illegal value, "%s" for constsetters field. Value must be "true" or "false".' % v)
654 elif (k == "construct"):
655 self.set_construct(v, is_vm_builtin)
656 else:
657 raise Error("unknown attribute native(%s)" % k)
658 if (self.traits.cpp_name_comps == None) and (self.itraits.cpp_name_comps == None):
659 raise Error("native metadata must specify (cls,instance)")
661 def fullyQualifiedCPPClassName(self, className):
662 r = className.split('::')
663 if (len(opts.rootImplNS) > 0) and not className.startswith('::') and not className in GLUECLASSES_WITHOUT_NS:
664 r.insert(0, opts.rootImplNS)
665 return r
667 def set_class(self, name):
668 if self.traits.cpp_name_comps != None:
669 raise Error("native(cls) may not be specified multiple times for the same class: %s %s" % (self.traits.fqcppname(), name))
670 self.traits.cpp_name_comps = self.fullyQualifiedCPPClassName(name)
672 def set_instance(self, name):
673 if self.itraits.cpp_name_comps != None:
674 raise Error("native(instance) may not be specified multiple times for the same class: %s %s" % (self.itraits.fqcppname(), name))
675 if name == "ScriptObject" or name == BASE_INSTANCE_NAME:
676 raise Error("native(instance='ScriptObject') is no longer supported:")
677 self.itraits.cpp_name_comps = self.fullyQualifiedCPPClassName(name)
679 def set_instancebase(self, name):
680 if self.instancebase_name != None:
681 raise Error("native(instancebase) may not be specified multiple times for the same class: %s %s" % (self.instancebase_name, name))
682 comps = self.fullyQualifiedCPPClassName(name)
683 self.instancebase_name = '::'.join(filter(lambda ns: len(ns) > 0, comps))
685 def set_construct(self, value, is_vm_builtin):
686 if self.construct != None:
687 raise Error("native(construct) may not be specified multiple times for the same class")
688 if value in ["override", "instance"]:
689 if not is_vm_builtin:
690 raise Error('construct=%s may only be specified for the VM builtins' % value)
691 self.construct = value
692 elif value in ["none", "abstract", "abstract-restricted", "restricted", "check", "restricted-check"]:
693 self.construct = value
694 else:
695 raise Error('native metadata specified illegal value, "%s" for construct field.' % value)
697 def set_classGC(self, gc):
698 if self.class_gc_exact != None:
699 raise Error("native(classgc) may not be specified multiple times for the same class: %s %s" % (self.traits.fqcppname(), gc))
700 if gc == "exact":
701 self.class_gc_exact = True
702 elif gc == "conservative":
703 True
704 else:
705 raise Error("native(classgc) can only be specified as 'exact' or 'conservative': %s %s" % (self.traits.fqcppname(), gc))
707 def set_instanceGC(self, gc):
708 if self.instance_gc_exact != None:
709 raise Error("native(instancegc) may not be specified multiple times for the same class: %s %s" % (self.itraits.fqcppname(), gc))
710 if gc == "exact":
711 self.instance_gc_exact = True
712 elif gc == "conservative":
713 True
714 else:
715 raise Error("native(instancegc) can only be specified as 'exact' or 'conservative': %s %s" % (self.itraits.fqcppname(), gc))
717 @staticmethod
718 def parse_native_info(t, is_vm_builtin):
720 itraits = t.itraits
721 ni = NativeInfo(t, itraits)
723 if t.metadata != None:
724 for md in t.metadata:
725 if md.name == "native":
726 ni.parse_one_nativeinfo(md.attrs, is_vm_builtin)
728 if itraits.cpp_name_comps != None and itraits.is_interface:
729 raise Error("interfaces may not specify native(instance)")
731 if ni.gen_method_map and t.cpp_name_comps == None and itraits.cpp_name_comps == None:
732 raise Error("cannot specify native(methods) without native(cls)")
734 no_native_instance_specified = (ni.construct == None and itraits.cpp_name_comps == None)
736 # if either is specified, make sure both are
737 if t.cpp_name_comps != None or itraits.cpp_name_comps != None:
738 if t.cpp_name_comps == None:
739 t.cpp_name_comps = BASE_CLASS_NAME.split('::')
740 if itraits.cpp_name_comps == None:
741 itraits.cpp_name_comps = BASE_INSTANCE_NAME.split('::')
742 elif not itraits.is_interface:
743 # we are going to create a synthetic class for this
744 t.cpp_name_comps = ni.fullyQualifiedCPPClassName(itraits.name.name + "Class")
745 t.is_synthetic = True
747 # force to True or False (no "None" allowed)
748 if ni.class_gc_exact != True:
749 ni.class_gc_exact = False;
750 if ni.instance_gc_exact != True:
751 ni.instance_gc_exact = False;
752 t.is_gc_exact = ni.class_gc_exact
753 itraits.is_gc_exact = ni.instance_gc_exact
755 if ni.construct != None:
756 t.construct = ni.construct
757 if t.construct in ["override", "instance"]:
758 t.has_construct_method_override = True
759 if t.construct in ["abstract-restricted", "restricted", "restricted-check"]:
760 t.is_restricted_inheritance = True
761 if t.construct in ["abstract", "abstract-restricted"]:
762 t.is_abstract_base = True
763 if t.construct in ["check", "restricted-check"]:
764 t.has_pre_create_check = True
765 if ni.construct == "instance":
766 itraits.construct = "override"
767 itraits.has_construct_method_override = True
769 if str(t.name) == "Object$" or no_native_instance_specified:
770 # "Object" is special-cased.
771 t.createInstanceProcName = "ClassClosure::createScriptObjectProc"
773 elif t.construct in ["none", "abstract", "abstract-restricted"]:
774 t.createInstanceProcName = "ClassClosure::cantInstantiateCreateInstanceProc"
776 elif t.construct in ["override", "instance"] or itraits.ctype != CTYPE_OBJECT:
777 t.createInstanceProcName = "ClassClosure::impossibleCreateInstanceProc"
779 elif t.cpp_name_comps != None:
780 t.createInstanceProcName = "%s::createInstanceProc" % t.fqcppname()
781 t.has_custom_createInstanceProc = True
783 t.has_const_setters = ni.has_const_setters
784 t.gen_method_map = ni.gen_method_map
785 # t.fqinstancebase_name = ni.instancebase_name # not supported for classes
786 if t.has_cpp_name():
787 t.method_map_name = t.fqcppname() # custom method_map_name never applies to class
788 t.slotsStructName = to_cname(t.fqcppname()) + 'Slots'
789 t.slotsInstanceName = 'm_slots_' + t.cppname()
791 assert itraits != None
792 itraits.has_const_setters = ni.has_const_setters
793 itraits.gen_method_map = ni.gen_method_map
794 itraits.fqinstancebase_name = ni.instancebase_name
795 if itraits.has_cpp_name():
796 if ni.method_map_name != None:
797 itraits.method_map_name = ni.method_map_name
798 else:
799 itraits.method_map_name = itraits.fqcppname()
800 itraits.slotsStructName = to_cname(itraits.fqcppname()) + 'Slots'
801 itraits.slotsInstanceName = 'm_slots_' + itraits.cppname()
804 BMAP = {
805 "Object": CTYPE_ATOM, # yes, items of exactly class "Object" are stored as Atom; subclasses are stored as pointer-to-Object
806 "null": CTYPE_ATOM,
807 "*": CTYPE_ATOM,
808 "void": CTYPE_VOID,
809 "int": CTYPE_INT,
810 "uint": CTYPE_UINT,
811 "Number": CTYPE_DOUBLE,
812 "Boolean": CTYPE_BOOLEAN,
813 "String": CTYPE_STRING,
814 "Namespace": CTYPE_NAMESPACE
817 class Traits:
818 name = ""
819 qname = None
820 init = None
821 itraits = None
822 ctraits = None
823 base = None
824 flags = 0
825 protectedNs = 0
826 is_sealed = False
827 is_final = False
828 is_interface = False
829 interfaces = None
830 names = None
831 slots = None
832 tmethods = None
833 members = None
834 class_id = -1
835 ctype = CTYPE_OBJECT
836 metadata = None
837 cpp_name_comps = None
838 slotsStructName = None
839 slotsInstanceName = None
840 nextSlotId = 0
841 is_gc_exact = False
842 construct = None
843 createInstanceProcName = None
844 # some values for "construct" imply an override to the ScriptObject::construct method,
845 # some don't. this simplifies things.
846 has_construct_method_override = False
847 has_custom_createInstanceProc = False
848 is_restricted_inheritance = False
849 is_abstract_base = False
850 has_pre_create_check = False
851 has_const_setters = False
852 gen_method_map = False
853 is_synthetic = False
854 # FIXME, this is a hack for MI classes in AIR
855 fqinstancebase_name = None
856 # FIXME, this is a hack for MI classes in AIR
857 method_map_name = None
859 def __init__(self, name):
860 self.names = {}
861 self.slots = []
862 self.tmethods = []
863 self.name = name
864 if BMAP.has_key(str(name)):
865 self.ctype = BMAP[str(name)]
867 def __str__(self):
868 return str(self.name)
870 def cpp_offsetof_slots(self):
871 if (len(self.slots) > 0):
872 return "offsetof(%s, %s)" % (self.fqcppname(), self.slotsInstanceName)
873 else:
874 return "0"
876 # What's the C++ type to use when declaring this type as a member of a GCObject?
877 def cpp_gcmember_name(self):
878 r = TYPEMAP_MEMBERTYPE[self.ctype]
879 if self.ctype == CTYPE_OBJECT:
880 if self.has_cpp_name():
881 fqcppname = self.fqcppname()
882 else:
883 fqcppname = BASE_INSTANCE_NAME
884 r = r % fqcppname
885 return r
887 # What's the C++ type to use when declaring this type as an input argument to a C++ method?
888 def cpp_argument_name(self):
889 r = TYPEMAP_ARGTYPE[self.ctype]
890 if self.ctype == CTYPE_OBJECT:
891 if self.has_cpp_name():
892 fqcppname = self.fqcppname()
893 else:
894 fqcppname = BASE_INSTANCE_NAME
895 r = r % fqcppname
896 return r
898 # What's the C++ type to use when unboxing this type as an input argument to a C++ method?
899 def cpp_unboxing_argument_name(self):
900 r = TYPEMAP_ARGTYPE_FOR_UNBOX[self.ctype]
901 if self.ctype == CTYPE_OBJECT:
902 if self.has_cpp_name():
903 fqcppname = self.fqcppname()
904 else:
905 fqcppname = BASE_INSTANCE_NAME
906 r = r % fqcppname
907 return r
909 # What's the C++ type to use when returning this as a function result?
910 def cpp_return_name(self):
911 r = TYPEMAP_RETTYPE[self.ctype]
912 if self.ctype == CTYPE_OBJECT:
913 if self.has_cpp_name():
914 fqcppname = self.fqcppname()
915 else:
916 fqcppname = BASE_INSTANCE_NAME
917 r = r % fqcppname
918 return r
920 def has_cpp_name(self):
921 return self.cpp_name_comps != None
923 # return the fully qualified cpp name, eg "foo::bar::FooObject"
924 def fqcppname(self):
925 return '::'.join(filter(lambda ns: len(ns) > 0, self.cpp_name_comps))
927 # return the cpp name minus namespaces, eg "FooObject"
928 def cppname(self):
929 return self.cpp_name_comps[-1]
931 # return the cpp namespace(s), eg "foo::bar"
932 def cppns(self):
933 return '::'.join(filter(lambda ns: len(ns) > 0, self.cpp_name_comps[:-1]))
936 NULL = Traits("*")
937 UNDEFINED = Traits("void")
939 class ByteArray:
940 data = None
941 pos = 0
942 def __init__(self, data):
943 self.data = data
944 self.pos = 0
946 def readU8(self):
947 r = unpack_from("B", self.data, self.pos)[0]
948 self.pos += 1
949 assert(r >= 0 and r <= 255)
950 return r
952 def readU16(self):
953 r = unpack_from("<h", self.data, self.pos)[0]
954 self.pos += 2
955 assert(r >= 0 and r <= 65535)
956 return r
958 def readDouble(self):
959 r = unpack_from("<d", self.data, self.pos)[0]
960 self.pos += 8
961 return r
963 def readBytes(self, lenbytes):
964 r = self.data[self.pos:self.pos+lenbytes]
965 self.pos += lenbytes
966 return r
968 def readUTF8(self):
969 lenbytes = self.readU30()
970 return self.readBytes(lenbytes)
972 def readU30(self):
973 result = self.readU8()
974 if not result & 0x00000080:
975 return result
976 result = (result & 0x0000007f) | (self.readU8() << 7)
977 if not result & 0x00004000:
978 return result
979 result = (result & 0x00003fff) | (self.readU8() << 14)
980 if not result & 0x00200000:
981 return result
982 result = (result & 0x001fffff) | (self.readU8() << 21)
983 if not result & 0x10000000:
984 return result
985 result = (result & 0x0fffffff) | (self.readU8() << 28)
986 return result
988 class Abc:
989 data = None
990 major = 0
991 minor = 0
992 ints = None
993 uints = None
994 doubles = None
995 strings = None
996 namespaces = None
997 nssets = None
998 names = None
999 defaults = None
1000 methods = None
1001 instances = None
1002 metadata = None
1003 classes = None
1004 scripts = None
1005 scriptName = ""
1006 publicNs = Namespace("", CONSTANT_Namespace)
1007 anyNs = Namespace("*", CONSTANT_Namespace)
1008 versioned_uris = {}
1009 is_vm_builtin = False
1011 magic = 0
1013 qnameToName = {}
1014 nameToQName = {}
1016 def __init__(self, data, scriptName):
1017 self.scriptName = scriptName
1018 self.data = ByteArray(data)
1020 if self.data.readU16() != 16 or self.data.readU16() != 46:
1021 raise Error("Bad Abc Version")
1023 self.parseCpool()
1025 self.defaults = [ (None, 0) ] * 32
1026 self.defaults[CONSTANT_Utf8] = (self.strings, CTYPE_STRING)
1027 self.defaults[CONSTANT_Int] = (self.ints, CTYPE_INT)
1028 self.defaults[CONSTANT_UInt] = (self.uints, CTYPE_UINT)
1029 self.defaults[CONSTANT_Double] = (self.doubles, CTYPE_DOUBLE)
1030 self.defaults[CONSTANT_False] = ({ CONSTANT_False: False }, CTYPE_BOOLEAN)
1031 self.defaults[CONSTANT_True] = ({ CONSTANT_True: True }, CTYPE_BOOLEAN)
1032 self.defaults[CONSTANT_Namespace] = (self.namespaces, CTYPE_NAMESPACE)
1033 self.defaults[CONSTANT_PrivateNs] = (self.namespaces, CTYPE_NAMESPACE)
1034 self.defaults[CONSTANT_PackageNs] = (self.namespaces, CTYPE_NAMESPACE)
1035 self.defaults[CONSTANT_PackageInternalNs] = (self.namespaces, CTYPE_NAMESPACE)
1036 self.defaults[CONSTANT_ProtectedNs] = (self.namespaces, CTYPE_NAMESPACE)
1037 self.defaults[CONSTANT_StaticProtectedNs] = (self.namespaces, CTYPE_NAMESPACE)
1038 self.defaults[CONSTANT_ExplicitNamespace] = (self.namespaces, CTYPE_NAMESPACE)
1039 self.defaults[CONSTANT_Null] = ({ CONSTANT_Null: None }, CTYPE_ATOM)
1041 self.parseMethodInfos()
1042 self.parseMetadataInfos()
1043 self.parseInstanceInfos()
1044 self.parseClassInfos()
1045 self.parseScriptInfos()
1046 self.parseMethodBodies()
1048 for i in range(0, len(self.classes)):
1049 c = self.classes[i]
1050 assert(isinstance(c.name, QName))
1051 prefix = ns_prefix(c.name.ns, True) + to_cname(c.name.name)
1052 c.class_id = i
1053 NativeInfo.parse_native_info(c, self.is_vm_builtin)
1054 self.assign_names(c, prefix)
1055 if c.itraits:
1056 self.assign_names(c.itraits, prefix)
1058 for i in range(0, len(self.scripts)):
1059 script = self.scripts[i]
1060 if script != None:
1061 for j in range(0, len(script.tmethods)):
1062 m = script.tmethods[j]
1063 if m.metadata != None:
1064 for md in m.metadata:
1065 if md.name == "native":
1066 if md.attrs.has_key("script"):
1067 raise Error("native(script) is no longer supported; please use a native(\"function-name\") instead: " + str(m.name))
1068 if len(md.attrs) != 1 or not md.attrs.has_key(""):
1069 raise Error("native(\"function-name\") is the only form supported here" + str(m.name))
1070 if not m.isNative():
1071 raise Error("native(\"function-name\") can only be used on native functions" + str(m.name))
1072 m.receiver = None
1073 m.native_method_name = md.attrs[""] # override
1074 m.native_id_name = "native_script_function_" + ns_prefix(m.name.ns, False) + m.name.name
1075 m.gen_method_map = True
1078 def assign_names(self, traits, prefix):
1079 if traits.init != None:
1080 traits.init.assign_names(traits, prefix)
1081 for j in range(0, len(traits.tmethods)):
1082 traits.tmethods[j].assign_names(traits, prefix)
1084 def default_ctype_and_value(self,d):
1085 kind, index = d
1086 deftable = self.defaults[kind]
1087 if deftable[0] != None:
1088 val = str(deftable[0][index])
1089 ct = deftable[1]
1090 else:
1091 assert(kind == 0 and index == 0)
1092 val = "undefinedAtom"
1093 ct = CTYPE_ATOM # yes, not void
1094 rawval = val
1095 if ct == CTYPE_DOUBLE:
1096 # Python apparently doesn't have isNaN, isInf
1097 if is_nan(val):
1098 val = "MathUtils::kNaN"
1099 elif is_neg_inf(val):
1100 val = "MathUtils::kNegInfinity"
1101 elif is_pos_inf(val):
1102 val = "MathUtils::kInfinity"
1103 elif float(val) >= -2147483648.0 and float(val) <= 2147483647.0 and float(val) == floor(float(val)):
1104 ct = CTYPE_INT
1105 val = "%.0f" % float(val)
1106 elif float(val) >= 0.0 and float(val) <= 4294967295.0 and float(val) == floor(float(val)):
1107 ct = CTYPE_UINT
1108 val = "%.0fU" % float(val)
1109 elif ct == CTYPE_STRING:
1110 for i in range(0, len(self.strings)):
1111 if (self.strings[i] == str(val)):
1112 val = "AvmThunkGetConstantString(%d)/* \"%s\" */" % (i, self.strings[i])
1113 break
1114 elif ct == CTYPE_BOOLEAN:
1115 assert(str(val) == "False" or str(val) == "True")
1116 if str(val) == "False":
1117 val = "false"
1118 else:
1119 val = "true"
1120 if str(val) == "None":
1121 val = "nullObjectAtom"
1122 return ct,val,rawval
1124 def parseCpool(self):
1126 n = self.data.readU30()
1127 self.ints = [0] * max(1,n)
1128 for i in range(1, n):
1129 ii = self.data.readU30()
1130 if float(ii) > 2147483647.0:
1131 ii = int(ii - 4294967296.0)
1132 assert(int(ii) >= -2147483648 and int(ii) <= 2147483647)
1133 self.ints[i] = int(ii)
1135 n = self.data.readU30()
1136 self.uints = [0] * max(1,n)
1137 for i in range(1, n):
1138 self.uints[i] = uint(self.data.readU30())
1140 n = self.data.readU30()
1141 self.doubles = [ kNaN ] * max(1,n)
1142 for i in range(1, n):
1143 self.doubles[i] = self.data.readDouble()
1145 n = self.data.readU30()
1146 self.strings = [""] * max(1,n)
1147 for i in range(1, n):
1148 self.strings[i] = self.data.readUTF8()
1150 n = self.data.readU30()
1151 self.namespaces = [self.anyNs] * max(1,n)
1152 for i in range(1, n):
1153 nskind = self.data.readU8()
1154 if nskind in [CONSTANT_Namespace,
1155 CONSTANT_PackageNs,
1156 CONSTANT_PackageInternalNs,
1157 CONSTANT_ProtectedNs,
1158 CONSTANT_ExplicitNamespace,
1159 CONSTANT_StaticProtectedNs]:
1160 uri = self.strings[self.data.readU30()]
1161 self.namespaces[i] = Namespace(uri, nskind)
1162 # if it's public, and the final char has the magic "version mark",
1163 # it's a versioned namespace uri
1164 if nskind in [CONSTANT_Namespace, CONSTANT_PackageNs]:
1165 api, strippeduri = stripVersion(uri)
1166 if api >= 0:
1167 if not strippeduri in self.versioned_uris:
1168 self.versioned_uris[strippeduri] = []
1169 if not api in self.versioned_uris[strippeduri]:
1170 self.versioned_uris[strippeduri].append(api)
1172 elif nskind in [CONSTANT_PrivateNs]:
1173 self.data.readU30() # skip
1174 self.namespaces[i] = Namespace("private", CONSTANT_PrivateNs)
1176 n = self.data.readU30()
1177 self.nssets = [ None ] * max(1,n)
1178 for i in range(1, n):
1179 count = self.data.readU30()
1180 self.nssets[i] = []
1181 for j in range(0, count):
1182 self.nssets[i].append(self.namespaces[self.data.readU30()])
1184 n = self.data.readU30()
1185 self.names = [ None ] * max(1,n)
1186 for i in range(1, n):
1187 namekind = self.data.readU8()
1188 if namekind in [CONSTANT_Qname, CONSTANT_QnameA]:
1189 self.names[i] = QName(self.namespaces[self.data.readU30()], self.strings[self.data.readU30()])
1191 elif namekind in [CONSTANT_RTQname, CONSTANT_RTQnameA]:
1192 self.names[i] = QName(self.anyNs, self.strings[self.data.readU30()])
1194 elif namekind in [CONSTANT_RTQnameL, CONSTANT_RTQnameLA]:
1195 self.names[i] = None
1197 elif namekind in [CONSTANT_NameL, CONSTANT_NameLA]:
1198 self.names[i] = QName(Namespace(""), None)
1200 elif namekind in [CONSTANT_Multiname, CONSTANT_MultinameA]:
1201 name = self.strings[self.data.readU30()]
1202 nsset = self.nssets[self.data.readU30()]
1203 self.names[i] = Multiname(nsset, name)
1205 elif namekind in [CONSTANT_MultinameL, CONSTANT_MultinameLA]:
1206 nsset = self.nssets[self.data.readU30()]
1207 self.names[i] = Multiname(nsset, None)
1209 elif namekind in [CONSTANT_TypeName]:
1210 name = self.names[self.data.readU30()];
1211 count = self.data.readU30();
1212 types = []
1213 for j in range(0, count):
1214 types.append(self.names[self.data.readU30()]);
1215 self.names[i] = TypeName(name, types);
1216 else:
1217 raise Error("Bad Kind")
1219 def parseMethodInfos(self):
1220 self.names[0] = QName(self.publicNs,"*")
1221 method_count = self.data.readU30()
1222 self.methods = [ None ] * method_count
1223 for i in range(0, method_count):
1224 m = MethodInfo()
1225 self.methods[i] = m
1226 param_count = self.data.readU30()
1227 m.returnType = self.names[self.data.readU30()]
1228 m.paramTypes = [ None ] * param_count
1229 m.paramNames = [ "" ] * param_count
1230 m.optional_count = 0
1231 for j in range(0, param_count):
1232 m.paramTypes[j] = self.names[self.data.readU30()]
1233 m.debugName = self.strings[self.data.readU30()]
1234 m.flags = self.data.readU8()
1235 if m.hasOptional():
1236 m.optional_count = self.data.readU30();
1237 m.optionalValues = [ (-1, -1) ] * param_count
1238 for k in range(param_count-m.optional_count, param_count):
1239 index = self.data.readU30()
1240 kind = self.data.readU8()
1241 m.optionalValues[k] = (kind, index)
1242 if (m.flags & HAS_ParamNames) != 0:
1243 for j in range(0, param_count):
1244 m.paramNames[j] = self.strings[self.data.readU30()]
1246 def parseMetadataInfos(self):
1247 count = self.data.readU30()
1248 self.metadata = [ None ] * count
1249 for i in range (0, count):
1250 mname = self.strings[self.data.readU30()]
1251 m = MetaData(mname)
1252 self.metadata[i] = m
1253 values_count = self.data.readU30()
1254 names = [ None ] * values_count
1255 for q in range(0, values_count):
1256 names[q] = self.strings[self.data.readU30()]
1257 for q in range(0, values_count):
1258 m.attrs[names[q]] = self.strings[self.data.readU30()]
1260 def parseInstanceInfos(self):
1261 count = self.data.readU30()
1262 self.instances = [ None ] * count
1263 instancesDict = {}
1264 for i in range (0, count):
1265 tname = self.names[self.data.readU30()]
1266 t = Traits(tname)
1267 self.instances[i] = t
1268 instancesDict[id(tname)] = t
1269 t.base = self.names[self.data.readU30()]
1270 t.flags = self.data.readU8()
1271 if (t.flags & 1) != 0:
1272 t.is_sealed = True
1273 if (t.flags & 2) != 0:
1274 t.is_final = True
1275 if (t.flags & 4) != 0:
1276 t.is_interface = True
1277 if (t.flags & 8) != 0:
1278 t.protectedNs = self.namespaces[self.data.readU30()]
1279 interface_count = self.data.readU30()
1280 t.interfaces = [None] * interface_count
1281 for j in range(0, interface_count):
1282 t.interfaces[j] = self.names[self.data.readU30()]
1283 methid = self.data.readU30()
1284 t.init = self.methods[methid]
1285 t.init.name = t.name
1286 t.init.kind = TRAIT_Method
1287 t.init.id = methid
1288 self.parseTraits(t, instancesDict.get(id(t.base), None))
1290 @staticmethod
1291 def __qname(name):
1292 if isinstance(name, QName):
1293 return name
1294 if len(name.nsset) == 0:
1295 return QName(Namespace("", CONSTANT_Namespace), name.name)
1296 return QName(name.nsset[0].stripVersion(), name.name)
1298 def qname(self, name):
1299 if (not self.nameToQName.has_key(id(name))):
1300 try:
1301 result = self.__qname(name)
1302 except:
1303 print dir(name)
1304 raise
1305 self.qnameToName[id(result)] = name
1306 self.nameToQName[id(name)] = result
1307 return result
1308 return self.nameToQName[id(name)]
1310 def parseTraits(self, t, baseTraits=None):
1311 lastBaseTraitsSlotId = 0 if baseTraits is None else baseTraits.nextSlotId
1312 namecount = self.data.readU30()
1313 t.members = [ None ] * namecount
1314 for i in range(0, namecount):
1315 name_index = self.data.readU30()
1316 name = self.names[name_index]
1317 name = self.qname(name)
1318 bindingOffset = self.data.pos
1319 tag = self.data.readU8()
1320 kind = tag & 0xf
1321 member = None
1322 if kind in [TRAIT_Slot, TRAIT_Const, TRAIT_Class]:
1323 member = SlotInfo()
1324 member.fileOffset = bindingOffset
1325 memberId = self.data.readU30()
1326 member.id = (memberId - 1) if memberId != 0 else (len(t.slots) + lastBaseTraitsSlotId)
1327 memberIndex = member.id - lastBaseTraitsSlotId
1328 while len(t.slots) <= memberIndex:
1329 t.slots.append(None)
1330 t.slots[member.id - lastBaseTraitsSlotId] = member
1331 t.nextSlotId = max(t.nextSlotId, member.id + 1)
1332 if kind in [TRAIT_Slot, TRAIT_Const]:
1333 member.type = self.names[self.data.readU30()]
1334 index = self.data.readU30()
1335 if index:
1336 deftable = self.defaults[self.data.readU8()]
1337 member.value = deftable[0][index]
1338 if deftable[1] == CTYPE_NAMESPACE:
1339 assert(isinstance(member.value, Namespace))
1340 member.value.srcname = name.name
1341 else:
1342 member.value = self.classes[self.data.readU30()]
1343 member.value.qname = name
1344 elif kind in [TRAIT_Method, TRAIT_Getter, TRAIT_Setter]:
1345 self.data.readU30() # disp_id, ignored
1346 methid = self.data.readU30()
1347 member = self.methods[methid]
1348 t.tmethods.append(member)
1349 member.id = methid
1350 member.final = (tag & ATTR_final) != 0
1351 member.override = (tag & ATTR_override) != 0
1352 member.kind = kind
1353 member.name = name
1354 t.members[i] = member
1355 t.names[str(name)] = member
1357 if (tag & ATTR_metadata) != 0:
1358 mdCount = self.data.readU30()
1359 member.metadata = [ None ] * mdCount
1360 for j in range(0, mdCount):
1361 member.metadata[j] = self.metadata[self.data.readU30()]
1362 # stash class metadata in the ctraits and itraits too, makes it much easier later
1363 if kind == TRAIT_Class:
1364 member.value.metadata = member.metadata
1365 member.value.itraits.metadata = member.metadata
1367 def parseClassInfos(self):
1368 count = len(self.instances)
1369 self.classes = [ None ] * count
1370 for i in range(0, count):
1371 itraits = self.instances[i]
1372 tname = QName(itraits.name.ns, (str(itraits.name.name) + "$"))
1373 if str(tname) == "Object$":
1374 self.is_vm_builtin = True
1375 t = Traits(tname)
1376 self.classes[i] = t
1377 t.init = self.methods[self.data.readU30()]
1378 t.base = "Class"
1379 t.itraits = itraits
1380 itraits.ctraits = t
1381 t.init.name = str(t.itraits.name) + "$cinit"
1382 t.init.kind = TRAIT_Method
1383 self.parseTraits(t)
1385 def parseScriptInfos(self):
1386 count = self.data.readU30()
1387 self.scripts = [ None ] * count
1388 for i in range(0, count):
1389 tname = self.scriptName + "_script_" + str(i)
1390 t = Traits(tname)
1391 self.scripts[i] = t
1392 t.init = self.methods[self.data.readU30()]
1393 t.base = self.names[0]
1394 t.itraits = None
1395 t.init.name = t.name + "$init"
1396 t.init.kind = TRAIT_Method
1397 self.parseTraits(t)
1399 def parseMethodBodies(self):
1400 count = self.data.readU30()
1401 for i in range(0, count):
1402 m = self.methods[self.data.readU30()]
1403 m.max_stack = self.data.readU30()
1404 m.local_count = self.data.readU30()
1405 initScopeDepth = self.data.readU30()
1406 maxScopeDepth = self.data.readU30()
1407 m.max_scope = maxScopeDepth - initScopeDepth
1408 code_length = self.data.readU30()
1409 m.code = self.data.readBytes(code_length)
1410 ex_count = self.data.readU30()
1411 for j in range(0, ex_count):
1412 frm = self.data.readU30()
1413 to = self.data.readU30()
1414 target = self.data.readU30()
1415 type = self.names[self.data.readU30()]
1416 name = self.names[self.data.readU30()];
1417 m.activation = Traits(None)
1418 self.parseTraits(m.activation)
1421 class IndentingPrintWriter:
1422 f = None
1423 indent = 0
1424 backslash = 0
1425 do_indent = True
1427 def __init__(self, file):
1428 self.f = file
1430 def dent(self):
1431 if self.do_indent:
1432 for i in range(0, self.indent):
1433 self.f.write(" ")
1434 self.do_indent = False
1436 def prnt(self, s):
1437 self.dent();
1438 self.f.write(s)
1440 def println(self, s):
1441 assert self.indent >= 0
1442 if s != "":
1443 self.dent();
1444 self.f.write(s)
1445 if self.backslash > 0:
1446 self.f.write(" \\")
1447 self.f.write("\n")
1448 self.do_indent = True
1451 NON_POINTER_4_BYTE_SLOT_BUCKET = 0
1452 POINTER_SLOT_BUCKET = 1
1453 NON_POINTER_8_BYTE_SLOT_BUCKET = 2
1455 CTYPE_TO_SLOT_SORT_BUCKET = {
1456 # following types are 4 bytes
1457 CTYPE_INT : NON_POINTER_4_BYTE_SLOT_BUCKET,
1458 CTYPE_UINT : NON_POINTER_4_BYTE_SLOT_BUCKET,
1459 CTYPE_BOOLEAN : NON_POINTER_4_BYTE_SLOT_BUCKET,
1460 # following types are pointer size ( either 4 or 8 bytes )
1461 CTYPE_OBJECT : POINTER_SLOT_BUCKET,
1462 CTYPE_ATOM : POINTER_SLOT_BUCKET,
1463 CTYPE_STRING : POINTER_SLOT_BUCKET,
1464 CTYPE_NAMESPACE : POINTER_SLOT_BUCKET,
1465 # doubles are 8 bytes
1466 CTYPE_DOUBLE : NON_POINTER_8_BYTE_SLOT_BUCKET,
1467 # slots should never be of type void
1468 CTYPE_VOID : -1
1471 CTYPE_TO_NEED_FORWARD_DECL = {
1472 CTYPE_INT : False,
1473 CTYPE_UINT : False,
1474 CTYPE_BOOLEAN : False,
1475 CTYPE_OBJECT : True,
1476 CTYPE_ATOM : False,
1477 CTYPE_STRING : True,
1478 CTYPE_NAMESPACE : True,
1479 CTYPE_DOUBLE : False,
1480 CTYPE_VOID : False
1483 GLUECLASSES_WITHOUT_SLOTS = frozenset((
1484 'bool',
1485 'double',
1486 'int32_t',
1487 'avmplus::Namespace',
1488 'avmplus::String',
1489 'uint32_t'))
1491 GLUECLASSES_WITHOUT_CONSTRUCT_WRAPPERS = frozenset((
1492 'bool',
1493 'double',
1494 'int32_t',
1495 'uint32_t'))
1497 class AbcThunkGen:
1498 abc = None
1499 abcs = []
1500 all_thunks = []
1501 lookup_traits = None
1502 namesDict = None
1504 def addAbc(self, a):
1505 self.abcs.append(a)
1506 self.lookup_traits = None
1508 def class_native_name(self, c):
1509 return ns_prefix(c.qname.ns, True) + to_cname(c.qname.name)
1511 def class_id_name(self, c):
1512 return "abcclass_" + self.class_native_name(c)
1514 def emitAOT(self, out, name, ctypeObject):
1515 out.println('#ifdef VMCFG_AOT')
1517 traits = filter(lambda t: (t.has_cpp_name()) and (not t.is_synthetic) and (t.fqcppname() != "double") and ((t.ctype == ctypeObject) or (t.fqcppname() == BASE_INSTANCE_NAME) or (t.fqcppname() == BASE_CLASS_NAME)), self.abc.classes + self.abc.instances)
1518 glueClasses = sorted(set(map(lambda t: t.fqcppname(), traits)))
1520 out.println('extern "C" const struct {')
1521 out.indent += 1
1523 i = 0
1524 for i in range(0, len(glueClasses)):
1525 out.println('const char* const n_%(count)u; %(glueClass)s* const m_%(count)u;' % { 'count' : i, 'glueClass': glueClasses[i]})
1527 out.indent -= 1
1528 out.println('} aotABCTypes_%s = {' % name)
1529 out.indent += 1
1531 i = 0
1532 for i in range(0, len(glueClasses)):
1533 out.println('"%(glueClass)s", 0,' % { 'count' : i, 'glueClass': glueClasses[i]})
1535 out.indent -= 1
1536 out.println('};')
1538 out.println('#endif')
1540 def emit_h(self, out, name):
1542 out.println(MPL_HEADER);
1543 out.println('')
1544 out.println("/* machine generated file -- do not edit */");
1545 out.println('')
1547 out.println("#ifndef _H_nativegen_header_%s" % name);
1548 out.println("#define _H_nativegen_header_%s" % name);
1549 out.println('')
1551 self.forwardDeclareGlueClasses(out)
1553 nativeIDNamespaces = opts.nativeIDNS.split('::')
1554 out.println(' '.join(map(lambda ns: 'namespace %s {' % ns, nativeIDNamespaces)))
1555 out.println('')
1557 out.println('extern const uint32_t '+name+"_abc_class_count;")
1558 out.println('extern const uint32_t '+name+"_abc_script_count;")
1559 out.println('extern const uint32_t '+name+"_abc_method_count;")
1560 out.println('extern const uint32_t '+name+"_abc_length;")
1561 out.println('extern const uint8_t '+name+"_abc_data[];");
1562 out.println('extern const char* const '+name+"_versioned_uris[];");
1564 out.println("AVMTHUNK_DECLARE_NATIVE_INITIALIZER(%s)" % (name));
1566 out.println('')
1567 out.println("/* classes */");
1568 for i in range(0, len(self.abc.classes)):
1569 c = self.abc.classes[i]
1570 out.println("const uint32_t " + self.class_id_name(c) + " = " + str(c.class_id) + ";");
1572 out.println('')
1573 out.println("/* methods */");
1574 for i in range(0, len(self.abc.methods)):
1575 m = self.abc.methods[i]
1576 if m.native_id_name != None:
1577 assert(m.id == i)
1578 if m.isNative():
1579 out.println("const uint32_t "+m.native_id_name+" = "+str(m.id)+";");
1580 else:
1581 # not sure if we want to expose method id's for non-native methods; emit as comments for now
1582 out.println("/* const uint32_t "+m.native_id_name+" = "+str(m.id)+"; */");
1583 out.println('')
1585 for receiver,m in self.all_thunks:
1586 self.emitThunkProto(out, receiver, m);
1588 out.println('class SlotOffsetsAndAsserts;')
1589 self.emitStructDeclarations(out)
1591 out.println(' '.join(('}',) * len(nativeIDNamespaces)))
1593 out.println('namespace %s {' % opts.rootImplNS)
1594 self.emitGlueClassManifest(out)
1595 out.println('}')
1597 out.println("#endif // _H_nativegen_header_%s" % name);
1600 def emit_cls(self, out, name):
1602 out.println(MPL_HEADER);
1603 out.println('')
1604 out.println("/* machine generated file -- do not edit */");
1605 out.println('')
1606 out.println("#ifndef _H_nativegen_classes_%s" % name);
1607 out.println("#define _H_nativegen_classes_%s" % name);
1608 out.println('')
1610 rootNS = opts.rootImplNS.split('::')
1611 out.println(' '.join(map(lambda ns: 'namespace %s {' % ns, rootNS)))
1612 out.println('')
1614 self.emitSyntheticClasses(out)
1616 out.println(' '.join(('}',) * len(rootNS)))
1618 out.println('')
1619 out.println("#endif // _H_nativegen_classes_%s" % name);
1621 def emit_cpp(self, out, name):
1623 out.println(MPL_HEADER);
1624 out.println('')
1625 out.println("/* machine generated file -- do not edit */");
1626 out.println('')
1628 nativeIDNamespaces = opts.nativeIDNS.split('::')
1629 out.println(' '.join(map(lambda ns: 'namespace %s {' % ns, nativeIDNamespaces)))
1630 out.println('')
1632 out.println("const uint32_t "+name+"_abc_class_count = "+str(len(self.abc.classes))+";");
1633 out.println("const uint32_t "+name+"_abc_script_count = "+str(len(self.abc.scripts))+";");
1634 out.println("const uint32_t "+name+"_abc_method_count = "+str(len(self.abc.methods))+";");
1635 out.println("const uint32_t "+name+"_abc_length = "+str(len(self.abc.data.data))+";");
1637 out.println("");
1638 out.println("/* thunks (%d total) */" % len(self.all_thunks));
1639 if opts.thunkvprof:
1640 out.println("#define DOPROF")
1641 out.println('#include "../vprof/vprof.h"')
1643 for receiver,m in self.all_thunks:
1644 self.emitThunkBody(out, receiver, m);
1646 out.println("")
1647 self.printStructAsserts(out, self.abc)
1648 out.println("")
1650 out.println("");
1651 out.println("AVMTHUNK_BEGIN_NATIVE_TABLES(%s)" % self.abc.scriptName)
1652 out.indent += 1
1654 out.println("");
1655 out.println("AVMTHUNK_BEGIN_NATIVE_METHODS(%s)" % self.abc.scriptName)
1656 out.indent += 1
1657 for i in range(0, len(self.abc.methods)):
1658 m = self.abc.methods[i]
1659 if m.isNative() and (m.receiver == None or m.receiver.gen_method_map):
1660 assert(m.native_method_name != None)
1661 assert(m.native_id_name != None)
1662 if m.receiver == None:
1663 out.println("AVMTHUNK_NATIVE_FUNCTION(%s, %s)" % (m.native_id_name, m.native_method_name))
1664 else:
1665 # special-case the two oddballs of the group: String and Namespace
1666 # don't descend from ScriptObject and so need a little extra love.
1667 if str(m.receiver.name) == "String":
1668 nmout = "AVMTHUNK_NATIVE_METHOD_STRING"
1669 elif str(m.receiver.name) == "Namespace":
1670 nmout = "AVMTHUNK_NATIVE_METHOD_NAMESPACE"
1671 else:
1672 nmout = "AVMTHUNK_NATIVE_METHOD"
1673 out.println("%s(%s, %s::%s)" % (nmout, m.native_id_name, m.receiver.method_map_name, m.native_method_name))
1674 out.indent -= 1
1675 out.println("AVMTHUNK_END_NATIVE_METHODS()")
1677 out.println("");
1678 out.println("AVMTHUNK_BEGIN_NATIVE_CLASSES(%s)" % self.abc.scriptName)
1679 out.indent += 1
1680 for i in range(0, len(self.abc.classes)):
1681 c = self.abc.classes[i]
1682 if (c.has_cpp_name() or c.itraits.has_cpp_name()) and not c.is_synthetic:
1683 if c.gen_method_map:
1684 offsetOfSlotsClass = "SlotOffsetsAndAsserts::kSlotsOffset%s" % c.cppname()
1685 offsetOfSlotsInstance = "SlotOffsetsAndAsserts::kSlotsOffset%s" % c.itraits.cppname()
1686 out.println("AVMTHUNK_NATIVE_CLASS(%s, %s, %s, %s, %s, %s, %s, %s, %s)" %\
1687 (self.class_id_name(c),\
1688 c.cppname(),\
1689 c.fqcppname(),\
1690 offsetOfSlotsClass,\
1691 c.itraits.fqcppname(),\
1692 offsetOfSlotsInstance,\
1693 str(c.has_construct_method_override).lower(),
1694 str(c.is_restricted_inheritance).lower(),
1695 str(c.is_abstract_base).lower()))
1696 else:
1697 out.println("NATIVE_CLASS(%s, %s, %s)" % (self.class_id_name(c), c.fqcppname(), c.itraits.fqcppname()))
1698 out.indent -= 1
1699 out.println("AVMTHUNK_END_NATIVE_CLASSES()")
1701 out.println("");
1702 out.indent -= 1
1703 out.println("AVMTHUNK_END_NATIVE_TABLES()")
1705 out.println("");
1706 out.println("AVMTHUNK_DEFINE_NATIVE_INITIALIZER(%s)" % (name));
1708 if opts.externmethodandclassetables:
1709 out.println("");
1710 out.println('extern const NativeClassInfo* '+name+"_classEntriesExtern = "+name+"_classEntries;");
1711 out.println('extern const NativeMethodInfo* '+name+"_methodEntriesExtern = "+name+"_methodEntries;");
1713 out.println("");
1714 out.println("/* abc */");
1715 n = len(self.abc.data.data)
1716 out.println("const uint8_t "+name+"_abc_data["+str(n)+"] = {");
1717 for i in range(0, n):
1718 x = ord(self.abc.data.data[i]) & 255;
1719 out.prnt("%4d" % x)
1720 if i+1 < n:
1721 out.prnt(",")
1722 if i%16 == 15:
1723 out.println("");
1724 out.println("};");
1725 out.println('')
1727 out.println("");
1728 out.println("/* versioned_uris */");
1729 out.println("const char* const "+name+"_versioned_uris[] = {");
1730 out.indent += 1
1731 # don't really need to sort 'em, but helps keep output stable
1732 sorted_keys = sorted(self.abc.versioned_uris.keys())
1733 for i in sorted_keys:
1734 # The empty URI (aka "public") is always versioned
1735 # (and special-cased by the versioning code in AvmCore)
1736 # so don't bother emitting it.
1737 if len(i) > 0:
1738 out.println('"%s", // %s' % (i, str(sorted(self.abc.versioned_uris[i]))))
1739 out.println('NULL')
1740 out.indent -= 1
1741 out.println("};");
1742 out.println('')
1744 self.emitAOT(out, name, CTYPE_OBJECT)
1746 out.println(' '.join(('}',) * len(nativeIDNamespaces)))
1748 # note, these are emitted *after* closing the namespaces
1749 self.emitStructStubs(out)
1751 def emit(self, abc, name, out_h, out_cls, out_c):
1752 self.abc = abc;
1753 self.all_thunks = []
1754 self.lookup_traits = None
1755 self.namesDict = {}
1756 for i in range(1, len(abc.names)):
1757 if (not isinstance(abc.names[i], TypeName)):
1758 self.namesDict[id(abc.qname(abc.names[i]))] = i
1760 for i in range(0, len(abc.scripts)):
1761 script = abc.scripts[i]
1762 if script != None:
1763 self.processTraits(script)
1765 self.emit_h(out_h, name)
1766 self.emit_cls(out_cls, name)
1767 self.emit_cpp(out_c, name)
1769 def forwardDeclareGlueClasses(self, out_h):
1770 # find all the native glue classes and write forward declarations for them
1771 cppNamespaceToGlueClasses = {}
1772 traitsSet = set()
1773 for i in range(0, len(self.abc.classes)):
1774 c = self.abc.classes[i]
1775 if c.has_cpp_name():
1776 traitsSet.add(c)
1777 if (c.itraits.has_cpp_name()):
1778 traitsSet.add(c.itraits)
1780 for t in frozenset(traitsSet):
1781 filteredSlots = filter(lambda s: s is not None, t.slots)
1782 for s in filteredSlots:
1783 slotTraits = self.lookupTraits(s.type)
1784 traitsSet.add(slotTraits)
1786 glueClassToTraits = {}
1787 for t in sorted(traitsSet):
1788 if ((t.has_cpp_name()) and (CTYPE_TO_NEED_FORWARD_DECL[t.ctype])):
1789 classNS = t.cppns()
1790 glueClassName = t.cppname()
1791 # special hack because the metadata for the class Math says its instance data is of type double
1792 if glueClassName != "double":
1793 cppNamespaceToGlueClasses.setdefault(classNS, set()).add(glueClassName)
1794 key = classNS + '::' + glueClassName
1795 if not key in glueClassToTraits:
1796 glueClassToTraits[key] = []
1797 glueClassToTraits[key].append(t)
1798 for (nsStr, glueClasses) in sorted(cppNamespaceToGlueClasses.iteritems()):
1799 # turn list of namespaces [foo, bar, baz] into "namespace foo { namespace bar { namespace baz {"
1800 nsList = nsStr.split('::')
1801 out_h.println(' '.join(map(lambda ns: 'namespace %s {' % ns, nsList)))
1802 out_h.indent += 1
1804 # this can emit the same class multiple times; that's by design,
1805 # for clarity & stability of output
1806 for glueClass in sorted(glueClasses):
1807 traitsList = glueClassToTraits[nsStr + '::' + glueClass]
1808 for traits in sorted(traitsList):
1809 out_h.println('class %s; // %s' % (glueClass, traits))
1810 out_h.indent -= 1
1811 out_h.println(' '.join(('}',) * len(nsList)))
1812 out_h.println('')
1814 def emitGlueClassManifest(self, out):
1815 names = []
1816 for i in range(0, len(self.abc.classes)):
1817 c = self.abc.classes[i]
1818 as3name = to_cname(c.itraits.name.name) + "Class"
1819 if c.has_cpp_name() or c.itraits.has_cpp_name():
1820 cppname = c.fqcppname()
1821 else:
1822 cppname = BASE_CLASS_NAME
1823 clsid = "%s::%s" % (opts.nativeIDNS, self.class_id_name(c))
1824 names.append((as3name,cppname,clsid))
1825 names = sorted(names)
1826 man_name = "%sClassManifest" % self.abc.scriptName
1827 out.println("");
1828 out.println("class %s : public avmplus::ClassManifestBase" % man_name)
1829 out.println("{")
1830 out.indent += 1
1831 out.println("friend class avmplus::AvmCore;")
1832 out.println("friend class avmplus::IntVectorClass;")
1833 out.println("friend class avmplus::UIntVectorClass;")
1834 out.println("friend class avmplus::DoubleVectorClass;")
1835 out.println("friend class avmplus::ObjectVectorClass;")
1836 out.indent -= 1
1837 out.println("private:")
1838 out.indent += 1
1839 out.println("REALLY_INLINE %s(avmplus::ScriptEnv* e) : ClassManifestBase(%d, e) { }" % (man_name, len(names)))
1840 out.println("REALLY_INLINE static %s* create(avmplus::ScriptEnv* e) { return new (MMgc::GC::GetGC(e), MMgc::kExact, sizeof(ClassClosure*)*%d) %s(e); }" % (man_name, len(names)-1, man_name))
1841 out.indent -= 1
1842 out.println("public:")
1843 out.indent += 1
1844 for as3name,cppname,clsid in names:
1845 # We can't use static_cast<> because the subclass is only forward-declared at this point
1846 out.println("REALLY_INLINE GCRef<%s> get_%s() { return (%s*)(lazyInitClass(%s)); }" % (cppname, as3name, cppname, clsid))
1847 out.indent -= 1
1848 out.println("};")
1850 @staticmethod
1851 def cmpSlots(slotA, slotB, slotsTypeInfo):
1852 if (slotA is slotB):
1853 return 0;
1855 # slotA or slotB could be None, which means they are an anonymous slot.
1856 # Anonymous slots should be at the end of the pointer slots.
1857 ctype_b = slotsTypeInfo[id(slotB)][0]
1858 slotBBucket = CTYPE_TO_SLOT_SORT_BUCKET[ctype_b] if (slotB is not None) else POINTER_SLOT_BUCKET
1859 if (slotA is None):
1860 if (slotBBucket <= POINTER_SLOT_BUCKET):
1861 return 1
1862 else:
1863 return -1
1865 assert slotA is not None
1866 ctype_a = slotsTypeInfo[id(slotA)][0]
1867 slotABucket = CTYPE_TO_SLOT_SORT_BUCKET[ctype_a]
1868 if (slotB is None):
1869 if (slotBBucket <= POINTER_SLOT_BUCKET):
1870 return -1
1871 else:
1872 return 1
1874 assert slotB is not None
1875 slotBucketCmp = cmp(slotABucket, slotBBucket)
1876 if (slotBucketCmp != 0):
1877 return slotBucketCmp
1878 return cmp(slotA.fileOffset, slotB.fileOffset)
1880 @staticmethod
1881 def needsInstanceSlotsStruct(c):
1882 return (c.itraits.has_cpp_name()) and (c.itraits.fqcppname() != BASE_INSTANCE_NAME)
1884 def emitStructDeclarations(self, out):
1885 visitedGlueClasses = set()
1886 for i in range(0, len(self.abc.classes)):
1887 c = self.abc.classes[i]
1888 if (c.has_cpp_name()):
1889 self.emitStructDeclarationsForTraits(out, c, visitedGlueClasses, True)
1890 if (self.needsInstanceSlotsStruct(c)):
1891 self.emitStructDeclarationsForTraits(out, c.itraits, visitedGlueClasses, False)
1892 else:
1893 assert not self.needsInstanceSlotsStruct(c)
1895 def emitStructInlines(self, out):
1896 for i in range(0, len(self.abc.classes)):
1897 c = self.abc.classes[i]
1898 if (c.has_cpp_name()):
1899 self.emitMethodWrapperBodies(out, c)
1900 self.emitMethodWrapperBodies(out, c.itraits)
1902 def emitStructStubs(self, out):
1903 visitedGlueClasses = set()
1904 for i in range(0, len(self.abc.classes)):
1905 c = self.abc.classes[i]
1906 if (c.has_cpp_name()):
1907 self.emitStructStubsForTraits(out, c, visitedGlueClasses, True)
1908 if (self.needsInstanceSlotsStruct(c)):
1909 self.emitStructStubsForTraits(out, c.itraits, visitedGlueClasses, False)
1910 else:
1911 assert not self.needsInstanceSlotsStruct(c)
1913 def sortSlots(self, t):
1914 filteredSlots = filter(lambda s: s is not None, t.slots)
1916 slotsTypeInfo = {}
1917 for slot in filteredSlots:
1918 slotTraits = self.lookupTraits(slot.type)
1919 slotCType = slotTraits.ctype
1920 if slotCType == CTYPE_VOID:
1921 raise Error("A slot should never be CTYPE_VOID")
1922 slotArgType = slotTraits.cpp_argument_name()
1923 slotRetType = slotTraits.cpp_return_name()
1924 slotMemberType = slotTraits.cpp_gcmember_name()
1925 slotsTypeInfo[id(slot)] = (slotCType, slotArgType, slotRetType, slotMemberType)
1927 sortedSlots = sorted(t.slots, lambda x,y: self.cmpSlots(x, y, slotsTypeInfo))
1928 return sortedSlots, slotsTypeInfo
1930 def emitDeclareSlotClass(self, out, t, sortedSlots, slotsTypeInfo, isClassTraits):
1931 memberVars = []
1932 out.println('//-----------------------------------------------------------')
1933 out.println('// %s' % str(t.name))
1934 out.println('//-----------------------------------------------------------')
1935 out.println('class %s' % t.slotsStructName)
1936 out.println('{')
1937 out.indent += 1
1938 out.println('friend class SlotOffsetsAndAsserts;')
1939 out.indent -= 1
1940 out.println('public:')
1941 out.indent += 1
1943 for slot in sortedSlots:
1944 if (slot is not None):
1945 assert slot.kind in (TRAIT_Slot, TRAIT_Const)
1946 (slotCType, slotArgType, slotRetType, slotMemberType) = slotsTypeInfo[id(slot)]
1947 name = to_cname(slot.name)
1948 if slotCType == CTYPE_BOOLEAN:
1949 out.println('REALLY_INLINE %s get_%s() const { return m_%s != 0; }' % (slotRetType, name, name))
1950 else:
1951 out.println('REALLY_INLINE %s get_%s() const { return m_%s; }' % (slotRetType, name, name))
1952 if ((slot.kind == TRAIT_Slot) or (t.has_const_setters)):
1953 out.println('REALLY_INLINE void set_%s(%s newVal) { m_%s = newVal; }' % (name, slotArgType, name))
1954 out.indent -= 1
1955 out.println('private:')
1956 out.indent += 1
1958 anonCount = 0
1959 for slot in sortedSlots:
1960 if (slot is not None):
1961 assert slot.kind in (TRAIT_Slot, TRAIT_Const)
1962 (slotCType, slotArgType, slotRetType, slotMemberType) = slotsTypeInfo[id(slot)]
1963 out.println('%s m_%s;' % (slotMemberType, to_cname(slot.name)))
1964 else:
1965 out.println('Atom __anonymous_slot_%u;' % (anonCount))
1966 anonCount = anonCount + 1
1968 if t.is_gc_exact:
1969 numTracedSlots = 0
1970 for slot in sortedSlots:
1971 if (slot is not None):
1972 slotTraits = self.lookupTraits(slot.type)
1973 if slotTraits.ctype == CTYPE_ATOM:
1974 numTracedSlots += 1
1975 elif (slotTraits.ctype == CTYPE_STRING) or (slotTraits.ctype == CTYPE_NAMESPACE) or (slotTraits.ctype == CTYPE_OBJECT):
1976 numTracedSlots += 1
1977 else:
1978 numTracedSlots += 1
1979 if numTracedSlots > 0:
1980 out.indent -= 1
1981 out.println('public:');
1982 out.indent += 1
1983 out.println('REALLY_INLINE void gcTracePrivateProperties(MMgc::GC* gc)')
1984 out.println('{')
1985 out.indent += 1
1986 anonCount = 0
1987 for slot in sortedSlots:
1988 if (slot is not None):
1989 slotTraits = self.lookupTraits(slot.type)
1990 if slotTraits.ctype == CTYPE_ATOM:
1991 out.println('gc->TraceAtom(&m_%s);' % to_cname(slot.name))
1992 elif (slotTraits.ctype == CTYPE_STRING) or (slotTraits.ctype == CTYPE_NAMESPACE) or (slotTraits.ctype == CTYPE_OBJECT):
1993 out.println('gc->TraceLocation(&m_%s);' % to_cname(slot.name))
1994 else:
1995 out.println('gc->TraceAtom(&__anonymous_slot_%u);' % (anonCount,))
1996 anonCount = anonCount + 1
1997 out.indent -= 1
1998 out.println('}')
1999 else:
2000 out.indent -= 1
2001 out.println('#define GC_TRIVIAL_TRACER_' + t.cppname())
2002 out.indent += 1
2004 out.indent -= 1
2005 out.println('};')
2007 def emitConstructDeclarations(self, out, t, sortedSlots, slotsTypeInfo, isClassTraits):
2009 if t.is_synthetic:
2010 return
2012 if isClassTraits:
2013 # FIXME: make these non-public, friend access only
2014 out.println("public:")
2015 out.indent += 1
2016 out.println("static %s* FASTCALL createClassClosure(avmplus::VTable* cvtable);" % BASE_CLASS_NAME)
2017 out.indent -= 1
2019 if t.has_custom_createInstanceProc:
2020 out.println("public:")
2021 out.indent += 1
2022 if t.has_pre_create_check:
2023 out.println("static void FASTCALL preCreateInstanceCheck(%s*);" % BASE_CLASS_NAME);
2024 out.println("static avmplus::ScriptObject* FASTCALL createInstanceProc(%s*);" % BASE_CLASS_NAME)
2025 out.indent -= 1
2027 if t.has_construct_method_override:
2028 # emit the construct declaration to ensure that construct is defined for this class
2029 out.println("public:")
2030 out.indent += 1
2031 out.println("virtual avmplus::Atom construct(int argc, avmplus::Atom* argv);")
2032 out.indent -= 1
2033 else:
2034 # TEMPORARY: emit a (debug-only) stub to ensure that construct is NOT defined for this class
2035 out.println("public:")
2036 out.indent += 1
2037 out.println("AvmThunk_DEBUG_ONLY( virtual avmplus::Atom construct(int argc, avmplus::Atom* argv); )")
2038 out.indent -= 1
2040 # createInstance() is no longer supported; emit a (debug-only) stub to generate compile errors for any dangling usages
2041 out.println("private:")
2042 out.indent += 1
2043 out.println("AvmThunk_DEBUG_ONLY( virtual void createInstance() { AvmAssert(0); } )")
2044 out.indent -= 1
2046 def emitConstructStubs(self, out, t, sortedSlots, slotsTypeInfo, isClassTraits):
2048 if t.is_synthetic:
2049 return
2051 if isClassTraits:
2052 out.println("/*static*/ %s* FASTCALL %s::createClassClosure(avmplus::VTable* cvtable)" % (BASE_CLASS_NAME,t.fqcppname()))
2053 out.println("{")
2054 out.indent += 1
2055 if t.is_gc_exact:
2056 out.println("cvtable->ivtable->createInstanceProc = %s;" % (t.createInstanceProcName));
2057 out.println("ClassClosure* const cc = new (cvtable->gc(), MMgc::kExact, cvtable->getExtraSize()) %s(cvtable);" % t.fqcppname())
2058 else:
2059 out.println("cvtable->ivtable->createInstanceProc = %s;" % (t.createInstanceProcName));
2060 out.println("ClassClosure* const cc = new (cvtable->gc(), cvtable->getExtraSize()) %s(cvtable);" % t.fqcppname())
2061 out.println("AvmThunk_DEBUG_ONLY( %s::SlotOffsetsAndAsserts::do%sAsserts(cc->traits(), cc->traits()->itraits); )" % (opts.nativeIDNS, t.cppname()))
2062 out.println("return cc;")
2063 out.indent -= 1
2064 out.println("}")
2066 if t.has_custom_createInstanceProc:
2067 out.println("/*static*/ avmplus::ScriptObject* FASTCALL %s::createInstanceProc(%s* cls)" % (t.fqcppname(), BASE_CLASS_NAME));
2068 out.println("{");
2069 out.indent += 1
2070 if t.has_pre_create_check:
2071 out.println("%s::preCreateInstanceCheck(cls);" % (t.fqcppname()));
2072 if t.itraits.is_gc_exact:
2073 out.println("return new (cls->gc(), MMgc::kExact, cls->getExtraSize()) %s(cls->ivtable(), cls->prototypePtr());" % (t.itraits.fqcppname()))
2074 else:
2075 out.println("return new (cls->gc(), cls->getExtraSize()) %s(cls->ivtable(), cls->prototypePtr());" % (t.itraits.fqcppname()))
2076 out.indent -= 1
2077 out.println("}");
2079 if not t.has_construct_method_override:
2080 if t.fqinstancebase_name != None:
2081 baseclassname = t.fqinstancebase_name
2082 else:
2083 baseclassname = self.lookupTraits(t.base).fqcppname()
2084 out.println("AvmThunk_DEBUG_ONLY( avmplus::Atom %s::construct(int argc, avmplus::Atom* argv) { return %s::construct(argc, argv); } )" % (t.fqcppname(), baseclassname))
2086 def get_args_info(self, t, m):
2087 argtraits = self.argTraits(t, m)
2088 args = []
2089 needcore = False
2090 for i in range(0, len(argtraits)):
2091 argt = argtraits[i]
2092 if i == 0:
2093 argname = "thisRef"
2094 else:
2095 argname = "arg%d" % i
2096 if argt.is_interface:
2097 # FIXME: interface arguments just pass in as ScriptObject, for now
2098 arg_typedef = BASE_INSTANCE_NAME
2099 elif argt.ctype == CTYPE_OBJECT:
2100 arg_typedef = self.findNativeBase(argt)
2101 else:
2102 arg_typedef = None # unused
2103 arg_typedef = TYPEMAP_RETTYPE_GCREF[argt.ctype](arg_typedef)
2104 args.append((argt, arg_typedef, argname))
2105 if TYPEMAP_TO_ATOM_NEEDS_CORE[argt.ctype]:
2106 needcore = True
2107 return args,needcore
2109 def shouldEmitConstructObject(self, t):
2110 return t.itraits != None and \
2111 t.construct != "none" and \
2112 not t.is_abstract_base and \
2113 (not t.itraits.has_cpp_name() or not t.itraits.fqcppname() in GLUECLASSES_WITHOUT_CONSTRUCT_WRAPPERS) and \
2114 not t.itraits.init.needRest()
2116 def emitConstructObjectDeclaration(self, out, t, args):
2117 ret_typedef = TYPEMAP_RETTYPE_GCREF[t.itraits.ctype](t.itraits.fqcppname())
2118 arglist = ', '.join(map(lambda (argt, arg_typedef, argname): "%s %s" % (arg_typedef, argname), args[1:]))
2119 out.println("%s constructObject(%s);" % (ret_typedef, arglist))
2121 def emitMethodWrappers(self, out, t):
2122 # For now, only emit a constructObject() wrapper;
2123 # someday, probably emit other C++ -> AS3 call wrappers.
2124 if self.shouldEmitConstructObject(t):
2125 out.println("public:")
2126 out.indent += 1
2127 args,needcore = self.get_args_info(t,t.itraits.init)
2128 ctype = t.itraits.ctype
2129 # t.itraits is a pure-AS3 class, so it has no C++ class;
2130 # the most closest native ancestor and use that.
2131 fqcppname = self.findNativeBase(t.itraits)
2132 ret_typedef = TYPEMAP_RETTYPE_GCREF[ctype](fqcppname)
2133 arglist = ', '.join(map(lambda (argt, arg_typedef, argname): "%s %s" % (arg_typedef, argname), args[1:]))
2134 out.println("REALLY_INLINE %s constructObject(%s)" % (ret_typedef, arglist))
2135 out.println("{")
2136 out.indent += 1
2137 if needcore:
2138 # explicitly cast to AvmCore* because it might be an only-forward-declared subclass
2139 out.println("avmplus::AvmCore* const core = ((AvmCore*)(this->core()));")
2140 arglist = ', '.join(map(lambda (argt, arg_typedef, argname): TYPEMAP_TO_ATOM[argt.ctype](argname), args))
2141 out.println("avmplus::Atom args[%d] = { %s };" % (len(args), arglist))
2142 out.println("avmplus::Atom const result = this->construct(%d, args);" % (len(args)-1))
2143 raw_result = TYPEMAP_FROM_ATOM[ctype]("result", fqcppname)
2144 out.println("return %s;" % TYPEMAP_TO_GCREF[ctype](raw_result,fqcppname))
2145 out.indent -= 1
2146 out.println("}")
2147 out.indent -= 1
2149 def emitSlotDeclarations(self, out, t, sortedSlots, slotsTypeInfo, closingSemi):
2150 out.println("private:")
2151 out.indent += 1
2152 out.println("friend class %s::SlotOffsetsAndAsserts;" % opts.nativeIDNS)
2153 out.indent -= 1
2154 if (len(t.slots) > 0):
2155 out.println("protected:")
2156 out.indent += 1
2157 for slot in sortedSlots:
2158 assert slot.kind in (TRAIT_Slot, TRAIT_Const)
2159 (slotCType, slotArgType, slotRetType, slotMemberType) = slotsTypeInfo[id(slot)]
2160 name = to_cname(slot.name)
2161 out.println("REALLY_INLINE %s get_%s() const { return %s.get_%s(); }" % (slotRetType, name, t.slotsInstanceName, name));
2162 if ((slot.kind == TRAIT_Slot) or (t.has_const_setters)):
2163 out.println("REALLY_INLINE void set_%s(%s newVal) { %s.set_%s(newVal); }" % (name, slotArgType, t.slotsInstanceName, name))
2164 out.indent -= 1
2165 out.println("private:")
2166 out.indent += 1
2167 out.println("%s::%s %s%s" % (opts.nativeIDNS, t.slotsStructName, t.slotsInstanceName,closingSemi) )
2168 out.indent -= 1
2170 def emitStructDeclarationsForTraits(self, out, t, visitedGlueClasses, isClassTraits):
2172 if (t.fqcppname() in visitedGlueClasses):
2173 if (len(t.slots) == 0):
2174 return
2175 raise Error('C++ glue classes for AS3 classes that have slots may only be referenced by metadata for one AS3 class: %s(%s)' % (t.name, t.fqcppname()))
2177 visitedGlueClasses.add(t.fqcppname())
2178 if (t.fqcppname() in GLUECLASSES_WITHOUT_SLOTS):
2179 return
2181 sortedSlots,slotsTypeInfo = self.sortSlots(t)
2182 self.emitDeclareSlotClass(out, t, sortedSlots, slotsTypeInfo, isClassTraits)
2184 if not t.is_synthetic:
2185 out.backslash += 1
2186 out.println('#define DECLARE_SLOTS_%s' % t.cppname())
2187 out.indent += 1
2188 self.emitConstructDeclarations(out, t, sortedSlots, slotsTypeInfo, isClassTraits)
2189 self.emitMethodWrappers(out, t)
2190 self.emitSlotDeclarations(out, t, sortedSlots, slotsTypeInfo, "")
2191 out.indent -= 1
2192 out.backslash -= 1
2193 out.println('')
2195 out.println('//-----------------------------------------------------------')
2196 out.println('')
2198 def emitSyntheticClasses(self, out):
2199 out.println('// NOTE: The following classes are never actually instantiated as such;')
2200 out.println('// they are provided as a C++ front end onto pure AS3 classes.')
2201 for i in range(0, len(self.abc.classes)):
2202 t = self.abc.classes[i]
2203 if t.is_synthetic:
2204 isClassTraits = True
2205 sortedSlots,slotsTypeInfo = self.sortSlots(t)
2206 out.println('//-----------------------------------------------------------')
2207 out.println('// %s' % str(t.name))
2208 out.println('//-----------------------------------------------------------')
2209 out.println("class %s : public %s" % (t.cppname(), BASE_CLASS_NAME))
2210 out.println("{")
2211 self.emitConstructDeclarations(out, t, sortedSlots, slotsTypeInfo, isClassTraits)
2212 self.emitMethodWrappers(out, t)
2213 self.emitSlotDeclarations(out, t, sortedSlots, slotsTypeInfo, ";")
2214 out.println("private:")
2215 out.indent += 1
2216 out.println("explicit %s(); // unimplemented" % t.cppname())
2217 out.println("explicit %s(const %s&); // unimplemented" % (t.cppname(), t.cppname()))
2218 out.println("void operator=(const %s&); // unimplemented" % t.cppname())
2219 out.indent -= 1
2220 out.println("};")
2221 out.println('')
2224 def emitStructStubsForTraits(self, out, t, visitedGlueClasses, isClassTraits):
2226 if (t.fqcppname() in visitedGlueClasses):
2227 if (len(t.slots) == 0):
2228 return
2229 raise Error('C++ glue classes for AS3 classes that have slots may only be referenced by metadata for one AS3 class: %s(%s)' % (t.name, t.fqcppname()))
2231 visitedGlueClasses.add(t.fqcppname())
2232 if (t.fqcppname() in GLUECLASSES_WITHOUT_SLOTS):
2233 return
2235 sortedSlots,slotsTypeInfo = self.sortSlots(t)
2236 self.emitConstructStubs(out, t, sortedSlots, slotsTypeInfo, isClassTraits)
2238 def printStructAsserts(self, out, abc):
2239 out.println('class SlotOffsetsAndAsserts')
2240 out.println('{')
2241 out.println('public:')
2242 out.indent += 1
2243 out.println('static uint32_t getSlotOffset(Traits* t, int nameId);')
2244 out.println('enum {')
2245 out.indent += 1
2246 visitedNativeClasses = set()
2247 for i in range(0, len(abc.classes)):
2248 c = abc.classes[i]
2249 if (c.gen_method_map):
2250 if (c.fqcppname() not in visitedNativeClasses):
2251 visitedNativeClasses.add(c.fqcppname())
2252 out.println('kSlotsOffset%s = %s,' % (c.cppname(), c.cpp_offsetof_slots()))
2253 if (c.itraits.fqcppname() not in visitedNativeClasses):
2254 visitedNativeClasses.add(c.itraits.fqcppname())
2255 out.println('kSlotsOffset%s = %s,' % (c.itraits.cppname(), c.itraits.cpp_offsetof_slots()))
2256 out.println('kSlotsOffset_fnord')
2257 out.indent -= 1
2258 out.println('};')
2260 out.println('#ifdef DEBUG')
2261 for i in range(0, len(abc.classes)):
2262 c = abc.classes[i]
2263 if (c.gen_method_map):
2264 out.println('static void do%sAsserts(Traits* ctraits, Traits* itraits);' % c.cppname())
2265 out.println('#endif');
2267 out.indent -= 1
2268 out.println('};')
2269 out.println('#ifdef DEBUG');
2270 for i in range(0, len(abc.classes)):
2271 c = abc.classes[i]
2272 if (c.has_cpp_name() and not c.is_synthetic):
2273 out.println('REALLY_INLINE void SlotOffsetsAndAsserts::do%sAsserts(Traits* ctraits, Traits* itraits)' % c.cppname())
2274 out.println('{')
2275 out.indent += 1
2276 assert (c.has_cpp_name)
2277 out.println('(void)ctraits; (void)itraits;')
2278 self.printStructAssertsForTraits(out, c, True, 'ctraits')
2279 if self.needsInstanceSlotsStruct(c):
2280 self.printStructAssertsForTraits(out, c.itraits, False, 'itraits')
2281 out.indent -= 1
2282 out.println('}')
2283 out.println('#endif // DEBUG')
2285 def printStructAssertsForTraits(self, out, t, isClassTraits, traitsVarName):
2286 if (len(t.slots) > 0):
2287 out.println('MMGC_STATIC_ASSERT(offsetof(%s, %s) == kSlotsOffset%s);' % (t.fqcppname(), t.slotsInstanceName, t.cppname()))
2288 out.println('MMGC_STATIC_ASSERT(offsetof(%s, %s) <= 0xFFFF);' % (t.fqcppname(), t.slotsInstanceName))
2289 out.println('MMGC_STATIC_ASSERT(sizeof(%s) <= 0xFFFF);' % (t.fqcppname()))
2290 for slot in t.slots:
2291 if slot != None:
2292 out.println('AvmAssert(getSlotOffset(%s, %u) == (offsetof(%s, %s) + offsetof(%s, m_%s)));'
2293 % (traitsVarName, self.namesDict[id(slot.name)], t.fqcppname(), t.slotsInstanceName, t.slotsStructName, to_cname(slot.name)))
2295 def argTraits(self, receiver, m):
2296 argtraits = [ receiver ]
2297 for i in range(0, len(m.paramTypes)):
2298 argtraits.append(self.lookupTraits(m.paramTypes[i]))
2299 return argtraits
2301 def findNativeBase(self, t):
2302 if t.has_cpp_name():
2303 return t.fqcppname()
2304 if t.base != None:
2305 return self.findNativeBase(self.lookupTraits(t.base))
2306 return None
2308 def thunkInfo(self, m):
2309 ret_traits = self.lookupTraits(m.returnType)
2310 ret_ctype = ret_traits.ctype
2311 if m.kind == TRAIT_Setter:
2312 ret_ctype = CTYPE_VOID
2313 # for return types of thunks, everything but double maps to Atom
2314 if ret_ctype != CTYPE_DOUBLE:
2315 thunk_ret_ctype = CTYPE_ATOM
2316 else:
2317 thunk_ret_ctype = CTYPE_DOUBLE
2318 decl = "%s %s_thunk(MethodEnv* env, uint32_t argc, Atom* argv)" % (TYPEMAP_RETTYPE[thunk_ret_ctype], m.native_id_name)
2319 return ret_traits,ret_ctype,thunk_ret_ctype,decl
2321 def emitThunkProto(self, out, receiver, m):
2322 ret_traits,ret_ctype,thunk_ret_ctype,decl = self.thunkInfo(m)
2323 out.println('extern ' + decl + ";");
2325 def emitThunkBody(self, out, receiver, m):
2326 ret_traits,ret_ctype,thunk_ret_ctype,decl = self.thunkInfo(m)
2328 unbox_receiver = self.calc_unbox_this(m)
2330 out.println(decl);
2331 out.println("{");
2332 out.indent += 1;
2334 if opts.thunkvprof:
2335 out.println('_nvprof("%s", 1);' % name)
2337 param_count = len(m.paramTypes);
2338 optional_count = m.optional_count;
2340 argtraits = self.argTraits(receiver, m)
2342 argszprev = "0"
2343 out.println("enum {");
2344 out.indent += 1;
2345 for i in range(0, len(argtraits)):
2346 if i == 0:
2347 out.println("argoff0 = 0");
2348 else:
2349 out.println(", argoff%d = argoff%d + %s" % (i, i-1, argszprev));
2350 argszprev = "AvmThunkArgSize_%s" % TYPEMAP_ARGTYPE_SUFFIX[argtraits[i].ctype];
2351 out.indent -= 1;
2352 out.println("};");
2354 if m.needRest():
2355 out.println("const uint32_t argoffV = argoff"+str(len(argtraits)-1)+" + "+argszprev+";");
2357 args = []
2359 arg0_typedef = argtraits[0].cpp_argument_name()
2360 assert(argtraits[0].ctype in [CTYPE_OBJECT,CTYPE_STRING,CTYPE_NAMESPACE])
2361 if unbox_receiver:
2362 val = "AvmThunkUnbox_AvmAtomReceiver("+arg0_typedef+", argv[argoff0])";
2363 else:
2364 val = "AvmThunkUnbox_AvmReceiver("+arg0_typedef+", argv[argoff0])";
2365 args.append((val, arg0_typedef))
2367 for i in range(1, len(argtraits)):
2368 arg_ctype = argtraits[i].ctype
2369 arg_typedef = argtraits[i].cpp_unboxing_argument_name()
2370 val = "AvmThunkUnbox_%s(%s, argv[argoff%d])" % (TYPEMAP_ARGTYPE_SUFFIX[arg_ctype], arg_typedef, i)
2371 if arg_ctype == CTYPE_OBJECT:
2372 unboxname = self.findNativeBase(argtraits[i])
2373 if unboxname != None:
2374 val = "(%s*)%s" % (unboxname, val)
2375 # argtraits includes receiver at 0, optionalValues does not
2376 if i > param_count - optional_count:
2377 dct,defval,defvalraw = self.abc.default_ctype_and_value(m.optionalValues[i-1]);
2378 if dct != arg_ctype:
2379 defval = "AvmThunkCoerce_%s_%s(%s)" % (TYPEMAP_ARGTYPE_SUFFIX[dct], TYPEMAP_ARGTYPE_SUFFIX[arg_ctype], defval)
2380 val = "(argc < "+str(i)+" ? "+defval+" : "+val+")";
2381 if arg_ctype == CTYPE_OBJECT:
2382 coercename = self.findNativeBase(argtraits[i])
2383 if coercename != None:
2384 val = "(%s*)%s" % (coercename, val)
2385 args.append((val, arg_typedef))
2387 if m.needRest():
2388 args.append(("(argc <= "+str(param_count)+" ? NULL : argv + argoffV)", "Atom*"))
2389 args.append(("(argc <= "+str(param_count)+" ? 0 : argc - "+str(param_count)+")", "uint32_t"))
2391 if not m.hasOptional() and not m.needRest():
2392 out.println("(void)argc;");
2394 out.println("(void)env;") # avoid "unreferenced formal parameter" in non-debugger builds
2395 if m.receiver == None or not m.receiver.has_cpp_name():
2396 rec_type = BASE_INSTANCE_NAME+"*"
2397 else:
2398 rec_type = m.receiver.cpp_argument_name()
2399 out.println("%s const obj = %s;" % (rec_type, args[0][0]))
2401 if ret_ctype != CTYPE_VOID:
2402 out.prnt("%s const ret = " % ret_traits.cpp_return_name())
2404 if m.receiver == None:
2405 out.prnt("%s(obj" % m.native_method_name)
2406 need_comma = True
2407 else:
2408 if m.receiver.method_map_name != m.receiver.fqcppname():
2409 native_method_name = m.receiver.method_map_name + "::" + m.native_method_name
2410 else:
2411 native_method_name = m.native_method_name
2412 out.prnt("obj->%s(" % native_method_name)
2413 need_comma = False
2415 if len(args) > 1:
2416 out.println("")
2417 out.indent += 1
2418 for i in range(1, len(args)):
2419 if need_comma:
2420 out.prnt(", ")
2421 out.println("%s" % args[i][0]);
2422 need_comma = True
2423 out.indent -= 1
2424 out.println(");")
2426 if ret_ctype != CTYPE_VOID:
2427 ret_result = TYPEMAP_THUNKRETTYPE[ret_ctype] % "ret";
2428 else:
2429 ret_result = "undefinedAtom";
2430 out.println("return %s;" % ret_result)
2431 out.indent -= 1
2432 out.println("}")
2434 # inefficient, but doesn't really matter
2435 def find_override_base(self, mi):
2436 if mi.override and mi.receiver.base != None:
2437 bt = self.lookupTraits(mi.receiver.base)
2438 for j in range(0, len(bt.tmethods)):
2439 bmi = bt.tmethods[j]
2440 if bmi.name.name == mi.name.name and bmi.name.ns == mi.name.ns and bmi != mi:
2441 #print "OVER", str(mi.name), str(mi.receiver)
2442 #print "BASE", str(bmi.name), str(bmi.receiver)
2443 return bt,bmi
2444 return None,None
2446 def calc_unbox_this(self, mi):
2447 if mi.unbox_this < 0:
2448 bt,bmi = self.find_override_base(mi)
2449 if bmi == None:
2450 mi.unbox_this = 0 # no need to unbox
2451 elif bmi.unbox_this > 0:
2452 mi.unbox_this = 1 # unbox_this is sticky, down the inheritance tree
2453 elif len(bmi.paramTypes) > 0:
2454 param0 = self.lookupTraits(bmi.paramTypes[0])
2455 if mi.receiver.ctype in [CTYPE_OBJECT,CTYPE_STRING,CTYPE_NAMESPACE] and param0.ctype == CTYPE_ATOM:
2456 mi.unbox_this = 1 # unbox_this is sticky, down the inheritance tree
2457 return mi.unbox_this > 0
2459 def lookupTraits(self, name):
2460 name = str(name)
2461 if self.lookup_traits == None:
2462 self.lookup_traits = {}
2463 self.lookup_traits["*"] = NULL
2464 self.lookup_traits["void"] = UNDEFINED
2465 for a in self.abcs:
2466 for t in a.scripts:
2467 if self.lookup_traits.has_key(str(t)):
2468 raise Error("duplicate name found: " + str(t))
2469 self.lookup_traits[str(t)] = t
2470 for t in a.classes:
2471 if self.lookup_traits.has_key(str(t)):
2472 raise Error("duplicate name found: " + str(t))
2473 self.lookup_traits[str(t)] = t
2474 for t in a.instances:
2475 if self.lookup_traits.has_key(str(t)):
2476 raise Error("duplicate name found: " + str(t))
2477 self.lookup_traits[str(t)] = t
2478 if not self.lookup_traits.has_key(name):
2479 raise Error("name not found: " + name)
2480 return self.lookup_traits[name]
2482 def gatherThunk(self, receiver, m):
2483 if m.native_id_name == None:
2484 raise Error("name not specified for native method " + str(m.name))
2485 self.all_thunks.append((receiver, m))
2487 def processClass(self, b):
2488 c = b.value
2489 self.processTraits(c)
2490 self.processTraits(c.itraits)
2492 def processMethod(self, receiver, m):
2493 if m.isNative():
2494 self.gatherThunk(receiver, m)
2496 def processTraits(self, s):
2497 if s.init != None:
2498 if s.init.isNative():
2499 raise Error("native constructors are not allowed: " + str(s))
2500 self.processMethod(s, s.init)
2501 for i in range(0, len(s.members)):
2502 if s.members[i].kind in [TRAIT_Method,TRAIT_Getter,TRAIT_Setter]:
2503 self.processMethod(s, s.members[i])
2504 elif s.members[i].kind in [TRAIT_Class]:
2505 self.processClass(s.members[i]);
2507 ngen = AbcThunkGen();
2508 abcGenFor = None
2509 abcGenName = ""
2510 for file in args:
2511 try:
2512 f = open(file,"rb")
2513 data = f.read()
2514 finally:
2515 f.close()
2517 abcScriptName = os.path.splitext(os.path.split(file)[1])[0]
2518 #print "read %s" % abcScriptName
2519 abcGenFor = Abc(data, abcScriptName)
2520 ngen.addAbc(abcGenFor)
2521 abcGenName = os.path.splitext(file)[0]
2523 if abcGenFor:
2524 hfile = None
2525 cppfile = None
2526 try:
2527 hfile = open(abcGenName+".h","w")
2528 clsfile = open(abcGenName+"-classes.hh","w")
2529 cppfile = open(abcGenName+".cpp","w")
2530 h = IndentingPrintWriter(hfile)
2531 cls = IndentingPrintWriter(clsfile)
2532 c = IndentingPrintWriter(cppfile)
2533 ngen.emit(abcGenFor, abcScriptName, h, cls, c);
2534 except Exception, e:
2535 sys.stderr.write("ERROR: "+str(e)+"\n")
2536 exit(1)
2537 finally:
2538 if hfile != None:
2539 hfile.close()
2540 if clsfile != None:
2541 clsfile.close()
2542 if cppfile != None:
2543 cppfile.close()