Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 # You can obtain one at http://mozilla.org/MPL/2.0/.
5 # Common codegen classes.
7 import os
8 import re
9 import string
10 import math
11 import textwrap
13 from WebIDL import BuiltinTypes, IDLBuiltinType, IDLNullValue, IDLSequenceType, IDLType, IDLAttribute, IDLUndefinedValue
14 from Configuration import NoSuchDescriptorError, getTypesFromDescriptor, getTypesFromDictionary, getTypesFromCallback, Descriptor
16 AUTOGENERATED_WARNING_COMMENT = \
17 "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n"
18 ADDPROPERTY_HOOK_NAME = '_addProperty'
19 FINALIZE_HOOK_NAME = '_finalize'
20 CONSTRUCT_HOOK_NAME = '_constructor'
21 LEGACYCALLER_HOOK_NAME = '_legacycaller'
22 HASINSTANCE_HOOK_NAME = '_hasInstance'
23 NEWRESOLVE_HOOK_NAME = '_newResolve'
24 ENUMERATE_HOOK_NAME = '_enumerate'
25 ENUM_ENTRY_VARIABLE_NAME = 'strings'
26 INSTANCE_RESERVED_SLOTS = 3
29 def memberReservedSlot(member):
30 return "(DOM_INSTANCE_RESERVED_SLOTS + %d)" % member.slotIndex
33 def toStringBool(arg):
34 return str(not not arg).lower()
37 def toBindingNamespace(arg):
38 return re.sub("((_workers)?$)", "Binding\\1", arg)
41 def isTypeCopyConstructible(type):
42 # Nullable and sequence stuff doesn't affect copy-constructibility
43 type = type.unroll()
44 return (type.isPrimitive() or type.isString() or type.isEnum() or
45 (type.isUnion() and
46 CGUnionStruct.isUnionCopyConstructible(type)) or
47 (type.isDictionary() and
48 CGDictionary.isDictionaryCopyConstructible(type.inner)) or
49 # Interface types are only copy-constructible if they're Gecko
50 # interfaces. SpiderMonkey interfaces are not copy-constructible
51 # because of rooting issues.
52 (type.isInterface() and type.isGeckoInterface()))
55 def wantsAddProperty(desc):
56 return (desc.concrete and
57 desc.wrapperCache and
58 not (desc.workers and
59 desc.interface.getExtendedAttribute("Global")))
62 # We'll want to insert the indent at the beginnings of lines, but we
63 # don't want to indent empty lines. So only indent lines that have a
64 # non-newline character on them.
65 lineStartDetector = re.compile("^(?=[^\n#])", re.MULTILINE)
68 def indent(s, indentLevel=2):
69 """
70 Indent C++ code.
72 Weird secret feature: this doesn't indent lines that start with # (such as
73 #include lines).
74 """
75 if s == "":
76 return s
77 return re.sub(lineStartDetector, indentLevel * " ", s)
80 def dedent(s):
81 """
82 Remove all leading whitespace from s, and remove a blank line
83 at the beginning.
84 """
85 if s.startswith('\n'):
86 s = s[1:]
87 return textwrap.dedent(s)
89 def fill(template, **args):
90 """
91 Convenience function for filling in a multiline template.
93 `fill(template, name1=v1, name2=v2)` is a lot like
94 `string.Template(template).substitute({"name1": v1, "name2": v2})`.
96 However, it's shorter, and has a few nice features:
98 * If `template` is indented, fill() automatically dedents it!
99 This makes code using fill() with Python's multiline strings
100 much nicer to look at.
102 * If `template` starts with a blank line, fill() strips it off.
103 (Again, convenient with multiline strings.)
105 * fill() recognizes a special kind of substitution
106 of the form `$*{name}`.
108 Use this to paste in, and automatically indent, multiple lines.
109 (Mnemonic: The `*` is for "multiple lines").
111 A `$*` substitution must appear by itself on a line, with optional
112 preceding indentation (spaces only). The whole line is replaced by the
113 corresponding keyword argument, indented appropriately. If the
114 argument is an empty string, no output is generated, not even a blank
115 line.
116 """
118 # This works by transforming the fill()-template to an equivalent
119 # string.Template.
120 multiline_substitution_re = re.compile(r"( *)\$\*{(\w+)}(\n)?")
122 def replace(match):
123 """
124 Replaces a line like ' $*{xyz}\n' with '${xyz_n}',
125 where n is the indent depth, and add a corresponding entry to args.
126 """
127 indentation, name, nl = match.groups()
128 depth = len(indentation)
130 # Check that $*{xyz} appears by itself on a line.
131 prev = match.string[:match.start()]
132 if (prev and not prev.endswith("\n")) or nl is None:
133 raise ValueError("Invalid fill() template: $*{%s} must appear by itself on a line" % name)
135 # Multiline text without a newline at the end is probably a mistake.
136 if not (args[name] == "" or args[name].endswith("\n")):
137 raise ValueError("Argument %s with value %r is missing a newline" % (name, args[name]))
139 # Now replace this whole line of template with the indented equivalent.
140 modified_name = name + "_" + str(depth)
141 indented_value = indent(args[name], depth)
142 if modified_name in args:
143 assert args[modified_name] == indented_value
144 else:
145 args[modified_name] = indented_value
146 return "${" + modified_name + "}"
148 t = dedent(template)
149 assert t.endswith("\n") or "\n" not in t
150 t = re.sub(multiline_substitution_re, replace, t)
151 t = string.Template(t)
152 return t.substitute(args)
155 class CGThing():
156 """
157 Abstract base class for things that spit out code.
158 """
159 def __init__(self):
160 pass # Nothing for now
162 def declare(self):
163 """Produce code for a header file."""
164 assert False # Override me!
166 def define(self):
167 """Produce code for a cpp file."""
168 assert False # Override me!
170 def deps(self):
171 """Produce the deps for a pp file"""
172 assert False # Override me!
175 class CGStringTable(CGThing):
176 """
177 Generate a string table for the given strings with a function accessor:
179 const char *accessorName(unsigned int index) {
180 static const char table[] = "...";
181 static const uint16_t indices = { ... };
182 return &table[indices[index]];
183 }
185 This is more efficient than the more natural:
187 const char *table[] = {
188 ...
189 };
191 The uint16_t indices are smaller than the pointer equivalents, and the
192 string table requires no runtime relocations.
193 """
194 def __init__(self, accessorName, strings):
195 CGThing.__init__(self)
196 self.accessorName = accessorName
197 self.strings = strings
199 def declare(self):
200 return "extern const char *%s(unsigned int aIndex);\n" % self.accessorName
202 def define(self):
203 table = ' "\\0" '.join('"%s"' % s for s in self.strings)
204 indices = []
205 currentIndex = 0
206 for s in self.strings:
207 indices.append(currentIndex)
208 currentIndex += len(s) + 1 # for the null terminator
209 return fill(
210 """
211 const char *${name}(unsigned int aIndex)
212 {
213 static const char table[] = ${table};
214 static const uint16_t indices[] = { ${indices} };
215 static_assert(${currentIndex} <= UINT16_MAX, "string table overflow!");
216 return &table[indices[aIndex]];
217 }
218 """,
219 name=self.accessorName,
220 table=table,
221 indices=", ".join("%d" % index for index in indices),
222 currentIndex=currentIndex)
225 class CGNativePropertyHooks(CGThing):
226 """
227 Generate a NativePropertyHooks for a given descriptor
228 """
229 def __init__(self, descriptor, properties):
230 CGThing.__init__(self)
231 self.descriptor = descriptor
232 self.properties = properties
234 def declare(self):
235 if self.descriptor.workers:
236 return ""
237 return dedent("""
238 // We declare this as an array so that retrieving a pointer to this
239 // binding's property hooks only requires compile/link-time resolvable
240 // address arithmetic. Declaring it as a pointer instead would require
241 // doing a run-time load to fetch a pointer to this binding's property
242 // hooks. And then structures which embedded a pointer to this structure
243 // would require a run-time load for proper initialization, which would
244 // then induce static constructors. Lots of static constructors.
245 extern const NativePropertyHooks sNativePropertyHooks[];
246 """).rstrip() # BOGUS strip newline from the last line here (!)
248 def define(self):
249 if self.descriptor.workers:
250 return ""
251 if self.descriptor.concrete and self.descriptor.proxy:
252 resolveOwnProperty = "ResolveOwnProperty"
253 enumerateOwnProperties = "EnumerateOwnProperties"
254 elif self.descriptor.needsXrayResolveHooks():
255 resolveOwnProperty = "ResolveOwnPropertyViaNewresolve"
256 enumerateOwnProperties = "EnumerateOwnPropertiesViaGetOwnPropertyNames"
257 else:
258 resolveOwnProperty = "nullptr"
259 enumerateOwnProperties = "nullptr"
260 if self.properties.hasNonChromeOnly():
261 regular = "&sNativeProperties"
262 else:
263 regular = "nullptr"
264 if self.properties.hasChromeOnly():
265 chrome = "&sChromeOnlyNativeProperties"
266 else:
267 chrome = "nullptr"
268 constructorID = "constructors::id::"
269 if self.descriptor.interface.hasInterfaceObject():
270 constructorID += self.descriptor.name
271 else:
272 constructorID += "_ID_Count"
273 prototypeID = "prototypes::id::"
274 if self.descriptor.interface.hasInterfacePrototypeObject():
275 prototypeID += self.descriptor.name
276 else:
277 prototypeID += "_ID_Count"
278 parent = self.descriptor.interface.parent
279 parentHooks = (toBindingNamespace(parent.identifier.name) + "::sNativePropertyHooks"
280 if parent else 'nullptr')
282 return fill(
283 """
284 const NativePropertyHooks sNativePropertyHooks[] = { {
285 ${resolveOwnProperty},
286 ${enumerateOwnProperties},
287 { ${regular}, ${chrome} },
288 ${prototypeID},
289 ${constructorID},
290 ${parentHooks}
291 } };
292 """,
293 resolveOwnProperty=resolveOwnProperty,
294 enumerateOwnProperties=enumerateOwnProperties,
295 regular=regular,
296 chrome=chrome,
297 prototypeID=prototypeID,
298 constructorID=constructorID,
299 parentHooks=parentHooks)
302 def NativePropertyHooks(descriptor):
303 return "&sWorkerNativePropertyHooks" if descriptor.workers else "sNativePropertyHooks"
306 def DOMClass(descriptor):
307 def make_name(d):
308 return "%s%s" % (d.interface.identifier.name, '_workers' if d.workers else '')
310 protoList = ['prototypes::id::' + make_name(descriptor.getDescriptor(proto)) for proto in descriptor.prototypeChain]
311 # Pad out the list to the right length with _ID_Count so we
312 # guarantee that all the lists are the same length. _ID_Count
313 # is never the ID of any prototype, so it's safe to use as
314 # padding.
315 protoList.extend(['prototypes::id::_ID_Count'] * (descriptor.config.maxProtoChainLength - len(protoList)))
317 return fill(
318 """
319 {
320 { ${protoChain} },
321 IsBaseOf<nsISupports, ${nativeType} >::value,
322 ${hooks},
323 GetParentObject<${nativeType}>::Get,
324 GetProtoObject,
325 GetCCParticipant<${nativeType}>::Get()
326 }
327 """,
328 protoChain=', '.join(protoList),
329 nativeType=descriptor.nativeType,
330 hooks=NativePropertyHooks(descriptor))
333 class CGDOMJSClass(CGThing):
334 """
335 Generate a DOMJSClass for a given descriptor
336 """
337 def __init__(self, descriptor):
338 CGThing.__init__(self)
339 self.descriptor = descriptor
341 def declare(self):
342 return ""
344 def define(self):
345 traceHook = 'nullptr'
346 callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr'
347 slotCount = INSTANCE_RESERVED_SLOTS + self.descriptor.interface.totalMembersInSlots
348 classFlags = "JSCLASS_IS_DOMJSCLASS | "
349 classExtensionAndObjectOps = """\
350 JS_NULL_CLASS_EXT,
351 JS_NULL_OBJECT_OPS
352 """
353 if self.descriptor.interface.getExtendedAttribute("Global"):
354 classFlags += "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS) | JSCLASS_IMPLEMENTS_BARRIERS"
355 traceHook = "JS_GlobalObjectTraceHook"
356 if not self.descriptor.workers:
357 classExtensionAndObjectOps = """\
358 {
359 nsGlobalWindow::OuterObject, /* outerObject */
360 nullptr, /* innerObject */
361 nullptr, /* iteratorObject */
362 false, /* isWrappedNative */
363 nullptr /* weakmapKeyDelegateOp */
364 },
365 {
366 nullptr, /* lookupGeneric */
367 nullptr, /* lookupProperty */
368 nullptr, /* lookupElement */
369 nullptr, /* defineGeneric */
370 nullptr, /* defineProperty */
371 nullptr, /* defineElement */
372 nullptr, /* getGeneric */
373 nullptr, /* getProperty */
374 nullptr, /* getElement */
375 nullptr, /* setGeneric */
376 nullptr, /* setProperty */
377 nullptr, /* setElement */
378 nullptr, /* getGenericAttributes */
379 nullptr, /* setGenericAttributes */
380 nullptr, /* deleteProperty */
381 nullptr, /* deleteElement */
382 nullptr, /* watch */
383 nullptr, /* unwatch */
384 nullptr, /* slice */
385 nullptr, /* enumerate */
386 JS_ObjectToOuterObject /* thisObject */
387 }
388 """
389 else:
390 classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount
391 if self.descriptor.interface.getExtendedAttribute("NeedNewResolve"):
392 newResolveHook = "(JSResolveOp)" + NEWRESOLVE_HOOK_NAME
393 classFlags += " | JSCLASS_NEW_RESOLVE"
394 enumerateHook = ENUMERATE_HOOK_NAME
395 elif self.descriptor.interface.getExtendedAttribute("Global"):
396 newResolveHook = "(JSResolveOp) mozilla::dom::ResolveGlobal"
397 classFlags += " | JSCLASS_NEW_RESOLVE"
398 enumerateHook = "mozilla::dom::EnumerateGlobal"
399 else:
400 newResolveHook = "JS_ResolveStub"
401 enumerateHook = "JS_EnumerateStub"
403 return fill( # BOGUS extra blank line at the top
404 """
406 static const DOMJSClass Class = {
407 { "${name}",
408 ${flags},
409 ${addProperty}, /* addProperty */
410 JS_DeletePropertyStub, /* delProperty */
411 JS_PropertyStub, /* getProperty */
412 JS_StrictPropertyStub, /* setProperty */
413 ${enumerate}, /* enumerate */
414 ${resolve}, /* resolve */
415 JS_ConvertStub,
416 ${finalize}, /* finalize */
417 ${call}, /* call */
418 nullptr, /* hasInstance */
419 nullptr, /* construct */
420 ${trace}, /* trace */
421 JS_NULL_CLASS_SPEC,
422 $*{classExtensionAndObjectOps}
423 },
424 $*{descriptor}
425 };
426 """,
427 name=self.descriptor.interface.identifier.name,
428 flags=classFlags,
429 addProperty=ADDPROPERTY_HOOK_NAME if wantsAddProperty(self.descriptor) else 'JS_PropertyStub',
430 enumerate=enumerateHook,
431 resolve=newResolveHook,
432 finalize=FINALIZE_HOOK_NAME,
433 call=callHook,
434 trace=traceHook,
435 classExtensionAndObjectOps=classExtensionAndObjectOps,
436 descriptor=DOMClass(self.descriptor))
439 class CGDOMProxyJSClass(CGThing):
440 """
441 Generate a DOMJSClass for a given proxy descriptor
442 """
443 def __init__(self, descriptor):
444 CGThing.__init__(self)
445 self.descriptor = descriptor
447 def declare(self):
448 return ""
450 def define(self):
451 flags = ["JSCLASS_IS_DOMJSCLASS"]
452 # We don't use an IDL annotation for JSCLASS_EMULATES_UNDEFINED because
453 # we don't want people ever adding that to any interface other than
454 # HTMLAllCollection. So just hardcode it here.
455 if self.descriptor.interface.identifier.name == "HTMLAllCollection":
456 flags.append("JSCLASS_EMULATES_UNDEFINED")
457 callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr'
458 return fill( # BOGUS extra blank line at the top
459 """
461 static const DOMJSClass Class = {
462 PROXY_CLASS_DEF("${name}",
463 0, /* extra slots */
464 ${flags},
465 ${call}, /* call */
466 nullptr /* construct */),
467 $*{descriptor}
468 };
469 """,
470 name=self.descriptor.interface.identifier.name,
471 flags=" | ".join(flags),
472 call=callHook,
473 descriptor=DOMClass(self.descriptor))
476 def PrototypeIDAndDepth(descriptor):
477 prototypeID = "prototypes::id::"
478 if descriptor.interface.hasInterfacePrototypeObject():
479 prototypeID += descriptor.interface.identifier.name
480 if descriptor.workers:
481 prototypeID += "_workers"
482 depth = "PrototypeTraits<%s>::Depth" % prototypeID
483 else:
484 prototypeID += "_ID_Count"
485 depth = "0"
486 return (prototypeID, depth)
489 def UseHolderForUnforgeable(descriptor):
490 return (descriptor.concrete and
491 descriptor.proxy and
492 any(m for m in descriptor.interface.members if m.isAttr() and m.isUnforgeable()))
495 def CallOnUnforgeableHolder(descriptor, code, isXrayCheck=None,
496 useSharedRoot=False):
497 """
498 Generate the code to execute the code in "code" on an unforgeable holder if
499 needed. code should be a string containing the code to execute. If it
500 contains a ${holder} string parameter it will be replaced with the
501 unforgeable holder object.
503 If isXrayCheck is not None it should be a string that contains a statement
504 returning whether proxy is an Xray. If isXrayCheck is None the generated
505 code won't try to unwrap Xrays.
507 If useSharedRoot is true, we will use an existing
508 JS::Rooted<JSObject*> sharedRoot for storing our unforgeable holder instead
509 of declaring a new Rooted.
510 """
511 if isXrayCheck is not None:
512 pre = fill(
513 """
514 // Scope for 'global', 'ac' and 'unforgeableHolder'
515 {
516 JS::Rooted<JSObject*> global(cx);
517 Maybe<JSAutoCompartment> ac;
518 if (${isXrayCheck}) {
519 global = js::GetGlobalForObjectCrossCompartment(js::UncheckedUnwrap(proxy));
520 ac.construct(cx, global);
521 } else {
522 global = js::GetGlobalForObjectCrossCompartment(proxy);
523 }
524 """,
525 isXrayCheck=isXrayCheck)
526 else:
527 pre = dedent("""
528 // Scope for 'global' and 'unforgeableHolder'
529 {
530 JSObject* global = js::GetGlobalForObjectCrossCompartment(proxy);
531 """)
533 if useSharedRoot:
534 holderDecl = "JS::Rooted<JSObject*>& unforgeableHolder(sharedRoot);\n"
535 else:
536 holderDecl = "JS::Rooted<JSObject*> unforgeableHolder(cx);\n"
538 code = string.Template(code).substitute({"holder": "unforgeableHolder"})
539 return fill(
540 """
541 $*{pre}
542 $*{holderDecl}
543 unforgeableHolder = GetUnforgeableHolder(global, prototypes::id::${name});
544 $*{code}
545 }
546 """,
547 pre=pre,
548 holderDecl=holderDecl,
549 name=descriptor.name,
550 code=code)
553 class CGPrototypeJSClass(CGThing):
554 def __init__(self, descriptor, properties):
555 CGThing.__init__(self)
556 self.descriptor = descriptor
557 self.properties = properties
559 def declare(self):
560 # We're purely for internal consumption
561 return ""
563 def define(self):
564 prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
565 slotCount = "DOM_INTERFACE_PROTO_SLOTS_BASE"
566 if UseHolderForUnforgeable(self.descriptor):
567 slotCount += " + 1 /* slot for the JSObject holding the unforgeable properties */"
568 return fill(
569 """
570 static const DOMIfaceAndProtoJSClass PrototypeClass = {
571 {
572 "${name}Prototype",
573 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
574 JS_PropertyStub, /* addProperty */
575 JS_DeletePropertyStub, /* delProperty */
576 JS_PropertyStub, /* getProperty */
577 JS_StrictPropertyStub, /* setProperty */
578 JS_EnumerateStub,
579 JS_ResolveStub,
580 JS_ConvertStub,
581 nullptr, /* finalize */
582 nullptr, /* call */
583 nullptr, /* hasInstance */
584 nullptr, /* construct */
585 nullptr, /* trace */
586 JSCLASS_NO_INTERNAL_MEMBERS
587 },
588 eInterfacePrototype,
589 ${hooks},
590 "[object ${name}Prototype]",
591 ${prototypeID},
592 ${depth}
593 };
594 """,
595 name=self.descriptor.interface.identifier.name,
596 slotCount=slotCount,
597 hooks=NativePropertyHooks(self.descriptor),
598 prototypeID=prototypeID,
599 depth=depth)
602 def NeedsGeneratedHasInstance(descriptor):
603 return descriptor.hasXPConnectImpls or descriptor.interface.isConsequential()
606 class CGInterfaceObjectJSClass(CGThing):
607 def __init__(self, descriptor, properties):
608 CGThing.__init__(self)
609 self.descriptor = descriptor
610 self.properties = properties
612 def declare(self):
613 # We're purely for internal consumption
614 return ""
616 def define(self):
617 if self.descriptor.interface.ctor():
618 ctorname = CONSTRUCT_HOOK_NAME
619 else:
620 ctorname = "ThrowingConstructor"
621 if NeedsGeneratedHasInstance(self.descriptor):
622 hasinstance = HASINSTANCE_HOOK_NAME
623 elif self.descriptor.interface.hasInterfacePrototypeObject():
624 hasinstance = "InterfaceHasInstance"
625 else:
626 hasinstance = "nullptr"
627 prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
628 slotCount = "DOM_INTERFACE_SLOTS_BASE"
629 if len(self.descriptor.interface.namedConstructors) > 0:
630 slotCount += (" + %i /* slots for the named constructors */" %
631 len(self.descriptor.interface.namedConstructors))
632 return fill( # BOGUS extra newline at the top
633 """
635 static const DOMIfaceAndProtoJSClass InterfaceObjectClass = {
636 {
637 "Function",
638 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
639 JS_PropertyStub, /* addProperty */
640 JS_DeletePropertyStub, /* delProperty */
641 JS_PropertyStub, /* getProperty */
642 JS_StrictPropertyStub, /* setProperty */
643 JS_EnumerateStub,
644 JS_ResolveStub,
645 JS_ConvertStub,
646 nullptr, /* finalize */
647 ${ctorname}, /* call */
648 ${hasInstance}, /* hasInstance */
649 ${ctorname}, /* construct */
650 nullptr, /* trace */
651 JSCLASS_NO_INTERNAL_MEMBERS
652 },
653 eInterface,
654 ${hooks},
655 "function ${name}() {\\n [native code]\\n}",
656 ${prototypeID},
657 ${depth}
658 };
659 """,
660 slotCount=slotCount,
661 ctorname=ctorname,
662 hasInstance=hasinstance,
663 hooks=NativePropertyHooks(self.descriptor),
664 name=self.descriptor.interface.identifier.name,
665 prototypeID=prototypeID,
666 depth=depth)
669 class CGList(CGThing):
670 """
671 Generate code for a list of GCThings. Just concatenates them together, with
672 an optional joiner string. "\n" is a common joiner.
673 """
674 def __init__(self, children, joiner=""):
675 CGThing.__init__(self)
676 # Make a copy of the kids into a list, because if someone passes in a
677 # generator we won't be able to both declare and define ourselves, or
678 # define ourselves more than once!
679 self.children = list(children)
680 self.joiner = joiner
682 def append(self, child):
683 self.children.append(child)
685 def prepend(self, child):
686 self.children.insert(0, child)
688 def extend(self, kids):
689 self.children.extend(kids)
691 def join(self, iterable):
692 return self.joiner.join(s for s in iterable if len(s) > 0)
694 def declare(self):
695 return self.join(child.declare() for child in self.children if child is not None)
697 def define(self):
698 return self.join(child.define() for child in self.children if child is not None)
700 def deps(self):
701 deps = set()
702 for child in self.children:
703 if child is None:
704 continue
705 deps = deps.union(child.deps())
706 return deps
709 class CGGeneric(CGThing):
710 """
711 A class that spits out a fixed string into the codegen. Can spit out a
712 separate string for the declaration too.
713 """
714 def __init__(self, define="", declare=""):
715 self.declareText = declare
716 self.defineText = define
718 def declare(self):
719 return self.declareText
721 def define(self):
722 return self.defineText
724 def deps(self):
725 return set()
728 class CGIndenter(CGThing):
729 """
730 A class that takes another CGThing and generates code that indents that
731 CGThing by some number of spaces. The default indent is two spaces.
732 """
733 def __init__(self, child, indentLevel=2, declareOnly=False):
734 assert isinstance(child, CGThing)
735 CGThing.__init__(self)
736 self.child = child
737 self.indentLevel = indentLevel
738 self.declareOnly = declareOnly
740 def declare(self):
741 return indent(self.child.declare(), self.indentLevel)
743 def define(self):
744 defn = self.child.define()
745 if self.declareOnly:
746 return defn
747 else:
748 return indent(defn, self.indentLevel)
751 class CGWrapper(CGThing):
752 """
753 Generic CGThing that wraps other CGThings with pre and post text.
754 """
755 def __init__(self, child, pre="", post="", declarePre=None,
756 declarePost=None, definePre=None, definePost=None,
757 declareOnly=False, defineOnly=False, reindent=False):
758 CGThing.__init__(self)
759 self.child = child
760 self.declarePre = declarePre or pre
761 self.declarePost = declarePost or post
762 self.definePre = definePre or pre
763 self.definePost = definePost or post
764 self.declareOnly = declareOnly
765 self.defineOnly = defineOnly
766 self.reindent = reindent
768 def declare(self):
769 if self.defineOnly:
770 return ''
771 decl = self.child.declare()
772 if self.reindent:
773 decl = self.reindentString(decl, self.declarePre)
774 return self.declarePre + decl + self.declarePost
776 def define(self):
777 if self.declareOnly:
778 return ''
779 defn = self.child.define()
780 if self.reindent:
781 defn = self.reindentString(defn, self.definePre)
782 return self.definePre + defn + self.definePost
784 @staticmethod
785 def reindentString(stringToIndent, widthString):
786 # We don't use lineStartDetector because we don't want to
787 # insert whitespace at the beginning of our _first_ line.
788 # Use the length of the last line of width string, in case
789 # it is a multiline string.
790 lastLineWidth = len(widthString.splitlines()[-1])
791 return stripTrailingWhitespace(
792 stringToIndent.replace("\n", "\n" + (" " * lastLineWidth)))
794 def deps(self):
795 return self.child.deps()
798 class CGIfWrapper(CGList):
799 def __init__(self, child, condition):
800 CGList.__init__(self, [
801 CGWrapper(CGGeneric(condition), pre="if (", post=") {\n", reindent=True),
802 CGIndenter(child),
803 CGGeneric("}\n")
804 ])
807 class CGIfElseWrapper(CGList):
808 def __init__(self, condition, ifTrue, ifFalse):
809 CGList.__init__(self, [
810 CGWrapper(CGGeneric(condition), pre="if (", post=") {\n", reindent=True),
811 CGIndenter(ifTrue),
812 CGGeneric("} else {\n"),
813 CGIndenter(ifFalse),
814 CGGeneric("}\n")
815 ])
818 class CGElseChain(CGThing):
819 """
820 Concatenate if statements in an if-else-if-else chain.
821 """
822 def __init__(self, children):
823 self.children = [c for c in children if c is not None]
825 def declare(self):
826 assert False
828 def define(self):
829 if not self.children:
830 return ""
831 s = self.children[0].define()
832 assert s.endswith("\n")
833 for child in self.children[1:]:
834 code = child.define()
835 assert code.startswith("if") or code.startswith("{")
836 assert code.endswith("\n")
837 s = s.rstrip() + " else " + code
838 return s
841 class CGTemplatedType(CGWrapper):
842 def __init__(self, templateName, child, isConst=False, isReference=False):
843 const = "const " if isConst else ""
844 pre = "%s%s<" % (const, templateName)
845 ref = "&" if isReference else ""
846 post = ">%s" % ref
847 CGWrapper.__init__(self, child, pre=pre, post=post)
850 class CGNamespace(CGWrapper):
851 def __init__(self, namespace, child, declareOnly=False):
852 pre = "namespace %s {\n" % namespace
853 post = "} // namespace %s\n" % namespace
854 CGWrapper.__init__(self, child, pre=pre, post=post,
855 declareOnly=declareOnly)
857 @staticmethod
858 def build(namespaces, child, declareOnly=False):
859 """
860 Static helper method to build multiple wrapped namespaces.
861 """
862 if not namespaces:
863 return CGWrapper(child, declareOnly=declareOnly)
864 inner = CGNamespace.build(namespaces[1:], child, declareOnly=declareOnly)
865 return CGNamespace(namespaces[0], inner, declareOnly=declareOnly)
868 class CGIncludeGuard(CGWrapper):
869 """
870 Generates include guards for a header.
871 """
872 def __init__(self, prefix, child):
873 """|prefix| is the filename without the extension."""
874 define = 'mozilla_dom_%s_h' % prefix
875 CGWrapper.__init__(self, child,
876 declarePre='#ifndef %s\n#define %s\n\n' % (define, define),
877 declarePost='\n#endif // %s\n' % define)
880 def getRelevantProviders(descriptor, config):
881 if descriptor is not None:
882 return [descriptor]
883 # Do both the non-worker and worker versions
884 return [
885 config.getDescriptorProvider(False),
886 config.getDescriptorProvider(True)
887 ]
890 def getAllTypes(descriptors, dictionaries, callbacks):
891 """
892 Generate all the types we're dealing with. For each type, a tuple
893 containing type, descriptor, dictionary is yielded. The
894 descriptor and dictionary can be None if the type does not come
895 from a descriptor or dictionary; they will never both be non-None.
896 """
897 for d in descriptors:
898 if d.interface.isExternal():
899 continue
900 for t in getTypesFromDescriptor(d):
901 yield (t, d, None)
902 for dictionary in dictionaries:
903 for t in getTypesFromDictionary(dictionary):
904 yield (t, None, dictionary)
905 for callback in callbacks:
906 for t in getTypesFromCallback(callback):
907 yield (t, None, None)
910 class CGHeaders(CGWrapper):
911 """
912 Generates the appropriate include statements.
913 """
914 def __init__(self, descriptors, dictionaries, callbacks,
915 callbackDescriptors,
916 declareIncludes, defineIncludes, prefix, child,
917 config=None, jsImplementedDescriptors=[]):
918 """
919 Builds a set of includes to cover |descriptors|.
921 Also includes the files in |declareIncludes| in the header
922 file and the files in |defineIncludes| in the .cpp.
924 |prefix| contains the basename of the file that we generate include
925 statements for.
926 """
928 # Determine the filenames for which we need headers.
929 interfaceDeps = [d.interface for d in descriptors]
930 ancestors = []
931 for iface in interfaceDeps:
932 if iface.parent:
933 # We're going to need our parent's prototype, to use as the
934 # prototype of our prototype object.
935 ancestors.append(iface.parent)
936 # And if we have an interface object, we'll need the nearest
937 # ancestor with an interface object too, so we can use its
938 # interface object as the proto of our interface object.
939 if iface.hasInterfaceObject():
940 parent = iface.parent
941 while parent and not parent.hasInterfaceObject():
942 parent = parent.parent
943 if parent:
944 ancestors.append(parent)
945 interfaceDeps.extend(ancestors)
946 bindingIncludes = set(self.getDeclarationFilename(d) for d in interfaceDeps)
948 # Grab all the implementation declaration files we need.
949 implementationIncludes = set(d.headerFile for d in descriptors if d.needsHeaderInclude())
951 # Grab the includes for checking hasInstance
952 interfacesImplementingSelf = set()
953 for d in descriptors:
954 interfacesImplementingSelf |= d.interface.interfacesImplementingSelf
955 implementationIncludes |= set(self.getDeclarationFilename(i) for i in
956 interfacesImplementingSelf)
958 # Grab the includes for the things that involve XPCOM interfaces
959 hasInstanceIncludes = set("nsIDOM" + d.interface.identifier.name + ".h" for d
960 in descriptors if
961 NeedsGeneratedHasInstance(d) and
962 d.interface.hasInterfacePrototypeObject())
964 # Now find all the things we'll need as arguments because we
965 # need to wrap or unwrap them.
966 bindingHeaders = set()
967 declareIncludes = set(declareIncludes)
969 def addHeadersForType((t, descriptor, dictionary)):
970 """
971 Add the relevant headers for this type. We use descriptor and
972 dictionary, if passed, to decide what to do with interface types.
973 """
974 assert not descriptor or not dictionary
975 # Dictionaries have members that need to be actually
976 # declared, not just forward-declared.
977 if dictionary:
978 headerSet = declareIncludes
979 else:
980 headerSet = bindingHeaders
981 if t.nullable():
982 # Need to make sure that Nullable as a dictionary
983 # member works.
984 headerSet.add("mozilla/dom/Nullable.h")
985 unrolled = t.unroll()
986 if unrolled.isUnion():
987 # UnionConversions.h includes UnionTypes.h
988 bindingHeaders.add("mozilla/dom/UnionConversions.h")
989 if dictionary:
990 # Our dictionary definition is in the header and
991 # needs the union type.
992 declareIncludes.add("mozilla/dom/UnionTypes.h")
993 elif unrolled.isDate():
994 if dictionary or jsImplementedDescriptors:
995 declareIncludes.add("mozilla/dom/Date.h")
996 else:
997 bindingHeaders.add("mozilla/dom/Date.h")
998 elif unrolled.isInterface():
999 if unrolled.isSpiderMonkeyInterface():
1000 bindingHeaders.add("jsfriendapi.h")
1001 headerSet.add("mozilla/dom/TypedArray.h")
1002 else:
1003 providers = getRelevantProviders(descriptor, config)
1004 for p in providers:
1005 try:
1006 typeDesc = p.getDescriptor(unrolled.inner.identifier.name)
1007 except NoSuchDescriptorError:
1008 continue
1009 # Dictionaries with interface members rely on the
1010 # actual class definition of that interface member
1011 # being visible in the binding header, because they
1012 # store them in nsRefPtr and have inline
1013 # constructors/destructors.
1014 #
1015 # XXXbz maybe dictionaries with interface members
1016 # should just have out-of-line constructors and
1017 # destructors?
1018 headerSet.add(typeDesc.headerFile)
1019 elif unrolled.isDictionary():
1020 headerSet.add(self.getDeclarationFilename(unrolled.inner))
1021 elif unrolled.isCallback():
1022 # Callbacks are both a type and an object
1023 headerSet.add(self.getDeclarationFilename(unrolled))
1024 elif unrolled.isFloat() and not unrolled.isUnrestricted():
1025 # Restricted floats are tested for finiteness
1026 bindingHeaders.add("mozilla/FloatingPoint.h")
1027 bindingHeaders.add("mozilla/dom/PrimitiveConversions.h")
1028 elif unrolled.isEnum():
1029 filename = self.getDeclarationFilename(unrolled.inner)
1030 declareIncludes.add(filename)
1031 elif unrolled.isPrimitive():
1032 bindingHeaders.add("mozilla/dom/PrimitiveConversions.h")
1033 elif unrolled.isMozMap():
1034 if dictionary or jsImplementedDescriptors:
1035 declareIncludes.add("mozilla/dom/MozMap.h")
1036 else:
1037 bindingHeaders.add("mozilla/dom/MozMap.h")
1038 # Also add headers for the type the MozMap is
1039 # parametrized over, if needed.
1040 addHeadersForType((t.inner, descriptor, dictionary))
1042 map(addHeadersForType,
1043 getAllTypes(descriptors + callbackDescriptors, dictionaries,
1044 callbacks))
1046 # Now make sure we're not trying to include the header from inside itself
1047 declareIncludes.discard(prefix + ".h")
1049 # Now for non-callback descriptors make sure we include any
1050 # headers needed by Func declarations.
1051 for desc in descriptors:
1052 if desc.interface.isExternal():
1053 continue
1055 def addHeaderForFunc(func):
1056 if func is None:
1057 return
1058 # Include the right class header, which we can only do
1059 # if this is a class member function.
1060 if not desc.headerIsDefault:
1061 # An explicit header file was provided, assume that we know
1062 # what we're doing.
1063 return
1065 if "::" in func:
1066 # Strip out the function name and convert "::" to "/"
1067 bindingHeaders.add("/".join(func.split("::")[:-1]) + ".h")
1069 for m in desc.interface.members:
1070 addHeaderForFunc(PropertyDefiner.getStringAttr(m, "Func"))
1071 # getExtendedAttribute() returns a list, extract the entry.
1072 funcList = desc.interface.getExtendedAttribute("Func")
1073 if funcList is not None:
1074 addHeaderForFunc(funcList[0])
1076 for d in dictionaries:
1077 if d.parent:
1078 declareIncludes.add(self.getDeclarationFilename(d.parent))
1079 bindingHeaders.add(self.getDeclarationFilename(d))
1081 for c in callbacks:
1082 bindingHeaders.add(self.getDeclarationFilename(c))
1084 for c in callbackDescriptors:
1085 bindingHeaders.add(self.getDeclarationFilename(c.interface))
1087 if len(callbacks) != 0:
1088 # We need CallbackFunction to serve as our parent class
1089 declareIncludes.add("mozilla/dom/CallbackFunction.h")
1090 # And we need BindingUtils.h so we can wrap "this" objects
1091 declareIncludes.add("mozilla/dom/BindingUtils.h")
1093 if len(callbackDescriptors) != 0 or len(jsImplementedDescriptors) != 0:
1094 # We need CallbackInterface to serve as our parent class
1095 declareIncludes.add("mozilla/dom/CallbackInterface.h")
1096 # And we need BindingUtils.h so we can wrap "this" objects
1097 declareIncludes.add("mozilla/dom/BindingUtils.h")
1099 # Also need to include the headers for ancestors of
1100 # JS-implemented interfaces.
1101 for jsImplemented in jsImplementedDescriptors:
1102 jsParent = jsImplemented.interface.parent
1103 if jsParent:
1104 parentDesc = jsImplemented.getDescriptor(jsParent.identifier.name)
1105 declareIncludes.add(parentDesc.jsImplParentHeader)
1107 # Let the machinery do its thing.
1108 def _includeString(includes):
1109 return ''.join(['#include "%s"\n' % i for i in includes]) + '\n'
1110 CGWrapper.__init__(self, child,
1111 declarePre=_includeString(sorted(declareIncludes)),
1112 definePre=_includeString(sorted(set(defineIncludes) |
1113 bindingIncludes |
1114 bindingHeaders |
1115 hasInstanceIncludes |
1116 implementationIncludes)))
1118 @staticmethod
1119 def getDeclarationFilename(decl):
1120 # Use our local version of the header, not the exported one, so that
1121 # test bindings, which don't export, will work correctly.
1122 basename = os.path.basename(decl.filename())
1123 return basename.replace('.webidl', 'Binding.h')
1126 def SortedDictValues(d):
1127 """
1128 Returns a list of values from the dict sorted by key.
1129 """
1130 return [v for k, v in sorted(d.items())]
1133 def UnionTypes(descriptors, dictionaries, callbacks, config):
1134 """
1135 Returns a tuple containing a set of header filenames to include in
1136 UnionTypes.h, a set of header filenames to include in UnionTypes.cpp, a set
1137 of tuples containing a type declaration and a boolean if the type is a
1138 struct for member types of the unions and a CGList containing CGUnionStructs
1139 for every union.
1140 """
1142 # Now find all the things we'll need as arguments and return values because
1143 # we need to wrap or unwrap them.
1144 headers = set()
1145 implheaders = set(["UnionTypes.h"])
1146 declarations = set()
1147 unionStructs = dict()
1148 owningUnionStructs = dict()
1150 for t, descriptor, dictionary in getAllTypes(descriptors, dictionaries, callbacks):
1151 # Add info for the given type. descriptor and dictionary, if present, are
1152 # used to figure out what to do with interface types.
1153 assert not descriptor or not dictionary
1155 t = t.unroll()
1156 while t.isMozMap():
1157 t = t.inner.unroll()
1158 if not t.isUnion():
1159 continue
1160 name = str(t)
1161 if name not in unionStructs:
1162 providers = getRelevantProviders(descriptor, config)
1163 # FIXME: Unions are broken in workers. See bug 809899.
1164 unionStructs[name] = CGUnionStruct(t, providers[0])
1165 owningUnionStructs[name] = CGUnionStruct(t, providers[0],
1166 ownsMembers=True)
1168 def addHeadersForType(f):
1169 if f.nullable():
1170 headers.add("mozilla/dom/Nullable.h")
1171 f = f.unroll()
1172 if f.isInterface():
1173 if f.isSpiderMonkeyInterface():
1174 headers.add("jsfriendapi.h")
1175 headers.add("mozilla/dom/TypedArray.h")
1176 else:
1177 for p in providers:
1178 try:
1179 typeDesc = p.getDescriptor(f.inner.identifier.name)
1180 except NoSuchDescriptorError:
1181 continue
1182 if typeDesc.interface.isCallback():
1183 # Callback interfaces always use strong refs, so
1184 # we need to include the right header to be able
1185 # to Release() in our inlined code.
1186 headers.add(typeDesc.headerFile)
1187 else:
1188 declarations.add((typeDesc.nativeType, False))
1189 implheaders.add(typeDesc.headerFile)
1190 elif f.isDictionary():
1191 # For a dictionary, we need to see its declaration in
1192 # UnionTypes.h so we have its sizeof and know how big to
1193 # make our union.
1194 headers.add(CGHeaders.getDeclarationFilename(f.inner))
1195 # And if it needs rooting, we need RootedDictionary too
1196 if typeNeedsRooting(f):
1197 headers.add("mozilla/dom/RootedDictionary.h")
1198 elif f.isEnum():
1199 # Need to see the actual definition of the enum,
1200 # unfortunately.
1201 headers.add(CGHeaders.getDeclarationFilename(f.inner))
1202 elif f.isCallback():
1203 # Callbacks always use strong refs, so we need to include
1204 # the right header to be able to Release() in our inlined
1205 # code.
1206 headers.add(CGHeaders.getDeclarationFilename(f))
1207 elif f.isMozMap():
1208 headers.add("mozilla/dom/MozMap.h")
1209 # And add headers for the type we're parametrized over
1210 addHeadersForType(f.inner)
1212 for f in t.flatMemberTypes:
1213 assert not f.nullable()
1214 addHeadersForType(f)
1216 return (headers, implheaders, declarations,
1217 CGList(SortedDictValues(unionStructs) +
1218 SortedDictValues(owningUnionStructs),
1219 "\n"))
1222 def UnionConversions(descriptors, dictionaries, callbacks, config):
1223 """
1224 Returns a CGThing to declare all union argument conversion helper structs.
1225 """
1226 # Now find all the things we'll need as arguments because we
1227 # need to unwrap them.
1228 headers = set()
1229 unionConversions = dict()
1231 for t, descriptor, dictionary in getAllTypes(descriptors, dictionaries, callbacks):
1232 # Add info for the given type. descriptor and dictionary, if passed, are
1233 # used to figure out what to do with interface types.
1234 assert not descriptor or not dictionary
1236 t = t.unroll()
1237 if not t.isUnion():
1238 continue
1239 name = str(t)
1240 if name not in unionConversions:
1241 providers = getRelevantProviders(descriptor, config)
1242 unionConversions[name] = CGUnionConversionStruct(t, providers[0])
1243 def addHeadersForType(f, providers):
1244 f = f.unroll()
1245 if f.isInterface():
1246 if f.isSpiderMonkeyInterface():
1247 headers.add("jsfriendapi.h")
1248 headers.add("mozilla/dom/TypedArray.h")
1249 elif f.inner.isExternal():
1250 providers = getRelevantProviders(descriptor, config)
1251 for p in providers:
1252 try:
1253 typeDesc = p.getDescriptor(f.inner.identifier.name)
1254 except NoSuchDescriptorError:
1255 continue
1256 headers.add(typeDesc.headerFile)
1257 else:
1258 headers.add(CGHeaders.getDeclarationFilename(f.inner))
1259 # Check for whether we have a possibly-XPConnect-implemented
1260 # interface. If we do, the right descriptor will come from
1261 # providers[0], because that would be the non-worker
1262 # descriptor provider, if we have one at all.
1263 if (f.isGeckoInterface() and
1264 providers[0].getDescriptor(f.inner.identifier.name).hasXPConnectImpls):
1265 headers.add("nsDOMQS.h")
1266 elif f.isDictionary():
1267 headers.add(CGHeaders.getDeclarationFilename(f.inner))
1268 elif f.isPrimitive():
1269 headers.add("mozilla/dom/PrimitiveConversions.h")
1270 elif f.isMozMap():
1271 headers.add("mozilla/dom/MozMap.h")
1272 # And the internal type of the MozMap
1273 addHeadersForType(f.inner, providers)
1275 for f in t.flatMemberTypes:
1276 addHeadersForType(f, providers)
1278 return (headers,
1279 CGWrapper(CGList(SortedDictValues(unionConversions), "\n"),
1280 post="\n\n"))
1283 class Argument():
1284 """
1285 A class for outputting the type and name of an argument
1286 """
1287 def __init__(self, argType, name, default=None):
1288 self.argType = argType
1289 self.name = name
1290 self.default = default
1292 def declare(self):
1293 string = self.argType + ' ' + self.name
1294 if self.default is not None:
1295 string += " = " + self.default
1296 return string
1298 def define(self):
1299 return self.argType + ' ' + self.name
1302 class CGAbstractMethod(CGThing):
1303 """
1304 An abstract class for generating code for a method. Subclasses
1305 should override definition_body to create the actual code.
1307 descriptor is the descriptor for the interface the method is associated with
1309 name is the name of the method as a string
1311 returnType is the IDLType of the return value
1313 args is a list of Argument objects
1315 inline should be True to generate an inline method, whose body is
1316 part of the declaration.
1318 alwaysInline should be True to generate an inline method annotated with
1319 MOZ_ALWAYS_INLINE.
1321 static should be True to generate a static method, which only has
1322 a definition.
1324 If templateArgs is not None it should be a list of strings containing
1325 template arguments, and the function will be templatized using those
1326 arguments.
1327 """
1328 def __init__(self, descriptor, name, returnType, args, inline=False, alwaysInline=False, static=False, templateArgs=None):
1329 CGThing.__init__(self)
1330 self.descriptor = descriptor
1331 self.name = name
1332 self.returnType = returnType
1333 self.args = args
1334 self.inline = inline
1335 self.alwaysInline = alwaysInline
1336 self.static = static
1337 self.templateArgs = templateArgs
1339 def _argstring(self, declare):
1340 return ', '.join([a.declare() if declare else a.define() for a in self.args])
1342 def _template(self):
1343 if self.templateArgs is None:
1344 return ''
1345 return 'template <%s>\n' % ', '.join(self.templateArgs)
1347 def _decorators(self):
1348 decorators = []
1349 if self.alwaysInline:
1350 decorators.append('MOZ_ALWAYS_INLINE')
1351 elif self.inline:
1352 decorators.append('inline')
1353 if self.static:
1354 decorators.append('static')
1355 decorators.append(self.returnType)
1356 maybeNewline = " " if self.inline else "\n"
1357 return ' '.join(decorators) + maybeNewline
1359 def declare(self):
1360 if self.inline:
1361 return self._define(True)
1362 return "%s%s%s(%s);\n" % (self._template(), self._decorators(), self.name, self._argstring(True))
1364 def _define(self, fromDeclare=False):
1365 return self.definition_prologue(fromDeclare) + self.definition_body() + self.definition_epilogue()
1367 def define(self):
1368 return "" if self.inline else self._define()
1370 def definition_prologue(self, fromDeclare):
1371 return "%s%s%s(%s)\n{\n" % (self._template(), self._decorators(),
1372 self.name, self._argstring(fromDeclare))
1374 def definition_epilogue(self):
1375 return "}\n"
1377 def definition_body(self):
1378 assert False # Override me!
1381 class CGAbstractStaticMethod(CGAbstractMethod):
1382 """
1383 Abstract base class for codegen of implementation-only (no
1384 declaration) static methods.
1385 """
1386 def __init__(self, descriptor, name, returnType, args):
1387 CGAbstractMethod.__init__(self, descriptor, name, returnType, args,
1388 inline=False, static=True)
1390 def declare(self):
1391 # We only have implementation
1392 return ""
1395 class CGAbstractClassHook(CGAbstractStaticMethod):
1396 """
1397 Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw
1398 'this' unwrapping as it assumes that the unwrapped type is always known.
1399 """
1400 def __init__(self, descriptor, name, returnType, args):
1401 CGAbstractStaticMethod.__init__(self, descriptor, name, returnType,
1402 args)
1404 def definition_body_prologue(self):
1405 return ("\n" # BOGUS extra blank line at start of function
1406 " %s* self = UnwrapDOMObject<%s>(obj);\n" %
1407 (self.descriptor.nativeType, self.descriptor.nativeType))
1409 def definition_body(self):
1410 return self.definition_body_prologue() + self.generate_code()
1412 def generate_code(self):
1413 assert False # Override me!
1416 class CGGetJSClassMethod(CGAbstractMethod):
1417 def __init__(self, descriptor):
1418 CGAbstractMethod.__init__(self, descriptor, 'GetJSClass', 'const JSClass*',
1419 [])
1421 def definition_body(self):
1422 return " return Class.ToJSClass();\n"
1425 class CGAddPropertyHook(CGAbstractClassHook):
1426 """
1427 A hook for addProperty, used to preserve our wrapper from GC.
1428 """
1429 def __init__(self, descriptor):
1430 args = [Argument('JSContext*', 'cx'),
1431 Argument('JS::Handle<JSObject*>', 'obj'),
1432 Argument('JS::Handle<jsid>', 'id'),
1433 Argument('JS::MutableHandle<JS::Value>', 'vp')]
1434 CGAbstractClassHook.__init__(self, descriptor, ADDPROPERTY_HOOK_NAME,
1435 'bool', args)
1437 def generate_code(self):
1438 assert self.descriptor.wrapperCache
1439 return indent(dedent("""
1440 // We don't want to preserve if we don't have a wrapper.
1441 if (self->GetWrapperPreserveColor()) {
1442 PreserveWrapper(self);
1443 }
1444 return true;
1445 """))
1448 def DeferredFinalizeSmartPtr(descriptor):
1449 if descriptor.nativeOwnership == 'owned':
1450 smartPtr = 'nsAutoPtr'
1451 else:
1452 smartPtr = 'nsRefPtr'
1453 return smartPtr
1456 def finalizeHook(descriptor, hookName, freeOp):
1457 finalize = "JSBindingFinalized<%s>::Finalized(self);\n" % descriptor.nativeType
1458 if descriptor.wrapperCache:
1459 finalize += "ClearWrapper(self, self);\n"
1460 if descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
1461 finalize += "self->mExpandoAndGeneration.expando = JS::UndefinedValue();\n"
1462 if descriptor.interface.getExtendedAttribute("Global"):
1463 finalize += "mozilla::dom::FinalizeGlobal(CastToJSFreeOp(%s), obj);\n" % freeOp
1464 finalize += ("AddForDeferredFinalization<%s, %s >(self);\n" %
1465 (descriptor.nativeType, DeferredFinalizeSmartPtr(descriptor)))
1466 return CGIfWrapper(CGGeneric(finalize), "self")
1469 class CGClassFinalizeHook(CGAbstractClassHook):
1470 """
1471 A hook for finalize, used to release our native object.
1472 """
1473 def __init__(self, descriptor):
1474 args = [Argument('js::FreeOp*', 'fop'), Argument('JSObject*', 'obj')]
1475 CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME,
1476 'void', args)
1478 def generate_code(self):
1479 return indent(finalizeHook(self.descriptor, self.name, self.args[0].name).define())
1482 class CGClassConstructor(CGAbstractStaticMethod):
1483 """
1484 JS-visible constructor for our objects
1485 """
1486 def __init__(self, descriptor, ctor, name=CONSTRUCT_HOOK_NAME):
1487 args = [Argument('JSContext*', 'cx'),
1488 Argument('unsigned', 'argc'),
1489 Argument('JS::Value*', 'vp')]
1490 CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args)
1491 self._ctor = ctor
1493 def define(self):
1494 if not self._ctor:
1495 return ""
1496 return CGAbstractStaticMethod.define(self)
1498 def definition_body(self):
1499 return self.generate_code()
1501 def generate_code(self):
1502 # [ChromeOnly] interfaces may only be constructed by chrome.
1503 chromeOnlyCheck = ""
1504 if isChromeOnly(self._ctor):
1505 chromeOnlyCheck = dedent("""
1506 if (!nsContentUtils::ThreadsafeIsCallerChrome()) {
1507 return ThrowingConstructor(cx, argc, vp);
1508 }
1510 """)
1512 # Additionally, we want to throw if a caller does a bareword invocation
1513 # of a constructor without |new|. We don't enforce this for chrome in
1514 # realease builds to avoid the addon compat fallout of making that
1515 # change. See bug 916644.
1516 #
1517 # Figure out the name of our constructor for error reporting purposes.
1518 # For unnamed webidl constructors, identifier.name is "constructor" but
1519 # the name JS sees is the interface name; for named constructors
1520 # identifier.name is the actual name.
1521 name = self._ctor.identifier.name
1522 if name != "constructor":
1523 ctorName = name
1524 else:
1525 ctorName = self.descriptor.interface.identifier.name
1527 preamble = fill( # BOGUS extra blank line at beginning of function body
1528 """
1530 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
1531 JS::Rooted<JSObject*> obj(cx, &args.callee());
1532 $*{chromeOnlyCheck}
1533 bool mayInvoke = args.isConstructing();
1534 #ifdef RELEASE_BUILD
1535 mayInvoke = mayInvoke || nsContentUtils::ThreadsafeIsCallerChrome();
1536 #endif // RELEASE_BUILD
1537 if (!mayInvoke) {
1538 // XXXbz wish I could get the name from the callee instead of
1539 // Adding more relocations
1540 return ThrowConstructorWithoutNew(cx, "${ctorName}");
1541 }
1542 """,
1543 chromeOnlyCheck=chromeOnlyCheck,
1544 ctorName=ctorName)
1546 name = self._ctor.identifier.name
1547 nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
1548 callGenerator = CGMethodCall(nativeName, True, self.descriptor,
1549 self._ctor, isConstructor=True,
1550 constructorName=ctorName)
1551 return indent(preamble) + callGenerator.define()
1554 # Encapsulate the constructor in a helper method to share genConstructorBody with CGJSImplMethod.
1555 class CGConstructNavigatorObjectHelper(CGAbstractStaticMethod):
1556 """
1557 Construct a new JS-implemented WebIDL DOM object, for use on navigator.
1558 """
1559 def __init__(self, descriptor):
1560 name = "ConstructNavigatorObjectHelper"
1561 args = [Argument('JSContext*', 'cx'),
1562 Argument('GlobalObject&', 'global'),
1563 Argument('ErrorResult&', 'aRv')]
1564 rtype = 'already_AddRefed<%s>' % descriptor.name
1565 CGAbstractStaticMethod.__init__(self, descriptor, name, rtype, args)
1567 def definition_body(self):
1568 return indent(genConstructorBody(self.descriptor))
1571 class CGConstructNavigatorObject(CGAbstractMethod):
1572 """
1573 Wrap a JS-implemented WebIDL object into a JS value, for use on navigator.
1574 """
1575 def __init__(self, descriptor):
1576 name = 'ConstructNavigatorObject'
1577 args = [Argument('JSContext*', 'aCx'), Argument('JS::Handle<JSObject*>', 'aObj')]
1578 CGAbstractMethod.__init__(self, descriptor, name, 'JSObject*', args)
1580 def definition_body(self):
1581 if not self.descriptor.interface.isJSImplemented():
1582 raise TypeError("Only JS-implemented classes are currently supported "
1583 "on navigator. See bug 856820.")
1584 return indent(fill(
1585 """
1586 GlobalObject global(aCx, aObj);
1587 if (global.Failed()) {
1588 return nullptr;
1589 }
1590 ErrorResult rv;
1591 JS::Rooted<JS::Value> v(aCx);
1592 { // Scope to make sure |result| goes out of scope while |v| is rooted
1593 nsRefPtr<mozilla::dom::${descriptorName}> result = ConstructNavigatorObjectHelper(aCx, global, rv);
1594 rv.WouldReportJSException();
1595 if (rv.Failed()) {
1596 ThrowMethodFailedWithDetails(aCx, rv, "${descriptorName}", "navigatorConstructor");
1597 return nullptr;
1598 }
1599 if (!WrapNewBindingObject(aCx, result, &v)) {
1600 //XXX Assertion disabled for now, see bug 991271.
1601 MOZ_ASSERT(true || JS_IsExceptionPending(aCx));
1602 return nullptr;
1603 }
1604 }
1605 return &v.toObject();
1606 """,
1607 descriptorName=self.descriptor.name))
1610 class CGClassConstructHookHolder(CGGeneric):
1611 def __init__(self, descriptor):
1612 if descriptor.interface.ctor():
1613 constructHook = CONSTRUCT_HOOK_NAME
1614 else:
1615 constructHook = "ThrowingConstructor"
1616 CGGeneric.__init__(self, fill(
1617 """
1618 static const JSNativeHolder ${CONSTRUCT_HOOK_NAME}_holder = {
1619 ${constructHook},
1620 ${hooks}
1621 };
1622 """,
1623 CONSTRUCT_HOOK_NAME=CONSTRUCT_HOOK_NAME,
1624 constructHook=constructHook,
1625 hooks=NativePropertyHooks(descriptor)))
1628 def NamedConstructorName(m):
1629 return '_' + m.identifier.name
1632 class CGNamedConstructors(CGThing):
1633 def __init__(self, descriptor):
1634 self.descriptor = descriptor
1635 CGThing.__init__(self)
1637 def declare(self):
1638 return ""
1640 def define(self):
1641 if len(self.descriptor.interface.namedConstructors) == 0:
1642 return ""
1644 constructorID = "constructors::id::"
1645 if self.descriptor.interface.hasInterfaceObject():
1646 constructorID += self.descriptor.name
1647 else:
1648 constructorID += "_ID_Count"
1650 namedConstructors = ""
1651 for n in self.descriptor.interface.namedConstructors:
1652 namedConstructors += (
1653 "{ \"%s\", { %s, &sNamedConstructorNativePropertyHooks }, %i },\n" %
1654 (n.identifier.name, NamedConstructorName(n), methodLength(n)))
1656 return fill(
1657 """
1658 const NativePropertyHooks sNamedConstructorNativePropertyHooks = {
1659 nullptr,
1660 nullptr,
1661 { nullptr, nullptr },
1662 prototypes::id::${name},
1663 ${constructorID},
1664 nullptr
1665 };
1667 static const NamedConstructor namedConstructors[] = {
1668 $*{namedConstructors}
1669 { nullptr, { nullptr, nullptr }, 0 }
1670 };
1671 """,
1672 name=self.descriptor.name,
1673 constructorID=constructorID,
1674 namedConstructors=namedConstructors)
1677 class CGClassHasInstanceHook(CGAbstractStaticMethod):
1678 def __init__(self, descriptor):
1679 args = [Argument('JSContext*', 'cx'),
1680 Argument('JS::Handle<JSObject*>', 'obj'),
1681 Argument('JS::MutableHandle<JS::Value>', 'vp'),
1682 Argument('bool*', 'bp')]
1683 CGAbstractStaticMethod.__init__(self, descriptor, HASINSTANCE_HOOK_NAME,
1684 'bool', args)
1686 def define(self):
1687 if not NeedsGeneratedHasInstance(self.descriptor):
1688 return ""
1689 return CGAbstractStaticMethod.define(self)
1691 def definition_body(self):
1692 return self.generate_code()
1694 def generate_code(self):
1695 # BOGUS extra blank line at start of function
1696 header = dedent("""
1698 if (!vp.isObject()) {
1699 *bp = false;
1700 return true;
1701 }
1703 JS::Rooted<JSObject*> instance(cx, &vp.toObject());
1704 """)
1705 if self.descriptor.interface.hasInterfacePrototypeObject():
1706 return indent(
1707 header +
1708 fill(
1709 """
1711 static_assert(IsBaseOf<nsISupports, ${nativeType}>::value,
1712 "HasInstance only works for nsISupports-based classes.");
1714 bool ok = InterfaceHasInstance(cx, obj, instance, bp);
1715 if (!ok || *bp) {
1716 return ok;
1717 }
1719 // FIXME Limit this to chrome by checking xpc::AccessCheck::isChrome(obj).
1720 nsISupports* native =
1721 nsContentUtils::XPConnect()->GetNativeOfWrapper(cx,
1722 js::UncheckedUnwrap(instance));
1723 nsCOMPtr<nsIDOM${name}> qiResult = do_QueryInterface(native);
1724 *bp = !!qiResult;
1725 return true;
1727 """, # BOGUS extra blank line at end of function
1728 nativeType=self.descriptor.nativeType,
1729 name=self.descriptor.interface.identifier.name))
1731 hasInstanceCode = dedent("""
1733 const DOMClass* domClass = GetDOMClass(js::UncheckedUnwrap(instance));
1734 *bp = false;
1735 if (!domClass) {
1736 // Not a DOM object, so certainly not an instance of this interface
1737 return true;
1738 }
1739 """)
1740 if self.descriptor.interface.identifier.name == "ChromeWindow":
1741 setBp = "*bp = UnwrapDOMObject<nsGlobalWindow>(js::UncheckedUnwrap(instance))->IsChromeWindow()"
1742 else:
1743 setBp = "*bp = true"
1744 # Sort interaces implementing self by name so we get stable output.
1745 for iface in sorted(self.descriptor.interface.interfacesImplementingSelf,
1746 key=lambda iface: iface.identifier.name):
1747 hasInstanceCode += fill(
1748 """
1750 if (domClass->mInterfaceChain[PrototypeTraits<prototypes::id::${name}>::Depth] == prototypes::id::${name}) {
1751 ${setBp};
1752 return true;
1753 }
1754 """,
1755 name=iface.identifier.name,
1756 setBp=setBp)
1757 hasInstanceCode += "return true;\n"
1758 return indent(header + hasInstanceCode)
1761 def isChromeOnly(m):
1762 return m.getExtendedAttribute("ChromeOnly")
1765 def getAvailableInTestFunc(obj):
1766 availableIn = obj.getExtendedAttribute("AvailableIn")
1767 if availableIn is None:
1768 return None
1769 assert isinstance(availableIn, list) and len(availableIn) == 1
1770 if availableIn[0] == "PrivilegedApps":
1771 return "IsInPrivilegedApp"
1772 if availableIn[0] == "CertifiedApps":
1773 return "IsInCertifiedApp"
1774 raise TypeError("Unknown AvailableIn value '%s'" % availableIn[0])
1777 class MemberCondition:
1778 """
1779 An object representing the condition for a member to actually be
1780 exposed. Any of pref, func, and available can be None. If not
1781 None, they should be strings that have the pref name (for "pref")
1782 or function name (for "func" and "available").
1783 """
1784 def __init__(self, pref, func, available=None):
1785 assert pref is None or isinstance(pref, str)
1786 assert func is None or isinstance(func, str)
1787 assert available is None or isinstance(available, str)
1788 self.pref = pref
1790 def toFuncPtr(val):
1791 if val is None:
1792 return "nullptr"
1793 return "&" + val
1794 self.func = toFuncPtr(func)
1795 self.available = toFuncPtr(available)
1797 def __eq__(self, other):
1798 return (self.pref == other.pref and self.func == other.func and
1799 self.available == other.available)
1801 def __ne__(self, other):
1802 return not self.__eq__(other)
1805 class PropertyDefiner:
1806 """
1807 A common superclass for defining things on prototype objects.
1809 Subclasses should implement generateArray to generate the actual arrays of
1810 things we're defining. They should also set self.chrome to the list of
1811 things only exposed to chrome and self.regular to the list of things exposed
1812 to both chrome and web pages.
1813 """
1814 def __init__(self, descriptor, name):
1815 self.descriptor = descriptor
1816 self.name = name
1817 # self.prefCacheData will store an array of (prefname, bool*)
1818 # pairs for our bool var caches. generateArray will fill it
1819 # in as needed.
1820 self.prefCacheData = []
1822 def hasChromeOnly(self):
1823 return len(self.chrome) > 0
1825 def hasNonChromeOnly(self):
1826 return len(self.regular) > 0
1828 def variableName(self, chrome):
1829 if chrome:
1830 if self.hasChromeOnly():
1831 return "sChrome" + self.name
1832 else:
1833 if self.hasNonChromeOnly():
1834 return "s" + self.name
1835 return "nullptr"
1837 def usedForXrays(self):
1838 # No Xrays in workers.
1839 return not self.descriptor.workers
1841 def __str__(self):
1842 # We only need to generate id arrays for things that will end
1843 # up used via ResolveProperty or EnumerateProperties.
1844 str = self.generateArray(self.regular, self.variableName(False),
1845 self.usedForXrays())
1846 if self.hasChromeOnly():
1847 str += self.generateArray(self.chrome, self.variableName(True),
1848 self.usedForXrays())
1849 return str
1851 @staticmethod
1852 def getStringAttr(member, name):
1853 attr = member.getExtendedAttribute(name)
1854 if attr is None:
1855 return None
1856 # It's a list of strings
1857 assert len(attr) == 1
1858 assert attr[0] is not None
1859 return attr[0]
1861 @staticmethod
1862 def getControllingCondition(interfaceMember):
1863 return MemberCondition(PropertyDefiner.getStringAttr(interfaceMember,
1864 "Pref"),
1865 PropertyDefiner.getStringAttr(interfaceMember,
1866 "Func"),
1867 getAvailableInTestFunc(interfaceMember))
1869 def generatePrefableArray(self, array, name, specTemplate, specTerminator,
1870 specType, getCondition, getDataTuple, doIdArrays):
1871 """
1872 This method generates our various arrays.
1874 array is an array of interface members as passed to generateArray
1876 name is the name as passed to generateArray
1878 specTemplate is a template for each entry of the spec array
1880 specTerminator is a terminator for the spec array (inserted every time
1881 our controlling pref changes and at the end of the array)
1883 specType is the actual typename of our spec
1885 getCondition is a callback function that takes an array entry and
1886 returns the corresponding MemberCondition.
1888 getDataTuple is a callback function that takes an array entry and
1889 returns a tuple suitable for substitution into specTemplate.
1890 """
1892 # We want to generate a single list of specs, but with specTerminator
1893 # inserted at every point where the pref name controlling the member
1894 # changes. That will make sure the order of the properties as exposed
1895 # on the interface and interface prototype objects does not change when
1896 # pref control is added to members while still allowing us to define all
1897 # the members in the smallest number of JSAPI calls.
1898 assert len(array) != 0
1899 lastCondition = getCondition(array[0]) # So we won't put a specTerminator
1900 # at the very front of the list.
1901 specs = []
1902 prefableSpecs = []
1904 prefableTemplate = ' { true, %s, %s, &%s[%d] }'
1905 prefCacheTemplate = '&%s[%d].enabled'
1907 def switchToCondition(props, condition):
1908 # Remember the info about where our pref-controlled
1909 # booleans live.
1910 if condition.pref is not None:
1911 props.prefCacheData.append(
1912 (condition.pref,
1913 prefCacheTemplate % (name, len(prefableSpecs))))
1914 # Set up pointers to the new sets of specs inside prefableSpecs
1915 prefableSpecs.append(prefableTemplate %
1916 (condition.func,
1917 condition.available,
1918 name + "_specs", len(specs)))
1920 switchToCondition(self, lastCondition)
1922 for member in array:
1923 curCondition = getCondition(member)
1924 if lastCondition != curCondition:
1925 # Terminate previous list
1926 specs.append(specTerminator)
1927 # And switch to our new pref
1928 switchToCondition(self, curCondition)
1929 lastCondition = curCondition
1930 # And the actual spec
1931 specs.append(specTemplate % getDataTuple(member))
1932 specs.append(specTerminator)
1933 prefableSpecs.append(" { false, nullptr }")
1935 specType = "const " + specType
1936 arrays = fill(
1937 """
1938 static ${specType} ${name}_specs[] = {
1939 ${specs}
1940 };
1942 // Can't be const because the pref-enabled boolean needs to be writable
1943 static Prefable<${specType}> ${name}[] = {
1944 ${prefableSpecs}
1945 };
1947 """,
1948 specType=specType,
1949 name=name,
1950 specs=',\n'.join(specs),
1951 prefableSpecs=',\n'.join(prefableSpecs))
1952 if doIdArrays:
1953 arrays += "static jsid %s_ids[%i];\n\n" % (name, len(specs))
1954 return arrays
1957 # The length of a method is the minimum of the lengths of the
1958 # argument lists of all its overloads.
1959 def overloadLength(arguments):
1960 i = len(arguments)
1961 while i > 0 and arguments[i - 1].optional:
1962 i -= 1
1963 return i
1966 def methodLength(method):
1967 signatures = method.signatures()
1968 return min(overloadLength(arguments) for retType, arguments in signatures)
1971 class MethodDefiner(PropertyDefiner):
1972 """
1973 A class for defining methods on a prototype object.
1974 """
1975 def __init__(self, descriptor, name, static):
1976 PropertyDefiner.__init__(self, descriptor, name)
1978 # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822
1979 # We should be able to check for special operations without an
1980 # identifier. For now we check if the name starts with __
1982 # Ignore non-static methods for interfaces without a proto object
1983 if descriptor.interface.hasInterfacePrototypeObject() or static:
1984 methods = [m for m in descriptor.interface.members if
1985 m.isMethod() and m.isStatic() == static and
1986 not m.isIdentifierLess()]
1987 else:
1988 methods = []
1989 self.chrome = []
1990 self.regular = []
1991 for m in methods:
1992 if m.identifier.name == 'queryInterface':
1993 if self.descriptor.workers:
1994 continue
1995 if m.isStatic():
1996 raise TypeError("Legacy queryInterface member shouldn't be static")
1997 signatures = m.signatures()
1999 def argTypeIsIID(arg):
2000 return arg.type.inner.isExternal() and arg.type.inner.identifier.name == 'IID'
2001 if len(signatures) > 1 or len(signatures[0][1]) > 1 or not argTypeIsIID(signatures[0][1][0]):
2002 raise TypeError("There should be only one queryInterface method with 1 argument of type IID")
2004 # Make sure to not stick QueryInterface on abstract interfaces that
2005 # have hasXPConnectImpls (like EventTarget). So only put it on
2006 # interfaces that are concrete and all of whose ancestors are abstract.
2007 def allAncestorsAbstract(iface):
2008 if not iface.parent:
2009 return True
2010 desc = self.descriptor.getDescriptor(iface.parent.identifier.name)
2011 if desc.concrete:
2012 return False
2013 return allAncestorsAbstract(iface.parent)
2014 if (not self.descriptor.interface.hasInterfacePrototypeObject() or
2015 not self.descriptor.concrete or
2016 not allAncestorsAbstract(self.descriptor.interface)):
2017 raise TypeError("QueryInterface is only supported on "
2018 "interfaces that are concrete and all "
2019 "of whose ancestors are abstract: " +
2020 self.descriptor.name)
2021 condition = "WantsQueryInterface<%s>::Enabled" % descriptor.nativeType
2022 self.regular.append({
2023 "name": 'QueryInterface',
2024 "methodInfo": False,
2025 "length": 1,
2026 "flags": "0",
2027 "condition": MemberCondition(None, condition)
2028 })
2029 continue
2031 method = {
2032 "name": m.identifier.name,
2033 "methodInfo": not m.isStatic(),
2034 "length": methodLength(m),
2035 "flags": "JSPROP_ENUMERATE",
2036 "condition": PropertyDefiner.getControllingCondition(m),
2037 "allowCrossOriginThis": m.getExtendedAttribute("CrossOriginCallable"),
2038 "returnsPromise": m.returnsPromise()
2039 }
2040 if isChromeOnly(m):
2041 self.chrome.append(method)
2042 else:
2043 self.regular.append(method)
2045 # FIXME Check for an existing iterator on the interface first.
2046 if any(m.isGetter() and m.isIndexed() for m in methods):
2047 self.regular.append({
2048 "name": "@@iterator",
2049 "methodInfo": False,
2050 "selfHostedName": "ArrayValues",
2051 "length": 0,
2052 "flags": "JSPROP_ENUMERATE",
2053 "condition": MemberCondition(None, None)
2054 })
2056 if not static:
2057 stringifier = descriptor.operations['Stringifier']
2058 if stringifier:
2059 toStringDesc = {
2060 "name": "toString",
2061 "nativeName": stringifier.identifier.name,
2062 "length": 0,
2063 "flags": "JSPROP_ENUMERATE",
2064 "condition": PropertyDefiner.getControllingCondition(stringifier)
2065 }
2066 if isChromeOnly(stringifier):
2067 self.chrome.append(toStringDesc)
2068 else:
2069 self.regular.append(toStringDesc)
2070 jsonifier = descriptor.operations['Jsonifier']
2071 if jsonifier:
2072 toJSONDesc = {
2073 "name": "toJSON",
2074 "nativeName": jsonifier.identifier.name,
2075 "length": 0,
2076 "flags": "JSPROP_ENUMERATE",
2077 "condition": PropertyDefiner.getControllingCondition(jsonifier)
2078 }
2079 if isChromeOnly(jsonifier):
2080 self.chrome.append(toJSONDesc)
2081 else:
2082 self.regular.append(toJSONDesc)
2083 elif (descriptor.interface.isJSImplemented() and
2084 descriptor.interface.hasInterfaceObject()):
2085 self.chrome.append({
2086 "name": '_create',
2087 "nativeName": ("%s::_Create" % descriptor.name),
2088 "methodInfo": False,
2089 "length": 2,
2090 "flags": "0",
2091 "condition": MemberCondition(None, None)
2092 })
2094 if static:
2095 if not descriptor.interface.hasInterfaceObject():
2096 # static methods go on the interface object
2097 assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
2098 else:
2099 if not descriptor.interface.hasInterfacePrototypeObject():
2100 # non-static methods go on the interface prototype object
2101 assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
2103 def generateArray(self, array, name, doIdArrays):
2104 if len(array) == 0:
2105 return ""
2107 def condition(m):
2108 return m["condition"]
2110 def specData(m):
2111 if "selfHostedName" in m:
2112 selfHostedName = '"%s"' % m["selfHostedName"]
2113 assert not m.get("methodInfo", True)
2114 accessor = "nullptr"
2115 jitinfo = "nullptr"
2116 else:
2117 selfHostedName = "nullptr"
2118 accessor = m.get("nativeName", m["name"])
2119 if m.get("methodInfo", True):
2120 # Cast this in case the methodInfo is a
2121 # JSTypedMethodJitInfo.
2122 jitinfo = ("reinterpret_cast<const JSJitInfo*>(&%s_methodinfo)" % accessor)
2123 if m.get("allowCrossOriginThis", False):
2124 if m.get("returnsPromise", False):
2125 raise TypeError("%s returns a Promise but should "
2126 "be allowed cross-origin?" %
2127 accessor)
2128 accessor = "genericCrossOriginMethod"
2129 elif self.descriptor.needsSpecialGenericOps():
2130 if m.get("returnsPromise", False):
2131 raise TypeError("%s returns a Promise but needs "
2132 "special generic ops?" %
2133 accessor)
2134 accessor = "genericMethod"
2135 elif m.get("returnsPromise", False):
2136 accessor = "GenericPromiseReturningBindingMethod"
2137 else:
2138 accessor = "GenericBindingMethod"
2139 else:
2140 if m.get("returnsPromise", False):
2141 jitinfo = "&%s_methodinfo" % accessor
2142 accessor = "StaticMethodPromiseWrapper"
2143 else:
2144 jitinfo = "nullptr"
2146 return (m["name"], accessor, jitinfo, m["length"], m["flags"], selfHostedName)
2148 return self.generatePrefableArray(
2149 array, name,
2150 ' JS_FNSPEC("%s", %s, %s, %s, %s, %s)',
2151 ' JS_FS_END',
2152 'JSFunctionSpec',
2153 condition, specData, doIdArrays)
2156 class AttrDefiner(PropertyDefiner):
2157 def __init__(self, descriptor, name, static, unforgeable=False):
2158 assert not (static and unforgeable)
2159 PropertyDefiner.__init__(self, descriptor, name)
2160 self.name = name
2161 # Ignore non-static attributes for interfaces without a proto object
2162 if descriptor.interface.hasInterfacePrototypeObject() or static:
2163 attributes = [m for m in descriptor.interface.members if
2164 m.isAttr() and m.isStatic() == static and
2165 m.isUnforgeable() == unforgeable]
2166 else:
2167 attributes = []
2168 self.chrome = [m for m in attributes if isChromeOnly(m)]
2169 self.regular = [m for m in attributes if not isChromeOnly(m)]
2170 self.static = static
2171 self.unforgeable = unforgeable
2173 if static:
2174 if not descriptor.interface.hasInterfaceObject():
2175 # static attributes go on the interface object
2176 assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
2177 else:
2178 if not descriptor.interface.hasInterfacePrototypeObject():
2179 # non-static attributes go on the interface prototype object
2180 assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
2182 def generateArray(self, array, name, doIdArrays):
2183 if len(array) == 0:
2184 return ""
2186 def flags(attr):
2187 unforgeable = " | JSPROP_PERMANENT" if self.unforgeable else ""
2188 return ("JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS" +
2189 unforgeable)
2191 def getter(attr):
2192 if self.static:
2193 accessor = 'get_' + attr.identifier.name
2194 jitinfo = "nullptr"
2195 else:
2196 if attr.hasLenientThis():
2197 accessor = "genericLenientGetter"
2198 elif attr.getExtendedAttribute("CrossOriginReadable"):
2199 accessor = "genericCrossOriginGetter"
2200 elif self.descriptor.needsSpecialGenericOps():
2201 accessor = "genericGetter"
2202 else:
2203 accessor = "GenericBindingGetter"
2204 jitinfo = "&%s_getterinfo" % attr.identifier.name
2205 return "{ { JS_CAST_NATIVE_TO(%s, JSPropertyOp), %s } }" % \
2206 (accessor, jitinfo)
2208 def setter(attr):
2209 if (attr.readonly and
2210 attr.getExtendedAttribute("PutForwards") is None and
2211 attr.getExtendedAttribute("Replaceable") is None):
2212 return "JSOP_NULLWRAPPER"
2213 if self.static:
2214 accessor = 'set_' + attr.identifier.name
2215 jitinfo = "nullptr"
2216 else:
2217 if attr.hasLenientThis():
2218 accessor = "genericLenientSetter"
2219 elif attr.getExtendedAttribute("CrossOriginWritable"):
2220 accessor = "genericCrossOriginSetter"
2221 elif self.descriptor.needsSpecialGenericOps():
2222 accessor = "genericSetter"
2223 else:
2224 accessor = "GenericBindingSetter"
2225 jitinfo = "&%s_setterinfo" % attr.identifier.name
2226 return "{ { JS_CAST_NATIVE_TO(%s, JSStrictPropertyOp), %s } }" % \
2227 (accessor, jitinfo)
2229 def specData(attr):
2230 return (attr.identifier.name, flags(attr), getter(attr),
2231 setter(attr))
2233 return self.generatePrefableArray(
2234 array, name,
2235 ' { "%s", %s, %s, %s}',
2236 ' JS_PS_END',
2237 'JSPropertySpec',
2238 PropertyDefiner.getControllingCondition, specData, doIdArrays)
2241 class ConstDefiner(PropertyDefiner):
2242 """
2243 A class for definining constants on the interface object
2244 """
2245 def __init__(self, descriptor, name):
2246 PropertyDefiner.__init__(self, descriptor, name)
2247 self.name = name
2248 constants = [m for m in descriptor.interface.members if m.isConst()]
2249 self.chrome = [m for m in constants if isChromeOnly(m)]
2250 self.regular = [m for m in constants if not isChromeOnly(m)]
2252 def generateArray(self, array, name, doIdArrays):
2253 if len(array) == 0:
2254 return ""
2256 def specData(const):
2257 return (const.identifier.name,
2258 convertConstIDLValueToJSVal(const.value))
2260 return self.generatePrefableArray(
2261 array, name,
2262 ' { "%s", %s }',
2263 ' { 0, JS::UndefinedValue() }',
2264 'ConstantSpec',
2265 PropertyDefiner.getControllingCondition, specData, doIdArrays)
2268 class PropertyArrays():
2269 def __init__(self, descriptor):
2270 self.staticMethods = MethodDefiner(descriptor, "StaticMethods",
2271 static=True)
2272 self.staticAttrs = AttrDefiner(descriptor, "StaticAttributes",
2273 static=True)
2274 self.methods = MethodDefiner(descriptor, "Methods", static=False)
2275 self.attrs = AttrDefiner(descriptor, "Attributes", static=False)
2276 self.unforgeableAttrs = AttrDefiner(descriptor, "UnforgeableAttributes",
2277 static=False, unforgeable=True)
2278 self.consts = ConstDefiner(descriptor, "Constants")
2280 @staticmethod
2281 def arrayNames():
2282 return ["staticMethods", "staticAttrs", "methods", "attrs",
2283 "unforgeableAttrs", "consts"]
2285 def hasChromeOnly(self):
2286 return any(getattr(self, a).hasChromeOnly() for a in self.arrayNames())
2288 def hasNonChromeOnly(self):
2289 return any(getattr(self, a).hasNonChromeOnly() for a in self.arrayNames())
2291 def __str__(self):
2292 define = ""
2293 for array in self.arrayNames():
2294 define += str(getattr(self, array))
2295 return define
2298 class CGNativeProperties(CGList):
2299 def __init__(self, descriptor, properties):
2300 def generateNativeProperties(name, chrome):
2301 def check(p):
2302 return p.hasChromeOnly() if chrome else p.hasNonChromeOnly()
2304 nativeProps = []
2305 for array in properties.arrayNames():
2306 propertyArray = getattr(properties, array)
2307 if check(propertyArray):
2308 if propertyArray.usedForXrays():
2309 ids = "%(name)s_ids"
2310 else:
2311 ids = "nullptr"
2312 props = "%(name)s, " + ids + ", %(name)s_specs"
2313 props = (props % {'name': propertyArray.variableName(chrome)})
2314 else:
2315 props = "nullptr, nullptr, nullptr"
2316 nativeProps.append(CGGeneric(props))
2317 return CGWrapper(CGIndenter(CGList(nativeProps, ",\n")),
2318 pre="static const NativeProperties %s = {\n" % name,
2319 post="\n};\n")
2321 nativeProperties = []
2322 if properties.hasNonChromeOnly():
2323 nativeProperties.append(
2324 generateNativeProperties("sNativeProperties", False))
2325 if properties.hasChromeOnly():
2326 nativeProperties.append(
2327 generateNativeProperties("sChromeOnlyNativeProperties", True))
2329 CGList.__init__(self, nativeProperties, "\n")
2331 def declare(self):
2332 return ""
2334 def define(self):
2335 # BOGUSly strip off a newline
2336 return CGList.define(self).rstrip()
2339 class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
2340 """
2341 Generate the CreateInterfaceObjects method for an interface descriptor.
2343 properties should be a PropertyArrays instance.
2344 """
2345 def __init__(self, descriptor, properties):
2346 args = [Argument('JSContext*', 'aCx'),
2347 Argument('JS::Handle<JSObject*>', 'aGlobal'),
2348 Argument('ProtoAndIfaceCache&', 'aProtoAndIfaceCache'),
2349 Argument('bool', 'aDefineOnGlobal')]
2350 CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'void', args)
2351 self.properties = properties
2353 def definition_body(self):
2354 if len(self.descriptor.prototypeChain) == 1:
2355 parentProtoType = "Rooted"
2356 if self.descriptor.interface.getExtendedAttribute("ArrayClass"):
2357 getParentProto = "aCx, JS_GetArrayPrototype(aCx, aGlobal)"
2358 else:
2359 getParentProto = "aCx, JS_GetObjectPrototype(aCx, aGlobal)"
2360 else:
2361 parentProtoName = self.descriptor.prototypeChain[-2]
2362 parentDesc = self.descriptor.getDescriptor(parentProtoName)
2363 if parentDesc.workers:
2364 parentProtoName += '_workers'
2365 getParentProto = ("%s::GetProtoObject(aCx, aGlobal)" %
2366 toBindingNamespace(parentProtoName))
2367 parentProtoType = "Handle"
2369 parentWithInterfaceObject = self.descriptor.interface.parent
2370 while (parentWithInterfaceObject and
2371 not parentWithInterfaceObject.hasInterfaceObject()):
2372 parentWithInterfaceObject = parentWithInterfaceObject.parent
2373 if parentWithInterfaceObject:
2374 parentIfaceName = parentWithInterfaceObject.identifier.name
2375 parentDesc = self.descriptor.getDescriptor(parentIfaceName)
2376 if parentDesc.workers:
2377 parentIfaceName += "_workers"
2378 getConstructorProto = ("%s::GetConstructorObject(aCx, aGlobal)" %
2379 toBindingNamespace(parentIfaceName))
2380 constructorProtoType = "Handle"
2381 else:
2382 getConstructorProto = "aCx, JS_GetFunctionPrototype(aCx, aGlobal)"
2383 constructorProtoType = "Rooted"
2385 needInterfaceObject = self.descriptor.interface.hasInterfaceObject()
2386 needInterfacePrototypeObject = self.descriptor.interface.hasInterfacePrototypeObject()
2388 # if we don't need to create anything, why are we generating this?
2389 assert needInterfaceObject or needInterfacePrototypeObject
2391 idsToInit = []
2392 # There is no need to init any IDs in workers, because worker bindings
2393 # don't have Xrays.
2394 if not self.descriptor.workers:
2395 for var in self.properties.arrayNames():
2396 props = getattr(self.properties, var)
2397 # We only have non-chrome ids to init if we have no chrome ids.
2398 if props.hasChromeOnly():
2399 idsToInit.append(props.variableName(True))
2400 if props.hasNonChromeOnly():
2401 idsToInit.append(props.variableName(False))
2402 if len(idsToInit) > 0:
2403 initIdCalls = ["!InitIds(aCx, %s, %s_ids)" % (varname, varname)
2404 for varname in idsToInit]
2405 idsInitedFlag = CGGeneric("static bool sIdsInited = false;\n")
2406 setFlag = CGGeneric("sIdsInited = true;\n")
2407 initIdConditionals = [CGIfWrapper(CGGeneric("return;\n"), call)
2408 for call in initIdCalls]
2409 initIds = CGList([idsInitedFlag,
2410 CGIfWrapper(CGList(initIdConditionals + [setFlag]),
2411 "!sIdsInited && NS_IsMainThread()")])
2412 else:
2413 initIds = None
2415 prefCacheData = []
2416 for var in self.properties.arrayNames():
2417 props = getattr(self.properties, var)
2418 prefCacheData.extend(props.prefCacheData)
2419 if len(prefCacheData) != 0:
2420 prefCacheData = [
2421 CGGeneric('Preferences::AddBoolVarCache(%s, "%s");\n' % (ptr, pref))
2422 for pref, ptr in prefCacheData]
2423 prefCache = CGWrapper(CGIndenter(CGList(prefCacheData)),
2424 pre=("static bool sPrefCachesInited = false;\n"
2425 "if (!sPrefCachesInited) {\n"
2426 " sPrefCachesInited = true;\n"),
2427 post="}\n")
2428 else:
2429 prefCache = None
2431 if UseHolderForUnforgeable(self.descriptor):
2432 createUnforgeableHolder = CGGeneric(dedent("""
2433 JS::Rooted<JSObject*> unforgeableHolder(aCx,
2434 JS_NewObjectWithGivenProto(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
2435 if (!unforgeableHolder) {
2436 return;
2437 }
2438 """))
2439 defineUnforgeables = InitUnforgeablePropertiesOnObject(self.descriptor,
2440 "unforgeableHolder",
2441 self.properties)
2442 createUnforgeableHolder = CGList(
2443 [createUnforgeableHolder, defineUnforgeables])
2444 else:
2445 createUnforgeableHolder = None
2447 getParentProto = fill(
2448 """
2449 JS::${type}<JSObject*> parentProto(${getParentProto});
2450 if (!parentProto) {
2451 return;
2452 }
2453 """,
2454 type=parentProtoType,
2455 getParentProto=getParentProto)
2457 getConstructorProto = fill(
2458 """
2459 JS::${type}<JSObject*> constructorProto(${getConstructorProto});
2460 if (!constructorProto) {
2461 return;
2462 }
2463 """,
2464 type=constructorProtoType,
2465 getConstructorProto=getConstructorProto)
2467 if (needInterfaceObject and
2468 self.descriptor.needsConstructHookHolder()):
2469 constructHookHolder = "&" + CONSTRUCT_HOOK_NAME + "_holder"
2470 else:
2471 constructHookHolder = "nullptr"
2472 if self.descriptor.interface.ctor():
2473 constructArgs = methodLength(self.descriptor.interface.ctor())
2474 else:
2475 constructArgs = 0
2476 if len(self.descriptor.interface.namedConstructors) > 0:
2477 namedConstructors = "namedConstructors"
2478 else:
2479 namedConstructors = "nullptr"
2481 if needInterfacePrototypeObject:
2482 protoClass = "&PrototypeClass.mBase"
2483 protoCache = "&aProtoAndIfaceCache.EntrySlotOrCreate(prototypes::id::%s)" % self.descriptor.name
2484 else:
2485 protoClass = "nullptr"
2486 protoCache = "nullptr"
2487 if needInterfaceObject:
2488 interfaceClass = "&InterfaceObjectClass.mBase"
2489 interfaceCache = "&aProtoAndIfaceCache.EntrySlotOrCreate(constructors::id::%s)" % self.descriptor.name
2490 else:
2491 # We don't have slots to store the named constructors.
2492 assert len(self.descriptor.interface.namedConstructors) == 0
2493 interfaceClass = "nullptr"
2494 interfaceCache = "nullptr"
2496 if self.descriptor.concrete:
2497 domClass = "&Class.mClass"
2498 else:
2499 domClass = "nullptr"
2501 if self.properties.hasNonChromeOnly():
2502 properties = "&sNativeProperties"
2503 else:
2504 properties = "nullptr"
2505 if self.properties.hasChromeOnly():
2506 accessCheck = "nsContentUtils::ThreadsafeIsCallerChrome()"
2507 chromeProperties = accessCheck + " ? &sChromeOnlyNativeProperties : nullptr"
2508 else:
2509 chromeProperties = "nullptr"
2511 call = fill(
2512 """
2513 dom::CreateInterfaceObjects(aCx, aGlobal, parentProto,
2514 ${protoClass}, ${protoCache},
2515 constructorProto, ${interfaceClass}, ${constructHookHolder}, ${constructArgs}, ${namedConstructors},
2516 ${interfaceCache},
2517 ${domClass},
2518 ${properties},
2519 ${chromeProperties},
2520 ${name}, aDefineOnGlobal);
2521 """,
2522 protoClass=protoClass,
2523 protoCache=protoCache,
2524 interfaceClass=interfaceClass,
2525 constructHookHolder=constructHookHolder,
2526 constructArgs=constructArgs,
2527 namedConstructors=namedConstructors,
2528 interfaceCache=interfaceCache,
2529 domClass=domClass,
2530 properties=properties,
2531 chromeProperties=chromeProperties,
2532 name='"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "nullptr")
2534 if UseHolderForUnforgeable(self.descriptor):
2535 assert needInterfacePrototypeObject
2536 setUnforgeableHolder = CGGeneric(fill(
2537 """
2538 JSObject* proto = aProtoAndIfaceCache.EntrySlotOrCreate(prototypes::id::${name});
2539 if (proto) {
2540 js::SetReservedSlot(proto, DOM_INTERFACE_PROTO_SLOTS_BASE,
2541 JS::ObjectValue(*unforgeableHolder));
2542 }
2543 """,
2544 name=self.descriptor.name))
2545 else:
2546 setUnforgeableHolder = None
2547 functionBody = CGList(
2548 [CGGeneric(getParentProto), CGGeneric(getConstructorProto), initIds,
2549 prefCache, createUnforgeableHolder, CGGeneric(call), setUnforgeableHolder],
2550 "\n")
2551 return CGIndenter(functionBody).define()
2554 class CGGetPerInterfaceObject(CGAbstractMethod):
2555 """
2556 A method for getting a per-interface object (a prototype object or interface
2557 constructor object).
2558 """
2559 def __init__(self, descriptor, name, idPrefix="", extraArgs=[]):
2560 args = [Argument('JSContext*', 'aCx'),
2561 Argument('JS::Handle<JSObject*>', 'aGlobal')] + extraArgs
2562 CGAbstractMethod.__init__(self, descriptor, name,
2563 'JS::Handle<JSObject*>', args)
2564 self.id = idPrefix + "id::" + self.descriptor.name
2566 def definition_body(self):
2567 # BOGUS extra blank line at the beginning of the code below
2568 # BOGUS - should be a blank line between an if-block and following comment below
2569 return indent(fill(
2570 """
2572 /* Make sure our global is sane. Hopefully we can remove this sometime */
2573 if (!(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL)) {
2574 return JS::NullPtr();
2575 }
2576 /* Check to see whether the interface objects are already installed */
2577 ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(aGlobal);
2578 if (!protoAndIfaceCache.EntrySlotIfExists(${id})) {
2579 CreateInterfaceObjects(aCx, aGlobal, protoAndIfaceCache, aDefineOnGlobal);
2580 }
2582 /*
2583 * The object might _still_ be null, but that's OK.
2584 *
2585 * Calling fromMarkedLocation() is safe because protoAndIfaceCache is
2586 * traced by TraceProtoAndIfaceCache() and its contents are never
2587 * changed after they have been set.
2588 */
2589 return JS::Handle<JSObject*>::fromMarkedLocation(protoAndIfaceCache.EntrySlotMustExist(${id}).address());
2590 """,
2591 id=self.id))
2594 class CGGetProtoObjectMethod(CGGetPerInterfaceObject):
2595 """
2596 A method for getting the interface prototype object.
2597 """
2598 def __init__(self, descriptor):
2599 CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject",
2600 "prototypes::")
2602 def definition_body(self):
2603 # BOGUS extra blank line at start of method
2604 return indent(dedent("""
2606 /* Get the interface prototype object for this class. This will create the
2607 object as needed. */
2608 bool aDefineOnGlobal = true;
2609 """)) + CGGetPerInterfaceObject.definition_body(self)
2612 class CGGetConstructorObjectMethod(CGGetPerInterfaceObject):
2613 """
2614 A method for getting the interface constructor object.
2615 """
2616 def __init__(self, descriptor):
2617 CGGetPerInterfaceObject.__init__(
2618 self, descriptor, "GetConstructorObject",
2619 "constructors::",
2620 extraArgs=[Argument("bool", "aDefineOnGlobal", "true")])
2622 def definition_body(self):
2623 # BOGUS extra blank line at start of method
2624 return indent(dedent("""
2626 /* Get the interface object for this class. This will create the object as
2627 needed. */
2628 """)) + CGGetPerInterfaceObject.definition_body(self)
2631 class CGDefineDOMInterfaceMethod(CGAbstractMethod):
2632 """
2633 A method for resolve hooks to try to lazily define the interface object for
2634 a given interface.
2635 """
2636 def __init__(self, descriptor):
2637 args = [Argument('JSContext*', 'aCx'),
2638 Argument('JS::Handle<JSObject*>', 'aGlobal'),
2639 Argument('JS::Handle<jsid>', 'id'),
2640 Argument('bool', 'aDefineOnGlobal')]
2641 CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface', 'JSObject*', args)
2643 def declare(self):
2644 if self.descriptor.workers:
2645 return ''
2646 return CGAbstractMethod.declare(self)
2648 def define(self):
2649 if self.descriptor.workers:
2650 return ''
2651 return CGAbstractMethod.define(self)
2653 def definition_body(self):
2654 if len(self.descriptor.interface.namedConstructors) > 0:
2655 getConstructor = indent(dedent("""
2656 JSObject* interfaceObject = GetConstructorObject(aCx, aGlobal, aDefineOnGlobal);
2657 if (!interfaceObject) {
2658 return nullptr;
2659 }
2660 for (unsigned slot = DOM_INTERFACE_SLOTS_BASE; slot < JSCLASS_RESERVED_SLOTS(&InterfaceObjectClass.mBase); ++slot) {
2661 JSObject* constructor = &js::GetReservedSlot(interfaceObject, slot).toObject();
2662 if (JS_GetFunctionId(JS_GetObjectFunction(constructor)) == JSID_TO_STRING(id)) {
2663 return constructor;
2664 }
2665 }
2666 return interfaceObject;
2667 """))
2668 else:
2669 getConstructor = " return GetConstructorObject(aCx, aGlobal, aDefineOnGlobal);\n"
2670 return getConstructor
2673 class CGConstructorEnabled(CGAbstractMethod):
2674 """
2675 A method for testing whether we should be exposing this interface
2676 object or navigator property. This can perform various tests
2677 depending on what conditions are specified on the interface.
2678 """
2679 def __init__(self, descriptor):
2680 CGAbstractMethod.__init__(self, descriptor,
2681 'ConstructorEnabled', 'bool',
2682 [Argument("JSContext*", "aCx"),
2683 Argument("JS::Handle<JSObject*>", "aObj")])
2685 def definition_body(self):
2686 conditions = []
2687 iface = self.descriptor.interface
2688 pref = iface.getExtendedAttribute("Pref")
2689 if pref:
2690 assert isinstance(pref, list) and len(pref) == 1
2691 conditions.append('Preferences::GetBool("%s")' % pref[0])
2692 if iface.getExtendedAttribute("ChromeOnly"):
2693 conditions.append("nsContentUtils::ThreadsafeIsCallerChrome()")
2694 func = iface.getExtendedAttribute("Func")
2695 if func:
2696 assert isinstance(func, list) and len(func) == 1
2697 conditions.append("%s(aCx, aObj)" % func[0])
2698 availableIn = getAvailableInTestFunc(iface)
2699 if availableIn:
2700 conditions.append("%s(aCx, aObj)" % availableIn)
2701 # We should really have some conditions
2702 assert len(conditions)
2703 body = CGWrapper(CGList((CGGeneric(cond) for cond in conditions),
2704 " &&\n"),
2705 pre="return ", post=";\n", reindent=True)
2706 return CGIndenter(body).define()
2709 def CreateBindingJSObject(descriptor, properties, parent):
2710 # We don't always need to root obj, but there are a variety
2711 # of cases where we do, so for simplicity, just always root it.
2712 objDecl = "JS::Rooted<JSObject*> obj(aCx);\n"
2713 if descriptor.proxy:
2714 create = fill(
2715 """
2716 JS::Rooted<JS::Value> proxyPrivateVal(aCx, JS::PrivateValue(aObject));
2717 js::ProxyOptions options;
2718 options.setClass(&Class.mBase);
2719 obj = NewProxyObject(aCx, DOMProxyHandler::getInstance(),
2720 proxyPrivateVal, proto, ${parent}, options);
2721 if (!obj) {
2722 return nullptr;
2723 }
2725 """,
2726 parent=parent)
2727 if descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
2728 create += dedent("""
2729 js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO,
2730 JS::PrivateValue(&aObject->mExpandoAndGeneration));
2732 """)
2733 else:
2734 create = fill(
2735 """
2736 obj = JS_NewObject(aCx, Class.ToJSClass(), proto, ${parent});
2737 if (!obj) {
2738 return nullptr;
2739 }
2741 js::SetReservedSlot(obj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(aObject));
2742 """,
2743 parent=parent)
2744 if "Window" in descriptor.interface.identifier.name:
2745 create = dedent("""
2746 MOZ_ASSERT(false,
2747 "Our current reserved slot situation is unsafe for globals. Fix "
2748 "bug 760095!");
2749 """) + create
2750 create = objDecl + create
2752 if descriptor.nativeOwnership == 'refcounted':
2753 create += "NS_ADDREF(aObject);\n"
2754 else:
2755 create += dedent("""
2756 // Make sure the native objects inherit from NonRefcountedDOMObject so that we
2757 // log their ctor and dtor.
2758 MustInheritFromNonRefcountedDOMObject(aObject);
2759 *aTookOwnership = true;
2760 """)
2761 return create
2764 def InitUnforgeablePropertiesOnObject(descriptor, obj, properties, failureReturnValue=""):
2765 """
2766 properties is a PropertyArrays instance
2767 """
2768 defineUnforgeables = fill(
2769 """
2770 if (!DefineUnforgeableAttributes(aCx, ${obj}, %s)) {
2771 return${rv};
2772 }
2773 """,
2774 obj=obj,
2775 rv=" " + failureReturnValue if failureReturnValue else "")
2777 unforgeableAttrs = properties.unforgeableAttrs
2778 unforgeables = []
2779 if unforgeableAttrs.hasNonChromeOnly():
2780 unforgeables.append(CGGeneric(defineUnforgeables %
2781 unforgeableAttrs.variableName(False)))
2782 if unforgeableAttrs.hasChromeOnly():
2783 unforgeables.append(
2784 CGIfWrapper(CGGeneric(defineUnforgeables %
2785 unforgeableAttrs.variableName(True)),
2786 "nsContentUtils::ThreadsafeIsCallerChrome()"))
2787 return CGList(unforgeables)
2790 def InitUnforgeableProperties(descriptor, properties):
2791 """
2792 properties is a PropertyArrays instance
2793 """
2794 unforgeableAttrs = properties.unforgeableAttrs
2795 if not unforgeableAttrs.hasNonChromeOnly() and not unforgeableAttrs.hasChromeOnly():
2796 return ""
2798 if descriptor.proxy:
2799 unforgeableProperties = CGGeneric(
2800 "// Unforgeable properties on proxy-based bindings are stored in an object held\n"
2801 "// by the interface prototype object.\n"
2802 "\n") # BOGUS extra blank line
2803 else:
2804 unforgeableProperties = CGWrapper(
2805 InitUnforgeablePropertiesOnObject(descriptor, "obj", properties, "nullptr"),
2806 pre=(
2807 "// Important: do unforgeable property setup after we have handed\n"
2808 "// over ownership of the C++ object to obj as needed, so that if\n"
2809 "// we fail and it ends up GCed it won't have problems in the\n"
2810 "// finalizer trying to drop its ownership of the C++ object.\n"))
2811 return CGWrapper(unforgeableProperties, pre="\n").define()
2814 def AssertInheritanceChain(descriptor):
2815 asserts = ""
2816 iface = descriptor.interface
2817 while iface:
2818 desc = descriptor.getDescriptor(iface.identifier.name)
2819 asserts += (
2820 " MOZ_ASSERT(static_cast<%s*>(aObject) == \n"
2821 " reinterpret_cast<%s*>(aObject));\n" %
2822 (desc.nativeType, desc.nativeType))
2823 iface = iface.parent
2824 asserts += " MOZ_ASSERT(ToSupportsIsCorrect(aObject));\n"
2825 return asserts
2828 def InitMemberSlots(descriptor, wrapperCache):
2829 """
2830 Initialize member slots on our JS object if we're supposed to have some.
2832 Note that this is called after the SetWrapper() call in the
2833 wrapperCache case, since that can affect how our getters behave
2834 and we plan to invoke them here. So if we fail, we need to
2835 ClearWrapper.
2836 """
2837 if not descriptor.interface.hasMembersInSlots():
2838 return "\n" # BOGUS blank line only if this returns empty
2839 if wrapperCache:
2840 clearWrapper = " aCache->ClearWrapper();\n"
2841 else:
2842 clearWrapper = ""
2843 return ("if (!UpdateMemberSlots(aCx, obj, aObject)) {\n"
2844 "%s"
2845 " return nullptr;\n"
2846 "}\n" % clearWrapper)
2849 class CGWrapWithCacheMethod(CGAbstractMethod):
2850 """
2851 Create a wrapper JSObject for a given native that implements nsWrapperCache.
2853 properties should be a PropertyArrays instance.
2854 """
2855 def __init__(self, descriptor, properties):
2856 assert descriptor.interface.hasInterfacePrototypeObject()
2857 args = [Argument('JSContext*', 'aCx'),
2858 Argument(descriptor.nativeType + '*', 'aObject'),
2859 Argument('nsWrapperCache*', 'aCache')]
2860 CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args)
2861 self.properties = properties
2863 def definition_body(self):
2864 return fill(
2865 """
2866 ${assertion}
2867 MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
2868 "nsISupports must be on our primary inheritance chain");
2870 JS::Rooted<JSObject*> parent(aCx,
2871 GetRealParentObject(aObject,
2872 WrapNativeParent(aCx, aObject->GetParentObject())));
2873 if (!parent) {
2874 return nullptr;
2875 }
2877 // That might have ended up wrapping us already, due to the wonders
2878 // of XBL. Check for that, and bail out as needed. Scope so we don't
2879 // collide with the "obj" we declare in CreateBindingJSObject.
2880 {
2881 JSObject* obj = aCache->GetWrapper();
2882 if (obj) {
2883 return obj;
2884 }
2885 }
2887 JSAutoCompartment ac(aCx, parent);
2888 JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForObject(aCx, parent));
2889 JS::Handle<JSObject*> proto = GetProtoObject(aCx, global);
2890 if (!proto) {
2891 return nullptr;
2892 }
2894 $*{parent}
2896 $*{unforgeable}
2898 aCache->SetWrapper(obj);
2899 $*{slots}
2900 return obj;
2901 """,
2902 assertion=AssertInheritanceChain(self.descriptor),
2903 parent=CreateBindingJSObject(self.descriptor, self.properties,
2904 "parent"),
2905 unforgeable=InitUnforgeableProperties(self.descriptor, self.properties),
2906 slots=InitMemberSlots(self.descriptor, True))
2909 class CGWrapMethod(CGAbstractMethod):
2910 def __init__(self, descriptor):
2911 # XXX can we wrap if we don't have an interface prototype object?
2912 assert descriptor.interface.hasInterfacePrototypeObject()
2913 args = [Argument('JSContext*', 'aCx'),
2914 Argument('T*', 'aObject')]
2915 CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args,
2916 inline=True, templateArgs=["class T"])
2918 def definition_body(self):
2919 return " return Wrap(aCx, aObject, aObject);\n"
2922 class CGWrapNonWrapperCacheMethod(CGAbstractMethod):
2923 """
2924 Create a wrapper JSObject for a given native that does not implement
2925 nsWrapperCache.
2927 properties should be a PropertyArrays instance.
2928 """
2929 def __init__(self, descriptor, properties):
2930 # XXX can we wrap if we don't have an interface prototype object?
2931 assert descriptor.interface.hasInterfacePrototypeObject()
2932 args = [Argument('JSContext*', 'aCx'),
2933 Argument(descriptor.nativeType + '*', 'aObject')]
2934 if descriptor.nativeOwnership == 'owned':
2935 args.append(Argument('bool*', 'aTookOwnership'))
2936 CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args)
2937 self.properties = properties
2939 def definition_body(self):
2940 return fill(
2941 """
2942 ${assertions}
2943 JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
2944 JS::Handle<JSObject*> proto = GetProtoObject(aCx, global);
2945 if (!proto) {
2946 return nullptr;
2947 }
2949 $*{global_}
2951 $*{unforgeable}
2953 $*{slots}
2954 return obj;
2955 """,
2956 assertions=AssertInheritanceChain(self.descriptor),
2957 global_=CreateBindingJSObject(self.descriptor, self.properties,
2958 "global"),
2959 unforgeable=InitUnforgeableProperties(self.descriptor, self.properties),
2960 slots=InitMemberSlots(self.descriptor, False))
2963 class CGWrapGlobalMethod(CGAbstractMethod):
2964 """
2965 Create a wrapper JSObject for a global. The global must implement
2966 nsWrapperCache.
2968 properties should be a PropertyArrays instance.
2969 """
2970 def __init__(self, descriptor, properties):
2971 assert descriptor.interface.hasInterfacePrototypeObject()
2972 args = [Argument('JSContext*', 'aCx'),
2973 Argument(descriptor.nativeType + '*', 'aObject'),
2974 Argument('nsWrapperCache*', 'aCache'),
2975 Argument('JS::CompartmentOptions&', 'aOptions'),
2976 Argument('JSPrincipals*', 'aPrincipal')]
2977 CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args)
2978 self.descriptor = descriptor
2979 self.properties = properties
2981 def definition_body(self):
2982 return fill(
2983 """
2984 ${assertions}
2985 MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
2986 "nsISupports must be on our primary inheritance chain");
2988 JS::Rooted<JSObject*> obj(aCx);
2989 obj = CreateGlobal<${nativeType}, GetProtoObject>(aCx,
2990 aObject,
2991 aCache,
2992 Class.ToJSClass(),
2993 aOptions,
2994 aPrincipal);
2996 $*{unforgeable}
2998 $*{slots}
3000 // XXXkhuey can't do this yet until workers can lazy resolve.
3001 // JS_FireOnNewGlobalObject(aCx, obj);
3003 return obj;
3004 """,
3005 assertions=AssertInheritanceChain(self.descriptor),
3006 nativeType=self.descriptor.nativeType,
3007 unforgeable=InitUnforgeableProperties(self.descriptor, self.properties),
3008 slots=InitMemberSlots(self.descriptor, True))
3011 class CGUpdateMemberSlotsMethod(CGAbstractStaticMethod):
3012 def __init__(self, descriptor):
3013 args = [Argument('JSContext*', 'aCx'),
3014 Argument('JS::Handle<JSObject*>', 'aWrapper'),
3015 Argument(descriptor.nativeType + '*', 'aObject')]
3016 CGAbstractStaticMethod.__init__(self, descriptor, 'UpdateMemberSlots', 'bool', args)
3018 def definition_body(self):
3019 body = ("JS::Rooted<JS::Value> temp(aCx);\n"
3020 "JSJitGetterCallArgs args(&temp);\n")
3021 for m in self.descriptor.interface.members:
3022 if m.isAttr() and m.getExtendedAttribute("StoreInSlot"):
3023 body += fill(
3024 """
3026 static_assert(${slot} < js::shadow::Object::MAX_FIXED_SLOTS,
3027 "Not enough fixed slots to fit '${interface}.${member}'");
3028 if (!get_${member}(aCx, aWrapper, aObject, args)) {
3029 return false;
3030 }
3031 // Getter handled setting our reserved slots
3032 """,
3033 slot=memberReservedSlot(m),
3034 interface=self.descriptor.interface.identifier.name,
3035 member=m.identifier.name)
3037 body += "\nreturn true;\n"
3038 return indent(body)
3041 class CGClearCachedValueMethod(CGAbstractMethod):
3042 def __init__(self, descriptor, member):
3043 self.member = member
3044 # If we're StoreInSlot, we'll need to call the getter
3045 if member.getExtendedAttribute("StoreInSlot"):
3046 args = [Argument('JSContext*', 'aCx')]
3047 returnType = 'bool'
3048 else:
3049 args = []
3050 returnType = 'void'
3051 args.append(Argument(descriptor.nativeType + '*', 'aObject'))
3052 name = ("ClearCached%sValue" % MakeNativeName(member.identifier.name))
3053 CGAbstractMethod.__init__(self, descriptor, name, returnType, args)
3055 def definition_body(self):
3056 slotIndex = memberReservedSlot(self.member)
3057 if self.member.getExtendedAttribute("StoreInSlot"):
3058 # We have to root things and save the old value in case
3059 # regetting fails, so we can restore it.
3060 declObj = "JS::Rooted<JSObject*> obj(aCx);\n"
3061 noopRetval = " true"
3062 saveMember = (
3063 "JS::Rooted<JS::Value> oldValue(aCx, js::GetReservedSlot(obj, %s));\n" %
3064 slotIndex)
3065 regetMember = fill(
3066 """
3067 JS::Rooted<JS::Value> temp(aCx);
3068 JSJitGetterCallArgs args(&temp);
3069 JSAutoCompartment ac(aCx, obj);
3070 if (!get_${name}(aCx, obj, aObject, args)) {
3071 js::SetReservedSlot(obj, ${slotIndex}, oldValue);
3072 nsJSUtils::ReportPendingException(aCx);
3073 return false;
3074 }
3075 return true;
3076 """,
3077 name=self.member.identifier.name,
3078 slotIndex=slotIndex)
3079 else:
3080 declObj = "JSObject* obj;\n"
3081 noopRetval = ""
3082 saveMember = ""
3083 regetMember = ""
3085 return indent(fill(
3086 """
3087 $*{declObj}
3088 obj = aObject->GetWrapper();
3089 if (!obj) {
3090 return${noopRetval};
3091 }
3092 $*{saveMember}
3093 js::SetReservedSlot(obj, ${slotIndex}, JS::UndefinedValue());
3094 $*{regetMember}
3095 """,
3096 declObj=declObj,
3097 noopRetval=noopRetval,
3098 saveMember=saveMember,
3099 slotIndex=slotIndex,
3100 regetMember=regetMember))
3103 class CGIsPermittedMethod(CGAbstractMethod):
3104 """
3105 crossOriginGetters/Setters/Methods are sets of names of the relevant members.
3106 """
3107 def __init__(self, descriptor, crossOriginGetters, crossOriginSetters,
3108 crossOriginMethods):
3109 self.crossOriginGetters = crossOriginGetters
3110 self.crossOriginSetters = crossOriginSetters
3111 self.crossOriginMethods = crossOriginMethods
3112 args = [Argument("JSFlatString*", "prop"),
3113 Argument("jschar", "propFirstChar"),
3114 Argument("bool", "set")]
3115 CGAbstractMethod.__init__(self, descriptor, "IsPermitted", "bool", args,
3116 inline=True)
3118 def definition_body(self):
3119 allNames = self.crossOriginGetters | self.crossOriginSetters | self.crossOriginMethods
3120 readwrite = self.crossOriginGetters & self.crossOriginSetters
3121 readonly = (self.crossOriginGetters - self.crossOriginSetters) | self.crossOriginMethods
3122 writeonly = self.crossOriginSetters - self.crossOriginGetters
3123 cases = {}
3124 for name in sorted(allNames):
3125 cond = 'JS_FlatStringEqualsAscii(prop, "%s")' % name
3126 if name in readonly:
3127 cond = "!set && %s" % cond
3128 elif name in writeonly:
3129 cond = "set && %s" % cond
3130 else:
3131 assert name in readwrite
3132 firstLetter = name[0]
3133 case = cases.get(firstLetter, CGList([]))
3134 case.append(CGGeneric("if (%s) {\n"
3135 " return true;\n"
3136 "}\n" % cond))
3137 cases[firstLetter] = case
3138 caseList = []
3139 for firstLetter in sorted(cases.keys()):
3140 caseList.append(CGCase("'%s'" % firstLetter, cases[firstLetter]))
3141 switch = CGSwitch("propFirstChar", caseList)
3142 return indent(switch.define() + "\nreturn false;\n")
3144 builtinNames = {
3145 IDLType.Tags.bool: 'bool',
3146 IDLType.Tags.int8: 'int8_t',
3147 IDLType.Tags.int16: 'int16_t',
3148 IDLType.Tags.int32: 'int32_t',
3149 IDLType.Tags.int64: 'int64_t',
3150 IDLType.Tags.uint8: 'uint8_t',
3151 IDLType.Tags.uint16: 'uint16_t',
3152 IDLType.Tags.uint32: 'uint32_t',
3153 IDLType.Tags.uint64: 'uint64_t',
3154 IDLType.Tags.unrestricted_float: 'float',
3155 IDLType.Tags.float: 'float',
3156 IDLType.Tags.unrestricted_double: 'double',
3157 IDLType.Tags.double: 'double'
3158 }
3160 numericSuffixes = {
3161 IDLType.Tags.int8: '',
3162 IDLType.Tags.uint8: '',
3163 IDLType.Tags.int16: '',
3164 IDLType.Tags.uint16: '',
3165 IDLType.Tags.int32: '',
3166 IDLType.Tags.uint32: 'U',
3167 IDLType.Tags.int64: 'LL',
3168 IDLType.Tags.uint64: 'ULL',
3169 IDLType.Tags.unrestricted_float: 'F',
3170 IDLType.Tags.float: 'F',
3171 IDLType.Tags.unrestricted_double: '',
3172 IDLType.Tags.double: ''
3173 }
3176 def numericValue(t, v):
3177 if (t == IDLType.Tags.unrestricted_double or
3178 t == IDLType.Tags.unrestricted_float):
3179 typeName = builtinNames[t]
3180 if v == float("inf"):
3181 return "mozilla::PositiveInfinity<%s>()" % typeName
3182 if v == float("-inf"):
3183 return "mozilla::NegativeInfinity<%s>()" % typeName
3184 if math.isnan(v):
3185 return "mozilla::UnspecifiedNaN<%s>()" % typeName
3186 return "%s%s" % (v, numericSuffixes[t])
3189 class CastableObjectUnwrapper():
3190 """
3191 A class for unwrapping an object named by the "source" argument
3192 based on the passed-in descriptor and storing it in a variable
3193 called by the name in the "target" argument.
3195 codeOnFailure is the code to run if unwrapping fails.
3197 If isCallbackReturnValue is "JSImpl" and our descriptor is also
3198 JS-implemented, fall back to just creating the right object if what we
3199 have isn't one already.
3201 If allowCrossOriginObj is True, then we'll first do an
3202 UncheckedUnwrap and then operate on the result.
3203 """
3204 def __init__(self, descriptor, source, target, codeOnFailure,
3205 exceptionCode=None, isCallbackReturnValue=False,
3206 allowCrossOriginObj=False):
3207 self.substitution = {
3208 "type": descriptor.nativeType,
3209 "protoID": "prototypes::id::" + descriptor.name,
3210 "target": target,
3211 "codeOnFailure": codeOnFailure,
3212 }
3213 if allowCrossOriginObj:
3214 self.substitution["uncheckedObjDecl"] = (
3215 "JS::Rooted<JSObject*> uncheckedObj(cx, js::UncheckedUnwrap(%s));\n" % source)
3216 self.substitution["source"] = "uncheckedObj"
3217 xpconnectUnwrap = dedent("""
3218 nsresult rv;
3219 { // Scope for the JSAutoCompartment, because we only
3220 // want to be in that compartment for the UnwrapArg call.
3221 JSAutoCompartment ac(cx, ${source});
3222 rv = UnwrapArg<${type}>(cx, val, &objPtr, &objRef.ptr, &val);
3223 }
3224 """)
3225 else:
3226 self.substitution["uncheckedObjDecl"] = ""
3227 self.substitution["source"] = source
3228 xpconnectUnwrap = "nsresult rv = UnwrapArg<${type}>(cx, val, &objPtr, &objRef.ptr, &val);\n"
3230 if descriptor.hasXPConnectImpls:
3231 # We don't use xpc_qsUnwrapThis because it will always throw on
3232 # unwrap failure, whereas we want to control whether we throw or
3233 # not.
3234 self.substitution["codeOnFailure"] = string.Template(
3235 "${type} *objPtr;\n"
3236 "SelfRef objRef;\n"
3237 "JS::Rooted<JS::Value> val(cx, JS::ObjectValue(*${source}));\n" +
3238 xpconnectUnwrap +
3239 "if (NS_FAILED(rv)) {\n"
3240 "${indentedCodeOnFailure}"
3241 "}\n"
3242 "// We should be castable!\n"
3243 "MOZ_ASSERT(!objRef.ptr);\n"
3244 "// We should have an object, too!\n"
3245 "MOZ_ASSERT(objPtr);\n"
3246 "${target} = objPtr;\n"
3247 ).substitute(self.substitution,
3248 indentedCodeOnFailure=indent(codeOnFailure))
3249 elif (isCallbackReturnValue == "JSImpl" and
3250 descriptor.interface.isJSImplemented()):
3251 exceptionCode = exceptionCode or codeOnFailure
3252 self.substitution["codeOnFailure"] = fill(
3253 """
3254 // Be careful to not wrap random DOM objects here, even if
3255 // they're wrapped in opaque security wrappers for some reason.
3256 // XXXbz Wish we could check for a JS-implemented object
3257 // that already has a content reflection...
3258 if (!IsDOMObject(js::UncheckedUnwrap(${source}))) {
3259 nsCOMPtr<nsPIDOMWindow> ourWindow;
3260 if (!GetWindowForJSImplementedObject(cx, Callback(), getter_AddRefs(ourWindow))) {
3261 $*{exceptionCode}
3262 }
3263 JS::Rooted<JSObject*> jsImplSourceObj(cx, ${source});
3264 ${target} = new ${type}(jsImplSourceObj, ourWindow);
3265 } else {
3266 $*{codeOnFailure}
3267 }
3268 """,
3269 exceptionCode=exceptionCode,
3270 **self.substitution)
3271 else:
3272 self.substitution["codeOnFailure"] = codeOnFailure
3274 def __str__(self):
3275 substitution = self.substitution.copy()
3276 substitution["codeOnFailure"] %= {
3277 'securityError': 'rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO'
3278 }
3279 return fill(
3280 """
3281 {
3282 $*{uncheckedObjDecl}
3283 nsresult rv = UnwrapObject<${protoID}, ${type}>(${source}, ${target});
3284 if (NS_FAILED(rv)) {
3285 $*{codeOnFailure}
3286 }
3287 }
3288 """,
3289 **substitution)
3292 class FailureFatalCastableObjectUnwrapper(CastableObjectUnwrapper):
3293 """
3294 As CastableObjectUnwrapper, but defaulting to throwing if unwrapping fails
3295 """
3296 def __init__(self, descriptor, source, target, exceptionCode,
3297 isCallbackReturnValue, sourceDescription):
3298 CastableObjectUnwrapper.__init__(
3299 self, descriptor, source, target,
3300 'ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s", "%s");\n'
3301 '%s' % (sourceDescription, descriptor.interface.identifier.name,
3302 exceptionCode),
3303 exceptionCode,
3304 isCallbackReturnValue)
3307 class CGCallbackTempRoot(CGGeneric):
3308 def __init__(self, name):
3309 define = dedent("""
3310 { // Scope for tempRoot
3311 JS::Rooted<JSObject*> tempRoot(cx, &${val}.toObject());
3312 ${declName} = new %s(tempRoot, mozilla::dom::GetIncumbentGlobal());
3313 }
3314 """) % name
3315 CGGeneric.__init__(self, define=define)
3318 class JSToNativeConversionInfo():
3319 """
3320 An object representing information about a JS-to-native conversion.
3321 """
3322 def __init__(self, template, declType=None, holderType=None,
3323 dealWithOptional=False, declArgs=None,
3324 holderArgs=None):
3325 """
3326 template: A string representing the conversion code. This will have
3327 template substitution performed on it as follows:
3329 ${val} is a handle to the JS::Value in question
3330 ${mutableVal} is a mutable handle to the JS::Value in question
3331 ${holderName} replaced by the holder's name, if any
3332 ${declName} replaced by the declaration's name
3333 ${haveValue} replaced by an expression that evaluates to a boolean
3334 for whether we have a JS::Value. Only used when
3335 defaultValue is not None or when True is passed for
3336 checkForValue to instantiateJSToNativeConversion.
3338 declType: A CGThing representing the native C++ type we're converting
3339 to. This is allowed to be None if the conversion code is
3340 supposed to be used as-is.
3342 holderType: A CGThing representing the type of a "holder" which will
3343 hold a possible reference to the C++ thing whose type we
3344 returned in declType, or None if no such holder is needed.
3346 dealWithOptional: A boolean indicating whether the caller has to do
3347 optional-argument handling. This should only be set
3348 to true if the JS-to-native conversion is being done
3349 for an optional argument or dictionary member with no
3350 default value and if the returned template expects
3351 both declType and holderType to be wrapped in
3352 Optional<>, with ${declName} and ${holderName}
3353 adjusted to point to the Value() of the Optional, and
3354 Construct() calls to be made on the Optional<>s as
3355 needed.
3357 declArgs: If not None, the arguments to pass to the ${declName}
3358 constructor. These will have template substitution performed
3359 on them so you can use things like ${val}. This is a
3360 single string, not a list of strings.
3362 holderArgs: If not None, the arguments to pass to the ${holderName}
3363 constructor. These will have template substitution
3364 performed on them so you can use things like ${val}.
3365 This is a single string, not a list of strings.
3367 ${declName} must be in scope before the code from 'template' is entered.
3369 If holderType is not None then ${holderName} must be in scope before
3370 the code from 'template' is entered.
3371 """
3372 assert isinstance(template, str)
3373 assert declType is None or isinstance(declType, CGThing)
3374 assert holderType is None or isinstance(holderType, CGThing)
3375 self.template = template
3376 self.declType = declType
3377 self.holderType = holderType
3378 self.dealWithOptional = dealWithOptional
3379 self.declArgs = declArgs
3380 self.holderArgs = holderArgs
3383 def getHandleDefault(defaultValue):
3384 tag = defaultValue.type.tag()
3385 if tag in numericSuffixes:
3386 # Some numeric literals require a suffix to compile without warnings
3387 return numericValue(tag, defaultValue.value)
3388 assert tag == IDLType.Tags.bool
3389 return toStringBool(defaultValue.value)
3392 def handleDefaultStringValue(defaultValue, method):
3393 """
3394 Returns a string which ends up calling 'method' with a (char16_t*, length)
3395 pair that sets this string default value. This string is suitable for
3396 passing as the second argument of handleDefault; in particular it does not
3397 end with a ';'
3398 """
3399 assert defaultValue.type.isDOMString()
3400 return ("static const char16_t data[] = { %s };\n"
3401 "%s(data, ArrayLength(data) - 1)" %
3402 (", ".join(["'" + char + "'" for char in
3403 defaultValue.value] + ["0"]),
3404 method))
3407 # If this function is modified, modify CGNativeMember.getArg and
3408 # CGNativeMember.getRetvalInfo accordingly. The latter cares about the decltype
3409 # and holdertype we end up using, because it needs to be able to return the code
3410 # that will convert those to the actual return value of the callback function.
3411 def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
3412 isDefinitelyObject=False,
3413 isMember=False,
3414 isOptional=False,
3415 invalidEnumValueFatal=True,
3416 defaultValue=None,
3417 treatNullAs="Default",
3418 isEnforceRange=False,
3419 isClamp=False,
3420 isNullOrUndefined=False,
3421 exceptionCode=None,
3422 lenientFloatCode=None,
3423 allowTreatNonCallableAsNull=False,
3424 isCallbackReturnValue=False,
3425 sourceDescription="value"):
3426 """
3427 Get a template for converting a JS value to a native object based on the
3428 given type and descriptor. If failureCode is given, then we're actually
3429 testing whether we can convert the argument to the desired type. That
3430 means that failures to convert due to the JS value being the wrong type of
3431 value need to use failureCode instead of throwing exceptions. Failures to
3432 convert that are due to JS exceptions (from toString or valueOf methods) or
3433 out of memory conditions need to throw exceptions no matter what
3434 failureCode is. However what actually happens when throwing an exception
3435 can be controlled by exceptionCode. The only requirement on that is that
3436 exceptionCode must end up doing a return, and every return from this
3437 function must happen via exceptionCode if exceptionCode is not None.
3439 If isDefinitelyObject is True, that means we know the value
3440 isObject() and we have no need to recheck that.
3442 if isMember is not False, we're being converted from a property of some JS
3443 object, not from an actual method argument, so we can't rely on our jsval
3444 being rooted or outliving us in any way. Callers can pass "Dictionary",
3445 "Variadic", "Sequence", or "OwningUnion" to indicate that the conversion is
3446 for something that is a dictionary member, a variadic argument, a sequence,
3447 or an owning union respectively.
3449 If isOptional is true, then we are doing conversion of an optional
3450 argument with no default value.
3452 invalidEnumValueFatal controls whether an invalid enum value conversion
3453 attempt will throw (if true) or simply return without doing anything (if
3454 false).
3456 If defaultValue is not None, it's the IDL default value for this conversion
3458 If isEnforceRange is true, we're converting an integer and throwing if the
3459 value is out of range.
3461 If isClamp is true, we're converting an integer and clamping if the
3462 value is out of range.
3464 If lenientFloatCode is not None, it should be used in cases when
3465 we're a non-finite float that's not unrestricted.
3467 If allowTreatNonCallableAsNull is true, then [TreatNonCallableAsNull] and
3468 [TreatNonObjectAsNull] extended attributes on nullable callback functions
3469 will be honored.
3471 If isCallbackReturnValue is "JSImpl" or "Callback", then the declType may be
3472 adjusted to make it easier to return from a callback. Since that type is
3473 never directly observable by any consumers of the callback code, this is OK.
3474 Furthermore, if isCallbackReturnValue is "JSImpl", that affects the behavior
3475 of the FailureFatalCastableObjectUnwrapper conversion; this is used for
3476 implementing auto-wrapping of JS-implemented return values from a
3477 JS-implemented interface.
3479 sourceDescription is a description of what this JS value represents, to be
3480 used in error reporting. Callers should assume that it might get placed in
3481 the middle of a sentence. If it ends up at the beginning of a sentence, its
3482 first character will be automatically uppercased.
3484 The return value from this function is a JSToNativeConversionInfo.
3485 """
3486 # If we have a defaultValue then we're not actually optional for
3487 # purposes of what we need to be declared as.
3488 assert defaultValue is None or not isOptional
3490 # Also, we should not have a defaultValue if we know we're an object
3491 assert not isDefinitelyObject or defaultValue is None
3493 # And we can't both be an object and be null or undefined
3494 assert not isDefinitelyObject or not isNullOrUndefined
3496 # If exceptionCode is not set, we'll just rethrow the exception we got.
3497 # Note that we can't just set failureCode to exceptionCode, because setting
3498 # failureCode will prevent pending exceptions from being set in cases when
3499 # they really should be!
3500 if exceptionCode is None:
3501 exceptionCode = "return false;\n"
3502 # We often want exceptionCode to be indented, since it often appears in an
3503 # if body.
3504 exceptionCodeIndented = CGIndenter(CGGeneric(exceptionCode))
3506 # Unfortunately, .capitalize() on a string will lowercase things inside the
3507 # string, which we do not want.
3508 def firstCap(string):
3509 return string[0].upper() + string[1:]
3511 # Helper functions for dealing with failures due to the JS value being the
3512 # wrong type of value
3513 def onFailureNotAnObject(failureCode):
3514 return CGGeneric(
3515 failureCode or
3516 ('ThrowErrorMessage(cx, MSG_NOT_OBJECT, "%s");\n'
3517 '%s' % (firstCap(sourceDescription), exceptionCode)))
3519 def onFailureBadType(failureCode, typeName):
3520 return CGGeneric(
3521 failureCode or
3522 ('ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s", "%s");\n'
3523 '%s' % (firstCap(sourceDescription), typeName, exceptionCode)))
3525 def onFailureNotCallable(failureCode):
3526 return CGGeneric(
3527 failureCode or
3528 ('ThrowErrorMessage(cx, MSG_NOT_CALLABLE, "%s");\n'
3529 '%s' % (firstCap(sourceDescription), exceptionCode)))
3531 # A helper function for handling default values. Takes a template
3532 # body and the C++ code to set the default value and wraps the
3533 # given template body in handling for the default value.
3534 def handleDefault(template, setDefault):
3535 if defaultValue is None:
3536 return template
3537 return (
3538 "if (${haveValue}) {\n" +
3539 indent(template) +
3540 "} else {\n" +
3541 indent(setDefault) +
3542 "}\n")
3544 # A helper function for handling null default values. Much like
3545 # handleDefault, but checks that the default value, if it exists, is null.
3546 def handleDefaultNull(template, codeToSetNull):
3547 if (defaultValue is not None and
3548 not isinstance(defaultValue, IDLNullValue)):
3549 raise TypeError("Can't handle non-null default value here")
3550 return handleDefault(template, codeToSetNull)
3552 # A helper function for wrapping up the template body for
3553 # possibly-nullable objecty stuff
3554 def wrapObjectTemplate(templateBody, type, codeToSetNull, failureCode=None):
3555 if isNullOrUndefined and type.nullable():
3556 # Just ignore templateBody and set ourselves to null.
3557 # Note that we don't have to worry about default values
3558 # here either, since we already examined this value.
3559 return codeToSetNull
3561 if not isDefinitelyObject:
3562 # Handle the non-object cases by wrapping up the whole
3563 # thing in an if cascade.
3564 if type.nullable():
3565 elifLine = "} else if (${val}.isNullOrUndefined()) {\n"
3566 elifBody = codeToSetNull
3567 else:
3568 elifLine = ""
3569 elifBody = ""
3571 # Note that $${val} below expands to ${val}. This string is
3572 # used as a template later, and val will be filled in then.
3573 templateBody = fill(
3574 """
3575 if ($${val}.isObject()) {
3576 $*{templateBody}
3577 $*{elifLine}
3578 $*{elifBody}
3579 } else {
3580 $*{failureBody}
3581 }
3582 """,
3583 templateBody=templateBody,
3584 elifLine=elifLine,
3585 elifBody=elifBody,
3586 failureBody=onFailureNotAnObject(failureCode).define())
3588 if type.nullable():
3589 templateBody = handleDefaultNull(templateBody, codeToSetNull)
3590 else:
3591 assert defaultValue is None
3593 return templateBody
3595 # A helper function for converting things that look like a JSObject*.
3596 def handleJSObjectType(type, isMember, failureCode):
3597 if not isMember:
3598 if isOptional:
3599 # We have a specialization of Optional that will use a
3600 # Rooted for the storage here.
3601 declType = CGGeneric("JS::Handle<JSObject*>")
3602 else:
3603 declType = CGGeneric("JS::Rooted<JSObject*>")
3604 declArgs = "cx"
3605 else:
3606 assert (isMember in
3607 ("Sequence", "Variadic", "Dictionary", "OwningUnion", "MozMap"))
3608 # We'll get traced by the sequence or dictionary or union tracer
3609 declType = CGGeneric("JSObject*")
3610 declArgs = None
3611 templateBody = "${declName} = &${val}.toObject();\n"
3612 setToNullCode = "${declName} = nullptr;\n"
3613 template = wrapObjectTemplate(templateBody, type, setToNullCode,
3614 failureCode)
3615 return JSToNativeConversionInfo(template, declType=declType,
3616 dealWithOptional=isOptional,
3617 declArgs=declArgs)
3619 assert not (isEnforceRange and isClamp) # These are mutually exclusive
3621 if type.isArray():
3622 raise TypeError("Can't handle array arguments yet")
3624 if type.isSequence():
3625 assert not isEnforceRange and not isClamp
3627 if failureCode is None:
3628 notSequence = ('ThrowErrorMessage(cx, MSG_NOT_SEQUENCE, "%s");\n'
3629 "%s" % (firstCap(sourceDescription), exceptionCode))
3630 else:
3631 notSequence = failureCode
3633 nullable = type.nullable()
3634 # Be very careful not to change "type": we need it later
3635 if nullable:
3636 elementType = type.inner.inner
3637 else:
3638 elementType = type.inner
3640 # We want to use auto arrays if we can, but we have to be careful with
3641 # reallocation behavior for arrays. In particular, if we use auto
3642 # arrays for sequences and have a sequence of elements which are
3643 # themselves sequences or have sequences as members, we have a problem.
3644 # In that case, resizing the outermost nsAutoTarray to the right size
3645 # will memmove its elements, but nsAutoTArrays are not memmovable and
3646 # hence will end up with pointers to bogus memory, which is bad. To
3647 # deal with this, we typically map WebIDL sequences to our Sequence
3648 # type, which is in fact memmovable. The one exception is when we're
3649 # passing in a sequence directly as an argument without any sort of
3650 # optional or nullable complexity going on. In that situation, we can
3651 # use an AutoSequence instead. We have to keep using Sequence in the
3652 # nullable and optional cases because we don't want to leak the
3653 # AutoSequence type to consumers, which would be unavoidable with
3654 # Nullable<AutoSequence> or Optional<AutoSequence>.
3655 if isMember or isOptional or nullable or isCallbackReturnValue:
3656 sequenceClass = "Sequence"
3657 else:
3658 sequenceClass = "binding_detail::AutoSequence"
3660 # XXXbz we can't include the index in the sourceDescription, because
3661 # we don't really have a way to pass one in dynamically at runtime...
3662 elementInfo = getJSToNativeConversionInfo(
3663 elementType, descriptorProvider, isMember="Sequence",
3664 exceptionCode=exceptionCode, lenientFloatCode=lenientFloatCode,
3665 isCallbackReturnValue=isCallbackReturnValue,
3666 sourceDescription="element of %s" % sourceDescription)
3667 if elementInfo.dealWithOptional:
3668 raise TypeError("Shouldn't have optional things in sequences")
3669 if elementInfo.holderType is not None:
3670 raise TypeError("Shouldn't need holders for sequences")
3672 typeName = CGTemplatedType(sequenceClass, elementInfo.declType)
3673 sequenceType = typeName.define()
3674 if nullable:
3675 typeName = CGTemplatedType("Nullable", typeName)
3676 arrayRef = "${declName}.SetValue()"
3677 else:
3678 arrayRef = "${declName}"
3680 elementConversion = string.Template(elementInfo.template).substitute({
3681 "val": "temp",
3682 "mutableVal": "&temp",
3683 "declName": "slot",
3684 # We only need holderName here to handle isExternal()
3685 # interfaces, which use an internal holder for the
3686 # conversion even when forceOwningType ends up true.
3687 "holderName": "tempHolder"
3688 })
3690 # NOTE: Keep this in sync with variadic conversions as needed
3691 templateBody = fill(
3692 """
3693 JS::ForOfIterator iter(cx);
3694 if (!iter.init($${val}, JS::ForOfIterator::AllowNonIterable)) {
3695 $*{exceptionCode}
3696 }
3697 if (!iter.valueIsIterable()) {
3698 $*{notSequence}
3699 }
3700 ${sequenceType} &arr = ${arrayRef};
3701 JS::Rooted<JS::Value> temp(cx);
3702 while (true) {
3703 bool done;
3704 if (!iter.next(&temp, &done)) {
3705 $*{exceptionCode}
3706 }
3707 if (done) {
3708 break;
3709 }
3710 ${elementType}* slotPtr = arr.AppendElement();
3711 if (!slotPtr) {
3712 JS_ReportOutOfMemory(cx);
3713 $*{exceptionCode}
3714 }
3715 ${elementType}& slot = *slotPtr;
3716 $*{elementConversion}
3717 }
3718 """,
3719 exceptionCode=exceptionCode,
3720 notSequence=notSequence,
3721 sequenceType=sequenceType,
3722 arrayRef=arrayRef,
3723 elementType=elementInfo.declType.define(),
3724 elementConversion=elementConversion)
3726 templateBody = wrapObjectTemplate(templateBody, type,
3727 "${declName}.SetNull();\n", notSequence)
3728 # Sequence arguments that might contain traceable things need
3729 # to get traced
3730 if not isMember and typeNeedsRooting(elementType):
3731 holderType = CGTemplatedType("SequenceRooter", elementInfo.declType)
3732 # If our sequence is nullable, this will set the Nullable to be
3733 # not-null, but that's ok because we make an explicit SetNull() call
3734 # on it as needed if our JS value is actually null.
3735 holderArgs = "cx, &%s" % arrayRef
3736 else:
3737 holderType = None
3738 holderArgs = None
3740 return JSToNativeConversionInfo(templateBody, declType=typeName,
3741 holderType=holderType,
3742 dealWithOptional=isOptional,
3743 holderArgs=holderArgs)
3745 if type.isMozMap():
3746 assert not isEnforceRange and not isClamp
3747 if failureCode is None:
3748 notMozMap = ('ThrowErrorMessage(cx, MSG_NOT_OBJECT, "%s");\n'
3749 "%s" % (firstCap(sourceDescription), exceptionCode))
3750 else:
3751 notMozMap = failureCode
3753 nullable = type.nullable()
3754 # Be very careful not to change "type": we need it later
3755 if nullable:
3756 valueType = type.inner.inner
3757 else:
3758 valueType = type.inner
3760 valueInfo = getJSToNativeConversionInfo(
3761 valueType, descriptorProvider, isMember="MozMap",
3762 exceptionCode=exceptionCode, lenientFloatCode=lenientFloatCode,
3763 isCallbackReturnValue=isCallbackReturnValue,
3764 sourceDescription="value in %s" % sourceDescription)
3765 if valueInfo.dealWithOptional:
3766 raise TypeError("Shouldn't have optional things in MozMap")
3767 if valueInfo.holderType is not None:
3768 raise TypeError("Shouldn't need holders for MozMap")
3770 typeName = CGTemplatedType("MozMap", valueInfo.declType)
3771 mozMapType = typeName.define()
3772 if nullable:
3773 typeName = CGTemplatedType("Nullable", typeName)
3774 mozMapRef = "${declName}.SetValue()"
3775 else:
3776 mozMapRef = "${declName}"
3778 valueConversion = string.Template(valueInfo.template).substitute({
3779 "val": "temp",
3780 "mutableVal": "&temp",
3781 "declName": "slot",
3782 # We only need holderName here to handle isExternal()
3783 # interfaces, which use an internal holder for the
3784 # conversion even when forceOwningType ends up true.
3785 "holderName": "tempHolder"
3786 })
3788 templateBody = fill(
3789 """
3790 ${mozMapType} &mozMap = ${mozMapRef};
3792 JS::Rooted<JSObject*> mozMapObj(cx, &$${val}.toObject());
3793 JS::AutoIdArray ids(cx, JS_Enumerate(cx, mozMapObj));
3794 if (!ids) {
3795 $*{exceptionCode}
3796 }
3797 JS::Rooted<JS::Value> propNameValue(cx);
3798 JS::Rooted<JS::Value> temp(cx);
3799 JS::Rooted<jsid> curId(cx);
3800 for (size_t i = 0; i < ids.length(); ++i) {
3801 // Make sure we get the value before converting the name, since
3802 // getting the value can trigger GC but our name is a dependent
3803 // string.
3804 curId = ids[i];
3805 binding_detail::FakeDependentString propName;
3806 if (!JS_GetPropertyById(cx, mozMapObj, curId, &temp) ||
3807 !JS_IdToValue(cx, curId, &propNameValue) ||
3808 !ConvertJSValueToString(cx, propNameValue, &propNameValue,
3809 eStringify, eStringify, propName)) {
3810 $*{exceptionCode}
3811 }
3813 ${valueType}* slotPtr = mozMap.AddEntry(propName);
3814 if (!slotPtr) {
3815 JS_ReportOutOfMemory(cx);
3816 $*{exceptionCode}
3817 }
3818 ${valueType}& slot = *slotPtr;
3819 $*{valueConversion}
3820 }
3821 """,
3822 exceptionCode=exceptionCode,
3823 mozMapType=mozMapType,
3824 mozMapRef=mozMapRef,
3825 valueType=valueInfo.declType.define(),
3826 valueConversion=valueConversion)
3828 templateBody = wrapObjectTemplate(templateBody, type,
3829 "${declName}.SetNull();\n",
3830 notMozMap)
3832 declType = typeName
3833 declArgs = None
3834 holderType = None
3835 holderArgs = None
3836 # MozMap arguments that might contain traceable things need
3837 # to get traced
3838 if not isMember and isCallbackReturnValue:
3839 # Go ahead and just convert directly into our actual return value
3840 declType = CGWrapper(declType, post="&")
3841 declArgs = "aRetVal"
3842 elif not isMember and typeNeedsRooting(valueType):
3843 holderType = CGTemplatedType("MozMapRooter", valueInfo.declType)
3844 # If our MozMap is nullable, this will set the Nullable to be
3845 # not-null, but that's ok because we make an explicit SetNull() call
3846 # on it as needed if our JS value is actually null.
3847 holderArgs = "cx, &%s" % mozMapRef
3849 return JSToNativeConversionInfo(templateBody, declType=declType,
3850 declArgs=declArgs,
3851 holderType=holderType,
3852 dealWithOptional=isOptional,
3853 holderArgs=holderArgs)
3855 if type.isUnion():
3856 nullable = type.nullable()
3857 if nullable:
3858 type = type.inner
3860 unionArgumentObj = "${declName}" if isMember else "${holderName}"
3861 if nullable:
3862 # If we're a member, we're a Nullable, which hasn't been told it has
3863 # a value. Otherwise we're an already-constructed Maybe.
3864 unionArgumentObj += ".SetValue()" if isMember else ".ref()"
3866 memberTypes = type.flatMemberTypes
3867 names = []
3869 interfaceMemberTypes = filter(lambda t: t.isNonCallbackInterface(), memberTypes)
3870 if len(interfaceMemberTypes) > 0:
3871 interfaceObject = []
3872 for memberType in interfaceMemberTypes:
3873 if type.isGeckoInterface():
3874 name = memberType.inner.identifier.name
3875 else:
3876 name = memberType.name
3877 interfaceObject.append(
3878 CGGeneric("(failed = !%s.TrySetTo%s(cx, ${val}, ${mutableVal}, tryNext)) || !tryNext" %
3879 (unionArgumentObj, name)))
3880 names.append(name)
3881 interfaceObject = CGWrapper(CGList(interfaceObject, " ||\n"),
3882 pre="done = ", post=";\n\n", reindent=True)
3883 else:
3884 interfaceObject = None
3886 arrayObjectMemberTypes = filter(lambda t: t.isArray() or t.isSequence(), memberTypes)
3887 if len(arrayObjectMemberTypes) > 0:
3888 raise TypeError("Bug 767924: We don't support sequences in unions yet")
3889 else:
3890 arrayObject = None
3892 dateObjectMemberTypes = filter(lambda t: t.isDate(), memberTypes)
3893 if len(dateObjectMemberTypes) > 0:
3894 assert len(dateObjectMemberTypes) == 1
3895 memberType = dateObjectMemberTypes[0]
3896 name = memberType.name
3897 dateObject = CGGeneric("%s.SetTo%s(cx, ${val}, ${mutableVal});\n"
3898 "done = true;\n" % (unionArgumentObj, name))
3899 dateObject = CGIfWrapper(dateObject, "JS_ObjectIsDate(cx, argObj)")
3900 names.append(name)
3901 else:
3902 dateObject = None
3904 callbackMemberTypes = filter(lambda t: t.isCallback() or t.isCallbackInterface(), memberTypes)
3905 if len(callbackMemberTypes) > 0:
3906 assert len(callbackMemberTypes) == 1
3907 memberType = callbackMemberTypes[0]
3908 name = memberType.name
3909 callbackObject = CGGeneric(
3910 "done = (failed = !%s.TrySetTo%s(cx, ${val}, ${mutableVal}, tryNext)) || !tryNext;\n" %
3911 (unionArgumentObj, name))
3912 names.append(name)
3913 else:
3914 callbackObject = None
3916 dictionaryMemberTypes = filter(lambda t: t.isDictionary(), memberTypes)
3917 if len(dictionaryMemberTypes) > 0:
3918 assert len(dictionaryMemberTypes) == 1
3919 name = dictionaryMemberTypes[0].inner.identifier.name
3920 setDictionary = CGGeneric(
3921 "done = (failed = !%s.TrySetTo%s(cx, ${val}, ${mutableVal}, tryNext)) || !tryNext;\n" %
3922 (unionArgumentObj, name))
3923 names.append(name)
3924 else:
3925 setDictionary = None
3927 mozMapMemberTypes = filter(lambda t: t.isMozMap(), memberTypes)
3928 if len(mozMapMemberTypes) > 0:
3929 raise TypeError("We don't support MozMap in unions yet")
3931 objectMemberTypes = filter(lambda t: t.isObject(), memberTypes)
3932 if len(objectMemberTypes) > 0:
3933 assert len(objectMemberTypes) == 1
3934 # Very important to NOT construct a temporary Rooted here, since the
3935 # SetToObject call can call a Rooted constructor and we need to keep
3936 # stack discipline for Rooted.
3937 object = CGGeneric("%s.SetToObject(cx, &${val}.toObject());\n"
3938 "done = true;\n" % unionArgumentObj)
3939 names.append(objectMemberTypes[0].name)
3940 else:
3941 object = None
3943 hasObjectTypes = interfaceObject or arrayObject or dateObject or callbackObject or object
3944 if hasObjectTypes:
3945 # "object" is not distinguishable from other types
3946 assert not object or not (interfaceObject or arrayObject or dateObject or callbackObject)
3947 if arrayObject or dateObject or callbackObject:
3948 # An object can be both an array object and a callback or
3949 # dictionary, but we shouldn't have both in the union's members
3950 # because they are not distinguishable.
3951 assert not (arrayObject and callbackObject)
3952 templateBody = CGElseChain([arrayObject, dateObject, callbackObject])
3953 else:
3954 templateBody = None
3955 if interfaceObject:
3956 assert not object
3957 if templateBody:
3958 templateBody = CGIfWrapper(templateBody, "!done")
3959 templateBody = CGList([interfaceObject, templateBody])
3960 else:
3961 templateBody = CGList([templateBody, object])
3963 if dateObject:
3964 templateBody.prepend(CGGeneric("JS::Rooted<JSObject*> argObj(cx, &${val}.toObject());\n"))
3965 templateBody = CGIfWrapper(templateBody, "${val}.isObject()")
3966 else:
3967 templateBody = CGGeneric()
3969 if setDictionary:
3970 assert not object
3971 templateBody = CGList([templateBody,
3972 CGIfWrapper(setDictionary, "!done")])
3974 stringTypes = [t for t in memberTypes if t.isString() or t.isEnum()]
3975 numericTypes = [t for t in memberTypes if t.isNumeric()]
3976 booleanTypes = [t for t in memberTypes if t.isBoolean()]
3977 if stringTypes or numericTypes or booleanTypes:
3978 assert len(stringTypes) <= 1
3979 assert len(numericTypes) <= 1
3980 assert len(booleanTypes) <= 1
3982 # We will wrap all this stuff in a do { } while (0); so we
3983 # can use "break" for flow control.
3984 def getStringOrPrimitiveConversion(memberType):
3985 if memberType.isEnum():
3986 name = memberType.inner.identifier.name
3987 else:
3988 name = memberType.name
3989 return CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${mutableVal}, tryNext)) || !tryNext;\n"
3990 "break;\n" % (unionArgumentObj, name))
3991 other = CGList([])
3992 stringConversion = map(getStringOrPrimitiveConversion, stringTypes)
3993 numericConversion = map(getStringOrPrimitiveConversion, numericTypes)
3994 booleanConversion = map(getStringOrPrimitiveConversion, booleanTypes)
3995 if stringConversion:
3996 if booleanConversion:
3997 other.append(CGIfWrapper(booleanConversion[0],
3998 "${val}.isBoolean()"))
3999 if numericConversion:
4000 other.append(CGIfWrapper(numericConversion[0],
4001 "${val}.isNumber()"))
4002 other.append(stringConversion[0])
4003 elif numericConversion:
4004 if booleanConversion:
4005 other.append(CGIfWrapper(booleanConversion[0],
4006 "${val}.isBoolean()"))
4007 other.append(numericConversion[0])
4008 else:
4009 assert booleanConversion
4010 other.append(booleanConversion[0])
4012 other = CGWrapper(CGIndenter(other), pre="do {\n", post="} while (0);\n")
4013 if hasObjectTypes or setDictionary:
4014 other = CGWrapper(CGIndenter(other), "{\n", post="}\n")
4015 if object:
4016 templateBody = CGElseChain([templateBody, other])
4017 else:
4018 other = CGWrapper(other, pre="if (!done) ")
4019 templateBody = CGList([templateBody, other])
4020 else:
4021 assert templateBody.define() == ""
4022 templateBody = other
4023 else:
4024 other = None
4026 templateBody = CGWrapper(templateBody, pre="bool done = false, failed = false, tryNext;\n")
4027 throw = CGGeneric(fill(
4028 """
4029 if (failed) {
4030 $*{exceptionCode}
4031 }
4032 if (!done) {
4033 ThrowErrorMessage(cx, MSG_NOT_IN_UNION, "${desc}", "${names}");
4034 $*{exceptionCode}
4035 }
4036 """,
4037 exceptionCode=exceptionCode,
4038 desc=firstCap(sourceDescription),
4039 names=", ".join(names)))
4041 templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw])), pre="{\n", post="}\n")
4043 typeName = CGUnionStruct.unionTypeDecl(type, isMember)
4044 argumentTypeName = typeName + "Argument"
4045 if nullable:
4046 typeName = "Nullable<" + typeName + " >"
4048 def handleNull(templateBody, setToNullVar, extraConditionForNull=""):
4049 nullTest = "%s${val}.isNullOrUndefined()" % extraConditionForNull
4050 return CGIfElseWrapper(nullTest,
4051 CGGeneric("%s.SetNull();\n" % setToNullVar),
4052 templateBody)
4054 if type.hasNullableType:
4055 assert not nullable
4056 # Make sure to handle a null default value here
4057 if defaultValue and isinstance(defaultValue, IDLNullValue):
4058 assert defaultValue.type == type
4059 extraConditionForNull = "!(${haveValue}) || "
4060 else:
4061 extraConditionForNull = ""
4062 templateBody = handleNull(templateBody, unionArgumentObj,
4063 extraConditionForNull=extraConditionForNull)
4065 declType = CGGeneric(typeName)
4066 if isMember:
4067 holderType = None
4068 else:
4069 holderType = CGGeneric(argumentTypeName)
4070 if nullable:
4071 holderType = CGTemplatedType("Maybe", holderType)
4073 # If we're isOptional and not nullable the normal optional handling will
4074 # handle lazy construction of our holder. If we're nullable and not
4075 # isMember we do it all by hand because we do not want our holder
4076 # constructed if we're null. But if we're isMember we don't have a
4077 # holder anyway, so we can do the normal Optional codepath.
4078 declLoc = "${declName}"
4079 constructDecl = None
4080 if nullable:
4081 if isOptional and not isMember:
4082 holderArgs = "${declName}.Value().SetValue()"
4083 declType = CGTemplatedType("Optional", declType)
4084 constructDecl = CGGeneric("${declName}.Construct();\n")
4085 declLoc = "${declName}.Value()"
4086 else:
4087 holderArgs = "${declName}.SetValue()"
4088 if holderType is not None:
4089 constructHolder = CGGeneric("${holderName}.construct(%s);\n" % holderArgs)
4090 else:
4091 constructHolder = None
4092 # Don't need to pass those args when the holder is being constructed
4093 holderArgs = None
4094 else:
4095 holderArgs = "${declName}"
4096 constructHolder = None
4098 if defaultValue and not isinstance(defaultValue, IDLNullValue):
4099 tag = defaultValue.type.tag()
4101 if tag in numericSuffixes or tag is IDLType.Tags.bool:
4102 defaultStr = getHandleDefault(defaultValue)
4103 value = declLoc + (".Value()" if nullable else "")
4104 default = CGGeneric("%s.RawSetAs%s() = %s;\n" %
4105 (value, defaultValue.type, defaultStr))
4106 else:
4107 default = CGGeneric(
4108 handleDefaultStringValue(
4109 defaultValue, "%s.SetStringData" % unionArgumentObj) +
4110 ";\n")
4112 templateBody = CGIfElseWrapper("!(${haveValue})", default, templateBody)
4114 templateBody = CGList([constructHolder, templateBody])
4116 if nullable:
4117 if defaultValue:
4118 if isinstance(defaultValue, IDLNullValue):
4119 extraConditionForNull = "!(${haveValue}) || "
4120 else:
4121 extraConditionForNull = "${haveValue} && "
4122 else:
4123 extraConditionForNull = ""
4124 templateBody = handleNull(templateBody, declLoc,
4125 extraConditionForNull=extraConditionForNull)
4126 elif (not type.hasNullableType and defaultValue and
4127 isinstance(defaultValue, IDLNullValue)):
4128 assert type.hasDictionaryType
4129 assert defaultValue.type.isDictionary()
4130 if not isMember and typeNeedsRooting(defaultValue.type):
4131 ctorArgs = "cx"
4132 else:
4133 ctorArgs = ""
4134 initDictionaryWithNull = CGIfWrapper(
4135 CGGeneric("return false;\n"),
4136 ('!%s.RawSetAs%s(%s).Init(cx, JS::NullHandleValue, "Member of %s")'
4137 % (declLoc, getUnionMemberName(defaultValue.type),
4138 ctorArgs, type)))
4139 templateBody = CGIfElseWrapper("!(${haveValue})",
4140 initDictionaryWithNull,
4141 templateBody)
4143 templateBody = CGList([constructDecl, templateBody])
4145 return JSToNativeConversionInfo(templateBody.define(),
4146 declType=declType,
4147 holderType=holderType,
4148 holderArgs=holderArgs,
4149 dealWithOptional=isOptional and (not nullable or isMember))
4151 if type.isGeckoInterface():
4152 assert not isEnforceRange and not isClamp
4154 descriptor = descriptorProvider.getDescriptor(
4155 type.unroll().inner.identifier.name)
4157 if descriptor.nativeType == 'JSObject':
4158 # XXXbz Workers code does this sometimes
4159 assert descriptor.workers
4160 return handleJSObjectType(type, isMember, failureCode)
4162 if descriptor.interface.isCallback():
4163 name = descriptor.interface.identifier.name
4164 if type.nullable() or isCallbackReturnValue:
4165 declType = CGGeneric("nsRefPtr<%s>" % name)
4166 else:
4167 declType = CGGeneric("OwningNonNull<%s>" % name)
4168 # BOGUS extra blank line here turns out to be at the end of a block:
4169 conversion = indent(CGCallbackTempRoot(name).define()) + "\n"
4171 template = wrapObjectTemplate(conversion, type,
4172 "${declName} = nullptr;\n",
4173 failureCode)
4174 return JSToNativeConversionInfo(template, declType=declType,
4175 dealWithOptional=isOptional)
4177 # This is an interface that we implement as a concrete class
4178 # or an XPCOM interface.
4180 # Allow null pointers for nullable types and old-binding classes, and
4181 # use an nsRefPtr or raw pointer for callback return values to make
4182 # them easier to return.
4183 argIsPointer = (type.nullable() or type.unroll().inner.isExternal() or
4184 isCallbackReturnValue)
4186 # Sequences and non-worker callbacks have to hold a strong ref to the
4187 # thing being passed down. Union return values must hold a strong ref
4188 # because they may be returning an addrefed pointer.
4189 # Also, callback return values always end up
4190 # addrefing anyway, so there is no point trying to avoid it here and it
4191 # makes other things simpler since we can assume the return value is a
4192 # strong ref.
4193 forceOwningType = ((descriptor.interface.isCallback() and
4194 not descriptor.workers) or
4195 isMember or
4196 isCallbackReturnValue)
4198 if forceOwningType and descriptor.nativeOwnership == 'owned':
4199 raise TypeError("Interface %s has 'owned' nativeOwnership, so we "
4200 "don't know how to keep it alive in %s" %
4201 (descriptor.interface.identifier.name,
4202 sourceDescription))
4204 typeName = descriptor.nativeType
4205 typePtr = typeName + "*"
4207 # Compute a few things:
4208 # - declType is the type we want to return as the first element of our
4209 # tuple.
4210 # - holderType is the type we want to return as the third element
4211 # of our tuple.
4213 # Set up some sensible defaults for these things insofar as we can.
4214 holderType = None
4215 if argIsPointer:
4216 if forceOwningType:
4217 declType = "nsRefPtr<" + typeName + ">"
4218 else:
4219 declType = typePtr
4220 else:
4221 if forceOwningType:
4222 declType = "OwningNonNull<" + typeName + ">"
4223 else:
4224 declType = "NonNull<" + typeName + ">"
4226 templateBody = ""
4227 if not descriptor.skipGen and not descriptor.interface.isConsequential() and not descriptor.interface.isExternal():
4228 if failureCode is not None:
4229 templateBody += str(CastableObjectUnwrapper(
4230 descriptor,
4231 "&${val}.toObject()",
4232 "${declName}",
4233 failureCode))
4234 else:
4235 templateBody += str(FailureFatalCastableObjectUnwrapper(
4236 descriptor,
4237 "&${val}.toObject()",
4238 "${declName}",
4239 exceptionCode,
4240 isCallbackReturnValue,
4241 firstCap(sourceDescription)))
4242 elif descriptor.workers:
4243 return handleJSObjectType(type, isMember, failureCode)
4244 else:
4245 # Either external, or new-binding non-castable. We always have a
4246 # holder for these, because we don't actually know whether we have
4247 # to addref when unwrapping or not. So we just pass an
4248 # getter_AddRefs(nsRefPtr) to XPConnect and if we'll need a release
4249 # it'll put a non-null pointer in there.
4250 if forceOwningType:
4251 # Don't return a holderType in this case; our declName
4252 # will just own stuff.
4253 templateBody += "nsRefPtr<" + typeName + "> ${holderName};\n"
4254 else:
4255 holderType = "nsRefPtr<" + typeName + ">"
4256 templateBody += (
4257 "JS::Rooted<JS::Value> tmpVal(cx, ${val});\n" +
4258 typePtr + " tmp;\n"
4259 "if (NS_FAILED(UnwrapArg<" + typeName + ">(cx, ${val}, &tmp, static_cast<" + typeName + "**>(getter_AddRefs(${holderName})), &tmpVal))) {\n")
4260 templateBody += CGIndenter(onFailureBadType(failureCode,
4261 descriptor.interface.identifier.name)).define()
4262 templateBody += ("}\n"
4263 "MOZ_ASSERT(tmp);\n")
4265 if not isDefinitelyObject and not forceOwningType:
4266 # Our tmpVal will go out of scope, so we can't rely on it
4267 # for rooting
4268 templateBody += dedent("""
4269 if (tmpVal != ${val} && !${holderName}) {
4270 // We have to have a strong ref, because we got this off
4271 // some random object that might get GCed
4272 ${holderName} = tmp;
4273 }
4274 """)
4276 # And store our tmp, before it goes out of scope.
4277 templateBody += "${declName} = tmp;\n"
4279 # Just pass failureCode, not onFailureBadType, here, so we'll report the
4280 # thing as not an object as opposed to not implementing whatever our
4281 # interface is.
4282 templateBody = wrapObjectTemplate(templateBody, type,
4283 "${declName} = nullptr;\n", failureCode)
4285 declType = CGGeneric(declType)
4286 if holderType is not None:
4287 holderType = CGGeneric(holderType)
4288 return JSToNativeConversionInfo(templateBody,
4289 declType=declType,
4290 holderType=holderType,
4291 dealWithOptional=isOptional)
4293 if type.isSpiderMonkeyInterface():
4294 assert not isEnforceRange and not isClamp
4295 name = type.name
4296 arrayType = CGGeneric(name)
4297 declType = arrayType
4298 if type.nullable():
4299 declType = CGTemplatedType("Nullable", declType)
4300 objRef = "${declName}.SetValue()"
4301 else:
4302 objRef = "${declName}"
4304 # Again, this is a bit strange since we are actually building a
4305 # template string here. ${objRef} and $*{badType} below are filled in
4306 # right now; $${val} expands to ${val}, to be filled in later.
4307 template = fill(
4308 """
4309 if (!${objRef}.Init(&$${val}.toObject())) {
4310 $*{badType}
4311 }
4313 """, # BOGUS extra blank line
4314 objRef=objRef,
4315 badType=onFailureBadType(failureCode, type.name).define())
4316 template = wrapObjectTemplate(template, type, "${declName}.SetNull();\n",
4317 failureCode)
4318 if not isMember:
4319 # This is a bit annoying. In a union we don't want to have a
4320 # holder, since unions don't support that. But if we're optional we
4321 # want to have a holder, so that the callee doesn't see
4322 # Optional<RootedTypedArray<ArrayType> >. So do a holder if we're
4323 # optional and use a RootedTypedArray otherwise.
4324 if isOptional:
4325 holderType = CGTemplatedType("TypedArrayRooter", arrayType)
4326 # If our typed array is nullable, this will set the Nullable to
4327 # be not-null, but that's ok because we make an explicit
4328 # SetNull() call on it as needed if our JS value is actually
4329 # null. XXXbz Because "Maybe" takes const refs for constructor
4330 # arguments, we can't pass a reference here; have to pass a
4331 # pointer.
4332 holderArgs = "cx, &%s" % objRef
4333 declArgs = None
4334 else:
4335 holderType = None
4336 holderArgs = None
4337 declType = CGTemplatedType("RootedTypedArray", declType)
4338 declArgs = "cx"
4339 else:
4340 holderType = None
4341 holderArgs = None
4342 declArgs = None
4343 return JSToNativeConversionInfo(template,
4344 declType=declType,
4345 holderType=holderType,
4346 dealWithOptional=isOptional,
4347 declArgs=declArgs,
4348 holderArgs=holderArgs)
4350 if type.isDOMString():
4351 assert not isEnforceRange and not isClamp
4353 treatAs = {
4354 "Default": "eStringify",
4355 "EmptyString": "eEmpty",
4356 "Null": "eNull",
4357 }
4358 if type.nullable():
4359 # For nullable strings null becomes a null string.
4360 treatNullAs = "Null"
4361 # For nullable strings undefined also becomes a null string.
4362 undefinedBehavior = "eNull"
4363 else:
4364 undefinedBehavior = "eStringify"
4365 nullBehavior = treatAs[treatNullAs]
4367 def getConversionCode(varName):
4368 conversionCode = (
4369 "if (!ConvertJSValueToString(cx, ${val}, ${mutableVal}, %s, %s, %s)) {\n"
4370 "%s"
4371 "}\n" % (nullBehavior, undefinedBehavior, varName,
4372 exceptionCodeIndented.define()))
4373 if defaultValue is None:
4374 return conversionCode
4376 if isinstance(defaultValue, IDLNullValue):
4377 assert(type.nullable())
4378 defaultCode = "%s.SetNull()" % varName
4379 else:
4380 defaultCode = handleDefaultStringValue(defaultValue,
4381 "%s.SetData" % varName)
4382 return handleDefault(conversionCode, defaultCode + ";\n")
4384 if isMember:
4385 # We have to make a copy, except in the variadic case, because our
4386 # jsval may well not live as long as our string needs to.
4387 declType = CGGeneric("nsString")
4388 if isMember == "Variadic":
4389 # The string is kept alive by the argument, so we can just
4390 # depend on it.
4391 assignString = "${declName}.Rebind(str.Data(), str.Length());\n"
4392 else:
4393 assignString = "${declName} = str;\n"
4394 return JSToNativeConversionInfo(
4395 fill(
4396 """
4397 {
4398 binding_detail::FakeDependentString str;
4399 $*{convert}
4400 $*{assign}
4401 }
4403 """, # BOGUS extra newline
4404 convert=getConversionCode("str"),
4405 assign=assignString),
4406 declType=declType,
4407 dealWithOptional=isOptional)
4409 if isOptional:
4410 declType = "Optional<nsAString>"
4411 holderType = CGGeneric("binding_detail::FakeDependentString")
4412 conversionCode = ("%s"
4413 "${declName} = &${holderName};\n" %
4414 getConversionCode("${holderName}"))
4415 else:
4416 declType = "binding_detail::FakeDependentString"
4417 holderType = None
4418 conversionCode = getConversionCode("${declName}")
4420 # No need to deal with optional here; we handled it already
4421 return JSToNativeConversionInfo(
4422 conversionCode,
4423 declType=CGGeneric(declType),
4424 holderType=holderType)
4426 if type.isByteString():
4427 assert not isEnforceRange and not isClamp
4429 nullable = toStringBool(type.nullable())
4431 conversionCode = (
4432 "if (!ConvertJSValueToByteString(cx, ${val}, ${mutableVal}, %s, ${declName})) {\n"
4433 "%s"
4434 "}\n" % (nullable, exceptionCodeIndented.define()))
4435 # ByteString arguments cannot have a default value.
4436 assert defaultValue is None
4438 return JSToNativeConversionInfo(
4439 conversionCode,
4440 declType=CGGeneric("nsCString"),
4441 dealWithOptional=isOptional)
4443 if type.isEnum():
4444 assert not isEnforceRange and not isClamp
4446 enumName = type.unroll().inner.identifier.name
4447 declType = CGGeneric(enumName)
4448 if type.nullable():
4449 declType = CGTemplatedType("Nullable", declType)
4450 declType = declType.define()
4451 enumLoc = "${declName}.SetValue()"
4452 else:
4453 enumLoc = "${declName}"
4454 declType = declType.define()
4456 if invalidEnumValueFatal:
4457 handleInvalidEnumValueCode = "MOZ_ASSERT(index >= 0);\n"
4458 else:
4459 # invalidEnumValueFatal is false only for attributes. So we won't
4460 # have a non-default exceptionCode here unless attribute "arg
4461 # conversion" code starts passing in an exceptionCode. At which
4462 # point we'll need to figure out what that even means.
4463 assert exceptionCode == "return false;\n"
4464 handleInvalidEnumValueCode = dedent("""
4465 if (index < 0) {
4466 return true;
4467 }
4468 """)
4470 template = fill(
4471 """
4472 {
4473 bool ok;
4474 int index = FindEnumStringIndex<${invalidEnumValueFatal}>(cx, $${val}, ${values}, "${enumtype}", "${sourceDescription}", &ok);
4475 if (!ok) {
4476 $*{exceptionCode}
4477 }
4478 $*{handleInvalidEnumValueCode}
4479 ${enumLoc} = static_cast<${enumtype}>(index);
4480 }
4481 """,
4482 enumtype=enumName,
4483 values=enumName + "Values::" + ENUM_ENTRY_VARIABLE_NAME,
4484 invalidEnumValueFatal=toStringBool(invalidEnumValueFatal),
4485 handleInvalidEnumValueCode=handleInvalidEnumValueCode,
4486 exceptionCode=exceptionCode,
4487 enumLoc=enumLoc,
4488 sourceDescription=firstCap(sourceDescription))
4490 setNull = "${declName}.SetNull();\n"
4492 if type.nullable():
4493 template = CGIfElseWrapper("${val}.isNullOrUndefined()",
4494 CGGeneric(setNull),
4495 CGGeneric(template)).define()
4497 if defaultValue is not None:
4498 if isinstance(defaultValue, IDLNullValue):
4499 assert type.nullable()
4500 template = handleDefault(template, setNull)
4501 else:
4502 assert(defaultValue.type.tag() == IDLType.Tags.domstring)
4503 template = handleDefault(template,
4504 ("%s = %s::%s;\n" %
4505 (enumLoc, enumName,
4506 getEnumValueName(defaultValue.value))))
4507 return JSToNativeConversionInfo(template, declType=CGGeneric(declType),
4508 dealWithOptional=isOptional)
4510 if type.isCallback():
4511 assert not isEnforceRange and not isClamp
4512 assert not type.treatNonCallableAsNull() or type.nullable()
4513 assert not type.treatNonObjectAsNull() or type.nullable()
4514 assert not type.treatNonObjectAsNull() or not type.treatNonCallableAsNull()
4516 name = type.unroll().identifier.name
4517 if type.nullable():
4518 declType = CGGeneric("nsRefPtr<%s>" % name)
4519 else:
4520 declType = CGGeneric("OwningNonNull<%s>" % name)
4521 conversion = indent(CGCallbackTempRoot(name).define())
4523 if allowTreatNonCallableAsNull and type.treatNonCallableAsNull():
4524 haveCallable = "JS_ObjectIsCallable(cx, &${val}.toObject())"
4525 if not isDefinitelyObject:
4526 haveCallable = "${val}.isObject() && " + haveCallable
4527 if defaultValue is not None:
4528 assert(isinstance(defaultValue, IDLNullValue))
4529 haveCallable = "${haveValue} && " + haveCallable
4530 template = (
4531 ("if (%s) {\n" % haveCallable) +
4532 conversion +
4533 "} else {\n"
4534 " ${declName} = nullptr;\n"
4535 "}\n")
4536 elif allowTreatNonCallableAsNull and type.treatNonObjectAsNull():
4537 if not isDefinitelyObject:
4538 haveObject = "${val}.isObject()"
4539 if defaultValue is not None:
4540 assert(isinstance(defaultValue, IDLNullValue))
4541 haveObject = "${haveValue} && " + haveObject
4542 template = CGIfElseWrapper(haveObject,
4543 CGGeneric(conversion + "\n"), # BOGUS extra blank line
4544 CGGeneric("${declName} = nullptr;\n")).define()
4545 else:
4546 template = conversion
4547 else:
4548 template = wrapObjectTemplate(
4549 "if (JS_ObjectIsCallable(cx, &${val}.toObject())) {\n" +
4550 conversion +
4551 "} else {\n" +
4552 indent(onFailureNotCallable(failureCode).define()) +
4553 "}\n",
4554 type,
4555 "${declName} = nullptr;\n",
4556 failureCode)
4557 return JSToNativeConversionInfo(template, declType=declType,
4558 dealWithOptional=isOptional)
4560 if type.isAny():
4561 assert not isEnforceRange and not isClamp
4563 declArgs = None
4564 if isMember in ("Variadic", "Sequence", "Dictionary", "MozMap"):
4565 # Rooting is handled by the sequence and dictionary tracers.
4566 declType = "JS::Value"
4567 else:
4568 assert not isMember
4569 declType = "JS::Rooted<JS::Value>"
4570 declArgs = "cx"
4572 assert not isOptional
4573 templateBody = "${declName} = ${val};\n"
4574 # We may not have a default value if we're being converted for
4575 # a setter, say.
4576 if defaultValue:
4577 if isinstance(defaultValue, IDLNullValue):
4578 defaultHandling = "${declName} = JS::NullValue();\n"
4579 else:
4580 assert isinstance(defaultValue, IDLUndefinedValue)
4581 defaultHandling = "${declName} = JS::UndefinedValue();\n"
4582 templateBody = handleDefault(templateBody, defaultHandling)
4583 return JSToNativeConversionInfo(templateBody,
4584 declType=CGGeneric(declType),
4585 declArgs=declArgs)
4587 if type.isObject():
4588 assert not isEnforceRange and not isClamp
4589 return handleJSObjectType(type, isMember, failureCode)
4591 if type.isDictionary():
4592 # There are no nullable dictionaries
4593 assert not type.nullable() or isCallbackReturnValue
4594 # All optional dictionaries always have default values, so we
4595 # should be able to assume not isOptional here.
4596 assert not isOptional
4597 # In the callback return value case we never have to worry
4598 # about a default value; we always have a value.
4599 assert not isCallbackReturnValue or defaultValue is None
4601 typeName = CGDictionary.makeDictionaryName(type.unroll().inner)
4602 if not isMember and not isCallbackReturnValue:
4603 # Since we're not a member and not nullable or optional, no one will
4604 # see our real type, so we can do the fast version of the dictionary
4605 # that doesn't pre-initialize members.
4606 typeName = "binding_detail::Fast" + typeName
4608 declType = CGGeneric(typeName)
4610 # We do manual default value handling here, because we
4611 # actually do want a jsval, and we only handle null anyway
4612 # NOTE: if isNullOrUndefined or isDefinitelyObject are true,
4613 # we know we have a value, so we don't have to worry about the
4614 # default value.
4615 if (not isNullOrUndefined and not isDefinitelyObject and
4616 defaultValue is not None):
4617 assert(isinstance(defaultValue, IDLNullValue))
4618 val = "(${haveValue}) ? ${val} : JS::NullHandleValue"
4619 else:
4620 val = "${val}"
4622 if failureCode is not None:
4623 if isDefinitelyObject:
4624 dictionaryTest = "IsObjectValueConvertibleToDictionary"
4625 else:
4626 dictionaryTest = "IsConvertibleToDictionary"
4627 # Check that the value we have can in fact be converted to
4628 # a dictionary, and return failureCode if not.
4629 template = CGIfWrapper(
4630 CGGeneric(failureCode),
4631 "!%s(cx, ${val})" % dictionaryTest).define() + "\n"
4632 else:
4633 template = ""
4635 dictLoc = "${declName}"
4636 if type.nullable():
4637 dictLoc += ".SetValue()"
4639 template += ('if (!%s.Init(cx, %s, "%s")) {\n'
4640 "%s"
4641 "}\n" % (dictLoc, val, firstCap(sourceDescription),
4642 exceptionCodeIndented.define()))
4644 if type.nullable():
4645 declType = CGTemplatedType("Nullable", declType)
4646 template = CGIfElseWrapper("${val}.isNullOrUndefined()",
4647 CGGeneric("${declName}.SetNull();\n"),
4648 CGGeneric(template)).define()
4650 # Dictionary arguments that might contain traceable things need to get
4651 # traced
4652 if not isMember and isCallbackReturnValue:
4653 # Go ahead and just convert directly into our actual return value
4654 declType = CGWrapper(declType, post="&")
4655 declArgs = "aRetVal"
4656 elif not isMember and typeNeedsRooting(type):
4657 declType = CGTemplatedType("RootedDictionary", declType)
4658 declArgs = "cx"
4659 else:
4660 declArgs = None
4662 return JSToNativeConversionInfo(template, declType=declType,
4663 declArgs=declArgs)
4665 if type.isVoid():
4666 assert not isOptional
4667 # This one only happens for return values, and its easy: Just
4668 # ignore the jsval.
4669 return JSToNativeConversionInfo("")
4671 if type.isDate():
4672 assert not isEnforceRange and not isClamp
4674 declType = CGGeneric("Date")
4675 if type.nullable():
4676 declType = CGTemplatedType("Nullable", declType)
4677 dateVal = "${declName}.SetValue()"
4678 else:
4679 dateVal = "${declName}"
4681 if failureCode is None:
4682 notDate = ('ThrowErrorMessage(cx, MSG_NOT_DATE, "%s");\n'
4683 "%s" % (firstCap(sourceDescription), exceptionCode))
4684 else:
4685 notDate = failureCode
4687 conversion = fill(
4688 """
4689 JS::Rooted<JSObject*> possibleDateObject(cx, &$${val}.toObject());
4690 if (!JS_ObjectIsDate(cx, possibleDateObject) ||
4691 !${dateVal}.SetTimeStamp(cx, possibleDateObject)) {
4692 $*{notDate}
4693 }
4694 """,
4695 dateVal=dateVal,
4696 notDate=notDate)
4698 conversion = wrapObjectTemplate(conversion, type,
4699 "${declName}.SetNull();\n", notDate)
4700 return JSToNativeConversionInfo(conversion,
4701 declType=declType,
4702 dealWithOptional=isOptional)
4704 if not type.isPrimitive():
4705 raise TypeError("Need conversion for argument type '%s'" % str(type))
4707 typeName = builtinNames[type.tag()]
4709 conversionBehavior = "eDefault"
4710 if isEnforceRange:
4711 assert type.isInteger()
4712 conversionBehavior = "eEnforceRange"
4713 elif isClamp:
4714 assert type.isInteger()
4715 conversionBehavior = "eClamp"
4717 if type.nullable():
4718 declType = CGGeneric("Nullable<" + typeName + ">")
4719 writeLoc = "${declName}.SetValue()"
4720 readLoc = "${declName}.Value()"
4721 nullCondition = "${val}.isNullOrUndefined()"
4722 if defaultValue is not None and isinstance(defaultValue, IDLNullValue):
4723 nullCondition = "!(${haveValue}) || " + nullCondition
4724 template = (
4725 "if (%s) {\n"
4726 " ${declName}.SetNull();\n"
4727 "} else if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n"
4728 "%s"
4729 "}\n" % (nullCondition, typeName, conversionBehavior,
4730 writeLoc, exceptionCodeIndented.define()))
4731 else:
4732 assert(defaultValue is None or
4733 not isinstance(defaultValue, IDLNullValue))
4734 writeLoc = "${declName}"
4735 readLoc = writeLoc
4736 template = (
4737 "if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n"
4738 "%s"
4739 "}\n" % (typeName, conversionBehavior, writeLoc,
4740 exceptionCodeIndented.define()))
4741 declType = CGGeneric(typeName)
4743 if type.isFloat() and not type.isUnrestricted():
4744 if lenientFloatCode is not None:
4745 nonFiniteCode = lenientFloatCode
4746 else:
4747 nonFiniteCode = ('ThrowErrorMessage(cx, MSG_NOT_FINITE, "%s");\n'
4748 "%s" % (firstCap(sourceDescription), exceptionCode))
4749 template = template.rstrip()
4750 template += fill(
4751 """
4752 else if (!mozilla::IsFinite(${readLoc})) {
4753 // Note: mozilla::IsFinite will do the right thing
4754 // when passed a non-finite float too.
4755 $*{nonFiniteCode}
4756 }
4757 """,
4758 readLoc=readLoc,
4759 nonFiniteCode=nonFiniteCode)
4761 if (defaultValue is not None and
4762 # We already handled IDLNullValue, so just deal with the other ones
4763 not isinstance(defaultValue, IDLNullValue)):
4764 tag = defaultValue.type.tag()
4765 defaultStr = getHandleDefault(defaultValue)
4766 template = CGIfElseWrapper(
4767 "${haveValue}",
4768 CGGeneric(template),
4769 CGGeneric("%s = %s;\n" % (writeLoc, defaultStr))).define()
4771 return JSToNativeConversionInfo(template, declType=declType,
4772 dealWithOptional=isOptional)
4775 def instantiateJSToNativeConversion(info, replacements, checkForValue=False):
4776 """
4777 Take a JSToNativeConversionInfo as returned by getJSToNativeConversionInfo
4778 and a set of replacements as required by the strings in such an object, and
4779 generate code to convert into stack C++ types.
4781 If checkForValue is True, then the conversion will get wrapped in
4782 a check for ${haveValue}.
4783 """
4784 templateBody, declType, holderType, dealWithOptional = (
4785 info.template, info.declType, info.holderType, info.dealWithOptional)
4787 if dealWithOptional and not checkForValue:
4788 raise TypeError("Have to deal with optional things, but don't know how")
4789 if checkForValue and declType is None:
4790 raise TypeError("Need to predeclare optional things, so they will be "
4791 "outside the check for big enough arg count!")
4793 # We can't precompute our holder constructor arguments, since
4794 # those might depend on ${declName}, which we change below. Just
4795 # compute arguments at the point when we need them as we go.
4796 def getArgsCGThing(args):
4797 return CGGeneric(string.Template(args).substitute(replacements))
4799 result = CGList([])
4800 # Make a copy of "replacements" since we may be about to start modifying it
4801 replacements = dict(replacements)
4802 originalDeclName = replacements["declName"]
4803 if declType is not None:
4804 if dealWithOptional:
4805 replacements["declName"] = "%s.Value()" % originalDeclName
4806 declType = CGTemplatedType("Optional", declType)
4807 declCtorArgs = None
4808 elif info.declArgs is not None:
4809 declCtorArgs = CGWrapper(getArgsCGThing(info.declArgs),
4810 pre="(", post=")")
4811 else:
4812 declCtorArgs = None
4813 result.append(
4814 CGList([declType, CGGeneric(" "),
4815 CGGeneric(originalDeclName),
4816 declCtorArgs, CGGeneric(";\n")]))
4818 originalHolderName = replacements["holderName"]
4819 if holderType is not None:
4820 if dealWithOptional:
4821 replacements["holderName"] = "%s.ref()" % originalHolderName
4822 holderType = CGTemplatedType("Maybe", holderType)
4823 holderCtorArgs = None
4824 elif info.holderArgs is not None:
4825 holderCtorArgs = CGWrapper(getArgsCGThing(info.holderArgs),
4826 pre="(", post=")")
4827 else:
4828 holderCtorArgs = None
4829 result.append(
4830 CGList([holderType, CGGeneric(" "),
4831 CGGeneric(originalHolderName),
4832 holderCtorArgs, CGGeneric(";\n")]))
4834 conversion = CGGeneric(
4835 string.Template(templateBody).substitute(replacements))
4837 if checkForValue:
4838 if dealWithOptional:
4839 declConstruct = CGIndenter(
4840 CGGeneric("%s.Construct(%s);\n" %
4841 (originalDeclName,
4842 getArgsCGThing(info.declArgs).define() if
4843 info.declArgs else "")))
4844 if holderType is not None:
4845 holderConstruct = CGIndenter(
4846 CGGeneric("%s.construct(%s);\n" %
4847 (originalHolderName,
4848 getArgsCGThing(info.holderArgs).define() if
4849 info.holderArgs else "")))
4850 else:
4851 holderConstruct = None
4852 else:
4853 declConstruct = None
4854 holderConstruct = None
4856 conversion = CGList([
4857 CGGeneric(
4858 string.Template("if (${haveValue}) {\n").substitute(replacements)),
4859 declConstruct,
4860 holderConstruct,
4861 CGIndenter(conversion),
4862 CGGeneric("}\n")
4863 ])
4865 result.append(conversion)
4866 return result
4869 def convertConstIDLValueToJSVal(value):
4870 if isinstance(value, IDLNullValue):
4871 return "JS::NullValue()"
4872 if isinstance(value, IDLUndefinedValue):
4873 return "JS::UndefinedValue()"
4874 tag = value.type.tag()
4875 if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
4876 IDLType.Tags.uint16, IDLType.Tags.int32]:
4877 return "INT_TO_JSVAL(%s)" % (value.value)
4878 if tag == IDLType.Tags.uint32:
4879 return "UINT_TO_JSVAL(%sU)" % (value.value)
4880 if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]:
4881 return "DOUBLE_TO_JSVAL(%s)" % numericValue(tag, value.value)
4882 if tag == IDLType.Tags.bool:
4883 return "JSVAL_TRUE" if value.value else "JSVAL_FALSE"
4884 if tag in [IDLType.Tags.float, IDLType.Tags.double]:
4885 return "DOUBLE_TO_JSVAL(%s)" % (value.value)
4886 raise TypeError("Const value of unhandled type: %s" % value.type)
4889 class CGArgumentConverter(CGThing):
4890 """
4891 A class that takes an IDL argument object and its index in the
4892 argument list and generates code to unwrap the argument to the
4893 right native type.
4895 argDescription is a description of the argument for error-reporting
4896 purposes. Callers should assume that it might get placed in the middle of a
4897 sentence. If it ends up at the beginning of a sentence, its first character
4898 will be automatically uppercased.
4899 """
4900 def __init__(self, argument, index, descriptorProvider,
4901 argDescription,
4902 invalidEnumValueFatal=True, lenientFloatCode=None):
4903 CGThing.__init__(self)
4904 self.argument = argument
4905 self.argDescription = argDescription
4906 assert(not argument.defaultValue or argument.optional)
4908 replacer = {
4909 "index": index,
4910 "argc": "args.length()"
4911 }
4912 self.replacementVariables = {
4913 "declName": "arg%d" % index,
4914 "holderName": ("arg%d" % index) + "_holder",
4915 "obj": "obj"
4916 }
4917 self.replacementVariables["val"] = string.Template(
4918 "args[${index}]").substitute(replacer)
4919 self.replacementVariables["mutableVal"] = self.replacementVariables["val"]
4920 haveValueCheck = string.Template(
4921 "args.hasDefined(${index})").substitute(replacer)
4922 self.replacementVariables["haveValue"] = haveValueCheck
4923 self.descriptorProvider = descriptorProvider
4924 if self.argument.optional and not self.argument.defaultValue:
4925 self.argcAndIndex = replacer
4926 else:
4927 self.argcAndIndex = None
4928 self.invalidEnumValueFatal = invalidEnumValueFatal
4929 self.lenientFloatCode = lenientFloatCode
4931 def define(self):
4932 typeConversion = getJSToNativeConversionInfo(
4933 self.argument.type,
4934 self.descriptorProvider,
4935 isOptional=(self.argcAndIndex is not None and
4936 not self.argument.variadic),
4937 invalidEnumValueFatal=self.invalidEnumValueFatal,
4938 defaultValue=self.argument.defaultValue,
4939 treatNullAs=self.argument.treatNullAs,
4940 isEnforceRange=self.argument.enforceRange,
4941 isClamp=self.argument.clamp,
4942 lenientFloatCode=self.lenientFloatCode,
4943 isMember="Variadic" if self.argument.variadic else False,
4944 allowTreatNonCallableAsNull=self.argument.allowTreatNonCallableAsNull(),
4945 sourceDescription=self.argDescription)
4947 if not self.argument.variadic:
4948 return instantiateJSToNativeConversion(
4949 typeConversion,
4950 self.replacementVariables,
4951 self.argcAndIndex is not None).define()
4953 # Variadic arguments get turned into a sequence.
4954 if typeConversion.dealWithOptional:
4955 raise TypeError("Shouldn't have optional things in variadics")
4956 if typeConversion.holderType is not None:
4957 raise TypeError("Shouldn't need holders for variadics")
4959 replacer = dict(self.argcAndIndex, **self.replacementVariables)
4960 replacer["seqType"] = CGTemplatedType("binding_detail::AutoSequence",
4961 typeConversion.declType).define()
4962 if typeNeedsRooting(self.argument.type):
4963 rooterDecl = ("SequenceRooter<%s> ${holderName}(cx, &${declName});\n" %
4964 typeConversion.declType.define())
4965 else:
4966 rooterDecl = ""
4967 replacer["elemType"] = typeConversion.declType.define()
4969 # NOTE: Keep this in sync with sequence conversions as needed
4970 variadicConversion = string.Template(
4971 "${seqType} ${declName};\n" +
4972 rooterDecl +
4973 dedent("""
4974 if (${argc} > ${index}) {
4975 if (!${declName}.SetCapacity(${argc} - ${index})) {
4976 JS_ReportOutOfMemory(cx);
4977 return false;
4978 }
4979 for (uint32_t variadicArg = ${index}; variadicArg < ${argc}; ++variadicArg) {
4980 ${elemType}& slot = *${declName}.AppendElement();
4981 """)
4982 ).substitute(replacer)
4984 val = string.Template("args[variadicArg]").substitute(replacer)
4985 variadicConversion += indent(
4986 string.Template(typeConversion.template).substitute({
4987 "val": val,
4988 "mutableVal": val,
4989 "declName": "slot",
4990 # We only need holderName here to handle isExternal()
4991 # interfaces, which use an internal holder for the
4992 # conversion even when forceOwningType ends up true.
4993 "holderName": "tempHolder",
4994 # Use the same ${obj} as for the variadic arg itself
4995 "obj": replacer["obj"]
4996 }), 4)
4998 variadicConversion += (" }\n"
4999 "}\n")
5000 return variadicConversion
5003 def getMaybeWrapValueFuncForType(type):
5004 # Callbacks might actually be DOM objects; nothing prevents a page from
5005 # doing that.
5006 if type.isCallback() or type.isCallbackInterface() or type.isObject():
5007 if type.nullable():
5008 return "MaybeWrapObjectOrNullValue"
5009 return "MaybeWrapObjectValue"
5010 # Spidermonkey interfaces are never DOM objects. Neither are sequences or
5011 # dictionaries, since those are always plain JS objects.
5012 if type.isSpiderMonkeyInterface() or type.isDictionary() or type.isSequence():
5013 if type.nullable():
5014 return "MaybeWrapNonDOMObjectOrNullValue"
5015 return "MaybeWrapNonDOMObjectValue"
5016 if type.isAny():
5017 return "MaybeWrapValue"
5019 # For other types, just go ahead an fall back on MaybeWrapValue for now:
5020 # it's always safe to do, and shouldn't be particularly slow for any of
5021 # them
5022 return "MaybeWrapValue"
5025 sequenceWrapLevel = 0
5026 mozMapWrapLevel = 0
5029 def getWrapTemplateForType(type, descriptorProvider, result, successCode,
5030 returnsNewObject, exceptionCode, typedArraysAreStructs):
5031 """
5032 Reflect a C++ value stored in "result", of IDL type "type" into JS. The
5033 "successCode" is the code to run once we have successfully done the
5034 conversion and must guarantee that execution of the conversion template
5035 stops once the successCode has executed (e.g. by doing a 'return', or by
5036 doing a 'break' if the entire conversion template is inside a block that
5037 the 'break' will exit).
5039 If typedArraysAreStructs is true, then if the type is a typed array,
5040 "result" is one of the dom::TypedArray subclasses, not a JSObject*.
5042 The resulting string should be used with string.Template. It
5043 needs the following keys when substituting:
5045 jsvalHandle: something that can be passed to methods taking a
5046 JS::MutableHandle<JS::Value>. This can be a
5047 JS::MutableHandle<JS::Value> or a JS::Rooted<JS::Value>*.
5048 jsvalRef: something that can have .address() called on it to get a
5049 JS::Value* and .set() called on it to set it to a JS::Value.
5050 This can be a JS::MutableHandle<JS::Value> or a
5051 JS::Rooted<JS::Value>.
5052 obj: a JS::Handle<JSObject*>.
5054 Returns (templateString, infallibility of conversion template)
5055 """
5056 if successCode is None:
5057 successCode = "return true;\n"
5059 def setUndefined():
5060 return _setValue("", setter="setUndefined")
5062 def setNull():
5063 return _setValue("", setter="setNull")
5065 def setInt32(value):
5066 return _setValue(value, setter="setInt32")
5068 def setString(value):
5069 return _setValue(value, setter="setString")
5071 def setObject(value, wrapAsType=None):
5072 return _setValue(value, wrapAsType=wrapAsType, setter="setObject")
5074 def setObjectOrNull(value, wrapAsType=None):
5075 return _setValue(value, wrapAsType=wrapAsType, setter="setObjectOrNull")
5077 def setUint32(value):
5078 return _setValue(value, setter="setNumber")
5080 def setDouble(value):
5081 return _setValue("JS_NumberValue(%s)" % value)
5083 def setBoolean(value):
5084 return _setValue(value, setter="setBoolean")
5086 def _setValue(value, wrapAsType=None, setter="set"):
5087 """
5088 Returns the code to set the jsval to value.
5090 If wrapAsType is not None, then will wrap the resulting value using the
5091 function that getMaybeWrapValueFuncForType(wrapAsType) returns.
5092 Otherwise, no wrapping will be done.
5093 """
5094 if wrapAsType is None:
5095 tail = successCode
5096 else:
5097 tail = fill(
5098 """
5099 if (!${maybeWrap}(cx, $${jsvalHandle})) {
5100 $*{exceptionCode}
5101 }
5102 $*{successCode}
5103 """,
5104 maybeWrap=getMaybeWrapValueFuncForType(wrapAsType),
5105 exceptionCode=exceptionCode,
5106 successCode=successCode)
5107 return ("${jsvalRef}.%s(%s);\n" % (setter, value)) + tail
5109 def wrapAndSetPtr(wrapCall, failureCode=None):
5110 """
5111 Returns the code to set the jsval by calling "wrapCall". "failureCode"
5112 is the code to run if calling "wrapCall" fails
5113 """
5114 if failureCode is None:
5115 failureCode = exceptionCode
5116 return fill(
5117 """
5118 if (!${wrapCall}) {
5119 $*{failureCode}
5120 }
5121 $*{successCode}
5122 """,
5123 wrapCall=wrapCall,
5124 failureCode=failureCode,
5125 successCode=successCode)
5127 if type is None or type.isVoid():
5128 return (setUndefined(), True)
5130 if type.isArray():
5131 raise TypeError("Can't handle array return values yet")
5133 if (type.isSequence() or type.isMozMap()) and type.nullable():
5134 # These are both wrapped in Nullable<>
5135 recTemplate, recInfall = getWrapTemplateForType(type.inner, descriptorProvider,
5136 "%s.Value()" % result, successCode,
5137 returnsNewObject, exceptionCode,
5138 typedArraysAreStructs)
5139 code = fill(
5140 """
5142 if (${result}.IsNull()) {
5143 $*{setNull}
5144 }
5145 $*{recTemplate}
5146 """,
5147 result=result,
5148 setNull=setNull(),
5149 recTemplate=recTemplate)
5150 return code, recInfall
5152 if type.isSequence():
5153 # Now do non-nullable sequences. Our success code is just to break to
5154 # where we set the element in the array. Note that we bump the
5155 # sequenceWrapLevel around this call so that nested sequence conversions
5156 # will use different iteration variables.
5157 global sequenceWrapLevel
5158 index = "sequenceIdx%d" % sequenceWrapLevel
5159 sequenceWrapLevel += 1
5160 innerTemplate = wrapForType(
5161 type.inner, descriptorProvider,
5162 {
5163 'result': "%s[%s]" % (result, index),
5164 'successCode': "break;\n",
5165 'jsvalRef': "tmp",
5166 'jsvalHandle': "&tmp",
5167 'returnsNewObject': returnsNewObject,
5168 'exceptionCode': exceptionCode,
5169 'obj': "returnArray"
5170 })
5171 sequenceWrapLevel -= 1
5172 code = fill(
5173 """
5175 uint32_t length = ${result}.Length();
5176 JS::Rooted<JSObject*> returnArray(cx, JS_NewArrayObject(cx, length));
5177 if (!returnArray) {
5178 $*{exceptionCode}
5179 }
5180 // Scope for 'tmp'
5181 {
5182 JS::Rooted<JS::Value> tmp(cx);
5183 for (uint32_t ${index} = 0; ${index} < length; ++${index}) {
5184 // Control block to let us common up the JS_DefineElement calls when there
5185 // are different ways to succeed at wrapping the object.
5186 do {
5187 $*{innerTemplate}
5188 } while (0);
5189 if (!JS_DefineElement(cx, returnArray, ${index}, tmp,
5190 nullptr, nullptr, JSPROP_ENUMERATE)) {
5191 $*{exceptionCode}
5192 }
5193 }
5194 }
5195 $*{set}
5196 """,
5197 result=result,
5198 exceptionCode=exceptionCode,
5199 index=index,
5200 innerTemplate=innerTemplate,
5201 set=setObject("*returnArray"))
5203 return (code, False)
5205 if type.isMozMap():
5206 # Now do non-nullable MozMap. Our success code is just to break to
5207 # where we define the property on the object. Note that we bump the
5208 # mozMapWrapLevel around this call so that nested MozMap conversions
5209 # will use different temp value names.
5210 global mozMapWrapLevel
5211 valueName = "mozMapValue%d" % mozMapWrapLevel
5212 mozMapWrapLevel += 1
5213 innerTemplate = wrapForType(
5214 type.inner, descriptorProvider,
5215 {
5216 'result': valueName,
5217 'successCode': "break;\n",
5218 'jsvalRef': "tmp",
5219 'jsvalHandle': "&tmp",
5220 'returnsNewObject': returnsNewObject,
5221 'exceptionCode': exceptionCode,
5222 'obj': "returnObj"
5223 })
5224 mozMapWrapLevel -= 1
5225 code = fill(
5226 """
5228 nsTArray<nsString> keys;
5229 ${result}.GetKeys(keys);
5230 JS::Rooted<JSObject*> returnObj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
5231 if (!returnObj) {
5232 $*{exceptionCode}
5233 }
5234 // Scope for 'tmp'
5235 {
5236 JS::Rooted<JS::Value> tmp(cx);
5237 for (size_t idx = 0; idx < keys.Length(); ++idx) {
5238 auto& ${valueName} = ${result}.Get(keys[idx]);
5239 // Control block to let us common up the JS_DefineUCProperty calls when there
5240 // are different ways to succeed at wrapping the value.
5241 do {
5242 $*{innerTemplate}
5243 } while (0);
5244 if (!JS_DefineUCProperty(cx, returnObj, keys[idx].get(),
5245 keys[idx].Length(), tmp,
5246 JS_PropertyStub, JS_StrictPropertyStub,
5247 JSPROP_ENUMERATE)) {
5248 $*{exceptionCode}
5249 }
5250 }
5251 }
5252 $*{set}
5253 """,
5254 result=result,
5255 exceptionCode=exceptionCode,
5256 valueName=valueName,
5257 innerTemplate=innerTemplate,
5258 set=setObject("*returnObj"))
5260 return (code, False)
5262 if type.isGeckoInterface() and not type.isCallbackInterface():
5263 descriptor = descriptorProvider.getDescriptor(type.unroll().inner.identifier.name)
5264 if type.nullable():
5265 wrappingCode = ("if (!%s) {\n" % (result) +
5266 indent(setNull()) +
5267 "}\n")
5268 else:
5269 wrappingCode = ""
5271 if not descriptor.interface.isExternal() and not descriptor.skipGen:
5272 if descriptor.wrapperCache:
5273 assert descriptor.nativeOwnership != 'owned'
5274 wrapMethod = "WrapNewBindingObject"
5275 else:
5276 if not returnsNewObject:
5277 raise MethodNotNewObjectError(descriptor.interface.identifier.name)
5278 if descriptor.nativeOwnership == 'owned':
5279 wrapMethod = "WrapNewBindingNonWrapperCachedOwnedObject"
5280 else:
5281 wrapMethod = "WrapNewBindingNonWrapperCachedObject"
5282 wrap = "%s(cx, ${obj}, %s, ${jsvalHandle})" % (wrapMethod, result)
5283 if not descriptor.hasXPConnectImpls:
5284 # Can only fail to wrap as a new-binding object
5285 # if they already threw an exception.
5286 #XXX Assertion disabled for now, see bug 991271.
5287 failed = ("MOZ_ASSERT(true || JS_IsExceptionPending(cx));\n" +
5288 exceptionCode)
5289 else:
5290 if descriptor.notflattened:
5291 raise TypeError("%s has XPConnect impls but not flattened; "
5292 "fallback won't work correctly" %
5293 descriptor.interface.identifier.name)
5294 # Try old-style wrapping for bindings which might be XPConnect impls.
5295 failed = wrapAndSetPtr("HandleNewBindingWrappingFailure(cx, ${obj}, %s, ${jsvalHandle})" % result)
5296 else:
5297 if descriptor.notflattened:
5298 getIID = "&NS_GET_IID(%s), " % descriptor.nativeType
5299 else:
5300 getIID = ""
5301 wrap = "WrapObject(cx, %s, %s${jsvalHandle})" % (result, getIID)
5302 failed = None
5304 wrappingCode += wrapAndSetPtr(wrap, failed)
5305 return (wrappingCode, False)
5307 if type.isDOMString():
5308 if type.nullable():
5309 return (wrapAndSetPtr("xpc::StringToJsval(cx, %s, ${jsvalHandle})" % result), False)
5310 else:
5311 return (wrapAndSetPtr("xpc::NonVoidStringToJsval(cx, %s, ${jsvalHandle})" % result), False)
5313 if type.isByteString():
5314 if type.nullable():
5315 return (wrapAndSetPtr("ByteStringToJsval(cx, %s, ${jsvalHandle})" % result), False)
5316 else:
5317 return (wrapAndSetPtr("NonVoidByteStringToJsval(cx, %s, ${jsvalHandle})" % result), False)
5319 if type.isEnum():
5320 if type.nullable():
5321 resultLoc = "%s.Value()" % result
5322 else:
5323 resultLoc = result
5324 conversion = fill(
5325 """
5326 {
5327 // Scope for resultStr
5328 MOZ_ASSERT(uint32_t(${result}) < ArrayLength(${strings}));
5329 JSString* resultStr = JS_NewStringCopyN(cx, ${strings}[uint32_t(${result})].value, ${strings}[uint32_t(${result})].length);
5330 if (!resultStr) {
5331 $*{exceptionCode}
5332 }
5333 $*{setResultStr}
5334 }
5335 """,
5336 result=resultLoc,
5337 strings=(type.unroll().inner.identifier.name + "Values::" +
5338 ENUM_ENTRY_VARIABLE_NAME),
5339 exceptionCode=exceptionCode,
5340 setResultStr=setString("resultStr"))
5342 if type.nullable():
5343 conversion = CGIfElseWrapper(
5344 "%s.IsNull()" % result,
5345 CGGeneric(setNull()),
5346 CGGeneric(conversion)).define()
5347 return conversion, False
5349 if type.isCallback() or type.isCallbackInterface():
5350 wrapCode = setObject(
5351 "*GetCallbackFromCallbackObject(%(result)s)",
5352 wrapAsType=type)
5353 if type.nullable():
5354 wrapCode = (
5355 "if (%(result)s) {\n" +
5356 indent(wrapCode) +
5357 "} else {\n" +
5358 indent(setNull()) +
5359 "}\n")
5360 wrapCode = wrapCode % {"result": result}
5361 return wrapCode, False
5363 if type.isAny():
5364 # See comments in WrapNewBindingObject explaining why we need
5365 # to wrap here.
5366 # NB: _setValue(..., type-that-is-any) calls JS_WrapValue(), so is fallible
5367 return (_setValue(result, wrapAsType=type), False)
5369 if (type.isObject() or (type.isSpiderMonkeyInterface() and
5370 not typedArraysAreStructs)):
5371 # See comments in WrapNewBindingObject explaining why we need
5372 # to wrap here.
5373 if type.nullable():
5374 toValue = "%s"
5375 setter = setObjectOrNull
5376 else:
5377 toValue = "*%s"
5378 setter = setObject
5379 # NB: setObject{,OrNull}(..., some-object-type) calls JS_WrapValue(), so is fallible
5380 return (setter(toValue % result, wrapAsType=type), False)
5382 if not (type.isUnion() or type.isPrimitive() or type.isDictionary() or
5383 type.isDate() or
5384 (type.isSpiderMonkeyInterface() and typedArraysAreStructs)):
5385 raise TypeError("Need to learn to wrap %s" % type)
5387 if type.nullable():
5388 recTemplate, recInfal = getWrapTemplateForType(type.inner, descriptorProvider,
5389 "%s.Value()" % result, successCode,
5390 returnsNewObject, exceptionCode,
5391 typedArraysAreStructs)
5392 return ("if (%s.IsNull()) {\n" % result +
5393 indent(setNull()) +
5394 "}\n" +
5395 recTemplate, recInfal)
5397 if type.isSpiderMonkeyInterface():
5398 assert typedArraysAreStructs
5399 # See comments in WrapNewBindingObject explaining why we need
5400 # to wrap here.
5401 # NB: setObject(..., some-object-type) calls JS_WrapValue(), so is fallible
5402 return (setObject("*%s.Obj()" % result,
5403 wrapAsType=type), False)
5405 if type.isUnion():
5406 return (wrapAndSetPtr("%s.ToJSVal(cx, ${obj}, ${jsvalHandle})" % result),
5407 False)
5409 if type.isDictionary():
5410 return (wrapAndSetPtr("%s.ToObject(cx, ${jsvalHandle})" % result),
5411 False)
5413 if type.isDate():
5414 return (wrapAndSetPtr("%s.ToDateObject(cx, ${jsvalHandle})" % result),
5415 False)
5417 tag = type.tag()
5419 if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
5420 IDLType.Tags.uint16, IDLType.Tags.int32]:
5421 return (setInt32("int32_t(%s)" % result), True)
5423 elif tag in [IDLType.Tags.int64, IDLType.Tags.uint64,
5424 IDLType.Tags.unrestricted_float, IDLType.Tags.float,
5425 IDLType.Tags.unrestricted_double, IDLType.Tags.double]:
5426 # XXXbz will cast to double do the "even significand" thing that webidl
5427 # calls for for 64-bit ints? Do we care?
5428 return (setDouble("double(%s)" % result), True)
5430 elif tag == IDLType.Tags.uint32:
5431 return (setUint32(result), True)
5433 elif tag == IDLType.Tags.bool:
5434 return (setBoolean(result), True)
5436 else:
5437 raise TypeError("Need to learn to wrap primitive: %s" % type)
5440 def wrapForType(type, descriptorProvider, templateValues):
5441 """
5442 Reflect a C++ value of IDL type "type" into JS. TemplateValues is a dict
5443 that should contain:
5445 * 'jsvalRef': something that can have .address() called on it to get a
5446 JS::Value* and .set() called on it to set it to a JS::Value.
5447 This can be a JS::MutableHandle<JS::Value> or a
5448 JS::Rooted<JS::Value>.
5449 * 'jsvalHandle': something that can be passed to methods taking a
5450 JS::MutableHandle<JS::Value>. This can be a
5451 JS::MutableHandle<JS::Value> or a JS::Rooted<JS::Value>*.
5452 * 'obj' (optional): the name of the variable that contains the JSObject to
5453 use as a scope when wrapping, if not supplied 'obj'
5454 will be used as the name
5455 * 'result' (optional): the name of the variable in which the C++ value is
5456 stored, if not supplied 'result' will be used as
5457 the name
5458 * 'successCode' (optional): the code to run once we have successfully
5459 done the conversion, if not supplied 'return
5460 true;' will be used as the code. The
5461 successCode must ensure that once it runs no
5462 more of the conversion template will be
5463 executed (e.g. by doing a 'return' or 'break'
5464 as appropriate).
5465 * 'returnsNewObject' (optional): If true, we're wrapping for the return
5466 value of a [NewObject] method. Assumed
5467 false if not set.
5468 * 'exceptionCode' (optional): Code to run when a JS exception is thrown.
5469 The default is "return false;". The code
5470 passed here must return.
5471 """
5472 wrap = getWrapTemplateForType(type, descriptorProvider,
5473 templateValues.get('result', 'result'),
5474 templateValues.get('successCode', None),
5475 templateValues.get('returnsNewObject', False),
5476 templateValues.get('exceptionCode',
5477 "return false;\n"),
5478 templateValues.get('typedArraysAreStructs',
5479 False))[0]
5481 defaultValues = {'obj': 'obj'}
5482 return string.Template(wrap).substitute(defaultValues, **templateValues)
5485 def infallibleForMember(member, type, descriptorProvider):
5486 """
5487 Determine the fallibility of changing a C++ value of IDL type "type" into
5488 JS for the given attribute. Apart from returnsNewObject, all the defaults
5489 are used, since the fallbility does not change based on the boolean values,
5490 and the template will be discarded.
5492 CURRENT ASSUMPTIONS:
5493 We assume that successCode for wrapping up return values cannot contain
5494 failure conditions.
5495 """
5496 return getWrapTemplateForType(type, descriptorProvider, 'result', None,
5497 memberReturnsNewObject(member), "return false;\n",
5498 False)[1]
5501 def leafTypeNeedsCx(type, retVal):
5502 return (type.isAny() or type.isObject() or
5503 (retVal and type.isSpiderMonkeyInterface()))
5506 def leafTypeNeedsScopeObject(type, retVal):
5507 return retVal and type.isSpiderMonkeyInterface()
5510 def leafTypeNeedsRooting(type):
5511 return leafTypeNeedsCx(type, False) or type.isSpiderMonkeyInterface()
5514 def typeNeedsRooting(type):
5515 return typeMatchesLambda(type,
5516 lambda t: leafTypeNeedsRooting(t))
5519 def typeNeedsCx(type, retVal=False):
5520 return typeMatchesLambda(type,
5521 lambda t: leafTypeNeedsCx(t, retVal))
5524 def typeNeedsScopeObject(type, retVal=False):
5525 return typeMatchesLambda(type,
5526 lambda t: leafTypeNeedsScopeObject(t, retVal))
5529 def typeMatchesLambda(type, func):
5530 if type is None:
5531 return False
5532 if type.nullable():
5533 return typeMatchesLambda(type.inner, func)
5534 if type.isSequence() or type.isMozMap() or type.isArray():
5535 return typeMatchesLambda(type.inner, func)
5536 if type.isUnion():
5537 return any(typeMatchesLambda(t, func) for t in
5538 type.unroll().flatMemberTypes)
5539 if type.isDictionary():
5540 return dictionaryMatchesLambda(type.inner, func)
5541 return func(type)
5544 def dictionaryMatchesLambda(dictionary, func):
5545 return (any(typeMatchesLambda(m.type, func) for m in dictionary.members) or
5546 (dictionary.parent and dictionaryMatchesLambda(dictionary.parent, func)))
5549 # Whenever this is modified, please update CGNativeMember.getRetvalInfo as
5550 # needed to keep the types compatible.
5551 def getRetvalDeclarationForType(returnType, descriptorProvider,
5552 resultAlreadyAddRefed,
5553 isMember=False):
5554 """
5555 Returns a tuple containing four things:
5557 1) A CGThing for the type of the return value, or None if there is no need
5558 for a return value.
5560 2) A value indicating the kind of ourparam to pass the value as. Valid
5561 options are None to not pass as an out param at all, "ref" (to pass a
5562 reference as an out param), and "ptr" (to pass a pointer as an out
5563 param).
5565 3) A CGThing for a tracer for the return value, or None if no tracing is
5566 needed.
5568 4) An argument string to pass to the retval declaration
5569 constructor or None if there are no arguments.
5570 """
5571 if returnType is None or returnType.isVoid():
5572 # Nothing to declare
5573 return None, None, None, None
5574 if returnType.isPrimitive() and returnType.tag() in builtinNames:
5575 result = CGGeneric(builtinNames[returnType.tag()])
5576 if returnType.nullable():
5577 result = CGTemplatedType("Nullable", result)
5578 return result, None, None, None
5579 if returnType.isDOMString():
5580 if isMember:
5581 return CGGeneric("nsString"), "ref", None, None
5582 return CGGeneric("DOMString"), "ref", None, None
5583 if returnType.isByteString():
5584 return CGGeneric("nsCString"), "ref", None, None
5585 if returnType.isEnum():
5586 result = CGGeneric(returnType.unroll().inner.identifier.name)
5587 if returnType.nullable():
5588 result = CGTemplatedType("Nullable", result)
5589 return result, None, None, None
5590 if returnType.isGeckoInterface():
5591 result = CGGeneric(descriptorProvider.getDescriptor(
5592 returnType.unroll().inner.identifier.name).nativeType)
5593 if descriptorProvider.getDescriptor(
5594 returnType.unroll().inner.identifier.name).nativeOwnership == 'owned':
5595 result = CGTemplatedType("nsAutoPtr", result)
5596 elif resultAlreadyAddRefed:
5597 result = CGTemplatedType("nsRefPtr", result)
5598 else:
5599 result = CGWrapper(result, post="*")
5600 return result, None, None, None
5601 if returnType.isCallback():
5602 name = returnType.unroll().identifier.name
5603 return CGGeneric("nsRefPtr<%s>" % name), None, None, None
5604 if returnType.isAny():
5605 if isMember:
5606 return CGGeneric("JS::Value"), None, None, None
5607 return CGGeneric("JS::Rooted<JS::Value>"), "ptr", None, "cx"
5608 if returnType.isObject() or returnType.isSpiderMonkeyInterface():
5609 if isMember:
5610 return CGGeneric("JSObject*"), None, None, None
5611 return CGGeneric("JS::Rooted<JSObject*>"), "ptr", None, "cx"
5612 if returnType.isSequence():
5613 nullable = returnType.nullable()
5614 if nullable:
5615 returnType = returnType.inner
5616 # If our result is already addrefed, use the right type in the
5617 # sequence argument here.
5618 result, _, _, _ = getRetvalDeclarationForType(returnType.inner,
5619 descriptorProvider,
5620 resultAlreadyAddRefed,
5621 isMember="Sequence")
5622 # While we have our inner type, set up our rooter, if needed
5623 if not isMember and typeNeedsRooting(returnType):
5624 rooter = CGGeneric("SequenceRooter<%s > resultRooter(cx, &result);\n" %
5625 result.define())
5626 else:
5627 rooter = None
5628 result = CGTemplatedType("nsTArray", result)
5629 if nullable:
5630 result = CGTemplatedType("Nullable", result)
5631 return result, "ref", rooter, None
5632 if returnType.isMozMap():
5633 nullable = returnType.nullable()
5634 if nullable:
5635 returnType = returnType.inner
5636 # If our result is already addrefed, use the right type in the
5637 # MozMap argument here.
5638 result, _, _, _ = getRetvalDeclarationForType(returnType.inner,
5639 descriptorProvider,
5640 resultAlreadyAddRefed,
5641 isMember="MozMap")
5642 # While we have our inner type, set up our rooter, if needed
5643 if not isMember and typeNeedsRooting(returnType):
5644 rooter = CGGeneric("MozMapRooter<%s> resultRooter(cx, &result);\n" %
5645 result.define())
5646 else:
5647 rooter = None
5648 result = CGTemplatedType("MozMap", result)
5649 if nullable:
5650 result = CGTemplatedType("Nullable", result)
5651 return result, "ref", rooter, None
5652 if returnType.isDictionary():
5653 nullable = returnType.nullable()
5654 dictName = CGDictionary.makeDictionaryName(returnType.unroll().inner)
5655 result = CGGeneric(dictName)
5656 if not isMember and typeNeedsRooting(returnType):
5657 if nullable:
5658 result = CGTemplatedType("NullableRootedDictionary", result)
5659 else:
5660 result = CGTemplatedType("RootedDictionary", result)
5661 resultArgs = "cx"
5662 else:
5663 if nullable:
5664 result = CGTemplatedType("Nullable", result)
5665 resultArgs = None
5666 return result, "ref", None, resultArgs
5667 if returnType.isUnion():
5668 result = CGGeneric(CGUnionStruct.unionTypeName(returnType.unroll(), True))
5669 if not isMember and typeNeedsRooting(returnType):
5670 if returnType.nullable():
5671 result = CGTemplatedType("NullableRootedUnion", result)
5672 else:
5673 result = CGTemplatedType("RootedUnion", result)
5674 resultArgs = "cx"
5675 else:
5676 if returnType.nullable():
5677 result = CGTemplatedType("Nullable", result)
5678 resultArgs = None
5679 return result, "ref", None, resultArgs
5680 if returnType.isDate():
5681 result = CGGeneric("Date")
5682 if returnType.nullable():
5683 result = CGTemplatedType("Nullable", result)
5684 return result, None, None, None
5685 raise TypeError("Don't know how to declare return value for %s" %
5686 returnType)
5689 def isResultAlreadyAddRefed(extendedAttributes):
5690 return 'resultNotAddRefed' not in extendedAttributes
5693 def needCx(returnType, arguments, extendedAttributes, considerTypes):
5694 return (considerTypes and
5695 (typeNeedsCx(returnType, True) or
5696 any(typeNeedsCx(a.type) for a in arguments)) or
5697 'implicitJSContext' in extendedAttributes)
5700 def needScopeObject(returnType, arguments, extendedAttributes,
5701 isWrapperCached, considerTypes, isMember):
5702 """
5703 isMember should be true if we're dealing with an attribute
5704 annotated as [StoreInSlot].
5705 """
5706 return (considerTypes and not isWrapperCached and
5707 ((not isMember and typeNeedsScopeObject(returnType, True)) or
5708 any(typeNeedsScopeObject(a.type) for a in arguments)))
5711 class CGCallGenerator(CGThing):
5712 """
5713 A class to generate an actual call to a C++ object. Assumes that the C++
5714 object is stored in a variable whose name is given by the |object| argument.
5716 errorReport should be a CGThing for an error report or None if no
5717 error reporting is needed.
5718 """
5719 def __init__(self, errorReport, arguments, argsPre, returnType,
5720 extendedAttributes, descriptorProvider, nativeMethodName,
5721 static, object="self", argsPost=[]):
5722 CGThing.__init__(self)
5724 assert errorReport is None or isinstance(errorReport, CGThing)
5726 isFallible = errorReport is not None
5728 resultAlreadyAddRefed = isResultAlreadyAddRefed(extendedAttributes)
5729 result, resultOutParam, resultRooter, resultArgs = \
5730 getRetvalDeclarationForType(
5731 returnType, descriptorProvider, resultAlreadyAddRefed)
5733 args = CGList([CGGeneric(arg) for arg in argsPre], ", ")
5734 for a, name in arguments:
5735 arg = CGGeneric(name)
5737 # Now constify the things that need it
5738 def needsConst(a):
5739 if a.type.isDictionary():
5740 return True
5741 if a.type.isSequence():
5742 return True
5743 if a.type.isMozMap():
5744 return True
5745 # isObject() types are always a JS::Rooted, whether
5746 # nullable or not, and it turns out a const JS::Rooted
5747 # is not very helpful at all (in particular, it won't
5748 # even convert to a JS::Handle).
5749 # XXX bz Well, why not???
5750 if a.type.nullable() and not a.type.isObject():
5751 return True
5752 if a.type.isString():
5753 return True
5754 if a.optional and not a.defaultValue:
5755 # If a.defaultValue, then it's not going to use an Optional,
5756 # so doesn't need to be const just due to being optional.
5757 # This also covers variadic arguments.
5758 return True
5759 if a.type.isUnion():
5760 return True
5761 if a.type.isSpiderMonkeyInterface():
5762 return True
5763 return False
5764 if needsConst(a):
5765 arg = CGWrapper(arg, pre="Constify(", post=")")
5766 # And convert NonNull<T> to T&
5767 if (((a.type.isGeckoInterface() or a.type.isCallback()) and not a.type.nullable()) or
5768 a.type.isDOMString()):
5769 arg = CGWrapper(arg, pre="NonNullHelper(", post=")")
5770 args.append(arg)
5772 # Return values that go in outparams go here
5773 if resultOutParam is not None:
5774 if resultOutParam is "ref":
5775 args.append(CGGeneric("result"))
5776 else:
5777 assert resultOutParam is "ptr"
5778 args.append(CGGeneric("&result"))
5780 if isFallible:
5781 args.append(CGGeneric("rv"))
5782 args.extend(CGGeneric(arg) for arg in argsPost)
5784 # Build up our actual call
5785 self.cgRoot = CGList([])
5787 call = CGGeneric(nativeMethodName)
5788 if not static:
5789 call = CGWrapper(call, pre="%s->" % object)
5790 call = CGList([call, CGWrapper(args, pre="(", post=");\n")])
5791 if result is not None:
5792 if resultRooter is not None:
5793 self.cgRoot.prepend(resultRooter)
5794 if resultArgs is not None:
5795 resultArgs = "(%s)" % resultArgs
5796 else:
5797 resultArgs = ""
5798 result = CGWrapper(result, post=(" result%s;\n" % resultArgs))
5799 self.cgRoot.prepend(result)
5800 if not resultOutParam:
5801 call = CGWrapper(call, pre="result = ")
5803 call = CGWrapper(call)
5804 self.cgRoot.append(call)
5806 if isFallible:
5807 self.cgRoot.prepend(CGGeneric("ErrorResult rv;\n"))
5808 self.cgRoot.append(CGGeneric("rv.WouldReportJSException();\n"))
5809 self.cgRoot.append(CGGeneric("if (rv.Failed()) {\n"))
5810 self.cgRoot.append(CGIndenter(errorReport))
5811 self.cgRoot.append(CGGeneric("}\n"))
5813 def define(self):
5814 return self.cgRoot.define()
5817 def getUnionMemberName(type):
5818 if type.isGeckoInterface():
5819 return type.inner.identifier.name
5820 if type.isEnum():
5821 return type.inner.identifier.name
5822 if type.isArray() or type.isSequence():
5823 return str(type)
5824 return type.name
5827 class MethodNotNewObjectError(Exception):
5828 def __init__(self, typename):
5829 self.typename = typename
5831 # A counter for making sure that when we're wrapping up things in
5832 # nested sequences we don't use the same variable name to iterate over
5833 # different sequences.
5834 sequenceWrapLevel = 0
5837 def wrapTypeIntoCurrentCompartment(type, value, isMember=True):
5838 """
5839 Take the thing named by "value" and if it contains "any",
5840 "object", or spidermonkey-interface types inside return a CGThing
5841 that will wrap them into the current compartment.
5842 """
5843 if type.isAny():
5844 assert not type.nullable()
5845 if isMember:
5846 value = "JS::MutableHandle<JS::Value>::fromMarkedLocation(&%s)" % value
5847 else:
5848 value = "&" + value
5849 return CGGeneric("if (!JS_WrapValue(cx, %s)) {\n"
5850 " return false;\n"
5851 "}\n" % value)
5853 if type.isObject():
5854 if isMember:
5855 value = "JS::MutableHandle<JSObject*>::fromMarkedLocation(&%s)" % value
5856 else:
5857 value = "&" + value
5858 return CGGeneric("if (!JS_WrapObject(cx, %s)) {\n"
5859 " return false;\n"
5860 "}\n" % value)
5862 if type.isSpiderMonkeyInterface():
5863 origValue = value
5864 if type.nullable():
5865 value = "%s.Value()" % value
5866 wrapCode = CGGeneric("if (!%s.WrapIntoNewCompartment(cx)) {\n"
5867 " return false;\n"
5868 "}\n" % value)
5869 if type.nullable():
5870 wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue)
5871 return wrapCode
5873 if type.isSequence():
5874 origValue = value
5875 origType = type
5876 if type.nullable():
5877 type = type.inner
5878 value = "%s.Value()" % value
5879 global sequenceWrapLevel
5880 index = "indexName%d" % sequenceWrapLevel
5881 sequenceWrapLevel += 1
5882 wrapElement = wrapTypeIntoCurrentCompartment(type.inner,
5883 "%s[%s]" % (value, index))
5884 sequenceWrapLevel -= 1
5885 if not wrapElement:
5886 return None
5887 wrapCode = CGWrapper(CGIndenter(wrapElement),
5888 pre=("for (uint32_t %s = 0; %s < %s.Length(); ++%s) {\n" %
5889 (index, index, value, index)),
5890 post="}\n")
5891 if origType.nullable():
5892 wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue)
5893 return wrapCode
5895 if type.isDictionary():
5896 assert not type.nullable()
5897 myDict = type.inner
5898 memberWraps = []
5899 while myDict:
5900 for member in myDict.members:
5901 memberWrap = wrapArgIntoCurrentCompartment(
5902 member,
5903 "%s.%s" % (value, CGDictionary.makeMemberName(member.identifier.name)))
5904 if memberWrap:
5905 memberWraps.append(memberWrap)
5906 myDict = myDict.parent
5907 return CGList(memberWraps) if len(memberWraps) != 0 else None
5909 if type.isUnion():
5910 memberWraps = []
5911 if type.nullable():
5912 type = type.inner
5913 value = "%s.Value()" % value
5914 for member in type.flatMemberTypes:
5915 memberName = getUnionMemberName(member)
5916 memberWrap = wrapTypeIntoCurrentCompartment(
5917 member, "%s.GetAs%s()" % (value, memberName))
5918 if memberWrap:
5919 memberWrap = CGIfWrapper(
5920 memberWrap, "%s.Is%s()" % (value, memberName))
5921 memberWraps.append(memberWrap)
5922 return CGList(memberWraps, "else ") if len(memberWraps) != 0 else None
5924 if (type.isString() or type.isPrimitive() or type.isEnum() or
5925 type.isGeckoInterface() or type.isCallback() or type.isDate()):
5926 # All of these don't need wrapping
5927 return None
5929 raise TypeError("Unknown type; we don't know how to wrap it in constructor "
5930 "arguments: %s" % type)
5933 def wrapArgIntoCurrentCompartment(arg, value, isMember=True):
5934 """
5935 As wrapTypeIntoCurrentCompartment but handles things being optional
5936 """
5937 origValue = value
5938 isOptional = arg.optional and not arg.defaultValue
5939 if isOptional:
5940 value = value + ".Value()"
5941 wrap = wrapTypeIntoCurrentCompartment(arg.type, value, isMember)
5942 if wrap and isOptional:
5943 wrap = CGIfWrapper(wrap, "%s.WasPassed()" % origValue)
5944 return wrap
5947 class CGPerSignatureCall(CGThing):
5948 """
5949 This class handles the guts of generating code for a particular
5950 call signature. A call signature consists of four things:
5952 1) A return type, which can be None to indicate that there is no
5953 actual return value (e.g. this is an attribute setter) or an
5954 IDLType if there's an IDL type involved (including |void|).
5955 2) An argument list, which is allowed to be empty.
5956 3) A name of a native method to call.
5957 4) Whether or not this method is static.
5959 We also need to know whether this is a method or a getter/setter
5960 to do error reporting correctly.
5962 The idlNode parameter can be either a method or an attr. We can query
5963 |idlNode.identifier| in both cases, so we can be agnostic between the two.
5964 """
5965 # XXXbz For now each entry in the argument list is either an
5966 # IDLArgument or a FakeArgument, but longer-term we may want to
5967 # have ways of flagging things like JSContext* or optional_argc in
5968 # there.
5970 def __init__(self, returnType, arguments, nativeMethodName, static,
5971 descriptor, idlNode, argConversionStartsAt=0, getter=False,
5972 setter=False, isConstructor=False):
5973 assert idlNode.isMethod() == (not getter and not setter)
5974 assert idlNode.isAttr() == (getter or setter)
5975 # Constructors are always static
5976 assert not isConstructor or static
5978 CGThing.__init__(self)
5979 self.returnType = returnType
5980 self.descriptor = descriptor
5981 self.idlNode = idlNode
5982 self.extendedAttributes = descriptor.getExtendedAttributes(idlNode,
5983 getter=getter,
5984 setter=setter)
5985 self.arguments = arguments
5986 self.argCount = len(arguments)
5987 cgThings = []
5988 lenientFloatCode = None
5989 if idlNode.getExtendedAttribute('LenientFloat') is not None:
5990 if setter:
5991 lenientFloatCode = "return true;\n"
5992 elif idlNode.isMethod():
5993 lenientFloatCode = ("args.rval().setUndefined();\n"
5994 "return true;\n")
5996 argsPre = []
5997 if static:
5998 nativeMethodName = "%s::%s" % (descriptor.nativeType,
5999 nativeMethodName)
6000 # If we're a constructor, "obj" may not be a function, so calling
6001 # XrayAwareCalleeGlobal() on it is not safe. Of course in the
6002 # constructor case either "obj" is an Xray or we're already in the
6003 # content compartment, not the Xray compartment, so just
6004 # constructing the GlobalObject from "obj" is fine.
6005 if isConstructor:
6006 objForGlobalObject = "obj"
6007 else:
6008 objForGlobalObject = "xpc::XrayAwareCalleeGlobal(obj)"
6009 cgThings.append(CGGeneric(fill(
6010 """
6011 GlobalObject global(cx, ${obj});
6012 if (global.Failed()) {
6013 return false;
6014 }
6016 """,
6017 obj=objForGlobalObject)))
6018 argsPre.append("global")
6020 # For JS-implemented interfaces we do not want to base the
6021 # needsCx decision on the types involved, just on our extended
6022 # attributes.
6023 needsCx = needCx(returnType, arguments, self.extendedAttributes,
6024 not descriptor.interface.isJSImplemented())
6025 if needsCx and not (static and descriptor.workers):
6026 argsPre.append("cx")
6028 needsUnwrap = False
6029 argsPost = []
6030 if isConstructor:
6031 needsUnwrap = True
6032 needsUnwrappedVar = False
6033 unwrappedVar = "obj"
6034 elif descriptor.interface.isJSImplemented():
6035 needsUnwrap = True
6036 needsUnwrappedVar = True
6037 argsPost.append("js::GetObjectCompartment(unwrappedObj.empty() ? obj : unwrappedObj.ref())")
6038 elif needScopeObject(returnType, arguments, self.extendedAttributes,
6039 descriptor.wrapperCache, True,
6040 idlNode.getExtendedAttribute("StoreInSlot")):
6041 needsUnwrap = True
6042 needsUnwrappedVar = True
6043 argsPre.append("unwrappedObj.empty() ? obj : unwrappedObj.ref()")
6045 if needsUnwrap and needsUnwrappedVar:
6046 # We cannot assign into obj because it's a Handle, not a
6047 # MutableHandle, so we need a separate Rooted.
6048 cgThings.append(CGGeneric("Maybe<JS::Rooted<JSObject*> > unwrappedObj;\n"))
6049 unwrappedVar = "unwrappedObj.ref()"
6051 if idlNode.isMethod() and idlNode.isLegacycaller():
6052 # If we can have legacycaller with identifier, we can't
6053 # just use the idlNode to determine whether we're
6054 # generating code for the legacycaller or not.
6055 assert idlNode.isIdentifierLess()
6056 # Pass in our thisVal
6057 argsPre.append("args.thisv()")
6059 ourName = "%s.%s" % (descriptor.interface.identifier.name,
6060 idlNode.identifier.name)
6061 if idlNode.isMethod():
6062 argDescription = "argument %(index)d of " + ourName
6063 elif setter:
6064 argDescription = "value being assigned to %s" % ourName
6065 else:
6066 assert self.argCount == 0
6068 if needsUnwrap:
6069 # It's very important that we construct our unwrappedObj, if we need
6070 # to do it, before we might start setting up Rooted things for our
6071 # arguments, so that we don't violate the stack discipline Rooted
6072 # depends on.
6073 cgThings.append(CGGeneric(
6074 "bool objIsXray = xpc::WrapperFactory::IsXrayWrapper(obj);\n"))
6075 if needsUnwrappedVar:
6076 cgThings.append(CGIfWrapper(
6077 CGGeneric("unwrappedObj.construct(cx, obj);\n"),
6078 "objIsXray"))
6080 for i in range(argConversionStartsAt, self.argCount):
6081 cgThings.append(
6082 CGArgumentConverter(arguments[i], i, self.descriptor,
6083 argDescription % {"index": i + 1},
6084 invalidEnumValueFatal=not setter,
6085 lenientFloatCode=lenientFloatCode))
6087 if needsUnwrap:
6088 # Something depends on having the unwrapped object, so unwrap it now.
6089 xraySteps = []
6090 # XXXkhuey we should be able to MOZ_ASSERT that ${obj} is
6091 # not null.
6092 xraySteps.append(
6093 CGGeneric(string.Template(dedent("""
6094 ${obj} = js::CheckedUnwrap(${obj});
6095 if (!${obj}) {
6096 return false;
6097 }
6098 """)).substitute({'obj': unwrappedVar})))
6099 if isConstructor:
6100 # If we're called via an xray, we need to enter the underlying
6101 # object's compartment and then wrap up all of our arguments into
6102 # that compartment as needed. This is all happening after we've
6103 # already done the conversions from JS values to WebIDL (C++)
6104 # values, so we only need to worry about cases where there are 'any'
6105 # or 'object' types, or other things that we represent as actual
6106 # JSAPI types, present. Effectively, we're emulating a
6107 # CrossCompartmentWrapper, but working with the C++ types, not the
6108 # original list of JS::Values.
6109 cgThings.append(CGGeneric("Maybe<JSAutoCompartment> ac;\n"))
6110 xraySteps.append(CGGeneric("ac.construct(cx, obj);\n"))
6111 xraySteps.extend(
6112 wrapArgIntoCurrentCompartment(arg, argname, isMember=False)
6113 for arg, argname in self.getArguments())
6115 cgThings.append(
6116 CGIfWrapper(CGList(xraySteps),
6117 "objIsXray"))
6119 cgThings.append(CGCallGenerator(
6120 self.getErrorReport() if self.isFallible() else None,
6121 self.getArguments(), argsPre, returnType,
6122 self.extendedAttributes, descriptor, nativeMethodName,
6123 static, argsPost=argsPost))
6124 self.cgRoot = CGList(cgThings)
6126 def getArguments(self):
6127 return [(a, "arg" + str(i)) for i, a in enumerate(self.arguments)]
6129 def isFallible(self):
6130 return 'infallible' not in self.extendedAttributes
6132 def wrap_return_value(self):
6133 returnsNewObject = memberReturnsNewObject(self.idlNode)
6134 if returnsNewObject:
6135 # We better be returning addrefed things!
6136 assert(isResultAlreadyAddRefed(self.extendedAttributes) or
6137 # NewObject can return raw pointers to owned objects
6138 (self.returnType.isGeckoInterface() and
6139 self.descriptor.getDescriptor(self.returnType.unroll().inner.identifier.name).nativeOwnership == 'owned'))
6141 setSlot = self.idlNode.isAttr() and self.idlNode.slotIndex is not None
6142 if setSlot:
6143 # For attributes in slots, we want to do some
6144 # post-processing once we've wrapped them.
6145 successCode = "break;\n"
6146 else:
6147 successCode = None
6149 resultTemplateValues = {
6150 'jsvalRef': 'args.rval()',
6151 'jsvalHandle': 'args.rval()',
6152 'returnsNewObject': returnsNewObject,
6153 'successCode': successCode,
6154 'obj': "reflector" if setSlot else "obj"
6155 }
6156 try:
6157 wrapCode = wrapForType(self.returnType, self.descriptor, resultTemplateValues)
6158 except MethodNotNewObjectError, err:
6159 assert not returnsNewObject
6160 raise TypeError("%s being returned from non-NewObject method or property %s.%s" %
6161 (err.typename,
6162 self.descriptor.interface.identifier.name,
6163 self.idlNode.identifier.name))
6164 if setSlot:
6165 # We need to make sure that our initial wrapping is done in the
6166 # reflector compartment, but that we finally set args.rval() in the
6167 # caller compartment. We also need to make sure that the actual
6168 # wrapping steps happen inside a do/while that they can break out
6169 # of.
6171 # postSteps are the steps that run while we're still in the
6172 # reflector compartment but after we've finished the initial
6173 # wrapping into args.rval().
6174 postSteps = ""
6175 if self.idlNode.getExtendedAttribute("Frozen"):
6176 assert self.idlNode.type.isSequence() or self.idlNode.type.isDictionary()
6177 freezeValue = CGGeneric(
6178 "JS::Rooted<JSObject*> rvalObj(cx, &args.rval().toObject());\n"
6179 "if (!JS_FreezeObject(cx, rvalObj)) {\n"
6180 " return false;\n"
6181 "}\n")
6182 if self.idlNode.type.nullable():
6183 freezeValue = CGIfWrapper(freezeValue,
6184 "args.rval().isObject()")
6185 postSteps += freezeValue.define()
6186 postSteps += ("js::SetReservedSlot(reflector, %s, args.rval());\n" %
6187 memberReservedSlot(self.idlNode))
6188 # For the case of Cached attributes, go ahead and preserve our
6189 # wrapper if needed. We need to do this because otherwise the
6190 # wrapper could get garbage-collected and the cached value would
6191 # suddenly disappear, but the whole premise of cached values is that
6192 # they never change without explicit action on someone's part. We
6193 # don't do this for StoreInSlot, since those get dealt with during
6194 # wrapper setup, and failure would involve us trying to clear an
6195 # already-preserved wrapper.
6196 if (self.idlNode.getExtendedAttribute("Cached") and
6197 self.descriptor.wrapperCache):
6198 postSteps += "PreserveWrapper(self);\n"
6200 wrapCode = fill(
6201 """
6202 { // Make sure we wrap and store in the slot in reflector's compartment
6203 JSAutoCompartment ac(cx, reflector);
6204 do { // block we break out of when done wrapping
6205 $*{wrapCode}
6206 } while (0);
6207 $*{postSteps}
6208 }
6209 // And now make sure args.rval() is in the caller compartment
6210 return ${maybeWrap}(cx, args.rval());
6211 """,
6212 wrapCode=wrapCode,
6213 postSteps=postSteps,
6214 maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type))
6215 return wrapCode
6217 def getErrorReport(self):
6218 jsImplemented = ""
6219 if self.descriptor.interface.isJSImplemented():
6220 jsImplemented = ", true"
6221 return CGGeneric('return ThrowMethodFailedWithDetails(cx, rv, "%s", "%s"%s);\n'
6222 % (self.descriptor.interface.identifier.name,
6223 self.idlNode.identifier.name,
6224 jsImplemented))
6226 def define(self):
6227 return (self.cgRoot.define() + self.wrap_return_value())
6230 class CGSwitch(CGList):
6231 """
6232 A class to generate code for a switch statement.
6234 Takes three constructor arguments: an expression, a list of cases,
6235 and an optional default.
6237 Each case is a CGCase. The default is a CGThing for the body of
6238 the default case, if any.
6239 """
6240 def __init__(self, expression, cases, default=None):
6241 CGList.__init__(self, [CGIndenter(c) for c in cases])
6242 self.prepend(CGGeneric("switch (" + expression + ") {\n"))
6243 if default is not None:
6244 self.append(
6245 CGIndenter(
6246 CGWrapper(
6247 CGIndenter(default),
6248 pre="default: {\n",
6249 post=" break;\n}\n")))
6251 self.append(CGGeneric("}\n"))
6254 class CGCase(CGList):
6255 """
6256 A class to generate code for a case statement.
6258 Takes three constructor arguments: an expression, a CGThing for
6259 the body (allowed to be None if there is no body), and an optional
6260 argument (defaulting to False) for whether to fall through.
6261 """
6262 def __init__(self, expression, body, fallThrough=False):
6263 CGList.__init__(self, [])
6264 self.append(CGGeneric("case " + expression + ": {\n"))
6265 bodyList = CGList([body])
6266 if fallThrough:
6267 bodyList.append(CGGeneric("/* Fall through */\n"))
6268 else:
6269 bodyList.append(CGGeneric("break;\n"))
6270 self.append(CGIndenter(bodyList))
6271 self.append(CGGeneric("}\n"))
6274 class CGMethodCall(CGThing):
6275 """
6276 A class to generate selection of a method signature from a set of
6277 signatures and generation of a call to that signature.
6278 """
6279 def __init__(self, nativeMethodName, static, descriptor, method,
6280 isConstructor=False, constructorName=None):
6281 CGThing.__init__(self)
6283 if isConstructor:
6284 assert constructorName is not None
6285 methodName = constructorName
6286 else:
6287 methodName = "%s.%s" % (descriptor.interface.identifier.name, method.identifier.name)
6288 argDesc = "argument %d of " + methodName
6290 def requiredArgCount(signature):
6291 arguments = signature[1]
6292 if len(arguments) == 0:
6293 return 0
6294 requiredArgs = len(arguments)
6295 while requiredArgs and arguments[requiredArgs-1].optional:
6296 requiredArgs -= 1
6297 return requiredArgs
6299 def getPerSignatureCall(signature, argConversionStartsAt=0):
6300 return CGPerSignatureCall(signature[0], signature[1],
6301 nativeMethodName, static, descriptor,
6302 method,
6303 argConversionStartsAt=argConversionStartsAt,
6304 isConstructor=isConstructor)
6306 signatures = method.signatures()
6307 if len(signatures) == 1:
6308 # Special case: we can just do a per-signature method call
6309 # here for our one signature and not worry about switching
6310 # on anything.
6311 signature = signatures[0]
6312 self.cgRoot = CGList([CGIndenter(getPerSignatureCall(signature))])
6313 requiredArgs = requiredArgCount(signature)
6315 if requiredArgs > 0:
6316 code = indent(fill( # BOGUS extra blank line
6317 """
6319 if (args.length() < ${requiredArgs}) {
6320 return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${methodName}");
6321 }
6322 """,
6323 requiredArgs=requiredArgs,
6324 methodName=methodName))
6325 self.cgRoot.prepend(CGGeneric(code))
6326 return
6328 # Need to find the right overload
6329 maxArgCount = method.maxArgCount
6330 allowedArgCounts = method.allowedArgCounts
6332 argCountCases = []
6333 for argCountIdx, argCount in enumerate(allowedArgCounts):
6334 possibleSignatures = method.signaturesForArgCount(argCount)
6336 # Try to optimize away cases when the next argCount in the list
6337 # will have the same code as us; if it does, we can fall through to
6338 # that case.
6339 if argCountIdx+1 < len(allowedArgCounts):
6340 nextPossibleSignatures = \
6341 method.signaturesForArgCount(allowedArgCounts[argCountIdx+1])
6342 else:
6343 nextPossibleSignatures = None
6344 if possibleSignatures == nextPossibleSignatures:
6345 # Same set of signatures means we better have the same
6346 # distinguishing index. So we can in fact just fall through to
6347 # the next case here.
6348 assert (len(possibleSignatures) == 1 or
6349 (method.distinguishingIndexForArgCount(argCount) ==
6350 method.distinguishingIndexForArgCount(allowedArgCounts[argCountIdx+1])))
6351 argCountCases.append(CGCase(str(argCount), None, True))
6352 continue
6354 if len(possibleSignatures) == 1:
6355 # easy case!
6356 signature = possibleSignatures[0]
6357 argCountCases.append(
6358 CGCase(str(argCount), getPerSignatureCall(signature)))
6359 continue
6361 distinguishingIndex = method.distinguishingIndexForArgCount(argCount)
6363 def distinguishingArgument(signature):
6364 args = signature[1]
6365 if distinguishingIndex < len(args):
6366 return args[distinguishingIndex]
6367 assert args[-1].variadic
6368 return args[-1]
6370 def distinguishingType(signature):
6371 return distinguishingArgument(signature).type
6373 for sig in possibleSignatures:
6374 # We should not have "any" args at distinguishingIndex,
6375 # since we have multiple possible signatures remaining,
6376 # but "any" is never distinguishable from anything else.
6377 assert not distinguishingType(sig).isAny()
6378 # We can't handle unions at the distinguishing index.
6379 if distinguishingType(sig).isUnion():
6380 raise TypeError("No support for unions as distinguishing "
6381 "arguments yet: %s" %
6382 distinguishingArgument(sig).location)
6383 # We don't support variadics as the distinguishingArgument yet.
6384 # If you want to add support, consider this case:
6385 #
6386 # void(long... foo);
6387 # void(long bar, Int32Array baz);
6388 #
6389 # in which we have to convert argument 0 to long before picking
6390 # an overload... but all the variadic stuff needs to go into a
6391 # single array in case we pick that overload, so we have to have
6392 # machinery for converting argument 0 to long and then either
6393 # placing it in the variadic bit or not. Or something. We may
6394 # be able to loosen this restriction if the variadic arg is in
6395 # fact at distinguishingIndex, perhaps. Would need to
6396 # double-check.
6397 if distinguishingArgument(sig).variadic:
6398 raise TypeError("No support for variadics as distinguishing "
6399 "arguments yet: %s" %
6400 distinguishingArgument(sig).location)
6402 # Convert all our arguments up to the distinguishing index.
6403 # Doesn't matter which of the possible signatures we use, since
6404 # they all have the same types up to that point; just use
6405 # possibleSignatures[0]
6406 caseBody = [CGArgumentConverter(possibleSignatures[0][1][i],
6407 i, descriptor,
6408 argDesc % (i + 1))
6409 for i in range(0, distinguishingIndex)]
6411 # Select the right overload from our set.
6412 distinguishingArg = "args[%d]" % distinguishingIndex
6414 def tryCall(signature, indent, isDefinitelyObject=False,
6415 isNullOrUndefined=False):
6416 assert not isDefinitelyObject or not isNullOrUndefined
6417 assert isDefinitelyObject or isNullOrUndefined
6418 if isDefinitelyObject:
6419 failureCode = "break;\n"
6420 else:
6421 failureCode = None
6422 type = distinguishingType(signature)
6423 # The argument at index distinguishingIndex can't possibly be
6424 # unset here, because we've already checked that argc is large
6425 # enough that we can examine this argument. But note that we
6426 # still want to claim that optional arguments are optional, in
6427 # case undefined was passed in.
6428 argIsOptional = (distinguishingArgument(signature).optional and
6429 not distinguishingArgument(signature).defaultValue)
6430 testCode = instantiateJSToNativeConversion(
6431 getJSToNativeConversionInfo(type, descriptor,
6432 failureCode=failureCode,
6433 isDefinitelyObject=isDefinitelyObject,
6434 isNullOrUndefined=isNullOrUndefined,
6435 isOptional=argIsOptional,
6436 sourceDescription=(argDesc % (distinguishingIndex + 1))),
6437 {
6438 "declName": "arg%d" % distinguishingIndex,
6439 "holderName": ("arg%d" % distinguishingIndex) + "_holder",
6440 "val": distinguishingArg,
6441 "mutableVal": distinguishingArg,
6442 "obj": "obj",
6443 "haveValue": "args.hasDefined(%d)" % distinguishingIndex
6444 },
6445 checkForValue=argIsOptional)
6446 caseBody.append(CGIndenter(testCode, indent))
6448 # If we got this far, we know we unwrapped to the right
6449 # C++ type, so just do the call. Start conversion with
6450 # distinguishingIndex + 1, since we already converted
6451 # distinguishingIndex.
6452 caseBody.append(CGIndenter(
6453 getPerSignatureCall(signature, distinguishingIndex + 1),
6454 indent))
6456 def hasConditionalConversion(type):
6457 """
6458 Return whether the argument conversion for this type will be
6459 conditional on the type of incoming JS value. For example, for
6460 interface types the conversion is conditional on the incoming
6461 value being isObject().
6463 For the types for which this returns false, we do not have to
6464 output extra isUndefined() or isNullOrUndefined() cases, because
6465 null/undefined values will just fall through into our
6466 unconditional conversion.
6467 """
6468 if type.isString() or type.isEnum():
6469 return False
6470 if type.isBoolean():
6471 distinguishingTypes = (distinguishingType(s) for s in
6472 possibleSignatures)
6473 return any(t.isString() or t.isEnum() or t.isNumeric()
6474 for t in distinguishingTypes)
6475 if type.isNumeric():
6476 distinguishingTypes = (distinguishingType(s) for s in
6477 possibleSignatures)
6478 return any(t.isString() or t.isEnum()
6479 for t in distinguishingTypes)
6480 return True
6482 def needsNullOrUndefinedCase(type):
6483 """
6484 Return true if the type needs a special isNullOrUndefined() case
6485 """
6486 return ((type.nullable() and
6487 hasConditionalConversion(type)) or
6488 type.isDictionary())
6490 # First check for undefined and optional distinguishing arguments
6491 # and output a special branch for that case. Note that we don't
6492 # use distinguishingArgument here because we actualy want to
6493 # exclude variadic arguments. Also note that we skip this check if
6494 # we plan to output a isNullOrUndefined() special case for this
6495 # argument anyway, since that will subsume our isUndefined() check.
6496 # This is safe, because there can be at most one nullable
6497 # distinguishing argument, so if we're it we'll definitely get
6498 # picked up by the nullable handling. Also, we can skip this check
6499 # if the argument has an unconditional conversion later on.
6500 undefSigs = [s for s in possibleSignatures if
6501 distinguishingIndex < len(s[1]) and
6502 s[1][distinguishingIndex].optional and
6503 hasConditionalConversion(s[1][distinguishingIndex].type) and
6504 not needsNullOrUndefinedCase(s[1][distinguishingIndex].type)]
6505 # Can't have multiple signatures with an optional argument at the
6506 # same index.
6507 assert len(undefSigs) < 2
6508 if len(undefSigs) > 0:
6509 caseBody.append(CGGeneric("if (%s.isUndefined()) {\n" %
6510 distinguishingArg))
6511 tryCall(undefSigs[0], 2, isNullOrUndefined=True)
6512 caseBody.append(CGGeneric("}\n"))
6514 # Next, check for null or undefined. That means looking for
6515 # nullable arguments at the distinguishing index and outputting a
6516 # separate branch for them. But if the nullable argument has an
6517 # unconditional conversion, we don't need to do that. The reason
6518 # for that is that at most one argument at the distinguishing index
6519 # is nullable (since two nullable arguments are not
6520 # distinguishable), and null/undefined values will always fall
6521 # through to the unconditional conversion we have, if any, since
6522 # they will fail whatever the conditions on the input value are for
6523 # our other conversions.
6524 nullOrUndefSigs = [s for s in possibleSignatures
6525 if needsNullOrUndefinedCase(distinguishingType(s))]
6526 # Can't have multiple nullable types here
6527 assert len(nullOrUndefSigs) < 2
6528 if len(nullOrUndefSigs) > 0:
6529 caseBody.append(CGGeneric("if (%s.isNullOrUndefined()) {\n" %
6530 distinguishingArg))
6531 tryCall(nullOrUndefSigs[0], 2, isNullOrUndefined=True)
6532 caseBody.append(CGGeneric("}\n"))
6534 # Now check for distinguishingArg being various kinds of objects.
6535 # The spec says to check for the following things in order:
6536 # 1) A platform object that's not a platform array object, being
6537 # passed to an interface or "object" arg.
6538 # 2) A Date object being passed to a Date or "object" arg.
6539 # 3) A RegExp object being passed to a RegExp or "object" arg.
6540 # 4) A callable object being passed to a callback or "object" arg.
6541 # 5) Any non-Date and non-RegExp object being passed to a
6542 # array or sequence or callback interface dictionary or
6543 # "object" arg.
6544 #
6545 # We can can coalesce these five cases together, as long as we make
6546 # sure to check whether our object works as an interface argument
6547 # before checking whether it works as an arraylike or dictionary or
6548 # callback function or callback interface.
6550 # First grab all the overloads that have a non-callback interface
6551 # (which includes typed arrays and arraybuffers) at the
6552 # distinguishing index. We can also include the ones that have an
6553 # "object" here, since if those are present no other object-typed
6554 # argument will be.
6555 objectSigs = [
6556 s for s in possibleSignatures
6557 if (distinguishingType(s).isObject() or
6558 distinguishingType(s).isNonCallbackInterface())]
6560 # And all the overloads that take Date
6561 objectSigs.extend(s for s in possibleSignatures
6562 if distinguishingType(s).isDate())
6564 # And all the overloads that take callbacks
6565 objectSigs.extend(s for s in possibleSignatures
6566 if distinguishingType(s).isCallback())
6568 # Now append all the overloads that take an array or sequence or
6569 # dictionary or callback interface:
6570 objectSigs.extend(s for s in possibleSignatures
6571 if (distinguishingType(s).isArray() or
6572 distinguishingType(s).isSequence() or
6573 distinguishingType(s).isDictionary() or
6574 distinguishingType(s).isCallbackInterface()))
6576 # There might be more than one thing in objectSigs; we need to check
6577 # which ones we unwrap to.
6578 if len(objectSigs) > 0:
6579 # Here it's enough to guard on our argument being an object. The
6580 # code for unwrapping non-callback interfaces, typed arrays,
6581 # sequences, arrays, and Dates will just bail out and move on to
6582 # the next overload if the object fails to unwrap correctly,
6583 # while "object" accepts any object anyway. We could even not
6584 # do the isObject() check up front here, but in cases where we
6585 # have multiple object overloads it makes sense to do it only
6586 # once instead of for each overload. That will also allow the
6587 # unwrapping test to skip having to do codegen for the
6588 # null-or-undefined case, which we already handled above.
6589 caseBody.append(CGGeneric("if (%s.isObject()) {\n" %
6590 distinguishingArg))
6591 for sig in objectSigs:
6592 caseBody.append(CGIndenter(CGGeneric("do {\n")))
6593 # Indent by 4, since we need to indent further
6594 # than our "do" statement
6595 tryCall(sig, 4, isDefinitelyObject=True)
6596 caseBody.append(CGIndenter(CGGeneric("} while (0);\n")))
6598 caseBody.append(CGGeneric("}\n"))
6600 # Now we only have to consider booleans, numerics, and strings. If
6601 # we only have one of them, then we can just output it. But if not,
6602 # then we need to output some of the cases conditionally: if we have
6603 # a string overload, then boolean and numeric are conditional, and
6604 # if not then boolean is conditional if we have a numeric overload.
6605 def findUniqueSignature(filterLambda):
6606 sigs = filter(filterLambda, possibleSignatures)
6607 assert len(sigs) < 2
6608 if len(sigs) > 0:
6609 return sigs[0]
6610 return None
6612 stringSignature = findUniqueSignature(
6613 lambda s: (distinguishingType(s).isString() or
6614 distinguishingType(s).isEnum()))
6615 numericSignature = findUniqueSignature(
6616 lambda s: distinguishingType(s).isNumeric())
6617 booleanSignature = findUniqueSignature(
6618 lambda s: distinguishingType(s).isBoolean())
6620 if stringSignature or numericSignature:
6621 booleanCondition = "%s.isBoolean()"
6622 else:
6623 booleanCondition = None
6625 if stringSignature:
6626 numericCondition = "%s.isNumber()"
6627 else:
6628 numericCondition = None
6630 def addCase(sig, condition):
6631 sigCode = getPerSignatureCall(sig, distinguishingIndex)
6632 if condition:
6633 sigCode = CGIfWrapper(sigCode,
6634 condition % distinguishingArg)
6635 caseBody.append(sigCode)
6637 if booleanSignature:
6638 addCase(booleanSignature, booleanCondition)
6639 if numericSignature:
6640 addCase(numericSignature, numericCondition)
6641 if stringSignature:
6642 addCase(stringSignature, None)
6644 if (not booleanSignature and not numericSignature and
6645 not stringSignature):
6646 # Just throw; we have no idea what we're supposed to
6647 # do with this.
6648 caseBody.append(CGGeneric(
6649 'return ThrowErrorMessage(cx, MSG_OVERLOAD_RESOLUTION_FAILED, "%d", "%d", "%s");\n' %
6650 (distinguishingIndex + 1, argCount, methodName)))
6652 argCountCases.append(CGCase(str(argCount), CGList(caseBody)))
6654 overloadCGThings = []
6655 overloadCGThings.append(
6656 CGGeneric("unsigned argcount = std::min(args.length(), %du);\n" %
6657 maxArgCount))
6658 overloadCGThings.append(
6659 CGSwitch("argcount",
6660 argCountCases,
6661 # BOGUS extra blank line at end of default block
6662 CGGeneric('return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "%s");\n\n' %
6663 methodName)))
6664 overloadCGThings.append(
6665 CGGeneric('MOZ_CRASH("We have an always-returning default case");\n'
6666 'return false;\n'))
6667 self.cgRoot = CGWrapper(CGIndenter(CGList(overloadCGThings)),
6668 pre="\n")
6670 def define(self):
6671 return self.cgRoot.define()
6674 class CGGetterCall(CGPerSignatureCall):
6675 """
6676 A class to generate a native object getter call for a particular IDL
6677 getter.
6678 """
6679 def __init__(self, returnType, nativeMethodName, descriptor, attr):
6680 CGPerSignatureCall.__init__(self, returnType, [], nativeMethodName,
6681 attr.isStatic(), descriptor, attr,
6682 getter=True)
6685 class FakeArgument():
6686 """
6687 A class that quacks like an IDLArgument. This is used to make
6688 setters look like method calls or for special operations.
6689 """
6690 def __init__(self, type, interfaceMember, name="arg", allowTreatNonCallableAsNull=False):
6691 self.type = type
6692 self.optional = False
6693 self.variadic = False
6694 self.defaultValue = None
6695 self._allowTreatNonCallableAsNull = allowTreatNonCallableAsNull
6696 self.treatNullAs = interfaceMember.treatNullAs
6697 if isinstance(interfaceMember, IDLAttribute):
6698 self.enforceRange = interfaceMember.enforceRange
6699 self.clamp = interfaceMember.clamp
6700 else:
6701 self.enforceRange = False
6702 self.clamp = False
6704 class FakeIdentifier():
6705 def __init__(self):
6706 self.name = name
6707 self.identifier = FakeIdentifier()
6709 def allowTreatNonCallableAsNull(self):
6710 return self._allowTreatNonCallableAsNull
6713 class CGSetterCall(CGPerSignatureCall):
6714 """
6715 A class to generate a native object setter call for a particular IDL
6716 setter.
6717 """
6718 def __init__(self, argType, nativeMethodName, descriptor, attr):
6719 CGPerSignatureCall.__init__(self, None, [FakeArgument(argType, attr, allowTreatNonCallableAsNull=True)],
6720 nativeMethodName, attr.isStatic(),
6721 descriptor, attr, setter=True)
6723 def wrap_return_value(self):
6724 attr = self.idlNode
6725 if self.descriptor.wrapperCache and attr.slotIndex is not None:
6726 if attr.getExtendedAttribute("StoreInSlot"):
6727 args = "cx, self"
6728 else:
6729 args = "self"
6730 clearSlot = ("ClearCached%sValue(%s);\n" %
6731 (MakeNativeName(self.idlNode.identifier.name), args))
6732 else:
6733 clearSlot = ""
6735 # We have no return value
6736 return ("\n"
6737 "%s"
6738 "return true;\n" % clearSlot)
6741 class CGAbstractBindingMethod(CGAbstractStaticMethod):
6742 """
6743 Common class to generate the JSNatives for all our methods, getters, and
6744 setters. This will generate the function declaration and unwrap the
6745 |this| object. Subclasses are expected to override the generate_code
6746 function to do the rest of the work. This function should return a
6747 CGThing which is already properly indented.
6749 getThisObj should be code for getting a JSObject* for the binding
6750 object. If this is None, we will auto-generate code based on
6751 descriptor to do the right thing. "" can be passed in if the
6752 binding object is already stored in 'obj'.
6754 callArgs should be code for getting a JS::CallArgs into a variable
6755 called 'args'. This can be "" if there is already such a variable
6756 around.
6758 If allowCrossOriginThis is true, then this-unwrapping will first do an
6759 UncheckedUnwrap and after that operate on the result.
6760 """
6761 def __init__(self, descriptor, name, args, unwrapFailureCode=None,
6762 getThisObj=None,
6763 callArgs="JS::CallArgs args = JS::CallArgsFromVp(argc, vp);\n",
6764 allowCrossOriginThis=False):
6765 CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
6767 if unwrapFailureCode is None:
6768 self.unwrapFailureCode = 'return ThrowErrorMessage(cx, MSG_THIS_DOES_NOT_IMPLEMENT_INTERFACE, "Value", "%s");\n' % descriptor.interface.identifier.name
6769 else:
6770 self.unwrapFailureCode = unwrapFailureCode
6772 if getThisObj == "":
6773 self.getThisObj = None
6774 else:
6775 if getThisObj is None:
6776 if descriptor.interface.isOnGlobalProtoChain():
6777 ensureCondition = "!args.thisv().isNullOrUndefined() && !args.thisv().isObject()"
6778 getThisObj = "args.thisv().isObject() ? &args.thisv().toObject() : js::GetGlobalForObjectCrossCompartment(&args.callee())"
6779 else:
6780 ensureCondition = "!args.thisv().isObject()"
6781 getThisObj = "&args.thisv().toObject()"
6782 unwrapFailureCode = self.unwrapFailureCode % {'securityError': 'false'}
6783 ensureThisObj = CGIfWrapper(CGGeneric(unwrapFailureCode),
6784 ensureCondition)
6785 else:
6786 ensureThisObj = None
6787 self.getThisObj = CGList(
6788 [ensureThisObj,
6789 CGGeneric("JS::Rooted<JSObject*> obj(cx, %s);\n" %
6790 getThisObj)])
6791 self.callArgs = callArgs
6792 self.allowCrossOriginThis = allowCrossOriginThis
6794 def definition_body(self):
6795 body = self.callArgs
6796 if self.getThisObj is not None:
6797 body += self.getThisObj.define() + "\n"
6798 body += "%s* self;\n" % self.descriptor.nativeType
6800 # Our descriptor might claim that we're not castable, simply because
6801 # we're someone's consequential interface. But for this-unwrapping, we
6802 # know that we're the real deal. So fake a descriptor here for
6803 # consumption by CastableObjectUnwrapper.
6804 body += str(CastableObjectUnwrapper(
6805 self.descriptor,
6806 "obj", "self", self.unwrapFailureCode,
6807 allowCrossOriginObj=self.allowCrossOriginThis))
6809 return indent(body) + self.generate_code().define()
6811 def generate_code(self):
6812 assert False # Override me
6815 class CGAbstractStaticBindingMethod(CGAbstractStaticMethod):
6816 """
6817 Common class to generate the JSNatives for all our static methods, getters
6818 and setters. This will generate the function declaration and unwrap the
6819 global object. Subclasses are expected to override the generate_code
6820 function to do the rest of the work. This function should return a
6821 CGThing which is already properly indented.
6822 """
6823 def __init__(self, descriptor, name):
6824 args = [Argument('JSContext*', 'cx'),
6825 Argument('unsigned', 'argc'),
6826 Argument('JS::Value*', 'vp')]
6827 CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
6829 def definition_body(self):
6830 # Make sure that "obj" is in the same compartment as "cx", since we'll
6831 # later use it to wrap return values.
6832 unwrap = indent(dedent("""
6833 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
6834 JS::Rooted<JSObject*> obj(cx, &args.callee());
6836 """))
6837 return unwrap + self.generate_code().define()
6839 def generate_code(self):
6840 assert False # Override me
6843 def MakeNativeName(name):
6844 return name[0].upper() + name[1:]
6847 class CGGenericMethod(CGAbstractBindingMethod):
6848 """
6849 A class for generating the C++ code for an IDL method.
6851 If allowCrossOriginThis is true, then this-unwrapping will first do an
6852 UncheckedUnwrap and after that operate on the result.
6853 """
6854 def __init__(self, descriptor, allowCrossOriginThis=False):
6855 args = [Argument('JSContext*', 'cx'),
6856 Argument('unsigned', 'argc'),
6857 Argument('JS::Value*', 'vp')]
6858 unwrapFailureCode = (
6859 'return ThrowInvalidThis(cx, args, GetInvalidThisErrorForMethod(%%(securityError)s), "%s");\n' %
6860 descriptor.interface.identifier.name)
6861 name = "genericCrossOriginMethod" if allowCrossOriginThis else "genericMethod"
6862 CGAbstractBindingMethod.__init__(self, descriptor, name,
6863 args,
6864 unwrapFailureCode=unwrapFailureCode,
6865 allowCrossOriginThis=allowCrossOriginThis)
6867 def generate_code(self):
6868 return CGGeneric(indent(dedent("""
6869 const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
6870 MOZ_ASSERT(info->type() == JSJitInfo::Method);
6871 JSJitMethodOp method = info->method;
6872 return method(cx, obj, self, JSJitMethodCallArgs(args));
6873 """)))
6876 class CGSpecializedMethod(CGAbstractStaticMethod):
6877 """
6878 A class for generating the C++ code for a specialized method that the JIT
6879 can call with lower overhead.
6880 """
6881 def __init__(self, descriptor, method):
6882 self.method = method
6883 name = CppKeywords.checkMethodName(method.identifier.name)
6884 args = [Argument('JSContext*', 'cx'),
6885 Argument('JS::Handle<JSObject*>', 'obj'),
6886 Argument('%s*' % descriptor.nativeType, 'self'),
6887 Argument('const JSJitMethodCallArgs&', 'args')]
6888 CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args)
6890 def definition_body(self):
6891 nativeName = CGSpecializedMethod.makeNativeName(self.descriptor,
6892 self.method)
6893 return CGMethodCall(nativeName, self.method.isStatic(), self.descriptor,
6894 self.method).define()
6896 @staticmethod
6897 def makeNativeName(descriptor, method):
6898 name = method.identifier.name
6899 return MakeNativeName(descriptor.binaryNames.get(name, name))
6902 class CGMethodPromiseWrapper(CGAbstractStaticMethod):
6903 """
6904 A class for generating a wrapper around another method that will
6905 convert exceptions to promises.
6906 """
6907 def __init__(self, descriptor, methodToWrap):
6908 self.method = methodToWrap
6909 name = self.makeName(methodToWrap.name)
6910 args = list(methodToWrap.args)
6911 CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args)
6913 def definition_body(self):
6914 return indent(fill(
6915 """
6916 // Make sure to save the callee before someone maybe messes
6917 // with rval().
6918 JS::Rooted<JSObject*> callee(cx, &args.callee());
6919 bool ok = ${methodName}(${args});
6920 if (ok) {
6921 return true;
6922 }
6923 return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
6924 args.rval());
6925 """,
6926 methodName=self.method.name,
6927 args=", ".join(arg.name for arg in self.args)))
6929 @staticmethod
6930 def makeName(methodName):
6931 return methodName + "_promiseWrapper"
6934 class CGJsonifierMethod(CGSpecializedMethod):
6935 def __init__(self, descriptor, method):
6936 assert method.isJsonifier()
6937 CGSpecializedMethod.__init__(self, descriptor, method)
6939 def definition_body(self):
6940 ret = dedent("""
6941 JS::Rooted<JSObject*> result(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
6942 if (!result) {
6943 return false;
6944 }
6945 """)
6946 for m in self.descriptor.interface.members:
6947 if m.isAttr() and not m.isStatic() and m.type.isSerializable():
6948 ret += fill(
6949 """
6950 { // scope for "temp"
6951 JS::Rooted<JS::Value> temp(cx);
6952 if (!get_${name}(cx, obj, self, JSJitGetterCallArgs(&temp))) {
6953 return false;
6954 }
6955 if (!JS_DefineProperty(cx, result, "${name}", temp, JSPROP_ENUMERATE)) {
6956 return false;
6957 }
6958 }
6959 """,
6960 name=m.identifier.name)
6962 ret += ('args.rval().setObject(*result);\n'
6963 'return true;\n')
6964 return indent(ret)
6967 class CGLegacyCallHook(CGAbstractBindingMethod):
6968 """
6969 Call hook for our object
6970 """
6971 def __init__(self, descriptor):
6972 self._legacycaller = descriptor.operations["LegacyCaller"]
6973 args = [Argument('JSContext*', 'cx'),
6974 Argument('unsigned', 'argc'),
6975 Argument('JS::Value*', 'vp')]
6976 # Our "self" is actually the callee in this case, not the thisval.
6977 CGAbstractBindingMethod.__init__(
6978 self, descriptor, LEGACYCALLER_HOOK_NAME,
6979 args, getThisObj="&args.callee()")
6981 def define(self):
6982 if not self._legacycaller:
6983 return ""
6984 return CGAbstractBindingMethod.define(self)
6986 def generate_code(self):
6987 name = self._legacycaller.identifier.name
6988 nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
6989 return CGMethodCall(nativeName, False, self.descriptor,
6990 self._legacycaller)
6993 class CGNewResolveHook(CGAbstractBindingMethod):
6994 """
6995 NewResolve hook for objects with custom hooks.
6996 """
6997 def __init__(self, descriptor):
6998 assert descriptor.interface.getExtendedAttribute("NeedNewResolve")
7000 args = [Argument('JSContext*', 'cx'),
7001 Argument('JS::Handle<JSObject*>', 'obj'),
7002 Argument('JS::Handle<jsid>', 'id'),
7003 Argument('JS::MutableHandle<JSObject*>', 'objp')]
7004 # Our "self" is actually the "obj" argument in this case, not the thisval.
7005 CGAbstractBindingMethod.__init__(
7006 self, descriptor, NEWRESOLVE_HOOK_NAME,
7007 args, getThisObj="", callArgs="")
7009 def generate_code(self):
7010 return CGGeneric(indent(dedent("""
7011 JS::Rooted<JSPropertyDescriptor> desc(cx);
7012 if (!self->DoNewResolve(cx, obj, id, &desc)) {
7013 return false;
7014 }
7015 if (!desc.object()) {
7016 return true;
7017 }
7018 // If desc.value() is undefined, then the DoNewResolve call
7019 // has already defined it on the object. Don't try to also
7020 // define it.
7021 if (!desc.value().isUndefined() &&
7022 !JS_DefinePropertyById(cx, obj, id, desc.value(),
7023 desc.getter(), desc.setter(),
7024 desc.attributes())) {
7025 return false;
7026 }
7027 objp.set(obj);
7028 return true;
7029 """)))
7031 def definition_body(self):
7032 if self.descriptor.interface.getExtendedAttribute("Global"):
7033 # Resolve standard classes
7034 prefix = indent(dedent("""
7035 if (!ResolveGlobal(cx, obj, id, objp)) {
7036 return false;
7037 }
7038 if (objp) {
7039 return true;
7040 }
7042 """))
7043 else:
7044 prefix = ""
7045 return prefix + CGAbstractBindingMethod.definition_body(self)
7048 class CGEnumerateHook(CGAbstractBindingMethod):
7049 """
7050 Enumerate hook for objects with custom hooks.
7051 """
7052 def __init__(self, descriptor):
7053 assert descriptor.interface.getExtendedAttribute("NeedNewResolve")
7055 args = [Argument('JSContext*', 'cx'),
7056 Argument('JS::Handle<JSObject*>', 'obj')]
7057 # Our "self" is actually the "obj" argument in this case, not the thisval.
7058 CGAbstractBindingMethod.__init__(
7059 self, descriptor, ENUMERATE_HOOK_NAME,
7060 args, getThisObj="", callArgs="")
7062 def generate_code(self):
7063 return CGGeneric(indent(dedent("""
7064 nsAutoTArray<nsString, 8> names;
7065 ErrorResult rv;
7066 self->GetOwnPropertyNames(cx, names, rv);
7067 rv.WouldReportJSException();
7068 if (rv.Failed()) {
7069 return ThrowMethodFailedWithDetails(cx, rv, "%s", "enumerate");
7070 }
7071 JS::Rooted<JS::Value> dummy(cx);
7072 for (uint32_t i = 0; i < names.Length(); ++i) {
7073 if (!JS_LookupUCProperty(cx, obj, names[i].get(), names[i].Length(), &dummy)) {
7074 return false;
7075 }
7076 }
7077 return true;
7078 """)))
7080 def definition_body(self):
7081 if self.descriptor.interface.getExtendedAttribute("Global"):
7082 # Enumerate standard classes
7083 prefix = indent(dedent("""
7084 if (!EnumerateGlobal(cx, obj)) {
7085 return false;
7086 }
7088 """))
7089 else:
7090 prefix = ""
7091 return prefix + CGAbstractBindingMethod.definition_body(self)
7094 class CppKeywords():
7095 """
7096 A class for checking if method names declared in webidl
7097 are not in conflict with C++ keywords.
7098 """
7099 keywords = frozenset([
7100 'alignas', 'alignof', 'and', 'and_eq', 'asm', 'assert', 'auto', 'bitand', 'bitor', 'bool',
7101 'break', 'case', 'catch', 'char', 'char16_t', 'char32_t', 'class', 'compl', 'const',
7102 'constexpr', 'const_cast', 'continue', 'decltype', 'default', 'delete', 'do', 'double',
7103 'dynamic_cast', 'else', 'enum', 'explicit', 'export', 'extern', 'false', 'final', 'float',
7104 'for', 'friend', 'goto', 'if', 'inline', 'int', 'long', 'mutable', 'namespace', 'new',
7105 'noexcept', 'not', 'not_eq', 'nullptr', 'operator', 'or', 'or_eq', 'override', 'private',
7106 'protected', 'public', 'register', 'reinterpret_cast', 'return', 'short', 'signed',
7107 'sizeof', 'static', 'static_assert', 'static_cast', 'struct', 'switch', 'template', 'this',
7108 'thread_local', 'throw', 'true', 'try', 'typedef', 'typeid', 'typename', 'union',
7109 'unsigned', 'using', 'virtual', 'void', 'volatile', 'wchar_t', 'while', 'xor', 'xor_eq'])
7111 @staticmethod
7112 def checkMethodName(name):
7113 # Double '_' because 'assert' and '_assert' cannot be used in MS2013 compiler.
7114 # Bug 964892 and bug 963560.
7115 if name in CppKeywords.keywords:
7116 name = '_' + name + '_'
7117 return name
7120 class CGStaticMethod(CGAbstractStaticBindingMethod):
7121 """
7122 A class for generating the C++ code for an IDL static method.
7123 """
7124 def __init__(self, descriptor, method):
7125 self.method = method
7126 name = method.identifier.name
7127 CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
7129 def generate_code(self):
7130 nativeName = CGSpecializedMethod.makeNativeName(self.descriptor,
7131 self.method)
7132 return CGMethodCall(nativeName, True, self.descriptor, self.method)
7135 class CGGenericGetter(CGAbstractBindingMethod):
7136 """
7137 A class for generating the C++ code for an IDL attribute getter.
7138 """
7139 def __init__(self, descriptor, lenientThis=False, allowCrossOriginThis=False):
7140 args = [Argument('JSContext*', 'cx'),
7141 Argument('unsigned', 'argc'),
7142 Argument('JS::Value*', 'vp')]
7143 if lenientThis:
7144 name = "genericLenientGetter"
7145 unwrapFailureCode = dedent("""
7146 MOZ_ASSERT(!JS_IsExceptionPending(cx));
7147 if (!ReportLenientThisUnwrappingFailure(cx, &args.callee())) {
7148 return false;
7149 }
7150 args.rval().set(JS::UndefinedValue());
7151 return true;
7152 """)
7153 else:
7154 if allowCrossOriginThis:
7155 name = "genericCrossOriginGetter"
7156 else:
7157 name = "genericGetter"
7158 unwrapFailureCode = (
7159 'return ThrowInvalidThis(cx, args, GetInvalidThisErrorForGetter(%%(securityError)s), "%s");\n' %
7160 descriptor.interface.identifier.name)
7161 CGAbstractBindingMethod.__init__(self, descriptor, name, args,
7162 unwrapFailureCode,
7163 allowCrossOriginThis=allowCrossOriginThis)
7165 def generate_code(self):
7166 return CGGeneric(indent(dedent("""
7167 const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
7168 MOZ_ASSERT(info->type() == JSJitInfo::Getter);
7169 JSJitGetterOp getter = info->getter;
7170 return getter(cx, obj, self, JSJitGetterCallArgs(args));
7171 """)))
7174 class CGSpecializedGetter(CGAbstractStaticMethod):
7175 """
7176 A class for generating the code for a specialized attribute getter
7177 that the JIT can call with lower overhead.
7178 """
7179 def __init__(self, descriptor, attr):
7180 self.attr = attr
7181 name = 'get_' + attr.identifier.name
7182 args = [
7183 Argument('JSContext*', 'cx'),
7184 Argument('JS::Handle<JSObject*>', 'obj'),
7185 Argument('%s*' % descriptor.nativeType, 'self'),
7186 Argument('JSJitGetterCallArgs', 'args')
7187 ]
7188 CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
7190 def definition_body(self):
7191 nativeName = CGSpecializedGetter.makeNativeName(self.descriptor,
7192 self.attr)
7193 if self.attr.slotIndex is not None:
7194 if (self.descriptor.hasXPConnectImpls and
7195 (self.descriptor.interface.identifier.name != 'Window' or
7196 self.attr.identifier.name != 'document')):
7197 raise TypeError("Interface '%s' has XPConnect impls, so we "
7198 "can't use our slot for property '%s'!" %
7199 (self.descriptor.interface.identifier.name,
7200 self.attr.identifier.name))
7201 prefix = indent(fill(
7202 """
7203 // Have to either root across the getter call or reget after.
7204 JS::Rooted<JSObject*> reflector(cx);
7205 // Safe to do an unchecked unwrap, since we've gotten this far.
7206 // Also make sure to unwrap outer windows, since we want the
7207 // real DOM object.
7208 reflector = IsDOMObject(obj) ? obj : js::UncheckedUnwrap(obj, /* stopAtOuter = */ false);
7209 {
7210 // Scope for cachedVal
7211 JS::Value cachedVal = js::GetReservedSlot(reflector, ${slot});
7212 if (!cachedVal.isUndefined()) {
7213 args.rval().set(cachedVal);
7214 // The cached value is in the compartment of reflector,
7215 // so wrap into the caller compartment as needed.
7216 return ${maybeWrap}(cx, args.rval());
7217 }
7218 }
7220 """,
7221 slot=memberReservedSlot(self.attr),
7222 maybeWrap=getMaybeWrapValueFuncForType(self.attr.type)))
7223 else:
7224 prefix = ""
7226 return (prefix +
7227 indent(CGGetterCall(self.attr.type, nativeName,
7228 self.descriptor, self.attr).define()))
7230 @staticmethod
7231 def makeNativeName(descriptor, attr):
7232 name = attr.identifier.name
7233 nativeName = MakeNativeName(descriptor.binaryNames.get(name, name))
7234 # resultOutParam does not depend on whether resultAlreadyAddRefed is set
7235 _, resultOutParam, _, _ = getRetvalDeclarationForType(attr.type,
7236 descriptor,
7237 False)
7238 infallible = ('infallible' in
7239 descriptor.getExtendedAttributes(attr, getter=True))
7240 if resultOutParam or attr.type.nullable() or not infallible:
7241 nativeName = "Get" + nativeName
7242 return nativeName
7245 class CGStaticGetter(CGAbstractStaticBindingMethod):
7246 """
7247 A class for generating the C++ code for an IDL static attribute getter.
7248 """
7249 def __init__(self, descriptor, attr):
7250 self.attr = attr
7251 name = 'get_' + attr.identifier.name
7252 CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
7254 def generate_code(self):
7255 nativeName = CGSpecializedGetter.makeNativeName(self.descriptor,
7256 self.attr)
7257 return CGIndenter(CGGetterCall(self.attr.type, nativeName,
7258 self.descriptor, self.attr))
7261 class CGGenericSetter(CGAbstractBindingMethod):
7262 """
7263 A class for generating the C++ code for an IDL attribute setter.
7264 """
7265 def __init__(self, descriptor, lenientThis=False, allowCrossOriginThis=False):
7266 args = [Argument('JSContext*', 'cx'),
7267 Argument('unsigned', 'argc'),
7268 Argument('JS::Value*', 'vp')]
7269 if lenientThis:
7270 name = "genericLenientSetter"
7271 unwrapFailureCode = dedent("""
7272 MOZ_ASSERT(!JS_IsExceptionPending(cx));
7273 if (!ReportLenientThisUnwrappingFailure(cx, &args.callee())) {
7274 return false;
7275 }
7276 args.rval().set(JS::UndefinedValue());
7277 return true;
7278 """)
7279 else:
7280 if allowCrossOriginThis:
7281 name = "genericCrossOriginSetter"
7282 else:
7283 name = "genericSetter"
7284 unwrapFailureCode = (
7285 'return ThrowInvalidThis(cx, args, GetInvalidThisErrorForSetter(%%(securityError)s), "%s");\n' %
7286 descriptor.interface.identifier.name)
7288 CGAbstractBindingMethod.__init__(self, descriptor, name, args,
7289 unwrapFailureCode,
7290 allowCrossOriginThis=allowCrossOriginThis)
7292 def generate_code(self):
7293 return CGGeneric(indent(fill(
7294 """
7295 if (args.length() == 0) {
7296 return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${name} attribute setter");
7297 }
7298 const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
7299 MOZ_ASSERT(info->type() == JSJitInfo::Setter);
7300 JSJitSetterOp setter = info->setter;
7301 if (!setter(cx, obj, self, JSJitSetterCallArgs(args))) {
7302 return false;
7303 }
7304 args.rval().set(JSVAL_VOID);
7305 return true;
7306 """,
7307 name=self.descriptor.interface.identifier.name)))
7310 class CGSpecializedSetter(CGAbstractStaticMethod):
7311 """
7312 A class for generating the code for a specialized attribute setter
7313 that the JIT can call with lower overhead.
7314 """
7315 def __init__(self, descriptor, attr):
7316 self.attr = attr
7317 name = 'set_' + attr.identifier.name
7318 args = [Argument('JSContext*', 'cx'),
7319 Argument('JS::Handle<JSObject*>', 'obj'),
7320 Argument('%s*' % descriptor.nativeType, 'self'),
7321 Argument('JSJitSetterCallArgs', 'args')]
7322 CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
7324 def definition_body(self):
7325 nativeName = CGSpecializedSetter.makeNativeName(self.descriptor,
7326 self.attr)
7327 return CGIndenter(CGSetterCall(self.attr.type, nativeName,
7328 self.descriptor, self.attr)).define()
7330 @staticmethod
7331 def makeNativeName(descriptor, attr):
7332 name = attr.identifier.name
7333 return "Set" + MakeNativeName(descriptor.binaryNames.get(name, name))
7336 class CGStaticSetter(CGAbstractStaticBindingMethod):
7337 """
7338 A class for generating the C++ code for an IDL static attribute setter.
7339 """
7340 def __init__(self, descriptor, attr):
7341 self.attr = attr
7342 name = 'set_' + attr.identifier.name
7343 CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
7345 def generate_code(self):
7346 nativeName = CGSpecializedSetter.makeNativeName(self.descriptor,
7347 self.attr)
7348 checkForArg = CGGeneric(fill(
7349 """
7350 if (args.length() == 0) {
7351 return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${name} setter");
7352 }
7353 """,
7354 name=self.attr.identifier.name))
7355 call = CGSetterCall(self.attr.type, nativeName, self.descriptor,
7356 self.attr)
7357 return CGIndenter(CGList([checkForArg, call]))
7360 class CGSpecializedForwardingSetter(CGSpecializedSetter):
7361 """
7362 A class for generating the code for a specialized attribute setter with
7363 PutForwards that the JIT can call with lower overhead.
7364 """
7365 def __init__(self, descriptor, attr):
7366 CGSpecializedSetter.__init__(self, descriptor, attr)
7368 def definition_body(self):
7369 attrName = self.attr.identifier.name
7370 forwardToAttrName = self.attr.getExtendedAttribute("PutForwards")[0]
7371 # JS_GetProperty and JS_SetProperty can only deal with ASCII
7372 assert all(ord(c) < 128 for c in attrName)
7373 assert all(ord(c) < 128 for c in forwardToAttrName)
7374 return indent(fill(
7375 """
7376 JS::Rooted<JS::Value> v(cx);
7377 if (!JS_GetProperty(cx, obj, "${attr}", &v)) {
7378 return false;
7379 }
7381 if (!v.isObject()) {
7382 return ThrowErrorMessage(cx, MSG_NOT_OBJECT, "${interface}.${attr}");
7383 }
7385 JS::Rooted<JSObject*> targetObj(cx, &v.toObject());
7386 return JS_SetProperty(cx, targetObj, "${forwardToAttrName}", args[0]);
7387 """,
7388 attr=attrName,
7389 interface=self.descriptor.interface.identifier.name,
7390 forwardToAttrName=forwardToAttrName))
7393 class CGSpecializedReplaceableSetter(CGSpecializedSetter):
7394 """
7395 A class for generating the code for a specialized attribute setter with
7396 Replaceable that the JIT can call with lower overhead.
7397 """
7398 def __init__(self, descriptor, attr):
7399 CGSpecializedSetter.__init__(self, descriptor, attr)
7401 def definition_body(self):
7402 attrName = self.attr.identifier.name
7403 # JS_DefineProperty can only deal with ASCII
7404 assert all(ord(c) < 128 for c in attrName)
7405 return indent('return JS_DefineProperty(cx, obj, "%s", args[0], JSPROP_ENUMERATE);\n' %
7406 attrName)
7409 def memberReturnsNewObject(member):
7410 return member.getExtendedAttribute("NewObject") is not None
7413 class CGMemberJITInfo(CGThing):
7414 """
7415 A class for generating the JITInfo for a property that points to
7416 our specialized getter and setter.
7417 """
7418 def __init__(self, descriptor, member):
7419 self.member = member
7420 self.descriptor = descriptor
7422 def declare(self):
7423 return ""
7425 def defineJitInfo(self, infoName, opName, opType, infallible, movable,
7426 aliasSet, hasSlot, slotIndex, returnTypes, args):
7427 """
7428 aliasSet is a JSJitInfo::AliasSet value, without the "JSJitInfo::" bit.
7430 args is None if we don't want to output argTypes for some
7431 reason (e.g. we have overloads or we're not a method) and
7432 otherwise an iterable of the arguments for this method.
7433 """
7434 assert(not movable or aliasSet != "AliasEverything") # Can't move write-aliasing things
7435 assert(not hasSlot or movable) # Things with slots had better be movable
7437 def jitInfoInitializer(isTypedMethod):
7438 initializer = fill(
7439 """
7440 {
7441 { ${opName} },
7442 prototypes::id::${name},
7443 PrototypeTraits<prototypes::id::${name}>::Depth,
7444 JSJitInfo::${opType},
7445 JSJitInfo::${aliasSet}, /* aliasSet. Not relevant for setters. */
7446 ${returnType}, /* returnType. Not relevant for setters. */
7447 ${isInfallible}, /* isInfallible. False in setters. */
7448 ${isMovable}, /* isMovable. Not relevant for setters. */
7449 ${isInSlot}, /* isInSlot. Only relevant for getters. */
7450 ${isTypedMethod}, /* isTypedMethod. Only relevant for methods. */
7451 ${slotIndex} /* Reserved slot index, if we're stored in a slot, else 0. */
7452 }
7453 """,
7454 opName=opName,
7455 name=self.descriptor.name,
7456 opType=opType,
7457 aliasSet=aliasSet,
7458 returnType=reduce(CGMemberJITInfo.getSingleReturnType, returnTypes,
7459 ""),
7460 isInfallible=toStringBool(infallible),
7461 isMovable=toStringBool(movable),
7462 isInSlot=toStringBool(hasSlot),
7463 isTypedMethod=toStringBool(isTypedMethod),
7464 slotIndex=slotIndex)
7465 return initializer.rstrip()
7467 if args is not None:
7468 argTypes = "%s_argTypes" % infoName
7469 args = [CGMemberJITInfo.getJSArgType(arg.type) for arg in args]
7470 args.append("JSJitInfo::ArgTypeListEnd")
7471 argTypesDecl = (
7472 "static const JSJitInfo::ArgType %s[] = { %s };\n" %
7473 (argTypes, ", ".join(args)))
7474 return fill(
7475 """
7477 $*{argTypesDecl}
7478 static const JSTypedMethodJitInfo ${infoName} = {
7479 ${jitInfo},
7480 ${argTypes}
7481 };
7482 """,
7483 argTypesDecl=argTypesDecl,
7484 infoName=infoName,
7485 jitInfo=jitInfoInitializer(True),
7486 argTypes=argTypes)
7488 return ("\n"
7489 "static const JSJitInfo %s = %s;\n"
7490 % (infoName, jitInfoInitializer(False)))
7492 def define(self):
7493 if self.member.isAttr():
7494 getterinfo = ("%s_getterinfo" % self.member.identifier.name)
7495 # We need the cast here because JSJitGetterOp has a "void* self"
7496 # while we have the right type.
7497 getter = ("(JSJitGetterOp)get_%s" % self.member.identifier.name)
7498 getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True)
7499 getterconst = (self.member.getExtendedAttribute("SameObject") or
7500 self.member.getExtendedAttribute("Constant"))
7501 getterpure = getterconst or self.member.getExtendedAttribute("Pure")
7502 if getterconst:
7503 aliasSet = "AliasNone"
7504 elif getterpure:
7505 aliasSet = "AliasDOMSets"
7506 else:
7507 aliasSet = "AliasEverything"
7508 movable = getterpure and getterinfal
7510 getterinfal = getterinfal and infallibleForMember(self.member, self.member.type, self.descriptor)
7511 isInSlot = self.member.getExtendedAttribute("StoreInSlot")
7512 if isInSlot:
7513 slotIndex = memberReservedSlot(self.member)
7514 # We'll statically assert that this is not too big in
7515 # CGUpdateMemberSlotsMethod
7516 else:
7517 slotIndex = "0"
7519 result = self.defineJitInfo(getterinfo, getter, "Getter",
7520 getterinfal, movable, aliasSet,
7521 isInSlot, slotIndex,
7522 [self.member.type], None)
7523 if (not self.member.readonly or
7524 self.member.getExtendedAttribute("PutForwards") is not None or
7525 self.member.getExtendedAttribute("Replaceable") is not None):
7526 setterinfo = ("%s_setterinfo" % self.member.identifier.name)
7527 # Actually a JSJitSetterOp, but JSJitGetterOp is first in the
7528 # union.
7529 setter = ("(JSJitGetterOp)set_%s" % self.member.identifier.name)
7530 # Setters are always fallible, since they have to do a typed unwrap.
7531 result += self.defineJitInfo(setterinfo, setter, "Setter",
7532 False, False, "AliasEverything",
7533 False, "0",
7534 [BuiltinTypes[IDLBuiltinType.Types.void]],
7535 None)
7536 return result
7537 if self.member.isMethod():
7538 methodinfo = ("%s_methodinfo" % self.member.identifier.name)
7539 name = CppKeywords.checkMethodName(self.member.identifier.name)
7540 if self.member.returnsPromise():
7541 name = CGMethodPromiseWrapper.makeName(name)
7542 # Actually a JSJitMethodOp, but JSJitGetterOp is first in the union.
7543 method = ("(JSJitGetterOp)%s" % name)
7544 methodPure = self.member.getExtendedAttribute("Pure")
7546 # Methods are infallible if they are infallible, have no arguments
7547 # to unwrap, and have a return type that's infallible to wrap up for
7548 # return.
7549 sigs = self.member.signatures()
7550 if len(sigs) != 1:
7551 # Don't handle overloading. If there's more than one signature,
7552 # one of them must take arguments.
7553 methodInfal = False
7554 args = None
7555 movable = False
7556 else:
7557 sig = sigs[0]
7558 # For pure methods, it's OK to set movable to our notion of
7559 # infallible on the C++ side, without considering argument
7560 # conversions, since argument conversions that can reliably
7561 # throw would be effectful anyway and the jit doesn't move
7562 # effectful things.
7563 hasInfallibleImpl = "infallible" in self.descriptor.getExtendedAttributes(self.member)
7564 movable = methodPure and hasInfallibleImpl
7565 # XXXbz can we move the smarts about fallibility due to arg
7566 # conversions into the JIT, using our new args stuff?
7567 if (len(sig[1]) != 0 or
7568 not infallibleForMember(self.member, sig[0], self.descriptor)):
7569 # We have arguments or our return-value boxing can fail
7570 methodInfal = False
7571 else:
7572 methodInfal = hasInfallibleImpl
7573 # For now, only bother to output args if we're pure
7574 if methodPure:
7575 args = sig[1]
7576 else:
7577 args = None
7579 if args is not None:
7580 aliasSet = "AliasDOMSets"
7581 else:
7582 aliasSet = "AliasEverything"
7583 result = self.defineJitInfo(methodinfo, method, "Method",
7584 methodInfal, movable, aliasSet, False, "0",
7585 [s[0] for s in sigs], args)
7586 return result
7587 raise TypeError("Illegal member type to CGPropertyJITInfo")
7589 @staticmethod
7590 def getJSReturnTypeTag(t):
7591 if t.nullable():
7592 # Sometimes it might return null, sometimes not
7593 return "JSVAL_TYPE_UNKNOWN"
7594 if t.isVoid():
7595 # No return, every time
7596 return "JSVAL_TYPE_UNDEFINED"
7597 if t.isArray():
7598 # No idea yet
7599 assert False
7600 if t.isSequence():
7601 return "JSVAL_TYPE_OBJECT"
7602 if t.isMozMap():
7603 return "JSVAL_TYPE_OBJECT"
7604 if t.isGeckoInterface():
7605 return "JSVAL_TYPE_OBJECT"
7606 if t.isString():
7607 return "JSVAL_TYPE_STRING"
7608 if t.isEnum():
7609 return "JSVAL_TYPE_STRING"
7610 if t.isCallback():
7611 return "JSVAL_TYPE_OBJECT"
7612 if t.isAny():
7613 # The whole point is to return various stuff
7614 return "JSVAL_TYPE_UNKNOWN"
7615 if t.isObject():
7616 return "JSVAL_TYPE_OBJECT"
7617 if t.isSpiderMonkeyInterface():
7618 return "JSVAL_TYPE_OBJECT"
7619 if t.isUnion():
7620 u = t.unroll()
7621 if u.hasNullableType:
7622 # Might be null or not
7623 return "JSVAL_TYPE_UNKNOWN"
7624 return reduce(CGMemberJITInfo.getSingleReturnType,
7625 u.flatMemberTypes, "")
7626 if t.isDictionary():
7627 return "JSVAL_TYPE_OBJECT"
7628 if t.isDate():
7629 return "JSVAL_TYPE_OBJECT"
7630 if not t.isPrimitive():
7631 raise TypeError("No idea what type " + str(t) + " is.")
7632 tag = t.tag()
7633 if tag == IDLType.Tags.bool:
7634 return "JSVAL_TYPE_BOOLEAN"
7635 if tag in [IDLType.Tags.int8, IDLType.Tags.uint8,
7636 IDLType.Tags.int16, IDLType.Tags.uint16,
7637 IDLType.Tags.int32]:
7638 return "JSVAL_TYPE_INT32"
7639 if tag in [IDLType.Tags.int64, IDLType.Tags.uint64,
7640 IDLType.Tags.unrestricted_float, IDLType.Tags.float,
7641 IDLType.Tags.unrestricted_double, IDLType.Tags.double]:
7642 # These all use JS_NumberValue, which can return int or double.
7643 # But TI treats "double" as meaning "int or double", so we're
7644 # good to return JSVAL_TYPE_DOUBLE here.
7645 return "JSVAL_TYPE_DOUBLE"
7646 if tag != IDLType.Tags.uint32:
7647 raise TypeError("No idea what type " + str(t) + " is.")
7648 # uint32 is sometimes int and sometimes double.
7649 return "JSVAL_TYPE_DOUBLE"
7651 @staticmethod
7652 def getSingleReturnType(existingType, t):
7653 type = CGMemberJITInfo.getJSReturnTypeTag(t)
7654 if existingType == "":
7655 # First element of the list; just return its type
7656 return type
7658 if type == existingType:
7659 return existingType
7660 if ((type == "JSVAL_TYPE_DOUBLE" and
7661 existingType == "JSVAL_TYPE_INT32") or
7662 (existingType == "JSVAL_TYPE_DOUBLE" and
7663 type == "JSVAL_TYPE_INT32")):
7664 # Promote INT32 to DOUBLE as needed
7665 return "JSVAL_TYPE_DOUBLE"
7666 # Different types
7667 return "JSVAL_TYPE_UNKNOWN"
7669 @staticmethod
7670 def getJSArgType(t):
7671 assert not t.isVoid()
7672 if t.nullable():
7673 # Sometimes it might return null, sometimes not
7674 return "JSJitInfo::ArgType(JSJitInfo::Null | %s)" % CGMemberJITInfo.getJSArgType(t.inner)
7675 if t.isArray():
7676 # No idea yet
7677 assert False
7678 if t.isSequence():
7679 return "JSJitInfo::Object"
7680 if t.isGeckoInterface():
7681 return "JSJitInfo::Object"
7682 if t.isString():
7683 return "JSJitInfo::String"
7684 if t.isEnum():
7685 return "JSJitInfo::String"
7686 if t.isCallback():
7687 return "JSJitInfo::Object"
7688 if t.isAny():
7689 # The whole point is to return various stuff
7690 return "JSJitInfo::Any"
7691 if t.isObject():
7692 return "JSJitInfo::Object"
7693 if t.isSpiderMonkeyInterface():
7694 return "JSJitInfo::Object"
7695 if t.isUnion():
7696 u = t.unroll()
7697 type = "JSJitInfo::Null" if u.hasNullableType else ""
7698 return ("JSJitInfo::ArgType(%s)" %
7699 reduce(CGMemberJITInfo.getSingleArgType,
7700 u.flatMemberTypes, type))
7701 if t.isDictionary():
7702 return "JSJitInfo::Object"
7703 if t.isDate():
7704 return "JSJitInfo::Object"
7705 if not t.isPrimitive():
7706 raise TypeError("No idea what type " + str(t) + " is.")
7707 tag = t.tag()
7708 if tag == IDLType.Tags.bool:
7709 return "JSJitInfo::Boolean"
7710 if tag in [IDLType.Tags.int8, IDLType.Tags.uint8,
7711 IDLType.Tags.int16, IDLType.Tags.uint16,
7712 IDLType.Tags.int32]:
7713 return "JSJitInfo::Integer"
7714 if tag in [IDLType.Tags.int64, IDLType.Tags.uint64,
7715 IDLType.Tags.unrestricted_float, IDLType.Tags.float,
7716 IDLType.Tags.unrestricted_double, IDLType.Tags.double]:
7717 # These all use JS_NumberValue, which can return int or double.
7718 # But TI treats "double" as meaning "int or double", so we're
7719 # good to return JSVAL_TYPE_DOUBLE here.
7720 return "JSJitInfo::Double"
7721 if tag != IDLType.Tags.uint32:
7722 raise TypeError("No idea what type " + str(t) + " is.")
7723 # uint32 is sometimes int and sometimes double.
7724 return "JSJitInfo::Double"
7726 @staticmethod
7727 def getSingleArgType(existingType, t):
7728 type = CGMemberJITInfo.getJSArgType(t)
7729 if existingType == "":
7730 # First element of the list; just return its type
7731 return type
7733 if type == existingType:
7734 return existingType
7735 return "%s | %s" % (existingType, type)
7738 class CGStaticMethodJitinfo(CGGeneric):
7739 """
7740 A class for generating the JITInfo for a promise-returning static method.
7741 """
7742 def __init__(self, method):
7743 CGGeneric.__init__(
7744 self,
7745 "\n"
7746 "static const JSJitInfo %s_methodinfo = {\n"
7747 " { (JSJitGetterOp)%s },\n"
7748 " prototypes::id::_ID_Count, 0, JSJitInfo::StaticMethod,\n"
7749 " JSJitInfo::AliasEverything, JSVAL_TYPE_MISSING, false, false,\n"
7750 " false, false, 0\n"
7751 "};\n" %
7752 (method.identifier.name, method.identifier.name))
7755 def getEnumValueName(value):
7756 # Some enum values can be empty strings. Others might have weird
7757 # characters in them. Deal with the former by returning "_empty",
7758 # deal with possible name collisions from that by throwing if the
7759 # enum value is actually "_empty", and throw on any value
7760 # containing non-ASCII chars for now. Replace all chars other than
7761 # [0-9A-Za-z_] with '_'.
7762 if re.match("[^\x20-\x7E]", value):
7763 raise SyntaxError('Enum value "' + value + '" contains non-ASCII characters')
7764 if re.match("^[0-9]", value):
7765 return '_' + value
7766 value = re.sub(r'[^0-9A-Za-z_]', '_', value)
7767 if re.match("^_[A-Z]|__", value):
7768 raise SyntaxError('Enum value "' + value + '" is reserved by the C++ spec')
7769 if value == "_empty":
7770 raise SyntaxError('"_empty" is not an IDL enum value we support yet')
7771 if value == "":
7772 return "_empty"
7773 return MakeNativeName(value)
7776 class CGEnum(CGThing):
7777 def __init__(self, enum):
7778 CGThing.__init__(self)
7779 self.enum = enum
7781 def stringsNamespace(self):
7782 return self.enum.identifier.name + "Values"
7784 def nEnumStrings(self):
7785 return len(self.enum.values()) + 1
7787 def declare(self):
7788 decl = fill( # BOGUS extra newline at top
7789 """
7791 MOZ_BEGIN_ENUM_CLASS(${name}, uint32_t)
7792 $*{enums}
7793 MOZ_END_ENUM_CLASS(${name})
7794 """,
7795 name=self.enum.identifier.name,
7796 enums=",\n".join(map(getEnumValueName, self.enum.values())) + "\n")
7797 strings = CGNamespace(self.stringsNamespace(),
7798 CGGeneric(declare="extern const EnumEntry %s[%d];\n"
7799 % (ENUM_ENTRY_VARIABLE_NAME, self.nEnumStrings())))
7800 return decl + "\n" + strings.declare()
7802 def define(self):
7803 strings = fill( # BOGUS extra newline at top
7804 """
7806 extern const EnumEntry ${name}[${count}] = {
7807 $*{entries}
7808 { nullptr, 0 }
7809 };
7810 """,
7811 name=ENUM_ENTRY_VARIABLE_NAME,
7812 count=self.nEnumStrings(),
7813 entries=''.join('{"%s", %d},\n' % (val, len(val))
7814 for val in self.enum.values()))
7815 # BOGUS - CGNamespace automatically indents; the extra indent() below causes
7816 # the output to be indented 4 spaces.
7817 return CGNamespace(self.stringsNamespace(),
7818 CGGeneric(define=indent(strings))).define()
7820 def deps(self):
7821 return self.enum.getDeps()
7824 def getUnionAccessorSignatureType(type, descriptorProvider):
7825 """
7826 Returns the types that are used in the getter and setter signatures for
7827 union types
7828 """
7829 if type.isArray():
7830 raise TypeError("Can't handle array arguments yet")
7832 if type.isSequence():
7833 nullable = type.nullable()
7834 if nullable:
7835 type = type.inner.inner
7836 else:
7837 type = type.inner
7838 # We don't use the returned template here, so it's OK to just pass no
7839 # sourceDescription.
7840 elementInfo = getJSToNativeConversionInfo(type, descriptorProvider,
7841 isMember="Sequence")
7842 typeName = CGTemplatedType("Sequence", elementInfo.declType,
7843 isReference=True)
7844 if nullable:
7845 typeName = CGTemplatedType("Nullable", typeName, isReference=True)
7847 return typeName
7849 if type.isUnion():
7850 typeName = CGGeneric(type.name)
7851 if type.nullable():
7852 typeName = CGTemplatedType("Nullable", typeName, isReference=True)
7854 return typeName
7856 if type.isGeckoInterface():
7857 descriptor = descriptorProvider.getDescriptor(
7858 type.unroll().inner.identifier.name)
7859 typeName = CGGeneric(descriptor.nativeType)
7860 # Allow null pointers for nullable types and old-binding classes
7861 if type.nullable() or type.unroll().inner.isExternal():
7862 typeName = CGWrapper(typeName, post="*")
7863 else:
7864 typeName = CGWrapper(typeName, post="&")
7865 return typeName
7867 if type.isSpiderMonkeyInterface():
7868 typeName = CGGeneric(type.name)
7869 if type.nullable():
7870 typeName = CGTemplatedType("Nullable", typeName)
7871 return CGWrapper(typeName, post="&")
7873 if type.isDOMString():
7874 return CGGeneric("const nsAString&")
7876 if type.isByteString():
7877 return CGGeneric("const nsCString&")
7879 if type.isEnum():
7880 if type.nullable():
7881 raise TypeError("We don't support nullable enumerated arguments or "
7882 "union members yet")
7883 return CGGeneric(type.inner.identifier.name)
7885 if type.isCallback():
7886 if type.nullable():
7887 typeName = "%s*"
7888 else:
7889 typeName = "%s&"
7890 return CGGeneric(typeName % type.unroll().identifier.name)
7892 if type.isAny():
7893 return CGGeneric("JS::Value")
7895 if type.isObject():
7896 return CGGeneric("JSObject*")
7898 if type.isDictionary():
7899 return CGGeneric("const %s&" % type.inner.identifier.name)
7901 if not type.isPrimitive():
7902 raise TypeError("Need native type for argument type '%s'" % str(type))
7904 typeName = CGGeneric(builtinNames[type.tag()])
7905 if type.nullable():
7906 typeName = CGTemplatedType("Nullable", typeName, isReference=True)
7907 return typeName
7910 def getUnionTypeTemplateVars(unionType, type, descriptorProvider,
7911 ownsMembers=False):
7912 # For dictionaries and sequences we need to pass None as the failureCode
7913 # for getJSToNativeConversionInfo.
7914 # Also, for dictionaries we would need to handle conversion of
7915 # null/undefined to the dictionary correctly.
7916 if type.isSequence():
7917 raise TypeError("Can't handle sequences in unions")
7919 name = getUnionMemberName(type)
7921 # By the time tryNextCode is invoked, we're guaranteed the union has been
7922 # constructed as some type, since we've been trying to convert into the
7923 # corresponding member.
7924 prefix = "" if ownsMembers else "mUnion."
7925 tryNextCode = ("%sDestroy%s();\n"
7926 "tryNext = true;\n"
7927 "return true;\n" % (prefix, name))
7928 conversionInfo = getJSToNativeConversionInfo(
7929 type, descriptorProvider, failureCode=tryNextCode,
7930 isDefinitelyObject=not type.isDictionary(),
7931 isMember=("OwningUnion" if ownsMembers else None),
7932 sourceDescription="member of %s" % unionType)
7934 ctorNeedsCx = conversionInfo.declArgs == "cx"
7935 ctorArgs = "cx" if ctorNeedsCx else ""
7937 # This is ugly, but UnionMember needs to call a constructor with no
7938 # arguments so the type can't be const.
7939 structType = conversionInfo.declType.define()
7940 if structType.startswith("const "):
7941 structType = structType[6:]
7942 externalType = getUnionAccessorSignatureType(type, descriptorProvider).define()
7944 if type.isObject():
7945 if ownsMembers:
7946 body = dedent("""
7947 MOZ_ASSERT(mType == eUninitialized);
7948 mValue.mObject.SetValue(obj);
7949 mType = eObject;
7950 """)
7951 else:
7952 body = dedent("""
7953 MOZ_ASSERT(mUnion.mType == mUnion.eUninitialized);
7954 mUnion.mValue.mObject.SetValue(cx, obj);
7955 mUnion.mType = mUnion.eObject;
7956 """)
7957 setter = ClassMethod("SetToObject", "void",
7958 [Argument("JSContext*", "cx"),
7959 Argument("JSObject*", "obj")],
7960 inline=True, bodyInHeader=True,
7961 body=body)
7963 else:
7964 # Important: we need to not have our declName involve
7965 # maybe-GCing operations.
7966 jsConversion = string.Template(conversionInfo.template).substitute({
7967 "val": "value",
7968 "mutableVal": "pvalue",
7969 "declName": "memberSlot",
7970 "holderName": "m" + name + "Holder",
7971 })
7972 jsConversion = fill(
7973 """
7974 tryNext = false;
7975 { // scope for memberSlot
7976 ${structType}& memberSlot = RawSetAs${name}(${ctorArgs});
7977 $*{jsConversion}
7978 }
7979 return true;
7980 """,
7981 structType=structType,
7982 name=name,
7983 ctorArgs=ctorArgs,
7984 jsConversion=jsConversion)
7986 setter = ClassMethod("TrySetTo" + name, "bool",
7987 [Argument("JSContext*", "cx"),
7988 Argument("JS::Handle<JS::Value>", "value"),
7989 Argument("JS::MutableHandle<JS::Value>", "pvalue"),
7990 Argument("bool&", "tryNext")],
7991 inline=not ownsMembers,
7992 bodyInHeader=not ownsMembers,
7993 body=jsConversion)
7995 return {
7996 "name": name,
7997 "structType": structType,
7998 "externalType": externalType,
7999 "setter": setter,
8000 "holderType": conversionInfo.holderType.define() if conversionInfo.holderType else None,
8001 "ctorArgs": ctorArgs,
8002 "ctorArgList": [Argument("JSContext*", "cx")] if ctorNeedsCx else []
8003 }
8006 class CGUnionStruct(CGThing):
8007 def __init__(self, type, descriptorProvider, ownsMembers=False):
8008 CGThing.__init__(self)
8009 self.type = type.unroll()
8010 self.descriptorProvider = descriptorProvider
8011 self.ownsMembers = ownsMembers
8012 self.struct = self.getStruct()
8014 def declare(self):
8015 return self.struct.declare()
8017 def define(self):
8018 return self.struct.define()
8020 def getStruct(self):
8022 members = [ClassMember("mType", "Type", body="eUninitialized"),
8023 ClassMember("mValue", "Value")]
8024 ctor = ClassConstructor([], bodyInHeader=True, visibility="public",
8025 explicit=True)
8027 methods = []
8028 enumValues = ["eUninitialized"]
8029 toJSValCases = [CGCase("eUninitialized", CGGeneric("return false;\n"))]
8030 destructorCases = [CGCase("eUninitialized", None)]
8031 assignmentCases = [
8032 CGCase("eUninitialized",
8033 CGGeneric('MOZ_ASSERT(mType == eUninitialized,\n'
8034 ' "We need to destroy ourselves?");\n'))]
8035 traceCases = []
8036 unionValues = []
8037 if self.type.hasNullableType:
8038 enumValues.append("eNull")
8039 methods.append(ClassMethod("IsNull", "bool", [], const=True, inline=True,
8040 body="return mType == eNull;\n",
8041 bodyInHeader=True))
8042 methods.append(ClassMethod("SetNull", "void", [], inline=True,
8043 body=("Uninit();\n"
8044 "mType = eNull;\n"),
8045 bodyInHeader=True))
8046 destructorCases.append(CGCase("eNull", None))
8047 assignmentCases.append(CGCase("eNull",
8048 CGGeneric("MOZ_ASSERT(mType == eUninitialized);\n"
8049 "mType = eNull;\n")))
8050 toJSValCases.append(CGCase("eNull", CGGeneric("rval.setNull();\n"
8051 "return true;\n")))
8053 hasObjectType = any(t.isObject() for t in self.type.flatMemberTypes)
8054 for t in self.type.flatMemberTypes:
8055 vars = getUnionTypeTemplateVars(self.type,
8056 t, self.descriptorProvider,
8057 ownsMembers=self.ownsMembers)
8058 if vars["name"] != "Object" or self.ownsMembers:
8059 body = fill(
8060 """
8061 if (mType == e${name}) {
8062 return mValue.m${name}.Value();
8063 }
8064 %s
8065 mType = e${name};
8066 return mValue.m${name}.SetValue(${ctorArgs});
8067 """,
8068 **vars)
8070 # bodyInHeader must be false for return values because they own
8071 # their union members and we don't want include headers in
8072 # UnionTypes.h just to call Addref/Release
8073 methods.append(ClassMethod(
8074 "RawSetAs" + vars["name"],
8075 vars["structType"] + "&",
8076 vars["ctorArgList"],
8077 bodyInHeader=not self.ownsMembers,
8078 body=body % "MOZ_ASSERT(mType == eUninitialized);"))
8079 uninit = "Uninit();"
8080 if hasObjectType and not self.ownsMembers:
8081 uninit = 'MOZ_ASSERT(mType != eObject, "This will not play well with Rooted");\n' + uninit
8082 methods.append(ClassMethod(
8083 "SetAs" + vars["name"],
8084 vars["structType"] + "&",
8085 vars["ctorArgList"],
8086 bodyInHeader=not self.ownsMembers,
8087 body=body % uninit))
8088 if self.ownsMembers:
8089 methods.append(vars["setter"])
8090 if t.isString():
8091 methods.append(
8092 ClassMethod("SetStringData", "void",
8093 [Argument("const nsString::char_type*", "aData"),
8094 Argument("nsString::size_type", "aLength")],
8095 inline=True, bodyInHeader=True,
8096 body="RawSetAsString().Assign(aData, aLength);\n"))
8098 body = fill(
8099 """
8100 MOZ_ASSERT(Is${name}(), "Wrong type!");
8101 mValue.m${name}.Destroy();
8102 mType = eUninitialized;
8103 """,
8104 **vars)
8105 methods.append(ClassMethod("Destroy" + vars["name"],
8106 "void",
8107 [],
8108 visibility="private",
8109 bodyInHeader=not self.ownsMembers,
8110 body=body))
8112 body = fill("return mType == e${name};\n", **vars)
8113 methods.append(ClassMethod("Is" + vars["name"],
8114 "bool",
8115 [],
8116 const=True,
8117 bodyInHeader=True,
8118 body=body))
8120 body = fill(
8121 """
8122 MOZ_ASSERT(Is${name}(), "Wrong type!");
8123 return const_cast<${structType}&>(mValue.m${name}.Value());
8124 """,
8125 **vars)
8126 if self.ownsMembers:
8127 getterReturnType = "%s&" % vars["structType"]
8128 else:
8129 getterReturnType = vars["externalType"]
8130 methods.append(ClassMethod("GetAs" + vars["name"],
8131 getterReturnType,
8132 [],
8133 const=True,
8134 bodyInHeader=True,
8135 body=body))
8137 unionValues.append(
8138 fill("UnionMember<${structType} > m${name}", **vars))
8139 enumValues.append("e" + vars["name"])
8141 toJSValCases.append(
8142 CGCase("e" + vars["name"],
8143 self.getConversionToJS(vars, t)))
8144 destructorCases.append(
8145 CGCase("e" + vars["name"],
8146 CGGeneric("Destroy%s();\n" % vars["name"])))
8147 assignmentCases.append(
8148 CGCase("e" + vars["name"],
8149 CGGeneric("SetAs%s() = aOther.GetAs%s();\n" %
8150 (vars["name"], vars["name"]))))
8151 if self.ownsMembers and typeNeedsRooting(t):
8152 if t.isObject():
8153 traceCases.append(
8154 CGCase("e" + vars["name"],
8155 CGGeneric('JS_CallObjectTracer(trc, %s, "%s");\n' %
8156 ("&mValue.m" + vars["name"] + ".Value()",
8157 "mValue.m" + vars["name"]))))
8158 elif t.isDictionary():
8159 traceCases.append(
8160 CGCase("e" + vars["name"],
8161 CGGeneric("mValue.m%s.Value().TraceDictionary(trc);\n" %
8162 vars["name"])))
8163 else:
8164 assert t.isSpiderMonkeyInterface()
8165 traceCases.append(
8166 CGCase("e" + vars["name"],
8167 CGGeneric("mValue.m%s.Value().TraceSelf(trc);\n" %
8168 vars["name"])))
8170 dtor = CGSwitch("mType", destructorCases).define()
8172 methods.append(ClassMethod("Uninit", "void", [],
8173 visibility="private", body=dtor,
8174 bodyInHeader=not self.ownsMembers,
8175 inline=not self.ownsMembers))
8177 methods.append(
8178 ClassMethod(
8179 "ToJSVal",
8180 "bool",
8181 [
8182 Argument("JSContext*", "cx"),
8183 Argument("JS::Handle<JSObject*>", "scopeObj"),
8184 Argument("JS::MutableHandle<JS::Value>", "rval")
8185 ],
8186 body=CGSwitch("mType", toJSValCases,
8187 default=CGGeneric("return false;\n")).define(),
8188 const=True))
8190 constructors = [ctor]
8191 selfName = CGUnionStruct.unionTypeName(self.type, self.ownsMembers)
8192 if self.ownsMembers:
8193 if traceCases:
8194 # BOGUS blank line in default case
8195 traceBody = CGSwitch("mType", traceCases,
8196 default=CGGeneric("\n")).define()
8197 else:
8198 # BOGUS blank line in method
8199 traceBody = "\n"
8200 methods.append(ClassMethod("TraceUnion", "void",
8201 [Argument("JSTracer*", "trc")],
8202 body=traceBody))
8203 if CGUnionStruct.isUnionCopyConstructible(self.type):
8204 constructors.append(
8205 ClassConstructor(
8206 [Argument("const %s&" % selfName, "aOther")],
8207 bodyInHeader=True,
8208 visibility="public",
8209 explicit=True,
8210 body="*this = aOther;\n"))
8211 methods.append(ClassMethod(
8212 "operator=", "void",
8213 [Argument("const %s&" % selfName, "aOther")],
8214 body=CGSwitch("aOther.mType", assignmentCases).define()))
8215 disallowCopyConstruction = False
8216 else:
8217 disallowCopyConstruction = True
8218 else:
8219 disallowCopyConstruction = True
8221 friend = " friend class %sArgument;\n" % str(self.type) if not self.ownsMembers else ""
8222 bases = [ClassBase("AllOwningUnionBase")] if self.ownsMembers else []
8223 return CGClass(selfName,
8224 bases=bases,
8225 members=members,
8226 constructors=constructors,
8227 methods=methods,
8228 disallowCopyConstruction=disallowCopyConstruction,
8229 extradeclarations=friend,
8230 destructor=ClassDestructor(visibility="public",
8231 body="Uninit();",
8232 bodyInHeader=True),
8233 enums=[ClassEnum("Type", enumValues, visibility="private")],
8234 unions=[ClassUnion("Value", unionValues, visibility="private")])
8236 def getConversionToJS(self, templateVars, type):
8237 assert not type.nullable() # flatMemberTypes never has nullable types
8238 val = "mValue.m%(name)s.Value()" % templateVars
8239 wrapCode = wrapForType(
8240 type, self.descriptorProvider,
8241 {
8242 "jsvalRef": "rval",
8243 "jsvalHandle": "rval",
8244 "obj": "scopeObj",
8245 "result": val,
8246 "typedArraysAreStructs": True
8247 })
8248 return CGGeneric(wrapCode)
8250 @staticmethod
8251 def isUnionCopyConstructible(type):
8252 return all(isTypeCopyConstructible(t) for t in type.flatMemberTypes)
8254 @staticmethod
8255 def unionTypeName(type, ownsMembers):
8256 """
8257 Returns a string name for this known union type.
8258 """
8259 assert type.isUnion() and not type.nullable()
8260 return ("Owning" if ownsMembers else "") + type.name
8262 @staticmethod
8263 def unionTypeDecl(type, ownsMembers):
8264 """
8265 Returns a string for declaring this possibly-nullable union type.
8266 """
8267 assert type.isUnion()
8268 nullable = type.nullable()
8269 if nullable:
8270 type = type.inner
8271 decl = CGGeneric(CGUnionStruct.unionTypeName(type, ownsMembers))
8272 if nullable:
8273 decl = CGTemplatedType("Nullable", decl)
8274 return decl.define()
8277 class CGUnionConversionStruct(CGThing):
8278 def __init__(self, type, descriptorProvider):
8279 CGThing.__init__(self)
8280 self.type = type.unroll()
8281 self.descriptorProvider = descriptorProvider
8283 def declare(self):
8285 structName = str(self.type)
8286 members = [ClassMember("mUnion", structName + "&",
8287 body="const_cast<%s&>(aUnion)" % structName)]
8288 # Argument needs to be a const ref because that's all Maybe<> allows
8289 ctor = ClassConstructor([Argument("const %s&" % structName, "aUnion")],
8290 bodyInHeader=True,
8291 visibility="public",
8292 explicit=True)
8293 methods = []
8295 if self.type.hasNullableType:
8296 methods.append(ClassMethod("SetNull", "bool", [],
8297 body=("MOZ_ASSERT(mUnion.mType == mUnion.eUninitialized);\n"
8298 "mUnion.mType = mUnion.eNull;\n"
8299 "return true;\n"),
8300 inline=True, bodyInHeader=True))
8302 for t in self.type.flatMemberTypes:
8303 vars = getUnionTypeTemplateVars(self.type,
8304 t, self.descriptorProvider)
8305 methods.append(vars["setter"])
8306 if vars["name"] != "Object":
8307 body = fill(
8308 """
8309 MOZ_ASSERT(mUnion.mType == mUnion.eUninitialized);
8310 mUnion.mType = mUnion.e${name};
8311 return mUnion.mValue.m${name}.SetValue(${ctorArgs});
8312 """,
8313 **vars)
8314 methods.append(ClassMethod("RawSetAs" + vars["name"],
8315 vars["structType"] + "&",
8316 vars["ctorArgList"],
8317 bodyInHeader=True,
8318 body=body,
8319 visibility="private"))
8320 if t.isString():
8321 methods.append(
8322 ClassMethod("SetStringData", "void",
8323 [Argument("const nsDependentString::char_type*", "aData"),
8324 Argument("nsDependentString::size_type", "aLength")],
8325 inline=True, bodyInHeader=True,
8326 body="RawSetAsString().SetData(aData, aLength);\n"))
8328 if vars["holderType"] is not None:
8329 members.append(ClassMember("m%sHolder" % vars["name"],
8330 vars["holderType"]))
8332 return CGClass(structName + "Argument",
8333 members=members,
8334 constructors=[ctor],
8335 methods=methods,
8336 disallowCopyConstruction=True).declare()
8338 def define(self):
8339 return "\n"
8342 class ClassItem:
8343 """ Use with CGClass """
8344 def __init__(self, name, visibility):
8345 self.name = name
8346 self.visibility = visibility
8348 def declare(self, cgClass):
8349 assert False
8351 def define(self, cgClass):
8352 assert False
8355 class ClassBase(ClassItem):
8356 def __init__(self, name, visibility='public'):
8357 ClassItem.__init__(self, name, visibility)
8359 def declare(self, cgClass):
8360 return '%s %s' % (self.visibility, self.name)
8362 def define(self, cgClass):
8363 # Only in the header
8364 return ''
8367 class ClassMethod(ClassItem):
8368 def __init__(self, name, returnType, args, inline=False, static=False,
8369 virtual=False, const=False, bodyInHeader=False,
8370 templateArgs=None, visibility='public', body=None,
8371 breakAfterReturnDecl="\n",
8372 breakAfterSelf="\n", override=False):
8373 """
8374 override indicates whether to flag the method as MOZ_OVERRIDE
8375 """
8376 assert not override or virtual
8377 assert not (override and static)
8378 self.returnType = returnType
8379 self.args = args
8380 self.inline = inline or bodyInHeader
8381 self.static = static
8382 self.virtual = virtual
8383 self.const = const
8384 self.bodyInHeader = bodyInHeader
8385 self.templateArgs = templateArgs
8386 self.body = body
8387 self.breakAfterReturnDecl = breakAfterReturnDecl
8388 self.breakAfterSelf = breakAfterSelf
8389 self.override = override
8390 ClassItem.__init__(self, name, visibility)
8392 def getDecorators(self, declaring):
8393 decorators = []
8394 if self.inline:
8395 decorators.append('inline')
8396 if declaring:
8397 if self.static:
8398 decorators.append('static')
8399 if self.virtual:
8400 decorators.append('virtual')
8401 if decorators:
8402 return ' '.join(decorators) + ' '
8403 return ''
8405 def getBody(self):
8406 # Override me or pass a string to constructor
8407 assert self.body is not None
8408 return self.body
8410 def declare(self, cgClass):
8411 templateClause = ('template <%s>\n' % ', '.join(self.templateArgs)
8412 if self.bodyInHeader and self.templateArgs else '')
8413 args = ', '.join([a.declare() for a in self.args])
8414 if self.bodyInHeader:
8415 body = indent(self.getBody())
8416 body = '\n{\n' + body + '}\n'
8417 else:
8418 body = ';\n'
8420 return fill(
8421 "${templateClause}${decorators}${returnType}${breakAfterReturnDecl}"
8422 "${name}(${args})${const}${override}${body}"
8423 "${breakAfterSelf}",
8424 templateClause=templateClause,
8425 decorators=self.getDecorators(True),
8426 returnType=self.returnType,
8427 breakAfterReturnDecl=self.breakAfterReturnDecl,
8428 name=self.name,
8429 args=args,
8430 const=' const' if self.const else '',
8431 override=' MOZ_OVERRIDE' if self.override else '',
8432 body=body,
8433 breakAfterSelf=self.breakAfterSelf)
8435 def define(self, cgClass):
8436 if self.bodyInHeader:
8437 return ''
8439 templateArgs = cgClass.templateArgs
8440 if templateArgs:
8441 if cgClass.templateSpecialization:
8442 templateArgs = \
8443 templateArgs[len(cgClass.templateSpecialization):]
8445 if templateArgs:
8446 templateClause = \
8447 'template <%s>\n' % ', '.join([str(a) for a in templateArgs])
8448 else:
8449 templateClause = ''
8451 return fill(
8452 """
8453 ${templateClause}${decorators}${returnType}
8454 ${className}::${name}(${args})${const}
8455 {
8456 $*{body}
8457 }
8458 """,
8459 templateClause=templateClause,
8460 decorators=self.getDecorators(False),
8461 returnType=self.returnType,
8462 className=cgClass.getNameString(),
8463 name=self.name,
8464 args=', '.join([a.define() for a in self.args]),
8465 const=' const' if self.const else '',
8466 body=self.getBody())
8469 class ClassUsingDeclaration(ClassItem):
8470 """
8471 Used for importing a name from a base class into a CGClass
8473 baseClass is the name of the base class to import the name from
8475 name is the name to import
8477 visibility determines the visibility of the name (public,
8478 protected, private), defaults to public.
8479 """
8480 def __init__(self, baseClass, name, visibility='public'):
8481 self.baseClass = baseClass
8482 ClassItem.__init__(self, name, visibility)
8484 def declare(self, cgClass):
8485 return "using %s::%s;\n\n" % (self.baseClass, self.name)
8487 def define(self, cgClass):
8488 return ''
8491 class ClassConstructor(ClassItem):
8492 """
8493 Used for adding a constructor to a CGClass.
8495 args is a list of Argument objects that are the arguments taken by the
8496 constructor.
8498 inline should be True if the constructor should be marked inline.
8500 bodyInHeader should be True if the body should be placed in the class
8501 declaration in the header.
8503 visibility determines the visibility of the constructor (public,
8504 protected, private), defaults to private.
8506 explicit should be True if the constructor should be marked explicit.
8508 baseConstructors is a list of strings containing calls to base constructors,
8509 defaults to None.
8511 body contains a string with the code for the constructor, defaults to empty.
8512 """
8513 def __init__(self, args, inline=False, bodyInHeader=False,
8514 visibility="private", explicit=False, baseConstructors=None,
8515 body=""):
8516 self.args = args
8517 self.inline = inline or bodyInHeader
8518 self.bodyInHeader = bodyInHeader
8519 self.explicit = explicit
8520 self.baseConstructors = baseConstructors or []
8521 self.body = body
8522 ClassItem.__init__(self, None, visibility)
8524 def getDecorators(self, declaring):
8525 decorators = []
8526 if self.explicit:
8527 decorators.append('explicit')
8528 if self.inline and declaring:
8529 decorators.append('inline')
8530 if decorators:
8531 return ' '.join(decorators) + ' '
8532 return ''
8534 def getInitializationList(self, cgClass):
8535 items = [str(c) for c in self.baseConstructors]
8536 for m in cgClass.members:
8537 if not m.static:
8538 initialize = m.body
8539 if initialize:
8540 items.append(m.name + "(" + initialize + ")")
8542 if len(items) > 0:
8543 return '\n : ' + ',\n '.join(items)
8544 return ''
8546 def getBody(self):
8547 return self.body
8549 def declare(self, cgClass):
8550 args = ', '.join([a.declare() for a in self.args])
8551 if self.bodyInHeader:
8552 body = self.getInitializationList(cgClass) + '\n{\n' + indent(self.getBody()) + '}\n'
8553 else:
8554 body = ';\n'
8556 return fill(
8557 "${decorators}${className}(${args})${body}\n",
8558 decorators=self.getDecorators(True),
8559 className=cgClass.getNameString(),
8560 args=args,
8561 body=body)
8563 def define(self, cgClass):
8564 if self.bodyInHeader:
8565 return ''
8567 return fill(
8568 """
8569 ${decorators}
8570 ${className}::${className}(${args})${initializationList}
8571 {
8572 $*{body}
8573 }
8574 """,
8575 decorators=self.getDecorators(False),
8576 className=cgClass.getNameString(),
8577 args=', '.join([a.define() for a in self.args]),
8578 initializationList=self.getInitializationList(cgClass),
8579 body=self.getBody())
8582 class ClassDestructor(ClassItem):
8583 """
8584 Used for adding a destructor to a CGClass.
8586 inline should be True if the destructor should be marked inline.
8588 bodyInHeader should be True if the body should be placed in the class
8589 declaration in the header.
8591 visibility determines the visibility of the destructor (public,
8592 protected, private), defaults to private.
8594 body contains a string with the code for the destructor, defaults to empty.
8596 virtual determines whether the destructor is virtual, defaults to False.
8597 """
8598 def __init__(self, inline=False, bodyInHeader=False,
8599 visibility="private", body='', virtual=False):
8600 self.inline = inline or bodyInHeader
8601 self.bodyInHeader = bodyInHeader
8602 self.body = body
8603 self.virtual = virtual
8604 ClassItem.__init__(self, None, visibility)
8606 def getDecorators(self, declaring):
8607 decorators = []
8608 if self.virtual and declaring:
8609 decorators.append('virtual')
8610 if self.inline and declaring:
8611 decorators.append('inline')
8612 if decorators:
8613 return ' '.join(decorators) + ' '
8614 return ''
8616 def getBody(self):
8617 return self.body
8619 def declare(self, cgClass):
8620 if self.bodyInHeader:
8621 body = '\n{\n' + indent(self.getBody()) + '}\n'
8622 else:
8623 body = ';\n'
8625 return fill(
8626 "${decorators}~${className}()${body}\n",
8627 decorators=self.getDecorators(True),
8628 className=cgClass.getNameString(),
8629 body=body)
8631 def define(self, cgClass):
8632 if self.bodyInHeader:
8633 return ''
8634 return fill(
8635 """
8636 ${decorators}
8637 ${className}::~${className}()
8638 {
8639 $*{body}
8640 }
8641 """,
8642 decorators=self.getDecorators(False),
8643 className=cgClass.getNameString(),
8644 body=self.getBody() or "\n") # BOGUS extra blank line if empty
8647 class ClassMember(ClassItem):
8648 def __init__(self, name, type, visibility="private", static=False,
8649 body=None):
8650 self.type = type
8651 self.static = static
8652 self.body = body
8653 ClassItem.__init__(self, name, visibility)
8655 def declare(self, cgClass):
8656 return '%s%s %s;\n' % ('static ' if self.static else '', self.type,
8657 self.name)
8659 def define(self, cgClass):
8660 if not self.static:
8661 return ''
8662 if self.body:
8663 body = " = " + self.body
8664 else:
8665 body = ""
8666 return '%s %s::%s%s;\n' % (self.type, cgClass.getNameString(),
8667 self.name, body)
8670 class ClassTypedef(ClassItem):
8671 def __init__(self, name, type, visibility="public"):
8672 self.type = type
8673 ClassItem.__init__(self, name, visibility)
8675 def declare(self, cgClass):
8676 return 'typedef %s %s;\n' % (self.type, self.name)
8678 def define(self, cgClass):
8679 # Only goes in the header
8680 return ''
8683 class ClassEnum(ClassItem):
8684 def __init__(self, name, entries, values=None, visibility="public"):
8685 self.entries = entries
8686 self.values = values
8687 ClassItem.__init__(self, name, visibility)
8689 def declare(self, cgClass):
8690 entries = []
8691 for i in range(0, len(self.entries)):
8692 if not self.values or i >= len(self.values):
8693 entry = '%s' % self.entries[i]
8694 else:
8695 entry = '%s = %s' % (self.entries[i], self.values[i])
8696 entries.append(entry)
8697 name = '' if not self.name else ' ' + self.name
8698 return 'enum%s\n{\n%s\n};\n' % (name, indent(',\n'.join(entries)))
8700 def define(self, cgClass):
8701 # Only goes in the header
8702 return ''
8705 class ClassUnion(ClassItem):
8706 def __init__(self, name, entries, visibility="public"):
8707 self.entries = [entry + ";\n" for entry in entries]
8708 ClassItem.__init__(self, name, visibility)
8710 def declare(self, cgClass):
8711 return "union %s\n{\n%s\n};\n" % (self.name, indent(''.join(self.entries)))
8713 def define(self, cgClass):
8714 # Only goes in the header
8715 return ''
8718 class CGClass(CGThing):
8719 def __init__(self, name, bases=[], members=[], constructors=[],
8720 destructor=None, methods=[],
8721 typedefs=[], enums=[], unions=[], templateArgs=[],
8722 templateSpecialization=[], isStruct=False,
8723 disallowCopyConstruction=False, indent='',
8724 decorators='',
8725 extradeclarations='',
8726 extradefinitions=''):
8727 CGThing.__init__(self)
8728 self.name = name
8729 self.bases = bases
8730 self.members = members
8731 self.constructors = constructors
8732 # We store our single destructor in a list, since all of our
8733 # code wants lists of members.
8734 self.destructors = [destructor] if destructor else []
8735 self.methods = methods
8736 self.typedefs = typedefs
8737 self.enums = enums
8738 self.unions = unions
8739 self.templateArgs = templateArgs
8740 self.templateSpecialization = templateSpecialization
8741 self.isStruct = isStruct
8742 self.disallowCopyConstruction = disallowCopyConstruction
8743 self.indent = indent
8744 self.defaultVisibility = 'public' if isStruct else 'private'
8745 self.decorators = decorators
8746 self.extradeclarations = extradeclarations
8747 self.extradefinitions = extradefinitions
8749 def getNameString(self):
8750 className = self.name
8751 if self.templateSpecialization:
8752 className += '<%s>' % ', '.join([str(a)
8753 for a in self.templateSpecialization])
8754 return className
8756 def declare(self):
8757 result = ''
8758 if self.templateArgs:
8759 templateArgs = [a.declare() for a in self.templateArgs]
8760 templateArgs = templateArgs[len(self.templateSpecialization):]
8761 result += ('template <%s>\n' %
8762 ','.join([str(a) for a in templateArgs]))
8764 type = 'struct' if self.isStruct else 'class'
8766 if self.templateSpecialization:
8767 specialization = \
8768 '<%s>' % ', '.join([str(a) for a in self.templateSpecialization])
8769 else:
8770 specialization = ''
8772 myself = '%s %s%s' % (type, self.name, specialization)
8773 if self.decorators != '':
8774 myself += " " + self.decorators
8775 result += myself
8777 if self.bases:
8778 inherit = ' : '
8779 result += inherit
8780 # Grab our first base
8781 baseItems = [CGGeneric(b.declare(self)) for b in self.bases]
8782 bases = baseItems[:1]
8783 # Indent the rest
8784 bases.extend(CGIndenter(b, len(myself) + len(inherit))
8785 for b in baseItems[1:])
8786 result += ",\n".join(b.define() for b in bases)
8788 result += '\n{\n'
8790 result += self.extradeclarations
8792 def declareMembers(cgClass, memberList, defaultVisibility):
8793 members = {'private': [], 'protected': [], 'public': []}
8795 for member in memberList:
8796 members[member.visibility].append(member)
8798 if defaultVisibility == 'public':
8799 order = ['public', 'protected', 'private']
8800 else:
8801 order = ['private', 'protected', 'public']
8803 result = ''
8805 lastVisibility = defaultVisibility
8806 for visibility in order:
8807 list = members[visibility]
8808 if list:
8809 if visibility != lastVisibility:
8810 result += visibility + ':\n'
8811 for member in list:
8812 result += indent(member.declare(cgClass))
8813 lastVisibility = visibility
8814 return (result, lastVisibility)
8816 if self.disallowCopyConstruction:
8817 class DisallowedCopyConstructor(object):
8818 def __init__(self):
8819 self.visibility = "private"
8821 def declare(self, cgClass):
8822 name = cgClass.getNameString()
8823 return ("%s(const %s&) MOZ_DELETE;\n"
8824 "void operator=(const %s) MOZ_DELETE;\n" % (name, name, name))
8826 disallowedCopyConstructors = [DisallowedCopyConstructor()]
8827 else:
8828 disallowedCopyConstructors = []
8830 order = [self.enums, self.unions,
8831 self.typedefs, self.members,
8832 self.constructors + disallowedCopyConstructors,
8833 self.destructors, self.methods]
8835 lastVisibility = self.defaultVisibility
8836 pieces = []
8837 for memberList in order:
8838 code, lastVisibility = declareMembers(self, memberList, lastVisibility)
8840 if code:
8841 code = code.rstrip() + "\n" # remove extra blank lines at the end
8842 pieces.append(code)
8844 result += '\n'.join(pieces)
8845 result += '};\n'
8846 result = indent(result, len(self.indent))
8847 return result
8849 def define(self):
8850 def defineMembers(cgClass, memberList, itemCount, separator=''):
8851 result = ''
8852 for member in memberList:
8853 if itemCount != 0:
8854 result = result + separator
8855 definition = member.define(cgClass)
8856 if definition:
8857 # Member variables would only produce empty lines here.
8858 result += definition
8859 itemCount += 1
8860 return (result, itemCount)
8862 order = [(self.members, ''), (self.constructors, '\n'),
8863 (self.destructors, '\n'), (self.methods, '\n')]
8865 result = self.extradefinitions
8866 itemCount = 0
8867 for memberList, separator in order:
8868 memberString, itemCount = defineMembers(self, memberList,
8869 itemCount, separator)
8870 result = result + memberString
8871 return result
8874 class CGResolveOwnProperty(CGAbstractStaticMethod):
8875 def __init__(self, descriptor):
8876 args = [Argument('JSContext*', 'cx'),
8877 Argument('JS::Handle<JSObject*>', 'wrapper'),
8878 Argument('JS::Handle<JSObject*>', 'obj'),
8879 Argument('JS::Handle<jsid>', 'id'),
8880 Argument('JS::MutableHandle<JSPropertyDescriptor>', 'desc'),
8881 ]
8882 CGAbstractStaticMethod.__init__(self, descriptor, "ResolveOwnProperty",
8883 "bool", args)
8885 def definition_body(self):
8886 # BOGUS extra blank line at end of function
8887 return " return js::GetProxyHandler(obj)->getOwnPropertyDescriptor(cx, wrapper, id, desc);\n\n"
8890 class CGResolveOwnPropertyViaNewresolve(CGAbstractBindingMethod):
8891 """
8892 An implementation of Xray ResolveOwnProperty stuff for things that have a
8893 newresolve hook.
8894 """
8895 def __init__(self, descriptor):
8896 args = [Argument('JSContext*', 'cx'),
8897 Argument('JS::Handle<JSObject*>', 'wrapper'),
8898 Argument('JS::Handle<JSObject*>', 'obj'),
8899 Argument('JS::Handle<jsid>', 'id'),
8900 Argument('JS::MutableHandle<JSPropertyDescriptor>', 'desc')]
8901 CGAbstractBindingMethod.__init__(self, descriptor,
8902 "ResolveOwnPropertyViaNewresolve",
8903 args, getThisObj="",
8904 callArgs="")
8906 def generate_code(self):
8907 return CGGeneric(indent(dedent("""
8908 {
8909 // Since we're dealing with an Xray, do the resolve on the
8910 // underlying object first. That gives it a chance to
8911 // define properties on the actual object as needed, and
8912 // then use the fact that it created the objects as a flag
8913 // to avoid re-resolving the properties if someone deletes
8914 // them.
8915 JSAutoCompartment ac(cx, obj);
8916 JS::Rooted<JSPropertyDescriptor> objDesc(cx);
8917 if (!self->DoNewResolve(cx, obj, id, &objDesc)) {
8918 return false;
8919 }
8920 // If desc.value() is undefined, then the DoNewResolve call
8921 // has already defined the property on the object. Don't
8922 // try to also define it.
8923 if (objDesc.object() &&
8924 !objDesc.value().isUndefined() &&
8925 !JS_DefinePropertyById(cx, obj, id, objDesc.value(),
8926 objDesc.getter(), objDesc.setter(),
8927 objDesc.attributes())) {
8928 return false;
8929 }
8930 }
8931 return self->DoNewResolve(cx, wrapper, id, desc);
8932 """)))
8935 class CGEnumerateOwnProperties(CGAbstractStaticMethod):
8936 def __init__(self, descriptor):
8937 args = [Argument('JSContext*', 'cx'),
8938 Argument('JS::Handle<JSObject*>', 'wrapper'),
8939 Argument('JS::Handle<JSObject*>', 'obj'),
8940 Argument('JS::AutoIdVector&', 'props')]
8941 CGAbstractStaticMethod.__init__(self, descriptor,
8942 "EnumerateOwnProperties", "bool", args)
8944 def definition_body(self):
8945 # BOGUS extra newline
8946 return " return js::GetProxyHandler(obj)->getOwnPropertyNames(cx, wrapper, props);\n\n"
8949 class CGEnumerateOwnPropertiesViaGetOwnPropertyNames(CGAbstractBindingMethod):
8950 """
8951 An implementation of Xray EnumerateOwnProperties stuff for things
8952 that have a newresolve hook.
8953 """
8954 def __init__(self, descriptor):
8955 args = [Argument('JSContext*', 'cx'),
8956 Argument('JS::Handle<JSObject*>', 'wrapper'),
8957 Argument('JS::Handle<JSObject*>', 'obj'),
8958 Argument('JS::AutoIdVector&', 'props')]
8959 CGAbstractBindingMethod.__init__(self, descriptor,
8960 "EnumerateOwnPropertiesViaGetOwnPropertyNames",
8961 args, getThisObj="",
8962 callArgs="")
8964 def generate_code(self):
8965 return CGIndenter(CGGeneric(dedent("""
8966 nsAutoTArray<nsString, 8> names;
8967 ErrorResult rv;
8968 self->GetOwnPropertyNames(cx, names, rv);
8969 rv.WouldReportJSException();
8970 if (rv.Failed()) {
8971 return ThrowMethodFailedWithDetails(cx, rv, "%s", "enumerate");
8972 }
8973 // OK to pass null as "proxy" because it's ignored if
8974 // shadowPrototypeProperties is true
8975 return AppendNamedPropertyIds(cx, JS::NullPtr(), names, true, props);
8976 """)))
8979 class CGPrototypeTraitsClass(CGClass):
8980 def __init__(self, descriptor, indent=''):
8981 templateArgs = [Argument('prototypes::ID', 'PrototypeID')]
8982 templateSpecialization = ['prototypes::id::' + descriptor.name]
8983 enums = [ClassEnum('', ['Depth'],
8984 [descriptor.interface.inheritanceDepth()])]
8985 CGClass.__init__(self, 'PrototypeTraits', indent=indent,
8986 templateArgs=templateArgs,
8987 templateSpecialization=templateSpecialization,
8988 enums=enums, isStruct=True)
8990 def deps(self):
8991 return set()
8994 class CGClassForwardDeclare(CGThing):
8995 def __init__(self, name, isStruct=False):
8996 CGThing.__init__(self)
8997 self.name = name
8998 self.isStruct = isStruct
9000 def declare(self):
9001 type = 'struct' if self.isStruct else 'class'
9002 return '%s %s;\n' % (type, self.name)
9004 def define(self):
9005 # Header only
9006 return ''
9008 def deps(self):
9009 return set()
9012 class CGProxySpecialOperation(CGPerSignatureCall):
9013 """
9014 Base class for classes for calling an indexed or named special operation
9015 (don't use this directly, use the derived classes below).
9017 If checkFound is False, will just assert that the prop is found instead of
9018 checking that it is before wrapping the value.
9019 """
9020 def __init__(self, descriptor, operation, checkFound=True, argumentMutableValue=None):
9021 self.checkFound = checkFound
9023 nativeName = MakeNativeName(descriptor.binaryNames.get(operation, operation))
9024 operation = descriptor.operations[operation]
9025 assert len(operation.signatures()) == 1
9026 signature = operation.signatures()[0]
9028 returnType, arguments = signature
9030 # We pass len(arguments) as the final argument so that the
9031 # CGPerSignatureCall won't do any argument conversion of its own.
9032 CGPerSignatureCall.__init__(self, returnType, arguments, nativeName,
9033 False, descriptor, operation,
9034 len(arguments))
9036 if operation.isSetter() or operation.isCreator():
9037 # arguments[0] is the index or name of the item that we're setting.
9038 argument = arguments[1]
9039 info = getJSToNativeConversionInfo(
9040 argument.type, descriptor,
9041 treatNullAs=argument.treatNullAs,
9042 sourceDescription=("value being assigned to %s setter" %
9043 descriptor.interface.identifier.name))
9044 if argumentMutableValue is None:
9045 argumentMutableValue = "desc.value()"
9046 templateValues = {
9047 "declName": argument.identifier.name,
9048 "holderName": argument.identifier.name + "_holder",
9049 "val": argumentMutableValue,
9050 "mutableVal": argumentMutableValue,
9051 "obj": "obj"
9052 }
9053 self.cgRoot.prepend(instantiateJSToNativeConversion(info, templateValues))
9054 elif operation.isGetter() or operation.isDeleter():
9055 self.cgRoot.prepend(CGGeneric("bool found;\n"))
9057 def getArguments(self):
9058 args = [(a, a.identifier.name) for a in self.arguments]
9059 if self.idlNode.isGetter() or self.idlNode.isDeleter():
9060 args.append((FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean],
9061 self.idlNode),
9062 "found"))
9063 return args
9065 def wrap_return_value(self):
9066 if not self.idlNode.isGetter() or self.templateValues is None:
9067 return ""
9069 wrap = CGGeneric(wrapForType(self.returnType, self.descriptor, self.templateValues))
9070 if self.checkFound:
9071 wrap = CGIfWrapper(wrap, "found")
9072 else:
9073 wrap = CGList([CGGeneric("MOZ_ASSERT(found);\n"), wrap])
9074 return "\n" + wrap.define()
9077 class CGProxyIndexedOperation(CGProxySpecialOperation):
9078 """
9079 Class to generate a call to an indexed operation.
9081 If doUnwrap is False, the caller is responsible for making sure a variable
9082 named 'self' holds the C++ object somewhere where the code we generate
9083 will see it.
9085 If checkFound is False, will just assert that the prop is found instead of
9086 checking that it is before wrapping the value.
9087 """
9088 def __init__(self, descriptor, name, doUnwrap=True, checkFound=True,
9089 argumentMutableValue=None):
9090 self.doUnwrap = doUnwrap
9091 CGProxySpecialOperation.__init__(self, descriptor, name, checkFound,
9092 argumentMutableValue=argumentMutableValue)
9094 def define(self):
9095 # Our first argument is the id we're getting.
9096 argName = self.arguments[0].identifier.name
9097 if argName == "index":
9098 # We already have our index in a variable with that name
9099 setIndex = ""
9100 else:
9101 setIndex = "uint32_t %s = index;\n" % argName
9102 if self.doUnwrap:
9103 unwrap = "%s* self = UnwrapProxy(proxy);\n" % self.descriptor.nativeType
9104 else:
9105 unwrap = ""
9106 return (setIndex + unwrap +
9107 CGProxySpecialOperation.define(self))
9110 class CGProxyIndexedGetter(CGProxyIndexedOperation):
9111 """
9112 Class to generate a call to an indexed getter. If templateValues is not None
9113 the returned value will be wrapped with wrapForType using templateValues.
9115 If doUnwrap is False, the caller is responsible for making sure a variable
9116 named 'self' holds the C++ object somewhere where the code we generate
9117 will see it.
9119 If checkFound is False, will just assert that the prop is found instead of
9120 checking that it is before wrapping the value.
9121 """
9122 def __init__(self, descriptor, templateValues=None, doUnwrap=True,
9123 checkFound=True):
9124 self.templateValues = templateValues
9125 CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedGetter',
9126 doUnwrap, checkFound)
9129 class CGProxyIndexedPresenceChecker(CGProxyIndexedGetter):
9130 """
9131 Class to generate a call that checks whether an indexed property exists.
9133 For now, we just delegate to CGProxyIndexedGetter
9134 """
9135 def __init__(self, descriptor):
9136 CGProxyIndexedGetter.__init__(self, descriptor)
9137 self.cgRoot.append(CGGeneric("(void)result;\n"))
9140 class CGProxyIndexedSetter(CGProxyIndexedOperation):
9141 """
9142 Class to generate a call to an indexed setter.
9143 """
9144 def __init__(self, descriptor, argumentMutableValue=None):
9145 CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedSetter',
9146 argumentMutableValue=argumentMutableValue)
9149 class CGProxyIndexedDeleter(CGProxyIndexedOperation):
9150 """
9151 Class to generate a call to an indexed deleter.
9152 """
9153 def __init__(self, descriptor):
9154 CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedDeleter')
9157 class CGProxyNamedOperation(CGProxySpecialOperation):
9158 """
9159 Class to generate a call to a named operation.
9161 'value' is the jsval to use for the name; None indicates that it should be
9162 gotten from the property id.
9163 """
9164 def __init__(self, descriptor, name, value=None, argumentMutableValue=None):
9165 CGProxySpecialOperation.__init__(self, descriptor, name,
9166 argumentMutableValue=argumentMutableValue)
9167 self.value = value
9169 def define(self):
9170 # Our first argument is the id we're getting.
9171 argName = self.arguments[0].identifier.name
9172 if argName == "id":
9173 # deal with the name collision
9174 idDecl = "JS::Rooted<jsid> id_(cx, id);\n"
9175 idName = "id_"
9176 else:
9177 idDecl = ""
9178 idName = "id"
9179 unwrapString = fill(
9180 """
9181 if (!ConvertJSValueToString(cx, nameVal, &nameVal,
9182 eStringify, eStringify, ${argName})) {
9183 return false;
9184 }
9185 """,
9186 argName=argName)
9187 if self.value is None:
9188 # We're just using 'id', and if it's an atom we can take a
9189 # fast path here.
9190 unwrapString = fill(
9191 """
9192 if (MOZ_LIKELY(JSID_IS_ATOM(${idName}))) {
9193 ${argName}.SetData(js::GetAtomChars(JSID_TO_ATOM(${idName})), js::GetAtomLength(JSID_TO_ATOM(${idName})));
9194 } else {
9195 nameVal = js::IdToValue(${idName});
9196 $*{unwrapString}
9197 }
9198 """,
9199 idName=idName,
9200 argName=argName,
9201 unwrapString=unwrapString)
9202 else:
9203 unwrapString = ("nameVal = %s;\n" % self.value) + unwrapString
9205 # Sadly, we have to set up nameVal even if we have an atom id,
9206 # because we don't know for sure, and we can end up needing it
9207 # so it needs to be higher up the stack. Using a Maybe here
9208 # seems like probable overkill.
9209 return fill(
9210 """
9211 JS::Rooted<JS::Value> nameVal(cx);
9212 $*{idDecl}
9213 binding_detail::FakeDependentString ${argName};
9214 $*{unwrapString}
9216 ${nativeType}* self = UnwrapProxy(proxy);
9217 $*{op}
9218 """,
9219 idDecl=idDecl,
9220 argName=argName,
9221 unwrapString=unwrapString,
9222 nativeType=self.descriptor.nativeType,
9223 op=CGProxySpecialOperation.define(self))
9226 class CGProxyNamedGetter(CGProxyNamedOperation):
9227 """
9228 Class to generate a call to an named getter. If templateValues is not None
9229 the returned value will be wrapped with wrapForType using templateValues.
9230 'value' is the jsval to use for the name; None indicates that it should be
9231 gotten from the property id.
9232 """
9233 def __init__(self, descriptor, templateValues=None, value=None):
9234 self.templateValues = templateValues
9235 CGProxyNamedOperation.__init__(self, descriptor, 'NamedGetter', value)
9238 class CGProxyNamedPresenceChecker(CGProxyNamedGetter):
9239 """
9240 Class to generate a call that checks whether a named property exists.
9242 For now, we just delegate to CGProxyNamedGetter
9243 """
9244 def __init__(self, descriptor):
9245 CGProxyNamedGetter.__init__(self, descriptor)
9246 self.cgRoot.append(CGGeneric("(void)result;\n"))
9249 class CGProxyNamedSetter(CGProxyNamedOperation):
9250 """
9251 Class to generate a call to a named setter.
9252 """
9253 def __init__(self, descriptor, argumentMutableValue=None):
9254 CGProxyNamedOperation.__init__(self, descriptor, 'NamedSetter',
9255 argumentMutableValue=argumentMutableValue)
9258 class CGProxyNamedDeleter(CGProxyNamedOperation):
9259 """
9260 Class to generate a call to a named deleter.
9261 """
9262 def __init__(self, descriptor):
9263 CGProxyNamedOperation.__init__(self, descriptor, 'NamedDeleter')
9266 class CGProxyIsProxy(CGAbstractMethod):
9267 def __init__(self, descriptor):
9268 args = [Argument('JSObject*', 'obj')]
9269 CGAbstractMethod.__init__(self, descriptor, "IsProxy", "bool", args, alwaysInline=True)
9271 def declare(self):
9272 return ""
9274 def definition_body(self):
9275 return " return js::IsProxy(obj) && js::GetProxyHandler(obj) == DOMProxyHandler::getInstance();\n"
9278 class CGProxyUnwrap(CGAbstractMethod):
9279 def __init__(self, descriptor):
9280 args = [Argument('JSObject*', 'obj')]
9281 CGAbstractMethod.__init__(self, descriptor, "UnwrapProxy", descriptor.nativeType + '*', args, alwaysInline=True)
9283 def declare(self):
9284 return ""
9286 def definition_body(self):
9287 return indent(fill(
9288 """
9289 MOZ_ASSERT(js::IsProxy(obj));
9290 if (js::GetProxyHandler(obj) != DOMProxyHandler::getInstance()) {
9291 MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(obj));
9292 obj = js::UncheckedUnwrap(obj);
9293 }
9294 MOZ_ASSERT(IsProxy(obj));
9295 return static_cast<${type}*>(js::GetProxyPrivate(obj).toPrivate());
9296 """,
9297 type=self.descriptor.nativeType))
9300 class CGDOMJSProxyHandler_getOwnPropDescriptor(ClassMethod):
9301 def __init__(self, descriptor):
9302 args = [Argument('JSContext*', 'cx'),
9303 Argument('JS::Handle<JSObject*>', 'proxy'),
9304 Argument('JS::Handle<jsid>', 'id'),
9305 Argument('bool', 'ignoreNamedProps'),
9306 Argument('JS::MutableHandle<JSPropertyDescriptor>', 'desc')]
9307 ClassMethod.__init__(self, "getOwnPropDescriptor", "bool", args,
9308 virtual=True, override=True)
9309 self.descriptor = descriptor
9311 def getBody(self):
9312 indexedGetter = self.descriptor.operations['IndexedGetter']
9313 indexedSetter = self.descriptor.operations['IndexedSetter']
9315 if self.descriptor.supportsIndexedProperties():
9316 readonly = toStringBool(indexedSetter is None)
9317 fillDescriptor = "FillPropertyDescriptor(desc, proxy, %s);\nreturn true;\n" % readonly
9318 templateValues = {
9319 'jsvalRef': 'desc.value()',
9320 'jsvalHandle': 'desc.value()',
9321 'obj': 'proxy',
9322 'successCode': fillDescriptor
9323 }
9324 getIndexed = fill(
9325 """
9326 int32_t index = GetArrayIndexFromId(cx, id);
9327 if (IsArrayIndex(index)) {
9328 $*{callGetter}
9329 }
9331 """,
9332 callGetter=CGProxyIndexedGetter(self.descriptor, templateValues).define())
9333 else:
9334 getIndexed = ""
9336 if UseHolderForUnforgeable(self.descriptor):
9337 tryHolder = dedent("""
9338 if (!JS_GetPropertyDescriptorById(cx, ${holder}, id, desc)) {
9339 return false;
9340 }
9341 MOZ_ASSERT_IF(desc.object(), desc.object() == ${holder});
9342 """)
9344 # We don't want to look at the unforgeable holder at all
9345 # in the xray case; that part got handled already.
9346 getUnforgeable = fill(
9347 """
9348 if (!isXray) {
9349 $*{callOnUnforgeable}
9350 if (desc.object()) {
9351 desc.object().set(proxy);
9352 return true;
9353 }
9354 }
9356 """,
9357 callOnUnforgeable=CallOnUnforgeableHolder(self.descriptor, tryHolder))
9358 else:
9359 getUnforgeable = ""
9361 if self.descriptor.supportsNamedProperties():
9362 operations = self.descriptor.operations
9363 readonly = toStringBool(operations['NamedSetter'] is None)
9364 enumerable = (
9365 "self->NameIsEnumerable(Constify(%s))" %
9366 # First [0] means first (and only) signature, [1] means
9367 # "arguments" as opposed to return type, [0] means first (and
9368 # only) argument.
9369 operations['NamedGetter'].signatures()[0][1][0].identifier.name)
9370 fillDescriptor = (
9371 "FillPropertyDescriptor(desc, proxy, %s, %s);\n"
9372 "return true;\n" % (readonly, enumerable))
9373 templateValues = {'jsvalRef': 'desc.value()', 'jsvalHandle': 'desc.value()',
9374 'obj': 'proxy', 'successCode': fillDescriptor}
9375 condition = "!HasPropertyOnPrototype(cx, proxy, id)"
9376 if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
9377 condition = "(!isXray || %s)" % condition
9378 condition = "!ignoreNamedProps && " + condition
9379 if self.descriptor.supportsIndexedProperties():
9380 condition = "!IsArrayIndex(index) && " + condition
9381 namedGet = (CGIfWrapper(CGProxyNamedGetter(self.descriptor, templateValues),
9382 condition).define() +
9383 "\n")
9384 else:
9385 namedGet = ""
9387 return fill(
9388 """
9389 bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy);
9390 $*{getIndexed}
9391 $*{getUnforgeable}
9392 JS::Rooted<JSObject*> expando(cx);
9393 if (!isXray && (expando = GetExpandoObject(proxy))) {
9394 if (!JS_GetPropertyDescriptorById(cx, expando, id, desc)) {
9395 return false;
9396 }
9397 if (desc.object()) {
9398 // Pretend the property lives on the wrapper.
9399 desc.object().set(proxy);
9400 return true;
9401 }
9402 }
9404 $*{namedGet}
9405 desc.object().set(nullptr);
9406 return true;
9407 """,
9408 getIndexed=getIndexed,
9409 getUnforgeable=getUnforgeable,
9410 namedGet=namedGet)
9413 class CGDOMJSProxyHandler_defineProperty(ClassMethod):
9414 def __init__(self, descriptor):
9415 args = [Argument('JSContext*', 'cx'),
9416 Argument('JS::Handle<JSObject*>', 'proxy'),
9417 Argument('JS::Handle<jsid>', 'id'),
9418 Argument('JS::MutableHandle<JSPropertyDescriptor>', 'desc'),
9419 Argument('bool*', 'defined')]
9420 ClassMethod.__init__(self, "defineProperty", "bool", args, virtual=True, override=True)
9421 self.descriptor = descriptor
9423 def getBody(self):
9424 set = ""
9426 indexedSetter = self.descriptor.operations['IndexedSetter']
9427 if indexedSetter:
9428 if self.descriptor.operations['IndexedCreator'] is not indexedSetter:
9429 raise TypeError("Can't handle creator that's different from the setter")
9430 set += fill(
9431 """
9432 int32_t index = GetArrayIndexFromId(cx, id);
9433 if (IsArrayIndex(index)) {
9434 *defined = true;
9435 $*{callSetter}
9436 return true;
9437 }
9438 """,
9439 callSetter=CGProxyIndexedSetter(self.descriptor).define())
9440 elif self.descriptor.supportsIndexedProperties():
9441 set += fill(
9442 """
9443 if (IsArrayIndex(GetArrayIndexFromId(cx, id))) {
9444 return js::IsInNonStrictPropertySet(cx) || ThrowErrorMessage(cx, MSG_NO_INDEXED_SETTER, "${name}");
9445 }
9446 """,
9447 name=self.descriptor.name)
9449 if UseHolderForUnforgeable(self.descriptor):
9450 defineOnUnforgeable = ("bool hasUnforgeable;\n"
9451 "if (!JS_HasPropertyById(cx, ${holder}, id, &hasUnforgeable)) {\n"
9452 " return false;\n"
9453 "}\n"
9454 "if (hasUnforgeable) {\n"
9455 " *defined = true;" # SUPER BOGUS missing newline
9456 " bool unused;\n"
9457 " return js_DefineOwnProperty(cx, ${holder}, id, desc, &unused);\n"
9458 "}\n"
9459 "\n") # BOGUS extra blank line at end of block or method
9460 set += CallOnUnforgeableHolder(self.descriptor,
9461 defineOnUnforgeable,
9462 "xpc::WrapperFactory::IsXrayWrapper(proxy)")
9464 namedSetter = self.descriptor.operations['NamedSetter']
9465 if namedSetter:
9466 if self.descriptor.operations['NamedCreator'] is not namedSetter:
9467 raise TypeError("Can't handle creator that's different from the setter")
9468 # If we support indexed properties, we won't get down here for
9469 # indices, so we can just do our setter unconditionally here.
9470 set += fill(
9471 """
9472 *defined = true;
9473 $*{callSetter}
9475 return true;
9477 """, # BOGUS extra blank line at end of method
9478 callSetter=CGProxyNamedSetter(self.descriptor).define())
9479 else:
9480 if self.descriptor.supportsNamedProperties():
9481 set += fill(
9482 """
9483 $*{presenceChecker}
9485 if (found) {
9486 return js::IsInNonStrictPropertySet(cx) || ThrowErrorMessage(cx, MSG_NO_NAMED_SETTER, "${name}");
9487 }
9488 """,
9489 presenceChecker=CGProxyNamedPresenceChecker(self.descriptor).define(),
9490 name=self.descriptor.name)
9491 set += ("return mozilla::dom::DOMProxyHandler::defineProperty(%s);\n" %
9492 ", ".join(a.name for a in self.args))
9493 return set
9496 class CGDOMJSProxyHandler_delete(ClassMethod):
9497 def __init__(self, descriptor):
9498 args = [Argument('JSContext*', 'cx'),
9499 Argument('JS::Handle<JSObject*>', 'proxy'),
9500 Argument('JS::Handle<jsid>', 'id'),
9501 Argument('bool*', 'bp')]
9502 ClassMethod.__init__(self, "delete_", "bool", args,
9503 virtual=True, override=True)
9504 self.descriptor = descriptor
9506 def getBody(self):
9507 def getDeleterBody(type):
9508 """
9509 type should be "Named" or "Indexed"
9510 """
9511 assert type in ("Named", "Indexed")
9512 deleter = self.descriptor.operations[type + 'Deleter']
9513 if deleter:
9514 if (not deleter.signatures()[0][0].isPrimitive() or
9515 deleter.signatures()[0][0].nullable() or
9516 deleter.signatures()[0][0].tag() != IDLType.Tags.bool):
9517 setBp = "*bp = true;\n"
9518 else:
9519 setBp = dedent("""
9520 if (found) {
9521 *bp = result;
9522 } else {
9523 *bp = true;
9524 }
9525 """)
9526 body = (eval("CGProxy%sDeleter" % type)(self.descriptor).define() +
9527 setBp)
9528 elif eval("self.descriptor.supports%sProperties()" % type):
9529 body = (eval("CGProxy%sPresenceChecker" % type)(self.descriptor).define() +
9530 dedent("""
9531 if (found) {
9532 *bp = false;
9533 } else {
9534 *bp = true;
9535 }
9536 """))
9537 else:
9538 body = None
9539 return body
9541 delete = dedent("""
9542 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
9543 "Should not have a XrayWrapper here");
9545 """)
9547 indexedBody = getDeleterBody("Indexed")
9548 if indexedBody is not None:
9549 delete += fill(
9550 """
9551 int32_t index = GetArrayIndexFromId(cx, id);
9552 if (IsArrayIndex(index)) {
9553 $*{indexedBody}
9554 // We always return here, even if the property was not found
9555 return true;
9556 }
9557 """,
9558 indexedBody=indexedBody)
9560 if UseHolderForUnforgeable(self.descriptor):
9561 unforgeable = dedent("""
9562 bool hasUnforgeable;
9563 if (!JS_HasPropertyById(cx, ${holder}, id, &hasUnforgeable)) {
9564 return false;
9565 }
9566 if (hasUnforgeable) {
9567 *bp = false;
9568 return true;
9569 }
9570 """)
9571 delete += CallOnUnforgeableHolder(self.descriptor, unforgeable)
9572 delete += "\n"
9574 namedBody = getDeleterBody("Named")
9575 if namedBody is not None:
9576 # We always return above for an index id in the case when we support
9577 # indexed properties, so we can just treat the id as a name
9578 # unconditionally here.
9579 delete += (namedBody +
9580 "if (found) {\n"
9581 " return true;\n"
9582 "}\n\n") # BOGUS extra blank line
9583 if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
9584 delete = CGIfWrapper(CGGeneric(delete),
9585 "!HasPropertyOnPrototype(cx, proxy, id)").define()
9586 else:
9587 delete += "\n" # BOGUS extra blank line
9589 delete += dedent("""
9591 return dom::DOMProxyHandler::delete_(cx, proxy, id, bp);
9592 """)
9594 return delete
9597 class CGDOMJSProxyHandler_ownPropNames(ClassMethod):
9598 def __init__(self, descriptor, ):
9599 args = [Argument('JSContext*', 'cx'),
9600 Argument('JS::Handle<JSObject*>', 'proxy'),
9601 Argument('unsigned', 'flags'),
9602 Argument('JS::AutoIdVector&', 'props')]
9603 ClassMethod.__init__(self, "ownPropNames", "bool", args,
9604 virtual=True, override=True)
9605 self.descriptor = descriptor
9607 def getBody(self):
9608 # Per spec, we do indices, then named props, then everything else
9609 if self.descriptor.supportsIndexedProperties():
9610 addIndices = dedent("""
9612 uint32_t length = UnwrapProxy(proxy)->Length();
9613 MOZ_ASSERT(int32_t(length) >= 0);
9614 for (int32_t i = 0; i < int32_t(length); ++i) {
9615 if (!props.append(INT_TO_JSID(i))) {
9616 return false;
9617 }
9618 }
9619 """)
9620 else:
9621 addIndices = ""
9623 if UseHolderForUnforgeable(self.descriptor):
9624 addUnforgeable = dedent("""
9625 if (!js::GetPropertyNames(cx, ${holder}, flags, &props)) {
9626 return false;
9627 }
9628 """)
9629 addUnforgeable = CallOnUnforgeableHolder(self.descriptor,
9630 addUnforgeable,
9631 "isXray")
9632 else:
9633 addUnforgeable = ""
9635 if self.descriptor.supportsNamedProperties():
9636 if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
9637 shadow = "!isXray"
9638 else:
9639 shadow = "false"
9640 addNames = fill(
9641 """
9643 nsTArray<nsString> names;
9644 UnwrapProxy(proxy)->GetSupportedNames(flags, names);
9645 if (!AppendNamedPropertyIds(cx, proxy, names, ${shadow}, props)) {
9646 return false;
9647 }
9648 """,
9649 shadow=shadow)
9650 else:
9651 addNames = ""
9653 return fill(
9654 """
9655 bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy);
9656 $*{addIndices}
9657 $*{addUnforgeable}
9658 $*{addNames}
9660 JS::Rooted<JSObject*> expando(cx);
9661 if (!isXray && (expando = DOMProxyHandler::GetExpandoObject(proxy)) &&
9662 !js::GetPropertyNames(cx, expando, flags, &props)) {
9663 return false;
9664 }
9666 return true;
9667 """,
9668 addIndices=addIndices,
9669 addUnforgeable=addUnforgeable,
9670 addNames=addNames)
9673 class CGDOMJSProxyHandler_hasOwn(ClassMethod):
9674 def __init__(self, descriptor):
9675 args = [Argument('JSContext*', 'cx'),
9676 Argument('JS::Handle<JSObject*>', 'proxy'),
9677 Argument('JS::Handle<jsid>', 'id'),
9678 Argument('bool*', 'bp')]
9679 ClassMethod.__init__(self, "hasOwn", "bool", args,
9680 virtual=True, override=True)
9681 self.descriptor = descriptor
9683 def getBody(self):
9684 if self.descriptor.supportsIndexedProperties():
9685 indexed = fill(
9686 """
9687 int32_t index = GetArrayIndexFromId(cx, id);
9688 if (IsArrayIndex(index)) {
9689 $*{presenceChecker}
9691 *bp = found;
9692 return true;
9693 }
9695 """,
9696 presenceChecker=CGProxyIndexedPresenceChecker(self.descriptor).define())
9697 else:
9698 indexed = ""
9700 if UseHolderForUnforgeable(self.descriptor):
9701 unforgeable = dedent("""
9702 bool b = true;
9703 bool ok = JS_AlreadyHasOwnPropertyById(cx, ${holder}, id, &b);
9704 *bp = !!b;
9705 if (!ok || *bp) {
9706 return ok;
9707 }
9708 """)
9709 unforgeable = CallOnUnforgeableHolder(self.descriptor, unforgeable)
9710 else:
9711 unforgeable = ""
9713 if self.descriptor.supportsNamedProperties():
9714 # If we support indexed properties we always return above for index
9715 # property names, so no need to check for those here.
9716 named = (CGProxyNamedPresenceChecker(self.descriptor).define() +
9717 "\n" +
9718 "*bp = found;\n")
9719 if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
9720 # BOGUS extra blank line at end of block
9721 named = CGIfWrapper(CGGeneric(named + "return true;\n\n"),
9722 "!HasPropertyOnPrototype(cx, proxy, id)").define()
9723 named += "*bp = false;\n"
9724 else:
9725 named += "\n"
9726 else:
9727 named = "*bp = false;\n"
9729 return fill(
9730 """
9731 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
9732 "Should not have a XrayWrapper here");
9734 $*{indexed}
9735 $*{unforgeable}
9737 JS::Rooted<JSObject*> expando(cx, GetExpandoObject(proxy));
9738 if (expando) {
9739 bool b = true;
9740 bool ok = JS_HasPropertyById(cx, expando, id, &b);
9741 *bp = !!b;
9742 if (!ok || *bp) {
9743 return ok;
9744 }
9745 }
9747 $*{named}
9748 return true;
9749 """,
9750 indexed=indexed,
9751 unforgeable=unforgeable,
9752 named=named)
9755 class CGDOMJSProxyHandler_get(ClassMethod):
9756 def __init__(self, descriptor):
9757 args = [Argument('JSContext*', 'cx'),
9758 Argument('JS::Handle<JSObject*>', 'proxy'),
9759 Argument('JS::Handle<JSObject*>', 'receiver'),
9760 Argument('JS::Handle<jsid>', 'id'),
9761 Argument('JS::MutableHandle<JS::Value>', 'vp')]
9762 ClassMethod.__init__(self, "get", "bool", args,
9763 virtual=True, override=True)
9764 self.descriptor = descriptor
9766 def getBody(self):
9767 getUnforgeableOrExpando = "JS::Rooted<JSObject*> sharedRoot(cx);\n"
9768 if UseHolderForUnforgeable(self.descriptor):
9769 hasUnforgeable = dedent("""
9770 bool hasUnforgeable;
9771 if (!JS_AlreadyHasOwnPropertyById(cx, ${holder}, id, &hasUnforgeable)) {
9772 return false;
9773 }
9774 if (hasUnforgeable) {
9775 return JS_ForwardGetPropertyTo(cx, ${holder}, id, proxy, vp);
9776 }
9777 """)
9778 getUnforgeableOrExpando += CallOnUnforgeableHolder(self.descriptor,
9779 hasUnforgeable,
9780 useSharedRoot=True)
9781 getUnforgeableOrExpando += dedent("""
9782 { // Scope for expando
9783 JS::Rooted<JSObject*>& expando(sharedRoot);
9784 expando = DOMProxyHandler::GetExpandoObject(proxy);
9785 if (expando) {
9786 bool hasProp;
9787 if (!JS_HasPropertyById(cx, expando, id, &hasProp)) {
9788 return false;
9789 }
9791 if (hasProp) {
9792 // Forward the get to the expando object, but our receiver is whatever our
9793 // receiver is.
9794 return JS_ForwardGetPropertyTo(cx, expando, id, receiver, vp);
9795 }
9796 }
9797 }
9798 """)
9800 templateValues = {'jsvalRef': 'vp', 'jsvalHandle': 'vp', 'obj': 'proxy'}
9802 if self.descriptor.supportsIndexedProperties():
9803 getIndexedOrExpando = fill(
9804 """
9805 int32_t index = GetArrayIndexFromId(cx, id);
9806 if (IsArrayIndex(index)) {
9807 $*{callGetter}
9808 // Even if we don't have this index, we don't forward the
9809 // get on to our expando object.
9810 } else {
9811 $*{getUnforgeableOrExpando}
9812 }
9813 """,
9814 callGetter=CGProxyIndexedGetter(self.descriptor, templateValues).define(),
9815 getUnforgeableOrExpando=getUnforgeableOrExpando)
9816 else:
9817 getIndexedOrExpando = getUnforgeableOrExpando
9819 if self.descriptor.supportsNamedProperties():
9820 getNamed = CGProxyNamedGetter(self.descriptor, templateValues)
9821 if self.descriptor.supportsIndexedProperties():
9822 getNamed = CGIfWrapper(getNamed, "!IsArrayIndex(index)")
9823 getNamed = getNamed.define() + "\n"
9824 else:
9825 getNamed = ""
9827 getOnPrototype = dedent("""
9828 bool foundOnPrototype;
9829 if (!GetPropertyOnPrototype(cx, proxy, id, &foundOnPrototype, vp.address())) {
9830 return false;
9831 }
9833 if (foundOnPrototype) {
9834 return true;
9835 }
9837 """)
9838 if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
9839 getNamed = getNamed + getOnPrototype
9840 else:
9841 getNamed = getOnPrototype + getNamed
9843 return fill(
9844 """
9845 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
9846 "Should not have a XrayWrapper here");
9848 $*{indexedOrExpando}
9850 $*{named}
9851 vp.setUndefined();
9852 return true;
9853 """,
9854 indexedOrExpando=getIndexedOrExpando,
9855 named=getNamed)
9858 class CGDOMJSProxyHandler_setCustom(ClassMethod):
9859 def __init__(self, descriptor):
9860 args = [Argument('JSContext*', 'cx'),
9861 Argument('JS::Handle<JSObject*>', 'proxy'),
9862 Argument('JS::Handle<jsid>', 'id'),
9863 Argument('JS::MutableHandle<JS::Value>', 'vp'),
9864 Argument('bool*', 'done')]
9865 ClassMethod.__init__(self, "setCustom", "bool", args, virtual=True, override=True)
9866 self.descriptor = descriptor
9868 def getBody(self):
9869 assertion = ("MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),\n"
9870 ' "Should not have a XrayWrapper here");\n')
9872 # Correctness first. If we have a NamedSetter and [OverrideBuiltins],
9873 # always call the NamedSetter and never do anything else.
9874 namedSetter = self.descriptor.operations['NamedSetter']
9875 if (namedSetter is not None and
9876 self.descriptor.interface.getExtendedAttribute('OverrideBuiltins')):
9877 # Check assumptions.
9878 if self.descriptor.supportsIndexedProperties():
9879 raise ValueError("In interface " + self.descriptor.name + ": " +
9880 "Can't cope with [OverrideBuiltins] and an indexed getter")
9881 if self.descriptor.operations['NamedCreator'] is not namedSetter:
9882 raise ValueError("In interface " + self.descriptor.name + ": " +
9883 "Can't cope with named setter that is not also a named creator")
9884 if UseHolderForUnforgeable(self.descriptor):
9885 raise ValueError("In interface " + self.descriptor.name + ": " +
9886 "Can't cope with [OverrideBuiltins] and unforgeable members")
9888 callSetter = CGProxyNamedSetter(self.descriptor, argumentMutableValue="vp")
9889 return (assertion +
9890 callSetter.define() +
9891 "*done = true;\n"
9892 "return true;\n")
9894 # As an optimization, if we are going to call an IndexedSetter, go
9895 # ahead and call it and have done.
9896 indexedSetter = self.descriptor.operations['IndexedSetter']
9897 if indexedSetter is not None:
9898 if self.descriptor.operations['IndexedCreator'] is not indexedSetter:
9899 raise ValueError("In interface " + self.descriptor.name + ": " +
9900 "Can't cope with indexed setter that is not " +
9901 "also an indexed creator")
9902 setIndexed = fill(
9903 """
9904 int32_t index = GetArrayIndexFromId(cx, id);
9905 if (IsArrayIndex(index)) {
9906 $*{callSetter}
9907 *done = true;
9908 return true;
9909 }
9911 """,
9912 callSetter=CGProxyIndexedSetter(self.descriptor,
9913 argumentMutableValue="vp").define())
9914 else:
9915 setIndexed = ""
9917 return (assertion +
9918 setIndexed +
9919 "*done = false;\n"
9920 "return true;\n")
9923 class CGDOMJSProxyHandler_className(ClassMethod):
9924 def __init__(self, descriptor):
9925 args = [Argument('JSContext*', 'cx'),
9926 Argument('JS::Handle<JSObject*>', 'proxy')]
9927 ClassMethod.__init__(self, "className", "const char*", args,
9928 virtual=True, override=True)
9929 self.descriptor = descriptor
9931 def getBody(self):
9932 return 'return "%s";\n' % self.descriptor.name
9935 class CGDOMJSProxyHandler_finalizeInBackground(ClassMethod):
9936 def __init__(self, descriptor):
9937 args = [Argument('JS::Value', 'priv')]
9938 ClassMethod.__init__(self, "finalizeInBackground", "bool", args,
9939 virtual=True, override=True)
9940 self.descriptor = descriptor
9942 def getBody(self):
9943 return "return false;\n"
9946 class CGDOMJSProxyHandler_finalize(ClassMethod):
9947 def __init__(self, descriptor):
9948 args = [Argument('JSFreeOp*', 'fop'), Argument('JSObject*', 'proxy')]
9949 ClassMethod.__init__(self, "finalize", "void", args,
9950 virtual=True, override=True)
9951 self.descriptor = descriptor
9953 def getBody(self):
9954 return ("%s* self = UnwrapProxy(proxy);\n\n" % self.descriptor.nativeType +
9955 finalizeHook(self.descriptor, FINALIZE_HOOK_NAME, self.args[0].name).define())
9958 class CGDOMJSProxyHandler_slice(ClassMethod):
9959 def __init__(self, descriptor):
9960 assert descriptor.supportsIndexedProperties()
9962 args = [Argument('JSContext*', 'cx'),
9963 Argument('JS::Handle<JSObject*>', 'proxy'),
9964 Argument('uint32_t', 'begin'),
9965 Argument('uint32_t', 'end'),
9966 Argument('JS::Handle<JSObject*>', 'array')]
9967 ClassMethod.__init__(self, "slice", "bool", args, virtual=True, override=True)
9968 self.descriptor = descriptor
9970 def getBody(self):
9971 # Just like getOwnPropertyNames we'll assume that we have no holes, so
9972 # we have all properties from 0 to length. If that ever changes
9973 # (unlikely), we'll need to do something a bit more clever with how we
9974 # forward on to our ancestor.
9976 templateValues = {
9977 'jsvalRef': 'temp',
9978 'jsvalHandle': '&temp',
9979 'obj': 'proxy',
9980 'successCode': ("js::UnsafeDefineElement(cx, array, index - begin, temp);\n"
9981 "continue;\n")
9982 }
9983 get = CGProxyIndexedGetter(self.descriptor, templateValues, False, False).define()
9985 return fill(
9986 """
9987 JS::Rooted<JS::Value> temp(cx);
9988 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
9989 "Should not have a XrayWrapper here");
9991 ${nativeType}* self = UnwrapProxy(proxy);
9992 uint32_t length = self->Length();
9993 // Compute the end of the indices we'll get ourselves
9994 uint32_t ourEnd = std::max(begin, std::min(end, length));
9996 for (uint32_t index = begin; index < ourEnd; ++index) {
9997 $*{get}
9998 }
10000 if (end > ourEnd) {
10001 JS::Rooted<JSObject*> proto(cx);
10002 if (!js::GetObjectProto(cx, proxy, &proto)) {
10003 return false;
10004 }
10005 return js::SliceSlowly(cx, proto, proxy, ourEnd, end, array);
10006 }
10008 return true;
10009 """,
10010 nativeType=self.descriptor.nativeType,
10011 get=get)
10014 class CGDOMJSProxyHandler_getInstance(ClassMethod):
10015 def __init__(self):
10016 ClassMethod.__init__(self, "getInstance", "DOMProxyHandler*", [], static=True)
10018 def getBody(self):
10019 return dedent("""
10020 static DOMProxyHandler instance;
10021 return &instance;
10022 """)
10025 class CGDOMJSProxyHandler(CGClass):
10026 def __init__(self, descriptor):
10027 assert (descriptor.supportsIndexedProperties() or
10028 descriptor.supportsNamedProperties())
10029 methods = [CGDOMJSProxyHandler_getOwnPropDescriptor(descriptor),
10030 CGDOMJSProxyHandler_defineProperty(descriptor),
10031 ClassUsingDeclaration("mozilla::dom::DOMProxyHandler",
10032 "defineProperty"),
10033 CGDOMJSProxyHandler_ownPropNames(descriptor),
10034 CGDOMJSProxyHandler_hasOwn(descriptor),
10035 CGDOMJSProxyHandler_get(descriptor),
10036 CGDOMJSProxyHandler_className(descriptor),
10037 CGDOMJSProxyHandler_finalizeInBackground(descriptor),
10038 CGDOMJSProxyHandler_finalize(descriptor),
10039 CGDOMJSProxyHandler_getInstance(),
10040 CGDOMJSProxyHandler_delete(descriptor)]
10041 if descriptor.supportsIndexedProperties():
10042 methods.append(CGDOMJSProxyHandler_slice(descriptor))
10043 if (descriptor.operations['IndexedSetter'] is not None or
10044 (descriptor.operations['NamedSetter'] is not None and
10045 descriptor.interface.getExtendedAttribute('OverrideBuiltins'))):
10046 methods.append(CGDOMJSProxyHandler_setCustom(descriptor))
10048 CGClass.__init__(self, 'DOMProxyHandler',
10049 bases=[ClassBase('mozilla::dom::DOMProxyHandler')],
10050 methods=methods)
10053 class CGDOMJSProxyHandlerDeclarer(CGThing):
10054 """
10055 A class for declaring a DOMProxyHandler.
10056 """
10057 def __init__(self, handlerThing):
10058 self.handlerThing = handlerThing
10060 def declare(self):
10061 # Our class declaration should happen when we're defining
10062 return ""
10064 def define(self):
10065 return self.handlerThing.declare()
10068 class CGDOMJSProxyHandlerDefiner(CGThing):
10069 """
10070 A class for defining a DOMProxyHandler.
10071 """
10072 def __init__(self, handlerThing):
10073 self.handlerThing = handlerThing
10075 def declare(self):
10076 return ""
10078 def define(self):
10079 return self.handlerThing.define()
10082 def stripTrailingWhitespace(text):
10083 tail = '\n' if text.endswith('\n') else ''
10084 lines = text.splitlines()
10085 return '\n'.join(line.rstrip() for line in lines) + tail
10088 class CGDescriptor(CGThing):
10089 def __init__(self, descriptor):
10090 CGThing.__init__(self)
10092 assert not descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject()
10094 if descriptor.nativeOwnership == 'owned' and (
10095 descriptor.interface.hasChildInterfaces() or
10096 descriptor.interface.parent):
10097 raise TypeError("Owned interface cannot have a parent or children")
10099 self._deps = descriptor.interface.getDeps()
10101 cgThings = []
10102 cgThings.append(CGGeneric(declare="typedef %s NativeType;\n" %
10103 descriptor.nativeType))
10104 # These are set to true if at least one non-static
10105 # method/getter/setter or jsonifier exist on the interface.
10106 (hasMethod, hasGetter, hasLenientGetter, hasSetter, hasJsonifier,
10107 hasLenientSetter) = False, False, False, False, False, False
10108 crossOriginMethods, crossOriginGetters, crossOriginSetters = set(), set(), set()
10109 for n in descriptor.interface.namedConstructors:
10110 cgThings.append(CGClassConstructor(descriptor, n,
10111 NamedConstructorName(n)))
10112 for m in descriptor.interface.members:
10113 if m.isMethod() and m.identifier.name == 'queryInterface':
10114 continue
10115 if m.isMethod() and m == descriptor.operations['Jsonifier']:
10116 hasJsonifier = True
10117 hasMethod = descriptor.needsSpecialGenericOps()
10118 jsonifierMethod = m
10119 elif (m.isMethod() and
10120 (not m.isIdentifierLess() or m == descriptor.operations['Stringifier'])):
10121 if m.isStatic():
10122 assert descriptor.interface.hasInterfaceObject
10123 cgThings.append(CGStaticMethod(descriptor, m))
10124 if m.returnsPromise():
10125 cgThings.append(CGStaticMethodJitinfo(m))
10126 elif descriptor.interface.hasInterfacePrototypeObject():
10127 specializedMethod = CGSpecializedMethod(descriptor, m)
10128 cgThings.append(specializedMethod)
10129 if m.returnsPromise():
10130 cgThings.append(CGMethodPromiseWrapper(descriptor, specializedMethod))
10131 cgThings.append(CGMemberJITInfo(descriptor, m))
10132 if m.getExtendedAttribute("CrossOriginCallable"):
10133 crossOriginMethods.add(m.identifier.name)
10134 elif descriptor.needsSpecialGenericOps():
10135 hasMethod = True
10136 elif m.isAttr():
10137 if m.stringifier:
10138 raise TypeError("Stringifier attributes not supported yet. "
10139 "See bug 824857.\n"
10140 "%s" % m.location)
10141 if m.isStatic():
10142 assert descriptor.interface.hasInterfaceObject
10143 cgThings.append(CGStaticGetter(descriptor, m))
10144 elif descriptor.interface.hasInterfacePrototypeObject():
10145 cgThings.append(CGSpecializedGetter(descriptor, m))
10146 if m.hasLenientThis():
10147 hasLenientGetter = True
10148 elif m.getExtendedAttribute("CrossOriginReadable"):
10149 crossOriginGetters.add(m.identifier.name)
10150 elif descriptor.needsSpecialGenericOps():
10151 hasGetter = True
10152 if not m.readonly:
10153 for extAttr in ["PutForwards", "Replaceable"]:
10154 if m.getExtendedAttribute(extAttr):
10155 raise TypeError("Writable attributes should not "
10156 "have %s specified.\n"
10157 "%s" %
10158 (extAttr, m.location))
10159 if m.isStatic():
10160 assert descriptor.interface.hasInterfaceObject
10161 cgThings.append(CGStaticSetter(descriptor, m))
10162 elif descriptor.interface.hasInterfacePrototypeObject():
10163 cgThings.append(CGSpecializedSetter(descriptor, m))
10164 if m.hasLenientThis():
10165 hasLenientSetter = True
10166 elif m.getExtendedAttribute("CrossOriginWritable"):
10167 crossOriginSetters.add(m.identifier.name)
10168 elif descriptor.needsSpecialGenericOps():
10169 hasSetter = True
10170 elif m.getExtendedAttribute("PutForwards"):
10171 cgThings.append(CGSpecializedForwardingSetter(descriptor, m))
10172 if m.getExtendedAttribute("CrossOriginWritable"):
10173 crossOriginSetters.add(m.identifier.name)
10174 elif descriptor.needsSpecialGenericOps():
10175 hasSetter = True
10176 elif m.getExtendedAttribute("Replaceable"):
10177 cgThings.append(CGSpecializedReplaceableSetter(descriptor, m))
10178 if descriptor.needsSpecialGenericOps():
10179 hasSetter = True
10180 if (not m.isStatic() and
10181 descriptor.interface.hasInterfacePrototypeObject()):
10182 cgThings.append(CGMemberJITInfo(descriptor, m))
10183 if hasJsonifier:
10184 cgThings.append(CGJsonifierMethod(descriptor, jsonifierMethod))
10185 cgThings.append(CGMemberJITInfo(descriptor, jsonifierMethod))
10186 if hasMethod:
10187 cgThings.append(CGGenericMethod(descriptor))
10188 if len(crossOriginMethods):
10189 cgThings.append(CGGenericMethod(descriptor,
10190 allowCrossOriginThis=True))
10191 if hasGetter:
10192 cgThings.append(CGGenericGetter(descriptor))
10193 if hasLenientGetter:
10194 cgThings.append(CGGenericGetter(descriptor, lenientThis=True))
10195 if len(crossOriginGetters):
10196 cgThings.append(CGGenericGetter(descriptor,
10197 allowCrossOriginThis=True))
10198 if hasSetter:
10199 cgThings.append(CGGenericSetter(descriptor))
10200 if hasLenientSetter:
10201 cgThings.append(CGGenericSetter(descriptor, lenientThis=True))
10202 if len(crossOriginSetters):
10203 cgThings.append(CGGenericSetter(descriptor,
10204 allowCrossOriginThis=True))
10206 if descriptor.interface.getNavigatorProperty():
10207 cgThings.append(CGConstructNavigatorObjectHelper(descriptor))
10208 cgThings.append(CGConstructNavigatorObject(descriptor))
10210 if descriptor.concrete and not descriptor.proxy:
10211 if wantsAddProperty(descriptor):
10212 cgThings.append(CGAddPropertyHook(descriptor))
10214 # Always have a finalize hook, regardless of whether the class
10215 # wants a custom hook.
10216 cgThings.append(CGClassFinalizeHook(descriptor))
10218 properties = PropertyArrays(descriptor)
10219 cgThings.append(CGGeneric(define=str(properties)))
10220 cgThings.append(CGNativeProperties(descriptor, properties))
10222 # Set up our Xray callbacks as needed. Note that we don't need to do
10223 # it in workers.
10224 if not descriptor.workers and descriptor.concrete and descriptor.proxy:
10225 cgThings.append(CGResolveOwnProperty(descriptor))
10226 cgThings.append(CGEnumerateOwnProperties(descriptor))
10227 elif descriptor.needsXrayResolveHooks():
10228 cgThings.append(CGResolveOwnPropertyViaNewresolve(descriptor))
10229 cgThings.append(CGEnumerateOwnPropertiesViaGetOwnPropertyNames(descriptor))
10231 # Now that we have our ResolveOwnProperty/EnumerateOwnProperties stuff
10232 # done, set up our NativePropertyHooks.
10233 cgThings.append(CGNativePropertyHooks(descriptor, properties))
10235 if descriptor.interface.hasInterfaceObject():
10236 cgThings.append(CGClassConstructor(descriptor,
10237 descriptor.interface.ctor()))
10238 cgThings.append(CGClassHasInstanceHook(descriptor))
10239 cgThings.append(CGInterfaceObjectJSClass(descriptor, properties))
10240 if descriptor.needsConstructHookHolder():
10241 cgThings.append(CGClassConstructHookHolder(descriptor))
10242 cgThings.append(CGNamedConstructors(descriptor))
10244 cgThings.append(CGLegacyCallHook(descriptor))
10245 if descriptor.interface.getExtendedAttribute("NeedNewResolve"):
10246 cgThings.append(CGNewResolveHook(descriptor))
10247 cgThings.append(CGEnumerateHook(descriptor))
10249 if descriptor.interface.hasInterfacePrototypeObject():
10250 cgThings.append(CGPrototypeJSClass(descriptor, properties))
10252 if descriptor.interface.hasInterfaceObject():
10253 cgThings.append(CGDefineDOMInterfaceMethod(descriptor))
10255 if ((descriptor.interface.hasInterfaceObject() or descriptor.interface.getNavigatorProperty()) and
10256 not descriptor.interface.isExternal() and
10257 descriptor.isExposedConditionally() and
10258 # Workers stuff is never conditional
10259 not descriptor.workers):
10260 cgThings.append(CGConstructorEnabled(descriptor))
10262 if descriptor.concrete:
10263 if descriptor.proxy:
10264 if descriptor.interface.totalMembersInSlots != 0:
10265 raise TypeError("We can't have extra reserved slots for "
10266 "proxy interface %s" %
10267 descriptor.interface.identifier.name)
10268 cgThings.append(CGGeneric(fill(
10269 """
10270 static_assert(IsBaseOf<nsISupports, ${nativeType} >::value,
10271 "We don't support non-nsISupports native classes for "
10272 "proxy-based bindings yet");
10274 """,
10275 nativeType=descriptor.nativeType)))
10276 if not descriptor.wrapperCache:
10277 raise TypeError("We need a wrappercache to support expandos for proxy-based "
10278 "bindings (" + descriptor.name + ")")
10279 handlerThing = CGDOMJSProxyHandler(descriptor)
10280 cgThings.append(CGDOMJSProxyHandlerDeclarer(handlerThing))
10281 cgThings.append(CGProxyIsProxy(descriptor))
10282 cgThings.append(CGProxyUnwrap(descriptor))
10283 cgThings.append(CGDOMJSProxyHandlerDefiner(handlerThing))
10284 cgThings.append(CGDOMProxyJSClass(descriptor))
10285 else:
10286 cgThings.append(CGDOMJSClass(descriptor))
10287 cgThings.append(CGGetJSClassMethod(descriptor))
10288 if descriptor.interface.hasMembersInSlots():
10289 if descriptor.interface.hasChildInterfaces():
10290 raise TypeError("We don't support members in slots on "
10291 "non-leaf interfaces like %s" %
10292 descriptor.interface.identifier.name)
10293 cgThings.append(CGUpdateMemberSlotsMethod(descriptor))
10295 if descriptor.interface.getExtendedAttribute("Global"):
10296 assert descriptor.wrapperCache
10297 cgThings.append(CGWrapGlobalMethod(descriptor, properties))
10298 elif descriptor.wrapperCache:
10299 cgThings.append(CGWrapWithCacheMethod(descriptor, properties))
10300 cgThings.append(CGWrapMethod(descriptor))
10301 else:
10302 cgThings.append(CGWrapNonWrapperCacheMethod(descriptor,
10303 properties))
10305 # If we're not wrappercached, we don't know how to clear our
10306 # cached values, since we can't get at the JSObject.
10307 if descriptor.wrapperCache:
10308 cgThings.extend(CGClearCachedValueMethod(descriptor, m) for
10309 m in descriptor.interface.members if
10310 m.isAttr() and
10311 # Constants should never need clearing!
10312 not m.getExtendedAttribute("Constant") and
10313 not m.getExtendedAttribute("SameObject") and
10314 m.slotIndex is not None)
10316 # CGCreateInterfaceObjectsMethod needs to come after our
10317 # CGDOMJSClass, if any.
10318 cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties))
10320 # CGGetProtoObjectMethod and CGGetConstructorObjectMethod need
10321 # to come after CGCreateInterfaceObjectsMethod.
10322 if descriptor.interface.hasInterfacePrototypeObject():
10323 cgThings.append(CGGetProtoObjectMethod(descriptor))
10324 if descriptor.interface.hasInterfaceObject():
10325 cgThings.append(CGGetConstructorObjectMethod(descriptor))
10327 # See whether we need we need to generate an IsPermitted method
10328 if crossOriginGetters or crossOriginSetters or crossOriginMethods:
10329 cgThings.append(CGIsPermittedMethod(descriptor,
10330 crossOriginGetters,
10331 crossOriginSetters,
10332 crossOriginMethods))
10334 cgThings = CGList((CGIndenter(t, declareOnly=True) for t in cgThings), "\n")
10335 cgThings = CGWrapper(cgThings, pre='\n', post='\n')
10336 self.cgRoot = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name),
10337 cgThings),
10338 post='\n')
10340 def declare(self):
10341 return self.cgRoot.declare()
10343 def define(self):
10344 return self.cgRoot.define()
10346 def deps(self):
10347 return self._deps
10350 class CGNamespacedEnum(CGThing):
10351 def __init__(self, namespace, enumName, names, values, comment=""):
10353 if not values:
10354 values = []
10356 # Account for explicit enum values.
10357 entries = []
10358 for i in range(0, len(names)):
10359 if len(values) > i and values[i] is not None:
10360 entry = "%s = %s" % (names[i], values[i])
10361 else:
10362 entry = names[i]
10363 entries.append(entry)
10365 # Append a Count.
10366 entries.append('_' + enumName + '_Count')
10368 # Indent.
10369 entries = [' ' + e for e in entries]
10371 # Build the enum body.
10372 enumstr = comment + 'enum %s\n{\n%s\n};\n' % (enumName, ',\n'.join(entries))
10373 curr = CGGeneric(declare=enumstr)
10375 # Add some whitespace padding.
10376 curr = CGWrapper(curr, pre='\n', post='\n')
10378 # Add the namespace.
10379 curr = CGNamespace(namespace, curr)
10381 # Add the typedef
10382 typedef = '\ntypedef %s::%s %s;\n\n' % (namespace, enumName, enumName)
10383 curr = CGList([curr, CGGeneric(declare=typedef)])
10385 # Save the result.
10386 self.node = curr
10388 def declare(self):
10389 return self.node.declare()
10391 def define(self):
10392 return ""
10395 class CGDictionary(CGThing):
10396 def __init__(self, dictionary, descriptorProvider):
10397 self.dictionary = dictionary
10398 self.descriptorProvider = descriptorProvider
10399 self.needToInitIds = len(dictionary.members) > 0
10400 self.memberInfo = [
10401 (member,
10402 getJSToNativeConversionInfo(
10403 member.type,
10404 descriptorProvider,
10405 isEnforceRange=member.enforceRange,
10406 isClamp=member.clamp,
10407 isMember="Dictionary",
10408 isOptional=(not member.defaultValue),
10409 defaultValue=member.defaultValue,
10410 sourceDescription=("'%s' member of %s" %
10411 (member.identifier.name, dictionary.identifier.name))))
10412 for member in dictionary.members]
10414 # If we have a union member containing something in the same
10415 # file as us, bail: the C++ includes won't work out.
10416 for member in dictionary.members:
10417 type = member.type.unroll()
10418 if type.isUnion():
10419 for t in type.flatMemberTypes:
10420 if (t.isDictionary() and
10421 CGHeaders.getDeclarationFilename(t.inner) ==
10422 CGHeaders.getDeclarationFilename(dictionary)):
10423 raise TypeError(
10424 "Dictionary contains a union that contains a "
10425 "dictionary in the same WebIDL file. This won't "
10426 "compile. Move the inner dictionary to a "
10427 "different file.\n%s\n%s" %
10428 (t.location, t.inner.location))
10429 self.structs = self.getStructs()
10431 def declare(self):
10432 return self.structs.declare()
10434 def define(self):
10435 return self.structs.define()
10437 def base(self):
10438 if self.dictionary.parent:
10439 return self.makeClassName(self.dictionary.parent)
10440 return "DictionaryBase"
10442 def initMethod(self):
10443 body = dedent("""
10444 // Passing a null JSContext is OK only if we're initing from null,
10445 // Since in that case we will not have to do any property gets
10446 MOZ_ASSERT_IF(!cx, val.isNull());
10447 """)
10449 if self.needToInitIds:
10450 body += fill(
10451 """
10452 ${dictName}Atoms* atomsCache = nullptr;
10453 if (cx) {
10454 atomsCache = GetAtomCache<${dictName}Atoms>(cx);
10455 if (!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) {
10456 return false;
10457 }
10458 }
10460 """,
10461 dictName=self.makeClassName(self.dictionary))
10463 if self.dictionary.parent:
10464 body += fill(
10465 """
10466 // Per spec, we init the parent's members first
10467 if (!${dictName}::Init(cx, val)) {
10468 return false;
10469 }
10470 MOZ_ASSERT(IsConvertibleToDictionary(cx, val));
10472 """,
10473 dictName=self.makeClassName(self.dictionary.parent))
10474 else:
10475 body += fill(
10476 """
10477 if (!IsConvertibleToDictionary(cx, val)) {
10478 return ThrowErrorMessage(cx, MSG_NOT_DICTIONARY, sourceDescription);
10479 }
10481 """)
10483 memberInits = [self.getMemberConversion(m).define()
10484 for m in self.memberInfo]
10485 if memberInits:
10486 body += fill(
10487 """
10488 bool isNull = val.isNullOrUndefined();
10489 // We only need these if !isNull, in which case we have |cx|.
10490 Maybe<JS::Rooted<JSObject *> > object;
10491 Maybe<JS::Rooted<JS::Value> > temp;
10492 if (!isNull) {
10493 object.construct(cx, &val.toObject());
10494 temp.construct(cx);
10495 }
10496 $*{memberInits}
10497 """,
10498 memberInits="\n".join(memberInits))
10500 body += "return true;\n"
10502 return ClassMethod("Init", "bool", [
10503 Argument('JSContext*', 'cx'),
10504 Argument('JS::Handle<JS::Value>', 'val'),
10505 Argument('const char*', 'sourceDescription', default='"Value"')
10506 ], body=body)
10508 def initFromJSONMethod(self):
10509 return ClassMethod(
10510 "Init", "bool",
10511 [Argument('const nsAString&', 'aJSON')],
10512 body=dedent("""
10513 MOZ_ASSERT(NS_IsMainThread());
10514 AutoSafeJSContext cx;
10515 JS::Rooted<JS::Value> json(cx);
10516 bool ok = ParseJSON(cx, aJSON, &json);
10517 NS_ENSURE_TRUE(ok, false);
10518 return Init(cx, json);
10519 """))
10521 def toObjectMethod(self):
10522 body = ""
10523 if self.needToInitIds:
10524 body += fill(
10525 """
10526 ${dictName}Atoms* atomsCache = GetAtomCache<${dictName}Atoms>(cx);
10527 if (!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) {
10528 return false;
10529 }
10531 """,
10532 dictName=self.makeClassName(self.dictionary))
10534 if self.dictionary.parent:
10535 body += fill(
10536 """
10537 // Per spec, we define the parent's members first
10538 if (!${dictName}::ToObject(cx, rval)) {
10539 return false;
10540 }
10541 JS::Rooted<JSObject*> obj(cx, &rval.toObject());
10543 """,
10544 dictName=self.makeClassName(self.dictionary.parent))
10545 else:
10546 body += fill(
10547 """
10548 JS::Rooted<JSObject*> obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
10549 if (!obj) {
10550 return false;
10551 }
10552 rval.set(JS::ObjectValue(*obj));
10554 """)
10556 if self.memberInfo:
10557 body += "\n".join(self.getMemberDefinition(m).define()
10558 for m in self.memberInfo)
10559 else:
10560 body += "\n" # BOGUS extra blank line
10561 body += "\nreturn true;\n"
10563 return ClassMethod("ToObject", "bool", [
10564 Argument('JSContext*', 'cx'),
10565 Argument('JS::MutableHandle<JS::Value>', 'rval'),
10566 ], const=True, body=body)
10568 def initIdsMethod(self):
10569 assert self.needToInitIds
10570 idinit = ['!atomsCache->%s.init(cx, "%s")' %
10571 (CGDictionary.makeIdName(m.identifier.name),
10572 m.identifier.name)
10573 for m in self.dictionary.members]
10574 idinit.reverse()
10575 body = fill(
10576 """
10577 MOZ_ASSERT(!*reinterpret_cast<jsid**>(atomsCache));
10579 // Initialize these in reverse order so that any failure leaves the first one
10580 // uninitialized.
10581 if (${idinit}) {
10582 return false;
10583 }
10584 return true;
10585 """,
10586 idinit=" ||\n ".join(idinit))
10588 return ClassMethod("InitIds", "bool", [
10589 Argument("JSContext*", "cx"),
10590 Argument("%sAtoms*" % self.makeClassName(self.dictionary),
10591 "atomsCache"),
10592 ], static=True, body=body, visibility="private")
10594 def traceDictionaryMethod(self):
10595 body = ""
10596 if self.dictionary.parent:
10597 cls = self.makeClassName(self.dictionary.parent)
10598 body += "%s::TraceDictionary(trc);\n" % cls
10600 memberTraces = [self.getMemberTrace(m)
10601 for m in self.dictionary.members
10602 if typeNeedsRooting(m.type)]
10604 if memberTraces:
10605 body += "\n".join(memberTraces)
10606 else:
10607 body += "\n" # BOGUS extra newline
10609 return ClassMethod("TraceDictionary", "void", [
10610 Argument("JSTracer*", "trc"),
10611 ], body=body)
10613 def assignmentOperator(self):
10614 body = CGList([])
10615 if self.dictionary.parent:
10616 body.append(CGGeneric(
10617 "%s::operator=(aOther);\n" %
10618 self.makeClassName(self.dictionary.parent)))
10619 for m, _ in self.memberInfo:
10620 memberName = self.makeMemberName(m.identifier.name)
10621 if not m.defaultValue:
10622 memberAssign = CGGeneric(fill(
10623 """
10624 if (aOther.${name}.WasPassed()) {
10625 ${name}.Construct();
10626 ${name}.Value() = aOther.${name}.Value();
10627 } else {
10628 ${name}.Reset();
10629 }
10630 """,
10631 name=memberName))
10632 else:
10633 memberAssign = CGGeneric(
10634 "%s = aOther.%s;\n" % (memberName, memberName))
10635 body.append(memberAssign)
10636 return ClassMethod(
10637 "operator=", "void",
10638 [Argument("const %s&" % self.makeClassName(self.dictionary),
10639 "aOther")],
10640 body=body.define() or "\n") # BOGUS blank line when empty
10642 def getStructs(self):
10643 d = self.dictionary
10644 selfName = self.makeClassName(d)
10645 members = [ClassMember(self.makeMemberName(m[0].identifier.name),
10646 self.getMemberType(m),
10647 visibility="public",
10648 body=self.getMemberInitializer(m))
10649 for m in self.memberInfo]
10650 ctors = [
10651 ClassConstructor(
10652 [],
10653 visibility="public",
10654 body=(
10655 "// Safe to pass a null context if we pass a null value\n"
10656 "Init(nullptr, JS::NullHandleValue);\n")),
10657 ClassConstructor(
10658 [Argument("int", "")],
10659 visibility="protected",
10660 explicit=True,
10661 bodyInHeader=True,
10662 body='// Do nothing here; this is used by our "Fast" subclass\n')
10663 ]
10664 methods = []
10666 if self.needToInitIds:
10667 methods.append(self.initIdsMethod())
10669 methods.append(self.initMethod())
10670 methods.append(self.initFromJSONMethod())
10671 try:
10672 methods.append(self.toObjectMethod())
10673 except MethodNotNewObjectError:
10674 # If we can't have a ToObject() because one of our members can only
10675 # be returned from [NewObject] methods, then just skip generating
10676 # ToObject().
10677 pass
10678 methods.append(self.traceDictionaryMethod())
10680 if CGDictionary.isDictionaryCopyConstructible(d):
10681 disallowCopyConstruction = False
10682 # Note: no base constructors because our operator= will
10683 # deal with that.
10684 ctors.append(ClassConstructor([Argument("const %s&" % selfName,
10685 "aOther")],
10686 bodyInHeader=True,
10687 visibility="public",
10688 explicit=True,
10689 body="*this = aOther;\n"))
10690 methods.append(self.assignmentOperator())
10691 else:
10692 disallowCopyConstruction = True
10694 struct = CGClass(selfName,
10695 bases=[ClassBase(self.base())],
10696 members=members,
10697 constructors=ctors,
10698 methods=methods,
10699 isStruct=True,
10700 disallowCopyConstruction=disallowCopyConstruction)
10702 fastDictionaryCtor = ClassConstructor(
10703 [],
10704 visibility="public",
10705 bodyInHeader=True,
10706 baseConstructors=["%s(42)" % selfName],
10707 body="// Doesn't matter what int we pass to the parent constructor\n")
10709 fastStruct = CGClass("Fast" + selfName,
10710 bases=[ClassBase(selfName)],
10711 constructors=[fastDictionaryCtor],
10712 isStruct=True)
10714 return CGList([struct,
10715 CGNamespace('binding_detail', fastStruct)],
10716 "\n")
10718 def deps(self):
10719 return self.dictionary.getDeps()
10721 @staticmethod
10722 def makeDictionaryName(dictionary):
10723 return dictionary.identifier.name
10725 def makeClassName(self, dictionary):
10726 return self.makeDictionaryName(dictionary)
10728 @staticmethod
10729 def makeMemberName(name):
10730 return "m" + name[0].upper() + name[1:]
10732 def getMemberType(self, memberInfo):
10733 _, conversionInfo = memberInfo
10734 # We can't handle having a holderType here
10735 assert conversionInfo.holderType is None
10736 declType = conversionInfo.declType
10737 if conversionInfo.dealWithOptional:
10738 declType = CGTemplatedType("Optional", declType)
10739 return declType.define()
10741 def getMemberConversion(self, memberInfo):
10742 member, conversionInfo = memberInfo
10743 replacements = {
10744 "val": "temp.ref()",
10745 "mutableVal": "&temp.ref()",
10746 "declName": self.makeMemberName(member.identifier.name),
10747 # We need a holder name for external interfaces, but
10748 # it's scoped down to the conversion so we can just use
10749 # anything we want.
10750 "holderName": "holder"
10751 }
10752 # We can't handle having a holderType here
10753 assert conversionInfo.holderType is None
10754 if conversionInfo.dealWithOptional:
10755 replacements["declName"] = "(" + replacements["declName"] + ".Value())"
10756 if member.defaultValue:
10757 replacements["haveValue"] = "!isNull && !temp.ref().isUndefined()"
10759 propId = self.makeIdName(member.identifier.name)
10760 propGet = ("JS_GetPropertyById(cx, object.ref(), atomsCache->%s, &temp.ref())" %
10761 propId)
10763 conversionReplacements = {
10764 "prop": self.makeMemberName(member.identifier.name),
10765 "convert": string.Template(conversionInfo.template).substitute(replacements),
10766 "propGet": propGet
10767 }
10768 conversion = ("if (!isNull && !${propGet}) {\n"
10769 " return false;\n"
10770 "}\n")
10771 if member.defaultValue:
10772 conversion += "${convert}"
10773 else:
10774 conversion += (
10775 "if (!isNull && !temp.ref().isUndefined()) {\n"
10776 " ${prop}.Construct();\n"
10777 "${convert}"
10778 "}\n")
10779 conversionReplacements["convert"] = indent(conversionReplacements["convert"])
10781 return CGGeneric(
10782 string.Template(conversion).substitute(conversionReplacements))
10784 def getMemberDefinition(self, memberInfo):
10785 member = memberInfo[0]
10786 declType = memberInfo[1].declType
10787 memberLoc = self.makeMemberName(member.identifier.name)
10788 if member.defaultValue:
10789 memberData = memberLoc
10790 else:
10791 # The data is inside the Optional<>
10792 memberData = "%s.InternalValue()" % memberLoc
10794 # If you have to change this list (which you shouldn't!), make sure it
10795 # continues to match the list in test_Object.prototype_props.html
10796 if (member.identifier.name in
10797 ["constructor", "toSource", "toString", "toLocaleString", "valueOf",
10798 "watch", "unwatch", "hasOwnProperty", "isPrototypeOf",
10799 "propertyIsEnumerable", "__defineGetter__", "__defineSetter__",
10800 "__lookupGetter__", "__lookupSetter__", "__proto__"]):
10801 raise TypeError("'%s' member of %s dictionary shadows "
10802 "a property of Object.prototype, and Xrays to "
10803 "Object can't handle that.\n"
10804 "%s" %
10805 (member.identifier.name,
10806 self.dictionary.identifier.name,
10807 member.location))
10809 propDef = (
10810 'JS_DefinePropertyById(cx, obj, atomsCache->%s, temp, nullptr, nullptr, JSPROP_ENUMERATE)' %
10811 self.makeIdName(member.identifier.name))
10813 innerTemplate = wrapForType(
10814 member.type, self.descriptorProvider,
10815 {
10816 'result': "currentValue",
10817 'successCode': ("if (!%s) {\n"
10818 " return false;\n"
10819 "}\n"
10820 "break;\n" % propDef),
10821 'jsvalRef': "temp",
10822 'jsvalHandle': "&temp",
10823 'returnsNewObject': False,
10824 # 'obj' can just be allowed to be the string "obj", since that
10825 # will be our dictionary object, which is presumably itself in
10826 # the right scope.
10827 'typedArraysAreStructs': True
10828 })
10829 conversion = CGGeneric(innerTemplate)
10830 conversion = CGWrapper(conversion,
10831 pre=("JS::Rooted<JS::Value> temp(cx);\n"
10832 "%s const & currentValue = %s;\n" %
10833 (declType.define(), memberData)
10834 ))
10836 # Now make sure that our successCode can actually break out of the
10837 # conversion. This incidentally gives us a scope for 'temp' and
10838 # 'currentValue'.
10839 conversion = CGWrapper(
10840 CGIndenter(conversion),
10841 pre=("do {\n"
10842 " // block for our 'break' successCode and scope for 'temp' and 'currentValue'\n"),
10843 post="} while(0);\n")
10844 if not member.defaultValue:
10845 # Only do the conversion if we have a value
10846 conversion = CGIfWrapper(conversion, "%s.WasPassed()" % memberLoc)
10847 return conversion
10849 def getMemberTrace(self, member):
10850 type = member.type
10851 assert typeNeedsRooting(type)
10852 memberLoc = self.makeMemberName(member.identifier.name)
10853 if member.defaultValue:
10854 memberData = memberLoc
10855 else:
10856 # The data is inside the Optional<>
10857 memberData = "%s.Value()" % memberLoc
10859 memberName = "%s.%s" % (self.makeClassName(self.dictionary),
10860 memberLoc)
10862 if type.isObject():
10863 trace = CGGeneric('JS_CallObjectTracer(trc, %s, "%s");\n' %
10864 ("&"+memberData, memberName))
10865 if type.nullable():
10866 trace = CGIfWrapper(trace, memberData)
10867 elif type.isAny():
10868 trace = CGGeneric('JS_CallValueTracer(trc, %s, "%s");\n' %
10869 ("&"+memberData, memberName))
10870 elif (type.isSequence() or type.isDictionary() or
10871 type.isSpiderMonkeyInterface() or type.isUnion()):
10872 if type.nullable():
10873 memberNullable = memberData
10874 memberData = "%s.Value()" % memberData
10875 if type.isSequence():
10876 trace = CGGeneric('DoTraceSequence(trc, %s);\n' % memberData)
10877 elif type.isDictionary():
10878 trace = CGGeneric('%s.TraceDictionary(trc);\n' % memberData)
10879 elif type.isUnion():
10880 trace = CGGeneric('%s.TraceUnion(trc);\n' % memberData)
10881 else:
10882 assert type.isSpiderMonkeyInterface()
10883 trace = CGGeneric('%s.TraceSelf(trc);\n' % memberData)
10884 if type.nullable():
10885 trace = CGIfWrapper(trace, "!%s.IsNull()" % memberNullable)
10886 else:
10887 assert False # unknown type
10889 if not member.defaultValue:
10890 trace = CGIfWrapper(trace, "%s.WasPassed()" % memberLoc)
10892 return trace.define()
10894 def getMemberInitializer(self, memberInfo):
10895 """
10896 Get the right initializer for the member. Most members don't need one,
10897 but we need to pre-initialize 'any' and 'object' that have a default
10898 value, so they're safe to trace at all times.
10899 """
10900 member, _ = memberInfo
10901 if not member.defaultValue:
10902 # No default value means no need to set it up front, since it's
10903 # inside an Optional and won't get traced until it's actually set
10904 # up.
10905 return None
10906 type = member.type
10907 if type.isAny():
10908 return "JS::UndefinedValue()"
10909 if type.isObject():
10910 return "nullptr"
10911 return None
10913 @staticmethod
10914 def makeIdName(name):
10915 return name + "_id"
10917 @staticmethod
10918 def getDictionaryDependenciesFromType(type):
10919 if type.isDictionary():
10920 return set([type.unroll().inner])
10921 if type.isSequence() or type.isArray():
10922 return CGDictionary.getDictionaryDependenciesFromType(type.unroll())
10923 return set()
10925 @staticmethod
10926 def getDictionaryDependencies(dictionary):
10927 deps = set()
10928 if dictionary.parent:
10929 deps.add(dictionary.parent)
10930 for member in dictionary.members:
10931 deps |= CGDictionary.getDictionaryDependenciesFromType(member.type)
10932 return deps
10934 @staticmethod
10935 def isDictionaryCopyConstructible(dictionary):
10936 if (dictionary.parent and
10937 not CGDictionary.isDictionaryCopyConstructible(dictionary.parent)):
10938 return False
10939 return all(isTypeCopyConstructible(m.type) for m in dictionary.members)
10942 class CGRegisterProtos(CGAbstractMethod):
10943 def __init__(self, config):
10944 CGAbstractMethod.__init__(self, None, 'Register', 'void',
10945 [Argument('nsScriptNameSpaceManager*', 'aNameSpaceManager')])
10946 self.config = config
10948 def _defineMacro(self):
10949 return dedent("""
10950 #define REGISTER_PROTO(_dom_class, _ctor_check) \\
10951 aNameSpaceManager->RegisterDefineDOMInterface(MOZ_UTF16(#_dom_class), _dom_class##Binding::DefineDOMInterface, _ctor_check);
10952 #define REGISTER_CONSTRUCTOR(_dom_constructor, _dom_class, _ctor_check) \\
10953 aNameSpaceManager->RegisterDefineDOMInterface(MOZ_UTF16(#_dom_constructor), _dom_class##Binding::DefineDOMInterface, _ctor_check);
10954 #define REGISTER_NAVIGATOR_CONSTRUCTOR(_prop, _dom_class, _ctor_check) \\
10955 aNameSpaceManager->RegisterNavigatorDOMConstructor(MOZ_UTF16(_prop), _dom_class##Binding::ConstructNavigatorObject, _ctor_check);
10956 """)
10958 def _undefineMacro(self):
10959 return dedent("""
10960 #undef REGISTER_CONSTRUCTOR
10961 #undef REGISTER_PROTO
10962 #undef REGISTER_NAVIGATOR_CONSTRUCTOR
10963 """)
10965 def _registerProtos(self):
10966 def getCheck(desc):
10967 if not desc.isExposedConditionally():
10968 return "nullptr"
10969 return "%sBinding::ConstructorEnabled" % desc.name
10970 lines = []
10971 for desc in self.config.getDescriptors(hasInterfaceObject=True,
10972 isExternal=False,
10973 workers=False,
10974 register=True):
10975 lines.append("REGISTER_PROTO(%s, %s);\n" % (desc.name, getCheck(desc)))
10976 lines.extend("REGISTER_CONSTRUCTOR(%s, %s, %s);\n" % (n.identifier.name, desc.name, getCheck(desc))
10977 for n in desc.interface.namedConstructors)
10978 for desc in self.config.getDescriptors(isNavigatorProperty=True, register=True):
10979 propName = desc.interface.getNavigatorProperty()
10980 assert propName
10981 lines.append('REGISTER_NAVIGATOR_CONSTRUCTOR("%s", %s, %s);\n' % (propName, desc.name, getCheck(desc)))
10982 return ''.join(lines)
10984 def definition_body(self):
10985 return "\n" + self._defineMacro() + "\n" + self._registerProtos() + "\n" + self._undefineMacro()
10988 def dependencySortObjects(objects, dependencyGetter, nameGetter):
10989 """
10990 Sort IDL objects with dependencies on each other such that if A
10991 depends on B then B will come before A. This is needed for
10992 declaring C++ classes in the right order, for example. Objects
10993 that have no dependencies are just sorted by name.
10995 objects should be something that can produce a set of objects
10996 (e.g. a set, iterator, list, etc).
10998 dependencyGetter is something that, given an object, should return
10999 the set of objects it depends on.
11000 """
11001 # XXXbz this will fail if we have two webidl files F1 and F2 such that F1
11002 # declares an object which depends on an object in F2, and F2 declares an
11003 # object (possibly a different one!) that depends on an object in F1. The
11004 # good news is that I expect this to never happen.
11005 sortedObjects = []
11006 objects = set(objects)
11007 while len(objects) != 0:
11008 # Find the dictionaries that don't depend on anything else
11009 # anymore and move them over.
11010 toMove = [o for o in objects if
11011 len(dependencyGetter(o) & objects) == 0]
11012 if len(toMove) == 0:
11013 raise TypeError("Loop in dependency graph\n" +
11014 "\n".join(o.location for o in objects))
11015 objects = objects - set(toMove)
11016 sortedObjects.extend(sorted(toMove, key=nameGetter))
11017 return sortedObjects
11020 class ForwardDeclarationBuilder:
11021 """
11022 Create a canonical representation of a set of namespaced forward
11023 declarations.
11024 """
11025 def __init__(self):
11026 """
11027 The set of declarations is represented as a tree of nested namespaces.
11028 Each tree node has a set of declarations |decls| and a dict |children|.
11029 Each declaration is a pair consisting of the class name and a boolean
11030 that is true iff the class is really a struct. |children| maps the
11031 names of inner namespaces to the declarations in that namespace.
11032 """
11033 self.decls = set()
11034 self.children = {}
11036 def _listAdd(self, namespaces, name, isStruct=False):
11037 """
11038 Add a forward declaration, where |namespaces| is a list of namespaces.
11039 |name| should not contain any other namespaces.
11040 """
11041 if namespaces:
11042 child = self.children.setdefault(namespaces[0], ForwardDeclarationBuilder())
11043 child._listAdd(namespaces[1:], name, isStruct)
11044 else:
11045 assert '::' not in name
11046 self.decls.add((name, isStruct))
11048 def addInMozillaDom(self, name, isStruct=False):
11049 """
11050 Add a forward declaration to the mozilla::dom:: namespace. |name| should not
11051 contain any other namespaces.
11052 """
11053 self._listAdd(["mozilla", "dom"], name, isStruct)
11055 def add(self, nativeType, isStruct=False):
11056 """
11057 Add a forward declaration, where |nativeType| is a string containing
11058 the type and its namespaces, in the usual C++ way.
11059 """
11060 components = nativeType.split('::')
11061 self._listAdd(components[:-1], components[-1], isStruct)
11063 def _build(self, atTopLevel):
11064 """
11065 Return a codegenerator for the forward declarations.
11066 """
11067 decls = []
11068 if self.decls:
11069 decls.append(CGList([CGClassForwardDeclare(cname, isStruct)
11070 for cname, isStruct in sorted(self.decls)]))
11071 for namespace, child in sorted(self.children.iteritems()):
11072 decls.append(CGNamespace(namespace, child._build(atTopLevel=False), declareOnly=True))
11074 cg = CGList(decls, "\n")
11075 if not atTopLevel and len(decls) + len(self.decls) > 1:
11076 cg = CGWrapper(cg, pre='\n', post='\n')
11077 return cg
11079 def build(self):
11080 return self._build(atTopLevel=True)
11083 class CGForwardDeclarations(CGWrapper):
11084 """
11085 Code generate the forward declarations for a header file.
11086 """
11087 def __init__(self, config, descriptors, mainCallbacks, workerCallbacks,
11088 dictionaries, callbackInterfaces):
11089 builder = ForwardDeclarationBuilder()
11091 def forwardDeclareForType(t, workerness='both'):
11092 t = t.unroll()
11093 if t.isGeckoInterface():
11094 name = t.inner.identifier.name
11095 # Find and add the non-worker implementation, if any.
11096 if workerness != 'workeronly':
11097 try:
11098 desc = config.getDescriptor(name, False)
11099 builder.add(desc.nativeType)
11100 except NoSuchDescriptorError:
11101 pass
11102 # Find and add the worker implementation, if any.
11103 if workerness != 'mainthreadonly':
11104 try:
11105 desc = config.getDescriptor(name, True)
11106 builder.add(desc.nativeType)
11107 except NoSuchDescriptorError:
11108 pass
11109 elif t.isCallback():
11110 builder.addInMozillaDom(str(t))
11111 elif t.isDictionary():
11112 builder.addInMozillaDom(t.inner.identifier.name, isStruct=True)
11113 elif t.isCallbackInterface():
11114 builder.addInMozillaDom(t.inner.identifier.name)
11115 elif t.isUnion():
11116 # Forward declare both the owning and non-owning version,
11117 # since we don't know which one we might want
11118 builder.addInMozillaDom(CGUnionStruct.unionTypeName(t, False))
11119 builder.addInMozillaDom(CGUnionStruct.unionTypeName(t, True))
11120 elif t.isMozMap():
11121 forwardDeclareForType(t.inner, workerness)
11122 # Don't need to do anything for void, primitive, string, any or object.
11123 # There may be some other cases we are missing.
11125 # Needed for at least Wrap.
11126 for d in descriptors:
11127 builder.add(d.nativeType)
11129 # We just about always need NativePropertyHooks
11130 builder.addInMozillaDom("NativePropertyHooks")
11131 builder.addInMozillaDom("ProtoAndIfaceCache")
11133 for callback in mainCallbacks:
11134 forwardDeclareForType(callback)
11135 for t in getTypesFromCallback(callback):
11136 forwardDeclareForType(t, workerness='mainthreadonly')
11138 for callback in workerCallbacks:
11139 forwardDeclareForType(callback)
11140 for t in getTypesFromCallback(callback):
11141 forwardDeclareForType(t, workerness='workeronly')
11143 for d in callbackInterfaces:
11144 builder.add(d.nativeType)
11145 for t in getTypesFromDescriptor(d):
11146 forwardDeclareForType(t)
11148 for d in dictionaries:
11149 if len(d.members) > 0:
11150 builder.addInMozillaDom(d.identifier.name + "Atoms", isStruct=True)
11151 for t in getTypesFromDictionary(d):
11152 forwardDeclareForType(t)
11154 CGWrapper.__init__(self, builder.build())
11157 class CGBindingRoot(CGThing):
11158 """
11159 Root codegen class for binding generation. Instantiate the class, and call
11160 declare or define to generate header or cpp code (respectively).
11161 """
11162 def __init__(self, config, prefix, webIDLFile):
11163 bindingHeaders = {}
11164 descriptors = config.getDescriptors(webIDLFile=webIDLFile,
11165 hasInterfaceOrInterfacePrototypeObject=True,
11166 skipGen=False)
11168 def descriptorRequiresPreferences(desc):
11169 iface = desc.interface
11170 return any(m.getExtendedAttribute("Pref") for m in iface.members + [iface])
11172 bindingHeaders["mozilla/Preferences.h"] = any(
11173 descriptorRequiresPreferences(d) for d in descriptors)
11174 bindingHeaders["mozilla/dom/NonRefcountedDOMObject.h"] = any(
11175 d.nativeOwnership == 'owned' for d in descriptors)
11176 bindingHeaders["mozilla/dom/DOMJSProxyHandler.h"] = any(
11177 d.concrete and d.proxy for d in descriptors)
11179 def descriptorHasChromeOnly(desc):
11180 return (any(isChromeOnly(a) for a in desc.interface.members) or
11181 desc.interface.getExtendedAttribute("ChromeOnly") is not None or
11182 # JS-implemented interfaces with an interface object get a
11183 # chromeonly _create method.
11184 (desc.interface.isJSImplemented() and
11185 desc.interface.hasInterfaceObject()))
11187 bindingHeaders["nsContentUtils.h"] = any(
11188 descriptorHasChromeOnly(d) for d in descriptors)
11189 # XXXkhuey ugly hack but this is going away soon.
11190 bindingHeaders['xpcprivate.h'] = webIDLFile.endswith("EventTarget.webidl")
11191 hasWorkerStuff = len(config.getDescriptors(webIDLFile=webIDLFile,
11192 workers=True)) != 0
11193 bindingHeaders["WorkerPrivate.h"] = hasWorkerStuff
11194 bindingHeaders["nsThreadUtils.h"] = hasWorkerStuff
11196 dictionaries = config.getDictionaries(webIDLFile=webIDLFile)
11197 bindingHeaders["nsCxPusher.h"] = dictionaries
11198 bindingHeaders["AtomList.h"] = any(
11199 len(dict.members) > 0 for dict in dictionaries)
11200 mainCallbacks = config.getCallbacks(webIDLFile=webIDLFile,
11201 workers=False)
11202 workerCallbacks = config.getCallbacks(webIDLFile=webIDLFile,
11203 workers=True)
11204 callbackDescriptors = config.getDescriptors(webIDLFile=webIDLFile,
11205 isCallback=True)
11206 jsImplemented = config.getDescriptors(webIDLFile=webIDLFile,
11207 isJSImplemented=True)
11208 bindingHeaders["nsPIDOMWindow.h"] = jsImplemented
11210 def addHeaderBasedOnTypes(header, typeChecker):
11211 bindingHeaders[header] = (
11212 bindingHeaders.get(header, False) or
11213 any(map(typeChecker,
11214 getAllTypes(descriptors + callbackDescriptors,
11215 dictionaries,
11216 mainCallbacks + workerCallbacks))))
11218 bindingHeaders["nsDOMQS.h"] = any(d.hasXPConnectImpls for d in descriptors)
11219 # Only mainthread things can have hasXPConnectImpls
11220 provider = config.getDescriptorProvider(False)
11222 def checkForXPConnectImpls(typeInfo):
11223 type, _, _ = typeInfo
11224 type = type.unroll()
11225 while type.isMozMap():
11226 type = type.inner.unroll()
11227 if not type.isInterface() or not type.isGeckoInterface():
11228 return False
11229 try:
11230 typeDesc = provider.getDescriptor(type.inner.identifier.name)
11231 except NoSuchDescriptorError:
11232 return False
11233 return typeDesc.hasXPConnectImpls
11234 addHeaderBasedOnTypes("nsDOMQS.h", checkForXPConnectImpls)
11236 def descriptorClearsPropsInSlots(descriptor):
11237 if not descriptor.wrapperCache:
11238 return False
11239 return any(m.isAttr() and m.getExtendedAttribute("StoreInSlot")
11240 for m in descriptor.interface.members)
11241 bindingHeaders["nsJSUtils.h"] = any(descriptorClearsPropsInSlots(d) for d in descriptors)
11243 # Do codegen for all the enums
11244 enums = config.getEnums(webIDLFile)
11245 cgthings = [CGEnum(e) for e in enums]
11247 hasCode = (descriptors or callbackDescriptors or dictionaries or
11248 mainCallbacks or workerCallbacks)
11249 bindingHeaders["mozilla/dom/BindingUtils.h"] = hasCode
11250 bindingHeaders["mozilla/dom/OwningNonNull.h"] = hasCode
11251 bindingHeaders["mozilla/dom/BindingDeclarations.h"] = (
11252 not hasCode and enums)
11254 bindingHeaders["WrapperFactory.h"] = descriptors
11255 bindingHeaders["mozilla/dom/DOMJSClass.h"] = descriptors
11257 # Do codegen for all the dictionaries. We have to be a bit careful
11258 # here, because we have to generate these in order from least derived
11259 # to most derived so that class inheritance works out. We also have to
11260 # generate members before the dictionary that contains them.
11261 cgthings.extend([CGDictionary(d, config.getDescriptorProvider(False))
11262 for d in
11263 dependencySortObjects(dictionaries,
11264 CGDictionary.getDictionaryDependencies,
11265 lambda d: d.identifier.name)])
11267 # Do codegen for all the callbacks.
11268 cgthings.extend(CGCallbackFunction(c, config.getDescriptorProvider(False))
11269 for c in mainCallbacks)
11271 cgthings.extend(CGCallbackFunction(c, config.getDescriptorProvider(True))
11272 for c in workerCallbacks if c not in mainCallbacks)
11274 # Do codegen for all the descriptors
11275 cgthings.extend([CGDescriptor(x) for x in descriptors])
11277 # Do codegen for all the callback interfaces. Skip worker callbacks.
11278 cgthings.extend([CGCallbackInterface(x) for x in callbackDescriptors if
11279 not x.workers])
11281 # Do codegen for JS implemented classes
11282 def getParentDescriptor(desc):
11283 if not desc.interface.parent:
11284 return set()
11285 return {desc.getDescriptor(desc.interface.parent.identifier.name)}
11286 for x in dependencySortObjects(jsImplemented, getParentDescriptor,
11287 lambda d: d.interface.identifier.name):
11288 cgthings.append(CGCallbackInterface(x))
11289 cgthings.append(CGJSImplClass(x))
11291 # And make sure we have the right number of newlines at the end
11292 curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n")
11294 # Wrap all of that in our namespaces.
11295 curr = CGNamespace.build(['mozilla', 'dom'],
11296 CGWrapper(curr, pre="\n"))
11298 curr = CGList([CGForwardDeclarations(config, descriptors,
11299 mainCallbacks, workerCallbacks,
11300 dictionaries,
11301 callbackDescriptors + jsImplemented),
11302 curr],
11303 "\n")
11305 # Add header includes.
11306 bindingHeaders = [header
11307 for header, include in bindingHeaders.iteritems()
11308 if include]
11309 declareIncludes = [
11310 'mozilla/dom/BindingDeclarations.h',
11311 'mozilla/dom/Nullable.h',
11312 'mozilla/ErrorResult.h',
11313 'jspubtd.h',
11314 'js/RootingAPI.h',
11315 ]
11316 if jsImplemented:
11317 declareIncludes.append('nsWeakReference.h')
11319 curr = CGHeaders(descriptors,
11320 dictionaries,
11321 mainCallbacks + workerCallbacks,
11322 callbackDescriptors,
11323 declareIncludes,
11324 bindingHeaders,
11325 prefix,
11326 curr,
11327 config,
11328 jsImplemented)
11330 # Add include guards.
11331 curr = CGIncludeGuard(prefix, curr)
11333 # Add the auto-generated comment.
11334 curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
11336 # Store the final result.
11337 self.root = curr
11339 def declare(self):
11340 return stripTrailingWhitespace(self.root.declare())
11342 def define(self):
11343 return stripTrailingWhitespace(self.root.define())
11345 def deps(self):
11346 return self.root.deps()
11349 class CGNativeMember(ClassMethod):
11350 def __init__(self, descriptorProvider, member, name, signature, extendedAttrs,
11351 breakAfter=True, passJSBitsAsNeeded=True, visibility="public",
11352 jsObjectsArePtr=False, variadicIsSequence=False):
11353 """
11354 If jsObjectsArePtr is true, typed arrays and "object" will be
11355 passed as JSObject*.
11357 If passJSBitsAsNeeded is false, we don't automatically pass in a
11358 JSContext* or a JSObject* based on the return and argument types. We
11359 can still pass it based on 'implicitJSContext' annotations.
11360 """
11361 self.descriptorProvider = descriptorProvider
11362 self.member = member
11363 self.extendedAttrs = extendedAttrs
11364 self.resultAlreadyAddRefed = isResultAlreadyAddRefed(self.extendedAttrs)
11365 self.passJSBitsAsNeeded = passJSBitsAsNeeded
11366 self.jsObjectsArePtr = jsObjectsArePtr
11367 self.variadicIsSequence = variadicIsSequence
11368 breakAfterSelf = "\n" if breakAfter else ""
11369 ClassMethod.__init__(self, name,
11370 self.getReturnType(signature[0], False),
11371 self.getArgs(signature[0], signature[1]),
11372 static=member.isStatic(),
11373 # Mark our getters, which are attrs that
11374 # have a non-void return type, as const.
11375 const=(not member.isStatic() and member.isAttr() and
11376 not signature[0].isVoid()),
11377 breakAfterReturnDecl=" ",
11378 breakAfterSelf=breakAfterSelf,
11379 visibility=visibility)
11381 def getReturnType(self, type, isMember):
11382 return self.getRetvalInfo(type, isMember)[0]
11384 def getRetvalInfo(self, type, isMember):
11385 """
11386 Returns a tuple:
11388 The first element is the type declaration for the retval
11390 The second element is a default value that can be used on error returns.
11391 For cases whose behavior depends on isMember, the second element will be
11392 None if isMember is true.
11394 The third element is a template for actually returning a value stored in
11395 "${declName}" and "${holderName}". This means actually returning it if
11396 we're not outparam, else assigning to the "retval" outparam. If
11397 isMember is true, this can be None, since in that case the caller will
11398 never examine this value.
11399 """
11400 if type.isVoid():
11401 return "void", "", ""
11402 if type.isPrimitive() and type.tag() in builtinNames:
11403 result = CGGeneric(builtinNames[type.tag()])
11404 defaultReturnArg = "0"
11405 if type.nullable():
11406 result = CGTemplatedType("Nullable", result)
11407 defaultReturnArg = ""
11408 return (result.define(),
11409 "%s(%s)" % (result.define(), defaultReturnArg),
11410 "return ${declName};\n")
11411 if type.isDOMString():
11412 if isMember:
11413 # No need for a third element in the isMember case
11414 return "nsString", None, None
11415 # Outparam
11416 return "void", "", "aRetVal = ${declName};\n"
11417 if type.isByteString():
11418 if isMember:
11419 # No need for a third element in the isMember case
11420 return "nsCString", None, None
11421 # Outparam
11422 return "void", "", "aRetVal = ${declName};\n"
11423 if type.isEnum():
11424 enumName = type.unroll().inner.identifier.name
11425 if type.nullable():
11426 enumName = CGTemplatedType("Nullable",
11427 CGGeneric(enumName)).define()
11428 defaultValue = "%s()" % enumName
11429 else:
11430 defaultValue = "%s(0)" % enumName
11431 return enumName, defaultValue, "return ${declName};\n"
11432 if type.isGeckoInterface():
11433 iface = type.unroll().inner
11434 result = CGGeneric(self.descriptorProvider.getDescriptor(
11435 iface.identifier.name).prettyNativeType)
11436 if self.resultAlreadyAddRefed:
11437 if isMember:
11438 holder = "nsRefPtr"
11439 else:
11440 holder = "already_AddRefed"
11441 if memberReturnsNewObject(self.member):
11442 warning = ""
11443 else:
11444 warning = "// Mark this as resultNotAddRefed to return raw pointers\n"
11445 result = CGWrapper(result,
11446 pre=("%s%s<" % (warning, holder)),
11447 post=">")
11448 else:
11449 result = CGWrapper(result, post="*")
11450 # Since we always force an owning type for callback return values,
11451 # our ${declName} is an OwningNonNull or nsRefPtr. So we can just
11452 # .forget() to get our already_AddRefed.
11453 return result.define(), "nullptr", "return ${declName}.forget();\n"
11454 if type.isCallback():
11455 return ("already_AddRefed<%s>" % type.unroll().identifier.name,
11456 "nullptr", "return ${declName}.forget();\n")
11457 if type.isAny():
11458 if isMember:
11459 # No need for a third element in the isMember case
11460 return "JS::Value", None, None
11461 # Outparam
11462 return "void", "", "aRetVal.set(${declName});\n"
11464 if type.isObject():
11465 if isMember:
11466 # No need for a third element in the isMember case
11467 return "JSObject*", None, None
11468 return "void", "", "aRetVal.set(${declName});\n"
11469 if type.isSpiderMonkeyInterface():
11470 if isMember:
11471 # No need for a third element in the isMember case
11472 return "JSObject*", None, None
11473 if type.nullable():
11474 returnCode = "${declName}.IsNull() ? nullptr : ${declName}.Value().Obj();\n"
11475 else:
11476 returnCode = "${declName}.Obj();\n"
11477 return "void", "", "aRetVal.set(%s);\n" % returnCode
11478 if type.isSequence():
11479 # If we want to handle sequence-of-sequences return values, we're
11480 # going to need to fix example codegen to not produce nsTArray<void>
11481 # for the relevant argument...
11482 assert not isMember
11483 # Outparam.
11484 if type.nullable():
11485 returnCode = dedent("""
11486 if (${declName}.IsNull()) {
11487 aRetVal.SetNull();
11488 } else {
11489 aRetVal.SetValue().SwapElements(${declName}.Value());
11490 }
11491 """)
11492 else:
11493 returnCode = "aRetVal.SwapElements(${declName});\n"
11494 return "void", "", returnCode
11495 if type.isMozMap():
11496 # If we want to handle MozMap-of-MozMap return values, we're
11497 # going to need to fix example codegen to not produce MozMap<void>
11498 # for the relevant argument...
11499 assert not isMember
11500 # In this case we convert directly into our outparam to start with
11501 return "void", "", ""
11502 if type.isDate():
11503 result = CGGeneric("Date")
11504 if type.nullable():
11505 result = CGTemplatedType("Nullable", result)
11506 return (result.define(), "%s()" % result.define(),
11507 "return ${declName};\n")
11508 if type.isDictionary():
11509 if isMember:
11510 # Only the first member of the tuple matters here, but return
11511 # bogus values for the others in case someone decides to use
11512 # them.
11513 return CGDictionary.makeDictionaryName(type.inner), None, None
11514 # In this case we convert directly into our outparam to start with
11515 return "void", "", ""
11516 if type.isUnion():
11517 if isMember:
11518 # Only the first member of the tuple matters here, but return
11519 # bogus values for the others in case someone decides to use
11520 # them.
11521 return CGUnionStruct.unionTypeDecl(type, True), None, None
11522 # In this case we convert directly into our outparam to start with
11523 return "void", "", ""
11525 raise TypeError("Don't know how to declare return value for %s" %
11526 type)
11528 def getArgs(self, returnType, argList):
11529 args = [self.getArg(arg) for arg in argList]
11530 # Now the outparams
11531 if returnType.isDOMString():
11532 args.append(Argument("nsString&", "aRetVal"))
11533 elif returnType.isByteString():
11534 args.append(Argument("nsCString&", "aRetVal"))
11535 elif returnType.isSequence():
11536 nullable = returnType.nullable()
11537 if nullable:
11538 returnType = returnType.inner
11539 # And now the actual underlying type
11540 elementDecl = self.getReturnType(returnType.inner, True)
11541 type = CGTemplatedType("nsTArray", CGGeneric(elementDecl))
11542 if nullable:
11543 type = CGTemplatedType("Nullable", type)
11544 args.append(Argument("%s&" % type.define(), "aRetVal"))
11545 elif returnType.isMozMap():
11546 nullable = returnType.nullable()
11547 if nullable:
11548 returnType = returnType.inner
11549 # And now the actual underlying type
11550 elementDecl = self.getReturnType(returnType.inner, True)
11551 type = CGTemplatedType("MozMap", CGGeneric(elementDecl))
11552 if nullable:
11553 type = CGTemplatedType("Nullable", type)
11554 args.append(Argument("%s&" % type.define(), "aRetVal"))
11555 elif returnType.isDictionary():
11556 nullable = returnType.nullable()
11557 if nullable:
11558 returnType = returnType.inner
11559 dictType = CGGeneric(CGDictionary.makeDictionaryName(returnType.inner))
11560 if nullable:
11561 dictType = CGTemplatedType("Nullable", dictType)
11562 args.append(Argument("%s&" % dictType.define(), "aRetVal"))
11563 elif returnType.isUnion():
11564 args.append(Argument("%s&" %
11565 CGUnionStruct.unionTypeDecl(returnType, True),
11566 "aRetVal"))
11567 elif returnType.isAny():
11568 args.append(Argument("JS::MutableHandle<JS::Value>", "aRetVal"))
11569 elif returnType.isObject() or returnType.isSpiderMonkeyInterface():
11570 args.append(Argument("JS::MutableHandle<JSObject*>", "aRetVal"))
11572 # And the ErrorResult
11573 if 'infallible' not in self.extendedAttrs:
11574 # Use aRv so it won't conflict with local vars named "rv"
11575 args.append(Argument("ErrorResult&", "aRv"))
11576 # The legacycaller thisval
11577 if self.member.isMethod() and self.member.isLegacycaller():
11578 # If it has an identifier, we can't deal with it yet
11579 assert self.member.isIdentifierLess()
11580 args.insert(0, Argument("JS::Value", "aThisVal"))
11581 # And jscontext bits.
11582 if needCx(returnType, argList, self.extendedAttrs,
11583 self.passJSBitsAsNeeded):
11584 args.insert(0, Argument("JSContext*", "cx"))
11585 if needScopeObject(returnType, argList, self.extendedAttrs,
11586 self.descriptorProvider.wrapperCache,
11587 self.passJSBitsAsNeeded,
11588 self.member.getExtendedAttribute("StoreInSlot")):
11589 args.insert(1, Argument("JS::Handle<JSObject*>", "obj"))
11590 # And if we're static, a global
11591 if self.member.isStatic():
11592 args.insert(0, Argument("const GlobalObject&", "global"))
11593 return args
11595 def doGetArgType(self, type, optional, isMember):
11596 """
11597 The main work of getArgType. Returns a string type decl, whether this
11598 is a const ref, as well as whether the type should be wrapped in
11599 Nullable as needed.
11601 isMember can be false or one of the strings "Sequence", "Variadic",
11602 "MozMap"
11603 """
11604 if type.isArray():
11605 raise TypeError("Can't handle array arguments yet")
11607 if type.isSequence():
11608 nullable = type.nullable()
11609 if nullable:
11610 type = type.inner
11611 elementType = type.inner
11612 argType = self.getArgType(elementType, False, "Sequence")[0]
11613 decl = CGTemplatedType("Sequence", argType)
11614 return decl.define(), True, True
11616 if type.isMozMap():
11617 nullable = type.nullable()
11618 if nullable:
11619 type = type.inner
11620 elementType = type.inner
11621 argType = self.getArgType(elementType, False, "MozMap")[0]
11622 decl = CGTemplatedType("MozMap", argType)
11623 return decl.define(), True, True
11625 if type.isUnion():
11626 # unionTypeDecl will handle nullable types, so return False for
11627 # auto-wrapping in Nullable
11628 return CGUnionStruct.unionTypeDecl(type, isMember), True, False
11630 if type.isGeckoInterface() and not type.isCallbackInterface():
11631 iface = type.unroll().inner
11632 argIsPointer = type.nullable() or iface.isExternal()
11633 forceOwningType = iface.isCallback() or isMember
11634 if argIsPointer:
11635 if (optional or isMember) and forceOwningType:
11636 typeDecl = "nsRefPtr<%s>"
11637 else:
11638 typeDecl = "%s*"
11639 else:
11640 if optional or isMember:
11641 if forceOwningType:
11642 typeDecl = "OwningNonNull<%s>"
11643 else:
11644 typeDecl = "NonNull<%s>"
11645 else:
11646 typeDecl = "%s&"
11647 return ((typeDecl %
11648 self.descriptorProvider.getDescriptor(iface.identifier.name).prettyNativeType),
11649 False, False)
11651 if type.isSpiderMonkeyInterface():
11652 if self.jsObjectsArePtr:
11653 return "JSObject*", False, False
11655 return type.name, True, True
11657 if type.isDOMString():
11658 if isMember:
11659 declType = "nsString"
11660 else:
11661 declType = "nsAString"
11662 return declType, True, False
11664 if type.isByteString():
11665 declType = "nsCString"
11666 return declType, True, False
11668 if type.isEnum():
11669 return type.unroll().inner.identifier.name, False, True
11671 if type.isCallback() or type.isCallbackInterface():
11672 forceOwningType = optional or isMember
11673 if type.nullable():
11674 if forceOwningType:
11675 declType = "nsRefPtr<%s>"
11676 else:
11677 declType = "%s*"
11678 else:
11679 if forceOwningType:
11680 declType = "OwningNonNull<%s>"
11681 else:
11682 declType = "%s&"
11683 if type.isCallback():
11684 name = type.unroll().identifier.name
11685 else:
11686 name = type.unroll().inner.identifier.name
11687 return declType % name, False, False
11689 if type.isAny():
11690 # Don't do the rooting stuff for variadics for now
11691 if isMember:
11692 declType = "JS::Value"
11693 else:
11694 declType = "JS::Handle<JS::Value>"
11695 return declType, False, False
11697 if type.isObject():
11698 if isMember:
11699 declType = "JSObject*"
11700 else:
11701 declType = "JS::Handle<JSObject*>"
11702 return declType, False, False
11704 if type.isDictionary():
11705 typeName = CGDictionary.makeDictionaryName(type.inner)
11706 return typeName, True, True
11708 if type.isDate():
11709 return "Date", False, True
11711 assert type.isPrimitive()
11713 return builtinNames[type.tag()], False, True
11715 def getArgType(self, type, optional, isMember):
11716 """
11717 Get the type of an argument declaration. Returns the type CGThing, and
11718 whether this should be a const ref.
11720 isMember can be False, "Sequence", or "Variadic"
11721 """
11722 decl, ref, handleNullable = self.doGetArgType(type, optional, isMember)
11723 decl = CGGeneric(decl)
11724 if handleNullable and type.nullable():
11725 decl = CGTemplatedType("Nullable", decl)
11726 ref = True
11727 if isMember == "Variadic":
11728 arrayType = "Sequence" if self.variadicIsSequence else "nsTArray"
11729 decl = CGTemplatedType(arrayType, decl)
11730 ref = True
11731 elif optional:
11732 # Note: All variadic args claim to be optional, but we can just use
11733 # empty arrays to represent them not being present.
11734 decl = CGTemplatedType("Optional", decl)
11735 ref = True
11736 return (decl, ref)
11738 def getArg(self, arg):
11739 """
11740 Get the full argument declaration for an argument
11741 """
11742 decl, ref = self.getArgType(arg.type,
11743 arg.optional and not arg.defaultValue,
11744 "Variadic" if arg.variadic else False)
11745 if ref:
11746 decl = CGWrapper(decl, pre="const ", post="&")
11748 return Argument(decl.define(), arg.identifier.name)
11751 class CGExampleMethod(CGNativeMember):
11752 def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
11753 CGNativeMember.__init__(self, descriptor, method,
11754 CGSpecializedMethod.makeNativeName(descriptor,
11755 method),
11756 signature,
11757 descriptor.getExtendedAttributes(method),
11758 breakAfter=breakAfter,
11759 variadicIsSequence=True)
11761 def define(self, cgClass):
11762 return ''
11765 class CGExampleGetter(CGNativeMember):
11766 def __init__(self, descriptor, attr):
11767 CGNativeMember.__init__(self, descriptor, attr,
11768 CGSpecializedGetter.makeNativeName(descriptor,
11769 attr),
11770 (attr.type, []),
11771 descriptor.getExtendedAttributes(attr,
11772 getter=True))
11774 def define(self, cgClass):
11775 return ''
11778 class CGExampleSetter(CGNativeMember):
11779 def __init__(self, descriptor, attr):
11780 CGNativeMember.__init__(self, descriptor, attr,
11781 CGSpecializedSetter.makeNativeName(descriptor,
11782 attr),
11783 (BuiltinTypes[IDLBuiltinType.Types.void],
11784 [FakeArgument(attr.type, attr)]),
11785 descriptor.getExtendedAttributes(attr,
11786 setter=True))
11788 def define(self, cgClass):
11789 return ''
11792 class CGBindingImplClass(CGClass):
11793 """
11794 Common codegen for generating a C++ implementation of a WebIDL interface
11795 """
11796 def __init__(self, descriptor, cgMethod, cgGetter, cgSetter, wantGetParent=True):
11797 """
11798 cgMethod, cgGetter and cgSetter are classes used to codegen methods,
11799 getters and setters.
11800 """
11801 self.descriptor = descriptor
11802 self._deps = descriptor.interface.getDeps()
11804 iface = descriptor.interface
11806 self.methodDecls = []
11808 def appendMethod(m, isConstructor=False):
11809 sigs = m.signatures()
11810 for s in sigs[:-1]:
11811 # Don't put a blank line after overloads, until we
11812 # get to the last one.
11813 self.methodDecls.append(cgMethod(descriptor, m, s,
11814 isConstructor,
11815 breakAfter=False))
11816 self.methodDecls.append(cgMethod(descriptor, m, sigs[-1],
11817 isConstructor))
11819 if iface.ctor():
11820 appendMethod(iface.ctor(), isConstructor=True)
11821 for n in iface.namedConstructors:
11822 appendMethod(n, isConstructor=True)
11823 for m in iface.members:
11824 if m.isMethod():
11825 if m.isIdentifierLess():
11826 continue
11827 appendMethod(m)
11828 elif m.isAttr():
11829 self.methodDecls.append(cgGetter(descriptor, m))
11830 if not m.readonly:
11831 self.methodDecls.append(cgSetter(descriptor, m))
11833 # Now do the special operations
11834 def appendSpecialOperation(name, op):
11835 if op is None:
11836 return
11837 if name == "IndexedCreator" or name == "NamedCreator":
11838 # These are identical to the setters
11839 return
11840 assert len(op.signatures()) == 1
11841 returnType, args = op.signatures()[0]
11842 # Make a copy of the args, since we plan to modify them.
11843 args = list(args)
11844 if op.isGetter() or op.isDeleter():
11845 # This is a total hack. The '&' belongs with the
11846 # type, not the name! But it works, and is simpler
11847 # than trying to somehow make this pretty.
11848 args.append(FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean],
11849 op, name="&found"))
11850 if name == "Stringifier":
11851 if op.isIdentifierLess():
11852 # XXXbz I wish we were consistent about our renaming here.
11853 name = "Stringify"
11854 else:
11855 # We already added this method
11856 return
11857 if name == "LegacyCaller":
11858 if op.isIdentifierLess():
11859 # XXXbz I wish we were consistent about our renaming here.
11860 name = "LegacyCall"
11861 else:
11862 # We already added this method
11863 return
11864 if name == "Jsonifier":
11865 # We already added this method
11866 return
11867 self.methodDecls.append(
11868 CGNativeMember(descriptor, op,
11869 name,
11870 (returnType, args),
11871 descriptor.getExtendedAttributes(op)))
11872 # Sort things by name so we get stable ordering in the output.
11873 ops = descriptor.operations.items()
11874 ops.sort(key=lambda x: x[0])
11875 for name, op in ops:
11876 appendSpecialOperation(name, op)
11877 # If we support indexed properties, then we need a Length()
11878 # method so we know which indices are supported.
11879 if descriptor.supportsIndexedProperties():
11880 # But we don't need it if we already have an infallible
11881 # "length" attribute, which we often do.
11882 haveLengthAttr = any(
11883 m for m in iface.members if m.isAttr() and
11884 CGSpecializedGetter.makeNativeName(descriptor, m) == "Length")
11885 if not haveLengthAttr:
11886 self.methodDecls.append(
11887 CGNativeMember(descriptor, FakeMember(),
11888 "Length",
11889 (BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
11890 []),
11891 {"infallible": True}))
11892 # And if we support named properties we need to be able to
11893 # enumerate the supported names and test whether they're enumerable.
11894 if descriptor.supportsNamedProperties():
11895 self.methodDecls.append(
11896 CGNativeMember(
11897 descriptor, FakeMember(),
11898 "GetSupportedNames",
11899 (IDLSequenceType(None,
11900 BuiltinTypes[IDLBuiltinType.Types.domstring]),
11901 # Let's use unsigned long for the type here, though really
11902 # it's just a C++ "unsigned"...
11903 [FakeArgument(BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
11904 FakeMember(),
11905 name="aFlags")]),
11906 {"infallible": True}))
11907 self.methodDecls.append(
11908 CGNativeMember(
11909 descriptor, FakeMember(),
11910 "NameIsEnumerable",
11911 (BuiltinTypes[IDLBuiltinType.Types.boolean],
11912 [FakeArgument(BuiltinTypes[IDLBuiltinType.Types.domstring],
11913 FakeMember(),
11914 name="aName")]),
11915 { "infallible": True }))
11917 wrapArgs = [Argument('JSContext*', 'aCx')]
11918 self.methodDecls.insert(0,
11919 ClassMethod("WrapObject", "JSObject*",
11920 wrapArgs, virtual=descriptor.wrapperCache,
11921 breakAfterReturnDecl=" ",
11922 override=descriptor.wrapperCache,
11923 body=self.getWrapObjectBody()))
11924 if wantGetParent:
11925 self.methodDecls.insert(0,
11926 ClassMethod("GetParentObject",
11927 self.getGetParentObjectReturnType(),
11928 [], const=True,
11929 breakAfterReturnDecl=" ",
11930 body=self.getGetParentObjectBody()))
11932 # Invoke CGClass.__init__ in any subclasses afterwards to do the actual codegen.
11934 def getWrapObjectBody(self):
11935 return None
11937 def getGetParentObjectReturnType(self):
11938 return ("// TODO: return something sensible here, and change the return type\n"
11939 "%s*" % self.descriptor.nativeType.split('::')[-1])
11941 def getGetParentObjectBody(self):
11942 return None
11944 def deps(self):
11945 return self._deps
11948 class CGExampleClass(CGBindingImplClass):
11949 """
11950 Codegen for the actual example class implementation for this descriptor
11951 """
11952 def __init__(self, descriptor):
11953 CGBindingImplClass.__init__(self, descriptor,
11954 CGExampleMethod, CGExampleGetter, CGExampleSetter,
11955 wantGetParent=descriptor.wrapperCache)
11957 self.refcounted = descriptor.nativeOwnership == "refcounted"
11959 self.parentIface = descriptor.interface.parent
11960 if self.parentIface:
11961 self.parentDesc = descriptor.getDescriptor(
11962 self.parentIface.identifier.name)
11963 bases = [ClassBase(self.nativeLeafName(self.parentDesc))]
11964 else:
11965 bases = []
11966 if self.refcounted:
11967 bases.append(ClassBase("nsISupports /* Change nativeOwnership in the binding configuration if you don't want this */"))
11968 if descriptor.wrapperCache:
11969 bases.append(ClassBase("nsWrapperCache /* Change wrapperCache in the binding configuration if you don't want this */"))
11970 else:
11971 bases.append(ClassBase("NonRefcountedDOMObject"))
11973 if self.refcounted:
11974 if self.parentIface:
11975 extradeclarations = (
11976 "public:\n"
11977 " NS_DECL_ISUPPORTS_INHERITED\n"
11978 " NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(%s, %s)\n"
11979 "\n" % (self.nativeLeafName(descriptor),
11980 self.nativeLeafName(self.parentDesc)))
11981 else:
11982 extradeclarations = (
11983 "public:\n"
11984 " NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n"
11985 " NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(%s)\n"
11986 "\n" % self.nativeLeafName(descriptor))
11987 else:
11988 extradeclarations = ""
11990 if descriptor.interface.hasChildInterfaces():
11991 decorators = ""
11992 else:
11993 decorators = "MOZ_FINAL"
11995 CGClass.__init__(self, self.nativeLeafName(descriptor),
11996 bases=bases,
11997 constructors=[ClassConstructor([],
11998 visibility="public")],
11999 destructor=ClassDestructor(visibility="public"),
12000 methods=self.methodDecls,
12001 decorators=decorators,
12002 extradeclarations=extradeclarations)
12004 def define(self):
12005 # Just override CGClass and do our own thing
12006 if self.descriptor.wrapperCache:
12007 setDOMBinding = " SetIsDOMBinding();\n"
12008 else:
12009 setDOMBinding = ""
12010 if self.refcounted:
12011 ctordtor = dedent("""
12012 ${nativeType}::${nativeType}()
12013 {
12014 %s}
12016 ${nativeType}::~${nativeType}()
12017 {
12018 }
12019 """) % setDOMBinding
12020 else:
12021 ctordtor = dedent("""
12022 ${nativeType}::${nativeType}()
12023 {
12024 MOZ_COUNT_CTOR(${nativeType});
12025 }
12027 ${nativeType}::~${nativeType}()
12028 {
12029 MOZ_COUNT_DTOR(${nativeType});
12030 }
12031 """)
12033 if self.refcounted:
12034 if self.parentIface:
12035 ccImpl = dedent("""
12037 NS_IMPL_CYCLE_COLLECTION_INHERITED_0(${nativeType}, ${parentType})
12038 NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType})
12039 NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType})
12040 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})
12041 NS_INTERFACE_MAP_END_INHERITING(${parentType})
12043 """)
12044 else:
12045 ccImpl = dedent("""
12047 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(${nativeType})
12048 NS_IMPL_CYCLE_COLLECTING_ADDREF(${nativeType})
12049 NS_IMPL_CYCLE_COLLECTING_RELEASE(${nativeType})
12050 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})
12051 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
12052 NS_INTERFACE_MAP_ENTRY(nsISupports)
12053 NS_INTERFACE_MAP_END
12055 """)
12056 else:
12057 ccImpl = ""
12059 classImpl = ccImpl + ctordtor + "\n" + dedent("""
12060 JSObject*
12061 ${nativeType}::WrapObject(JSContext* aCx)
12062 {
12063 return ${ifaceName}Binding::Wrap(aCx, this);
12064 }
12066 """)
12067 return string.Template(classImpl).substitute(
12068 ifaceName=self.descriptor.name,
12069 nativeType=self.nativeLeafName(self.descriptor),
12070 parentType=self.nativeLeafName(self.parentDesc) if self.parentIface else "")
12072 @staticmethod
12073 def nativeLeafName(descriptor):
12074 return descriptor.nativeType.split('::')[-1]
12077 class CGExampleRoot(CGThing):
12078 """
12079 Root codegen class for example implementation generation. Instantiate the
12080 class and call declare or define to generate header or cpp code,
12081 respectively.
12082 """
12083 def __init__(self, config, interfaceName):
12084 # Let's assume we're not doing workers stuff
12085 descriptor = config.getDescriptor(interfaceName, False)
12087 self.root = CGWrapper(CGExampleClass(descriptor),
12088 pre="\n", post="\n")
12090 self.root = CGNamespace.build(["mozilla", "dom"], self.root)
12092 self.root = CGList([CGClassForwardDeclare("JSContext", isStruct=True),
12093 self.root], "\n")
12095 # Throw in our #includes
12096 self.root = CGHeaders([], [], [], [],
12097 ["nsWrapperCache.h",
12098 "nsCycleCollectionParticipant.h",
12099 "mozilla/Attributes.h",
12100 "mozilla/ErrorResult.h"],
12101 ["mozilla/dom/%s.h" % interfaceName,
12102 ("mozilla/dom/%s" %
12103 CGHeaders.getDeclarationFilename(descriptor.interface))], "", self.root)
12105 # And now some include guards
12106 self.root = CGIncludeGuard(interfaceName, self.root)
12108 # And our license block comes before everything else
12109 self.root = CGWrapper(self.root, pre=dedent("""
12110 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
12111 /* vim:set ts=2 sw=2 sts=2 et cindent: */
12112 /* This Source Code Form is subject to the terms of the Mozilla Public
12113 * License, v. 2.0. If a copy of the MPL was not distributed with this
12114 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
12116 """))
12118 def declare(self):
12119 return self.root.declare()
12121 def define(self):
12122 return self.root.define()
12125 def jsImplName(name):
12126 return name + "JSImpl"
12129 class CGJSImplMember(CGNativeMember):
12130 """
12131 Base class for generating code for the members of the implementation class
12132 for a JS-implemented WebIDL interface.
12133 """
12134 def __init__(self, descriptorProvider, member, name, signature,
12135 extendedAttrs, breakAfter=True, passJSBitsAsNeeded=True,
12136 visibility="public", jsObjectsArePtr=False,
12137 variadicIsSequence=False):
12138 CGNativeMember.__init__(self, descriptorProvider, member, name,
12139 signature, extendedAttrs, breakAfter=breakAfter,
12140 passJSBitsAsNeeded=passJSBitsAsNeeded,
12141 visibility=visibility,
12142 jsObjectsArePtr=jsObjectsArePtr,
12143 variadicIsSequence=variadicIsSequence)
12144 self.body = self.getImpl()
12146 def getArgs(self, returnType, argList):
12147 args = CGNativeMember.getArgs(self, returnType, argList)
12148 args.append(Argument("JSCompartment*", "aCompartment", "nullptr"))
12149 return args
12152 class CGJSImplMethod(CGJSImplMember):
12153 """
12154 Class for generating code for the methods for a JS-implemented WebIDL
12155 interface.
12156 """
12157 def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
12158 self.signature = signature
12159 self.descriptor = descriptor
12160 self.isConstructor = isConstructor
12161 CGJSImplMember.__init__(self, descriptor, method,
12162 CGSpecializedMethod.makeNativeName(descriptor,
12163 method),
12164 signature,
12165 descriptor.getExtendedAttributes(method),
12166 breakAfter=breakAfter,
12167 variadicIsSequence=True,
12168 passJSBitsAsNeeded=False)
12170 def getArgs(self, returnType, argList):
12171 if self.isConstructor:
12172 # Skip the JSCompartment bits for constructors; it's handled
12173 # manually in getImpl.
12174 return CGNativeMember.getArgs(self, returnType, argList)
12175 return CGJSImplMember.getArgs(self, returnType, argList)
12177 def getImpl(self):
12178 args = self.getArgs(self.signature[0], self.signature[1])
12179 if not self.isConstructor:
12180 return 'return mImpl->%s(%s);\n' % (self.name, ", ".join(arg.name for arg in args))
12182 assert self.descriptor.interface.isJSImplemented()
12183 if self.name != 'Constructor':
12184 raise TypeError("Named constructors are not supported for JS implemented WebIDL. See bug 851287.")
12185 if len(self.signature[1]) != 0:
12186 # The first two arguments to the constructor implementation are not
12187 # arguments to the WebIDL constructor, so don't pass them to __Init()
12188 assert args[0].argType == 'const GlobalObject&'
12189 assert args[1].argType == 'JSContext*'
12190 constructorArgs = [arg.name for arg in args[2:]]
12191 constructorArgs.append("js::GetObjectCompartment(scopeObj)")
12192 initCall = fill(
12193 """
12194 // Wrap the object before calling __Init so that __DOM_IMPL__ is available.
12195 nsCOMPtr<nsIGlobalObject> globalHolder = do_QueryInterface(window);
12196 JS::Rooted<JSObject*> scopeObj(cx, globalHolder->GetGlobalJSObject());
12197 MOZ_ASSERT(js::IsObjectInContextCompartment(scopeObj, cx));
12198 JS::Rooted<JS::Value> wrappedVal(cx);
12199 if (!WrapNewBindingObject(cx, impl, &wrappedVal)) {
12200 //XXX Assertion disabled for now, see bug 991271.
12201 MOZ_ASSERT(true || JS_IsExceptionPending(cx));
12202 aRv.Throw(NS_ERROR_UNEXPECTED);
12203 return nullptr;
12204 }
12205 // Initialize the object with the constructor arguments.
12206 impl->mImpl->__Init(${args});
12207 if (aRv.Failed()) {
12208 return nullptr;
12209 }
12210 """,
12211 args=", ".join(constructorArgs))
12212 else:
12213 initCall = ""
12214 return genConstructorBody(self.descriptor, initCall)
12217 def genConstructorBody(descriptor, initCall=""):
12218 return fill(
12219 """
12220 JS::Rooted<JSObject*> jsImplObj(cx);
12221 nsCOMPtr<nsPIDOMWindow> window =
12222 ConstructJSImplementation(cx, "${contractId}", global, &jsImplObj, aRv);
12223 if (aRv.Failed()) {
12224 return nullptr;
12225 }
12226 // Build the C++ implementation.
12227 nsRefPtr<${implClass}> impl = new ${implClass}(jsImplObj, window);
12228 $*{initCall}
12229 return impl.forget();
12230 """,
12231 contractId=descriptor.interface.getJSImplementation(),
12232 implClass=descriptor.name,
12233 initCall=initCall)
12236 # We're always fallible
12237 def callbackGetterName(attr, descriptor):
12238 return "Get" + MakeNativeName(
12239 descriptor.binaryNames.get(attr.identifier.name, attr.identifier.name))
12242 def callbackSetterName(attr, descriptor):
12243 return "Set" + MakeNativeName(
12244 descriptor.binaryNames.get(attr.identifier.name, attr.identifier.name))
12247 class CGJSImplGetter(CGJSImplMember):
12248 """
12249 Class for generating code for the getters of attributes for a JS-implemented
12250 WebIDL interface.
12251 """
12252 def __init__(self, descriptor, attr):
12253 CGJSImplMember.__init__(self, descriptor, attr,
12254 CGSpecializedGetter.makeNativeName(descriptor,
12255 attr),
12256 (attr.type, []),
12257 descriptor.getExtendedAttributes(attr,
12258 getter=True),
12259 passJSBitsAsNeeded=False)
12261 def getImpl(self):
12262 callbackArgs = [arg.name for arg in self.getArgs(self.member.type, [])]
12263 return 'return mImpl->%s(%s);\n' % (
12264 callbackGetterName(self.member, self.descriptorProvider),
12265 ", ".join(callbackArgs))
12268 class CGJSImplSetter(CGJSImplMember):
12269 """
12270 Class for generating code for the setters of attributes for a JS-implemented
12271 WebIDL interface.
12272 """
12273 def __init__(self, descriptor, attr):
12274 CGJSImplMember.__init__(self, descriptor, attr,
12275 CGSpecializedSetter.makeNativeName(descriptor,
12276 attr),
12277 (BuiltinTypes[IDLBuiltinType.Types.void],
12278 [FakeArgument(attr.type, attr)]),
12279 descriptor.getExtendedAttributes(attr,
12280 setter=True),
12281 passJSBitsAsNeeded=False)
12283 def getImpl(self):
12284 callbackArgs = [arg.name for arg in self.getArgs(BuiltinTypes[IDLBuiltinType.Types.void],
12285 [FakeArgument(self.member.type, self.member)])]
12286 return 'mImpl->%s(%s);\n' % (
12287 callbackSetterName(self.member, self.descriptorProvider),
12288 ", ".join(callbackArgs))
12291 class CGJSImplClass(CGBindingImplClass):
12292 def __init__(self, descriptor):
12293 CGBindingImplClass.__init__(self, descriptor, CGJSImplMethod, CGJSImplGetter, CGJSImplSetter)
12295 if descriptor.interface.parent:
12296 parentClass = descriptor.getDescriptor(
12297 descriptor.interface.parent.identifier.name).jsImplParent
12298 baseClasses = [ClassBase(parentClass)]
12299 isupportsDecl = "NS_DECL_ISUPPORTS_INHERITED\n"
12300 ccDecl = ("NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(%s, %s)\n" %
12301 (descriptor.name, parentClass))
12302 constructorBody = dedent("""
12303 // Make sure we're an nsWrapperCache already
12304 MOZ_ASSERT(static_cast<nsWrapperCache*>(this));
12305 // And that our ancestor has called SetIsDOMBinding()
12306 MOZ_ASSERT(IsDOMBinding());
12307 """)
12308 extradefinitions = fill(
12309 """
12310 NS_IMPL_CYCLE_COLLECTION_INHERITED(${ifaceName}, ${parentClass}, mImpl, mParent)
12311 NS_IMPL_ADDREF_INHERITED(${ifaceName}, ${parentClass})
12312 NS_IMPL_RELEASE_INHERITED(${ifaceName}, ${parentClass})
12313 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(${ifaceName})
12314 NS_INTERFACE_MAP_END_INHERITING(${parentClass})
12315 """,
12316 ifaceName=self.descriptor.name,
12317 parentClass=parentClass)
12318 else:
12319 baseClasses = [ClassBase("nsSupportsWeakReference"),
12320 ClassBase("nsWrapperCache")]
12321 isupportsDecl = "NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n"
12322 ccDecl = ("NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(%s)\n" %
12323 descriptor.name)
12324 constructorBody = "SetIsDOMBinding();\n"
12325 extradefinitions = fill(
12326 """
12327 NS_IMPL_CYCLE_COLLECTION_CLASS(${ifaceName})
12328 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(${ifaceName})
12329 NS_IMPL_CYCLE_COLLECTION_UNLINK(mImpl)
12330 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
12331 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
12332 tmp->ClearWeakReferences();
12333 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
12334 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(${ifaceName})
12335 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImpl)
12336 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
12337 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
12338 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
12339 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(${ifaceName})
12340 NS_IMPL_CYCLE_COLLECTING_ADDREF(${ifaceName})
12341 NS_IMPL_CYCLE_COLLECTING_RELEASE(${ifaceName})
12342 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${ifaceName})
12343 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
12344 NS_INTERFACE_MAP_ENTRY(nsISupports)
12345 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
12346 NS_INTERFACE_MAP_END
12347 """,
12348 ifaceName=self.descriptor.name)
12350 extradeclarations = fill(
12351 """
12352 public:
12353 $*{isupportsDecl}
12354 $*{ccDecl}
12356 private:
12357 nsRefPtr<${jsImplName}> mImpl;
12358 nsCOMPtr<nsISupports> mParent;
12360 """,
12361 isupportsDecl=isupportsDecl,
12362 ccDecl=ccDecl,
12363 jsImplName=jsImplName(descriptor.name))
12365 if descriptor.interface.hasChildInterfaces():
12366 decorators = ""
12367 # We need a public virtual destructor our subclasses can use
12368 destructor = ClassDestructor(virtual=True, visibility="public")
12369 else:
12370 decorators = "MOZ_FINAL"
12371 destructor = None
12373 baseConstructors = [
12374 ("mImpl(new %s(aJSImplObject, /* aIncumbentGlobal = */ nullptr))" %
12375 jsImplName(descriptor.name)),
12376 "mParent(aParent)"]
12377 parentInterface = descriptor.interface.parent
12378 while parentInterface:
12379 if parentInterface.isJSImplemented():
12380 baseConstructors.insert(
12381 0, "%s(aJSImplObject, aParent)" % parentClass)
12382 break
12383 parentInterface = parentInterface.parent
12384 if not parentInterface and descriptor.interface.parent:
12385 # We only have C++ ancestors, so only pass along the window
12386 baseConstructors.insert(0,
12387 "%s(aParent)" % parentClass)
12389 constructor = ClassConstructor(
12390 [Argument("JS::Handle<JSObject*>", "aJSImplObject"),
12391 Argument("nsPIDOMWindow*", "aParent")],
12392 visibility="public",
12393 baseConstructors=baseConstructors,
12394 body=constructorBody)
12396 self.methodDecls.append(
12397 ClassMethod("_Create",
12398 "bool",
12399 [Argument("JSContext*", "cx"),
12400 Argument("unsigned", "argc"),
12401 Argument("JS::Value*", "vp")],
12402 static=True,
12403 body=self.getCreateFromExistingBody()))
12405 CGClass.__init__(self, descriptor.name,
12406 bases=baseClasses,
12407 constructors=[constructor],
12408 destructor=destructor,
12409 methods=self.methodDecls,
12410 decorators=decorators,
12411 extradeclarations=extradeclarations,
12412 extradefinitions=extradefinitions)
12414 def getWrapObjectBody(self):
12415 return fill(
12416 """
12417 JS::Rooted<JSObject*> obj(aCx, ${name}Binding::Wrap(aCx, this));
12418 if (!obj) {
12419 return nullptr;
12420 }
12422 // Now define it on our chrome object
12423 JSAutoCompartment ac(aCx, mImpl->Callback());
12424 if (!JS_WrapObject(aCx, &obj)) {
12425 return nullptr;
12426 }
12427 if (!JS_DefineProperty(aCx, mImpl->Callback(), "__DOM_IMPL__", obj, 0)) {
12428 return nullptr;
12429 }
12430 return obj;
12431 """,
12432 name=self.descriptor.name)
12434 def getGetParentObjectReturnType(self):
12435 return "nsISupports*"
12437 def getGetParentObjectBody(self):
12438 return "return mParent;\n"
12440 def getCreateFromExistingBody(self):
12441 # XXXbz we could try to get parts of this (e.g. the argument
12442 # conversions) auto-generated by somehow creating an IDLMethod and
12443 # adding it to our interface, but we'd still need to special-case the
12444 # implementation slightly to have it not try to forward to the JS
12445 # object...
12446 return fill(
12447 """
12448 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
12449 if (args.length() < 2) {
12450 return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${ifaceName}._create");
12451 }
12452 if (!args[0].isObject()) {
12453 return ThrowErrorMessage(cx, MSG_NOT_OBJECT, "Argument 1 of ${ifaceName}._create");
12454 }
12455 if (!args[1].isObject()) {
12456 return ThrowErrorMessage(cx, MSG_NOT_OBJECT, "Argument 2 of ${ifaceName}._create");
12457 }
12459 // GlobalObject will go through wrappers as needed for us, and
12460 // is simpler than the right UnwrapArg incantation.
12461 GlobalObject global(cx, &args[0].toObject());
12462 if (global.Failed()) {
12463 return false;
12464 }
12465 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(global.GetAsSupports());
12466 if (!window) {
12467 return ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "Argument 1 of ${ifaceName}._create", "Window");
12468 }
12469 JS::Rooted<JSObject*> arg(cx, &args[1].toObject());
12470 nsRefPtr<${implName}> impl = new ${implName}(arg, window);
12471 MOZ_ASSERT(js::IsObjectInContextCompartment(arg, cx));
12472 return WrapNewBindingObject(cx, impl, args.rval());
12473 """,
12474 ifaceName=self.descriptor.interface.identifier.name,
12475 implName=self.descriptor.name)
12478 def isJSImplementedDescriptor(descriptorProvider):
12479 return (isinstance(descriptorProvider, Descriptor) and
12480 descriptorProvider.interface.isJSImplemented())
12483 class CGCallback(CGClass):
12484 def __init__(self, idlObject, descriptorProvider, baseName, methods,
12485 getters=[], setters=[]):
12486 self.baseName = baseName
12487 self._deps = idlObject.getDeps()
12488 self.idlObject = idlObject
12489 name = idlObject.identifier.name
12490 if isJSImplementedDescriptor(descriptorProvider):
12491 name = jsImplName(name)
12492 # For our public methods that needThisHandling we want most of the
12493 # same args and the same return type as what CallbackMember
12494 # generates. So we want to take advantage of all its
12495 # CGNativeMember infrastructure, but that infrastructure can't deal
12496 # with templates and most especially template arguments. So just
12497 # cheat and have CallbackMember compute all those things for us.
12498 realMethods = []
12499 for method in methods:
12500 if not method.needThisHandling:
12501 realMethods.append(method)
12502 else:
12503 realMethods.extend(self.getMethodImpls(method))
12504 realMethods.append(
12505 ClassMethod("operator==", "bool",
12506 [Argument("const %s&" % name, "aOther")],
12507 inline=True, bodyInHeader=True,
12508 const=True,
12509 body=("return %s::operator==(aOther);\n" % baseName)))
12510 CGClass.__init__(self, name,
12511 bases=[ClassBase(baseName)],
12512 constructors=self.getConstructors(),
12513 methods=realMethods+getters+setters)
12515 def getConstructors(self):
12516 if (not self.idlObject.isInterface() and
12517 not self.idlObject._treatNonObjectAsNull):
12518 body = "MOZ_ASSERT(JS_ObjectIsCallable(nullptr, mCallback));\n"
12519 else:
12520 # Not much we can assert about it, other than not being null, and
12521 # CallbackObject does that already.
12522 body = ""
12523 return [ClassConstructor(
12524 [Argument("JS::Handle<JSObject*>", "aCallback"),
12525 Argument("nsIGlobalObject*", "aIncumbentGlobal")],
12526 bodyInHeader=True,
12527 visibility="public",
12528 explicit=True,
12529 baseConstructors=[
12530 "%s(aCallback, aIncumbentGlobal)" % self.baseName,
12531 ],
12532 body=body)]
12534 def getMethodImpls(self, method):
12535 assert method.needThisHandling
12536 args = list(method.args)
12537 # Strip out the JSContext*/JSObject* args
12538 # that got added.
12539 assert args[0].name == "cx" and args[0].argType == "JSContext*"
12540 assert args[1].name == "aThisVal" and args[1].argType == "JS::Handle<JS::Value>"
12541 args = args[2:]
12542 # Record the names of all the arguments, so we can use them when we call
12543 # the private method.
12544 argnames = [arg.name for arg in args]
12545 argnamesWithThis = ["s.GetContext()", "thisValJS"] + argnames
12546 argnamesWithoutThis = ["s.GetContext()", "JS::UndefinedHandleValue"] + argnames
12547 # Now that we've recorded the argnames for our call to our private
12548 # method, insert our optional argument for deciding whether the
12549 # CallSetup should re-throw exceptions on aRv.
12550 args.append(Argument("ExceptionHandling", "aExceptionHandling",
12551 "eReportExceptions"))
12552 # And now insert our template argument.
12553 argsWithoutThis = list(args)
12554 args.insert(0, Argument("const T&", "thisObjPtr"))
12555 errorReturn = method.getDefaultRetval()
12557 setupCall = fill(
12558 """
12559 CallSetup s(this, aRv, aExceptionHandling);
12560 if (!s.GetContext()) {
12561 aRv.Throw(NS_ERROR_UNEXPECTED);
12562 return${errorReturn};
12563 }
12564 """,
12565 errorReturn=errorReturn)
12567 bodyWithThis = fill(
12568 """
12569 $*{setupCall}
12570 JS::Rooted<JSObject*> thisObjJS(s.GetContext(),
12571 WrapCallThisObject(s.GetContext(), thisObjPtr));
12572 if (!thisObjJS) {
12573 aRv.Throw(NS_ERROR_FAILURE);
12574 return${errorReturn};
12575 }
12576 JS::Rooted<JS::Value> thisValJS(s.GetContext(),
12577 JS::ObjectValue(*thisObjJS));
12578 return ${methodName}(${callArgs});
12579 """,
12580 setupCall=setupCall,
12581 errorReturn=errorReturn,
12582 methodName=method.name,
12583 callArgs=", ".join(argnamesWithThis))
12584 bodyWithoutThis = fill(
12585 """
12586 $*{setupCall}
12587 return ${methodName}(${callArgs});
12588 """,
12589 setupCall=setupCall,
12590 errorReturn=errorReturn,
12591 methodName=method.name,
12592 callArgs=", ".join(argnamesWithoutThis))
12594 return [ClassMethod(method.name, method.returnType, args,
12595 bodyInHeader=True,
12596 templateArgs=["typename T"],
12597 body=bodyWithThis),
12598 ClassMethod(method.name, method.returnType, argsWithoutThis,
12599 bodyInHeader=True,
12600 body=bodyWithoutThis),
12601 method]
12603 def deps(self):
12604 return self._deps
12607 class CGCallbackFunction(CGCallback):
12608 def __init__(self, callback, descriptorProvider):
12609 self.callback = callback
12610 CGCallback.__init__(self, callback, descriptorProvider,
12611 "CallbackFunction",
12612 methods=[CallCallback(callback, descriptorProvider)])
12614 def getConstructors(self):
12615 return CGCallback.getConstructors(self) + [
12616 ClassConstructor(
12617 [Argument("CallbackFunction*", "aOther")],
12618 bodyInHeader=True,
12619 visibility="public",
12620 explicit=True,
12621 baseConstructors=["CallbackFunction(aOther)"])]
12624 class CGCallbackInterface(CGCallback):
12625 def __init__(self, descriptor):
12626 iface = descriptor.interface
12627 attrs = [m for m in iface.members if m.isAttr() and not m.isStatic()]
12628 getters = [CallbackGetter(a, descriptor) for a in attrs]
12629 setters = [CallbackSetter(a, descriptor) for a in attrs
12630 if not a.readonly]
12631 methods = [m for m in iface.members
12632 if m.isMethod() and not m.isStatic() and not m.isIdentifierLess()]
12633 methods = [CallbackOperation(m, sig, descriptor) for m in methods
12634 for sig in m.signatures()]
12635 if iface.isJSImplemented() and iface.ctor():
12636 sigs = descriptor.interface.ctor().signatures()
12637 if len(sigs) != 1:
12638 raise TypeError("We only handle one constructor. See bug 869268.")
12639 methods.append(CGJSImplInitOperation(sigs[0], descriptor))
12640 CGCallback.__init__(self, iface, descriptor, "CallbackInterface",
12641 methods, getters=getters, setters=setters)
12644 class FakeMember():
12645 def __init__(self):
12646 self.treatNullAs = "Default"
12648 def isStatic(self):
12649 return False
12651 def isAttr(self):
12652 return False
12654 def isMethod(self):
12655 return False
12657 def getExtendedAttribute(self, name):
12658 # Claim to be a [NewObject] so we can avoid the "mark this
12659 # resultNotAddRefed" comments CGNativeMember codegen would
12660 # otherwise stick in.
12661 if name == "NewObject":
12662 return True
12663 return None
12666 class CallbackMember(CGNativeMember):
12667 def __init__(self, sig, name, descriptorProvider, needThisHandling, rethrowContentException=False):
12668 """
12669 needThisHandling is True if we need to be able to accept a specified
12670 thisObj, False otherwise.
12671 """
12672 assert not rethrowContentException or not needThisHandling
12674 self.retvalType = sig[0]
12675 self.originalSig = sig
12676 args = sig[1]
12677 self.argCount = len(args)
12678 if self.argCount > 0:
12679 # Check for variadic arguments
12680 lastArg = args[self.argCount-1]
12681 if lastArg.variadic:
12682 self.argCountStr = ("(%d - 1) + %s.Length()" %
12683 (self.argCount, lastArg.identifier.name))
12684 else:
12685 self.argCountStr = "%d" % self.argCount
12686 self.needThisHandling = needThisHandling
12687 # If needThisHandling, we generate ourselves as private and the caller
12688 # will handle generating public versions that handle the "this" stuff.
12689 visibility = "private" if needThisHandling else "public"
12690 self.rethrowContentException = rethrowContentException
12691 # We don't care, for callback codegen, whether our original member was
12692 # a method or attribute or whatnot. Just always pass FakeMember()
12693 # here.
12694 CGNativeMember.__init__(self, descriptorProvider, FakeMember(),
12695 name, (self.retvalType, args),
12696 extendedAttrs={},
12697 passJSBitsAsNeeded=False,
12698 visibility=visibility,
12699 jsObjectsArePtr=True)
12700 # We have to do all the generation of our body now, because
12701 # the caller relies on us throwing if we can't manage it.
12702 self.exceptionCode = ("aRv.Throw(NS_ERROR_UNEXPECTED);\n"
12703 "return%s;\n" % self.getDefaultRetval())
12704 self.body = self.getImpl()
12706 def getImpl(self):
12707 setupCall = self.getCallSetup()
12708 declRval = self.getRvalDecl()
12709 if self.argCount > 0:
12710 argvDecl = fill(
12711 """
12712 JS::AutoValueVector argv(cx);
12713 if (!argv.resize(${argCount})) {
12714 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
12715 return${errorReturn};
12716 }
12717 """,
12718 argCount=self.argCountStr,
12719 errorReturn=self.getDefaultRetval())
12720 else:
12721 # Avoid weird 0-sized arrays
12722 argvDecl = ""
12723 convertArgs = self.getArgConversions()
12724 doCall = self.getCall()
12725 returnResult = self.getResultConversion()
12727 return setupCall + declRval + argvDecl + convertArgs + doCall + returnResult
12729 def getResultConversion(self):
12730 replacements = {
12731 "val": "rval",
12732 "mutableVal": "&rval",
12733 "holderName": "rvalHolder",
12734 "declName": "rvalDecl",
12735 # We actually want to pass in a null scope object here, because
12736 # wrapping things into our current compartment (that of mCallback)
12737 # is what we want.
12738 "obj": "nullptr"
12739 }
12741 if isJSImplementedDescriptor(self.descriptorProvider):
12742 isCallbackReturnValue = "JSImpl"
12743 else:
12744 isCallbackReturnValue = "Callback"
12745 sourceDescription = "return value of %s" % self.getPrettyName()
12746 convertType = instantiateJSToNativeConversion(
12747 getJSToNativeConversionInfo(self.retvalType,
12748 self.descriptorProvider,
12749 exceptionCode=self.exceptionCode,
12750 isCallbackReturnValue=isCallbackReturnValue,
12751 sourceDescription=sourceDescription),
12752 replacements)
12753 assignRetval = string.Template(
12754 self.getRetvalInfo(self.retvalType,
12755 False)[2]).substitute(replacements)
12756 type = convertType.define()
12757 if type == "":
12758 type = "\n" # BOGUS extra blank line
12759 if assignRetval == "":
12760 assignRetval = "\n" # BOGUS extra blank line
12761 return type + assignRetval
12763 def getArgConversions(self):
12764 # Just reget the arglist from self.originalSig, because our superclasses
12765 # just have way to many members they like to clobber, so I can't find a
12766 # safe member name to store it in.
12767 argConversions = [self.getArgConversion(i, arg)
12768 for i, arg in enumerate(self.originalSig[1])]
12769 if not argConversions:
12770 return "\n\n" # BOGUS extra blank line
12772 # Do them back to front, so our argc modifications will work
12773 # correctly, because we examine trailing arguments first.
12774 argConversions.reverse()
12775 # Wrap each one in a scope so that any locals it has don't leak out, and
12776 # also so that we can just "break;" for our successCode.
12777 argConversions = [CGWrapper(CGIndenter(CGGeneric(c)),
12778 pre="do {\n",
12779 post="} while (0);\n")
12780 for c in argConversions]
12781 if self.argCount > 0:
12782 argConversions.insert(0, self.getArgcDecl())
12783 # And slap them together.
12784 return CGList(argConversions, "\n").define() + "\n"
12786 def getArgConversion(self, i, arg):
12787 argval = arg.identifier.name
12789 if arg.variadic:
12790 argval = argval + "[idx]"
12791 jsvalIndex = "%d + idx" % i
12792 else:
12793 jsvalIndex = "%d" % i
12794 if arg.optional and not arg.defaultValue:
12795 argval += ".Value()"
12796 if arg.type.isDOMString():
12797 # XPConnect string-to-JS conversion wants to mutate the string. So
12798 # let's give it a string it can mutate
12799 # XXXbz if we try to do a sequence of strings, this will kinda fail.
12800 result = "mutableStr"
12801 prepend = "nsString mutableStr(%s);\n" % argval
12802 else:
12803 result = argval
12804 prepend = ""
12806 try:
12807 conversion = prepend + wrapForType(
12808 arg.type, self.descriptorProvider,
12809 {
12810 'result': result,
12811 'successCode': "continue;\n" if arg.variadic else "break;\n",
12812 'jsvalRef': "argv.handleAt(%s)" % jsvalIndex,
12813 'jsvalHandle': "argv.handleAt(%s)" % jsvalIndex,
12814 # XXXbz we don't have anything better to use for 'obj',
12815 # really... It's OK to use CallbackPreserveColor because
12816 # CallSetup already handled the unmark-gray bits for us.
12817 'obj': 'CallbackPreserveColor()',
12818 'returnsNewObject': False,
12819 'exceptionCode': self.exceptionCode
12820 })
12821 except MethodNotNewObjectError as err:
12822 raise TypeError("%s being passed as an argument to %s but is not "
12823 "wrapper cached, so can't be reliably converted to "
12824 "a JS object." %
12825 (err.typename, self.getPrettyName()))
12826 if arg.variadic:
12827 conversion = fill(
12828 """
12829 for (uint32_t idx = 0; idx < ${arg}.Length(); ++idx) {
12830 $*{conversion}
12831 }
12832 break;
12833 """,
12834 arg=arg.identifier.name,
12835 conversion=conversion)
12836 elif arg.optional and not arg.defaultValue:
12837 conversion = fill(
12838 """
12839 if (${argName}.WasPassed()) {
12840 $*{conversion}
12841 } else if (argc == ${iPlus1}) {
12842 // This is our current trailing argument; reduce argc
12843 --argc;
12844 } else {
12845 argv[${i}] = JS::UndefinedValue();
12846 }
12847 """,
12848 argName=arg.identifier.name,
12849 conversion=conversion,
12850 iPlus1=i + 1,
12851 i=i)
12852 return conversion
12854 def getDefaultRetval(self):
12855 default = self.getRetvalInfo(self.retvalType, False)[1]
12856 if len(default) != 0:
12857 default = " " + default
12858 return default
12860 def getArgs(self, returnType, argList):
12861 args = CGNativeMember.getArgs(self, returnType, argList)
12862 if not self.needThisHandling:
12863 # Since we don't need this handling, we're the actual method that
12864 # will be called, so we need an aRethrowExceptions argument.
12865 if self.rethrowContentException:
12866 args.append(Argument("JSCompartment*", "aCompartment", "nullptr"))
12867 else:
12868 args.append(Argument("ExceptionHandling", "aExceptionHandling",
12869 "eReportExceptions"))
12870 return args
12871 # We want to allow the caller to pass in a "this" value, as
12872 # well as a JSContext.
12873 return [Argument("JSContext*", "cx"),
12874 Argument("JS::Handle<JS::Value>", "aThisVal")] + args
12876 def getCallSetup(self):
12877 if self.needThisHandling:
12878 # It's been done for us already
12879 return ""
12880 callSetup = "CallSetup s(this, aRv"
12881 if self.rethrowContentException:
12882 # getArgs doesn't add the aExceptionHandling argument but does add
12883 # aCompartment for us.
12884 callSetup += ", eRethrowContentExceptions, aCompartment, /* aIsJSImplementedWebIDL = */ "
12885 callSetup += toStringBool(isJSImplementedDescriptor(self.descriptorProvider))
12886 else:
12887 callSetup += ", aExceptionHandling"
12888 callSetup += ");\n"
12889 return fill(
12890 """
12891 $*{callSetup}
12892 JSContext* cx = s.GetContext();
12893 if (!cx) {
12894 aRv.Throw(NS_ERROR_UNEXPECTED);
12895 return${errorReturn};
12896 }
12897 """,
12898 callSetup=callSetup,
12899 errorReturn=self.getDefaultRetval())
12901 def getArgcDecl(self):
12902 return CGGeneric("unsigned argc = %s;\n" % self.argCountStr)
12904 @staticmethod
12905 def ensureASCIIName(idlObject):
12906 type = "attribute" if idlObject.isAttr() else "operation"
12907 if re.match("[^\x20-\x7E]", idlObject.identifier.name):
12908 raise SyntaxError('Callback %s name "%s" contains non-ASCII '
12909 "characters. We can't handle that. %s" %
12910 (type, idlObject.identifier.name,
12911 idlObject.location))
12912 if re.match('"', idlObject.identifier.name):
12913 raise SyntaxError("Callback %s name '%s' contains "
12914 "double-quote character. We can't handle "
12915 "that. %s" %
12916 (type, idlObject.identifier.name,
12917 idlObject.location))
12920 class CallbackMethod(CallbackMember):
12921 def __init__(self, sig, name, descriptorProvider, needThisHandling,
12922 rethrowContentException=False):
12923 CallbackMember.__init__(self, sig, name, descriptorProvider,
12924 needThisHandling, rethrowContentException)
12926 def getRvalDecl(self):
12927 return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
12929 def getCall(self):
12930 if self.argCount > 0:
12931 args = "JS::HandleValueArray::subarray(argv, 0, argc)"
12932 else:
12933 args = "JS::HandleValueArray::empty()"
12935 return fill(
12936 """
12937 $*{declCallable}
12938 $*{declThis}
12939 if (${callGuard}!JS::Call(cx, ${thisVal}, callable,
12940 ${args}, &rval)) {
12941 aRv.Throw(NS_ERROR_UNEXPECTED);
12942 return${errorReturn};
12943 }
12944 """,
12945 declCallable=self.getCallableDecl(),
12946 declThis=self.getThisDecl(),
12947 callGuard=self.getCallGuard(),
12948 thisVal=self.getThisVal(),
12949 args=args,
12950 errorReturn=self.getDefaultRetval())
12953 class CallCallback(CallbackMethod):
12954 def __init__(self, callback, descriptorProvider):
12955 self.callback = callback
12956 CallbackMethod.__init__(self, callback.signatures()[0], "Call",
12957 descriptorProvider, needThisHandling=True)
12959 def getThisDecl(self):
12960 return ""
12962 def getThisVal(self):
12963 return "aThisVal"
12965 def getCallableDecl(self):
12966 return "JS::Rooted<JS::Value> callable(cx, JS::ObjectValue(*mCallback));\n"
12968 def getPrettyName(self):
12969 return self.callback.identifier.name
12971 def getCallGuard(self):
12972 if self.callback._treatNonObjectAsNull:
12973 return "JS_ObjectIsCallable(cx, mCallback) && "
12974 return ""
12977 class CallbackOperationBase(CallbackMethod):
12978 """
12979 Common class for implementing various callback operations.
12980 """
12981 def __init__(self, signature, jsName, nativeName, descriptor, singleOperation, rethrowContentException=False):
12982 self.singleOperation = singleOperation
12983 self.methodName = descriptor.binaryNames.get(jsName, jsName)
12984 CallbackMethod.__init__(self, signature, nativeName, descriptor, singleOperation, rethrowContentException)
12986 def getThisDecl(self):
12987 if not self.singleOperation:
12988 return "JS::Rooted<JS::Value> thisValue(cx, JS::ObjectValue(*mCallback));\n"
12989 # This relies on getCallableDecl declaring a boolean
12990 # isCallable in the case when we're a single-operation
12991 # interface.
12992 return dedent("""
12993 JS::Rooted<JS::Value> thisValue(cx, isCallable ? aThisVal.get()
12994 : JS::ObjectValue(*mCallback));
12995 """)
12997 def getThisVal(self):
12998 return "thisValue"
13000 def getCallableDecl(self):
13001 getCallableFromProp = fill(
13002 """
13003 if (!GetCallableProperty(cx, "${methodName}", &callable)) {
13004 aRv.Throw(NS_ERROR_UNEXPECTED);
13005 return${errorReturn};
13006 }
13007 """,
13008 methodName=self.methodName,
13009 errorReturn=self.getDefaultRetval())
13010 if not self.singleOperation:
13011 return 'JS::Rooted<JS::Value> callable(cx);\n' + getCallableFromProp
13012 return fill(
13013 """
13014 bool isCallable = JS_ObjectIsCallable(cx, mCallback);
13015 JS::Rooted<JS::Value> callable(cx);
13016 if (isCallable) {
13017 callable = JS::ObjectValue(*mCallback);
13018 } else {
13019 $*{getCallableFromProp}
13020 }
13021 """,
13022 getCallableFromProp=getCallableFromProp)
13024 def getCallGuard(self):
13025 return ""
13028 class CallbackOperation(CallbackOperationBase):
13029 """
13030 Codegen actual WebIDL operations on callback interfaces.
13031 """
13032 def __init__(self, method, signature, descriptor):
13033 self.ensureASCIIName(method)
13034 self.method = method
13035 jsName = method.identifier.name
13036 CallbackOperationBase.__init__(self, signature,
13037 jsName,
13038 MakeNativeName(descriptor.binaryNames.get(jsName, jsName)),
13039 descriptor, descriptor.interface.isSingleOperationInterface(),
13040 rethrowContentException=descriptor.interface.isJSImplemented())
13042 def getPrettyName(self):
13043 return "%s.%s" % (self.descriptorProvider.interface.identifier.name,
13044 self.method.identifier.name)
13047 class CallbackAccessor(CallbackMember):
13048 """
13049 Shared superclass for CallbackGetter and CallbackSetter.
13050 """
13051 def __init__(self, attr, sig, name, descriptor):
13052 self.ensureASCIIName(attr)
13053 self.attrName = attr.identifier.name
13054 CallbackMember.__init__(self, sig, name, descriptor,
13055 needThisHandling=False,
13056 rethrowContentException=descriptor.interface.isJSImplemented())
13058 def getPrettyName(self):
13059 return "%s.%s" % (self.descriptorProvider.interface.identifier.name,
13060 self.attrName)
13063 class CallbackGetter(CallbackAccessor):
13064 def __init__(self, attr, descriptor):
13065 CallbackAccessor.__init__(self, attr,
13066 (attr.type, []),
13067 callbackGetterName(attr, descriptor),
13068 descriptor)
13070 def getRvalDecl(self):
13071 return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
13073 def getCall(self):
13074 return fill(
13075 """
13076 JS::Rooted<JSObject *> callback(cx, mCallback);
13077 if (!JS_GetProperty(cx, callback, "${attrName}", &rval)) {
13078 aRv.Throw(NS_ERROR_UNEXPECTED);
13079 return${errorReturn};
13080 }
13081 """,
13082 attrName=self.descriptorProvider.binaryNames.get(self.attrName,
13083 self.attrName),
13084 errorReturn=self.getDefaultRetval())
13087 class CallbackSetter(CallbackAccessor):
13088 def __init__(self, attr, descriptor):
13089 CallbackAccessor.__init__(self, attr,
13090 (BuiltinTypes[IDLBuiltinType.Types.void],
13091 [FakeArgument(attr.type, attr)]),
13092 callbackSetterName(attr, descriptor),
13093 descriptor)
13095 def getRvalDecl(self):
13096 # We don't need an rval
13097 return ""
13099 def getCall(self):
13100 return fill(
13101 """
13102 MOZ_ASSERT(argv.length() == 1);
13103 if (!JS_SetProperty(cx, CallbackPreserveColor(), "${attrName}", argv.handleAt(0))) {
13104 aRv.Throw(NS_ERROR_UNEXPECTED);
13105 return${errorReturn};
13106 }
13107 """,
13108 attrName=self.descriptorProvider.binaryNames.get(self.attrName,
13109 self.attrName),
13110 errorReturn=self.getDefaultRetval())
13112 def getArgcDecl(self):
13113 return None
13116 class CGJSImplInitOperation(CallbackOperationBase):
13117 """
13118 Codegen the __Init() method used to pass along constructor arguments for JS-implemented WebIDL.
13119 """
13120 def __init__(self, sig, descriptor):
13121 assert sig in descriptor.interface.ctor().signatures()
13122 CallbackOperationBase.__init__(self, (BuiltinTypes[IDLBuiltinType.Types.void], sig[1]),
13123 "__init", "__Init", descriptor, False, True)
13125 def getPrettyName(self):
13126 return "__init"
13129 class GlobalGenRoots():
13130 """
13131 Roots for global codegen.
13133 To generate code, call the method associated with the target, and then
13134 call the appropriate define/declare method.
13135 """
13137 @staticmethod
13138 def GeneratedAtomList(config):
13139 # Atom enum
13140 dictionaries = config.dictionaries
13142 structs = []
13144 for dict in dictionaries:
13145 dictMembers = dict.members
13146 if len(dictMembers) == 0:
13147 continue
13149 classMembers = [ClassMember(CGDictionary.makeIdName(m.identifier.name),
13150 "InternedStringId",
13151 visibility="public") for m in dictMembers]
13153 structName = dict.identifier.name + "Atoms"
13154 structs.append((structName,
13155 CGWrapper(CGClass(structName,
13156 bases=None,
13157 isStruct=True,
13158 members=classMembers), post='\n')))
13160 structs.sort()
13161 generatedStructs = [struct for structName, struct in structs]
13162 structNames = [structName for structName, struct in structs]
13164 mainStruct = CGWrapper(CGClass("PerThreadAtomCache",
13165 bases=[ClassBase(structName) for structName in structNames],
13166 isStruct=True),
13167 post='\n')
13169 structs = CGList(generatedStructs + [mainStruct])
13171 # Wrap all of that in our namespaces.
13172 curr = CGNamespace.build(['mozilla', 'dom'],
13173 CGWrapper(structs, pre='\n'))
13174 curr = CGWrapper(curr, post='\n')
13176 # Add include statement for InternedStringId.
13177 declareIncludes = ['mozilla/dom/BindingUtils.h']
13178 curr = CGHeaders([], [], [], [], declareIncludes, [], 'GeneratedAtomList',
13179 curr)
13181 # Add include guards.
13182 curr = CGIncludeGuard('GeneratedAtomList', curr)
13184 # Add the auto-generated comment.
13185 curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
13187 # Done.
13188 return curr
13190 @staticmethod
13191 def PrototypeList(config):
13193 # Prototype ID enum.
13194 descriptorsWithPrototype = config.getDescriptors(hasInterfacePrototypeObject=True)
13195 protos = [d.name for d in descriptorsWithPrototype]
13196 idEnum = CGNamespacedEnum('id', 'ID', ['_ID_Start'] + protos,
13197 [0, '_ID_Start'])
13198 idEnum = CGList([idEnum])
13200 # This is only used by DOM worker code, once there are no more consumers
13201 # of INTERFACE_CHAIN_* this code should be removed.
13202 def ifaceChainMacro(ifaceCount):
13203 supplied = [CGGeneric(declare="_iface_" + str(i + 1)) for i in range(ifaceCount)]
13204 remaining = [CGGeneric(declare="prototypes::id::_ID_Count")] * (config.maxProtoChainLength - ifaceCount)
13205 macro = CGWrapper(CGList(supplied, ", "),
13206 pre="#define INTERFACE_CHAIN_" + str(ifaceCount) + "(",
13207 post=") \\\n",
13208 declareOnly=True)
13209 macroContent = CGIndenter(CGList(supplied + remaining, ", \\\n"))
13210 macroContent = CGIndenter(CGWrapper(macroContent, pre="{ \\\n",
13211 post=" \\\n}",
13212 declareOnly=True))
13213 return CGWrapper(CGList([macro, macroContent]), post="\n\n",
13214 declareOnly=True)
13216 idEnum.append(ifaceChainMacro(1))
13218 def fieldSizeAssert(amount, jitInfoField, message):
13219 maxFieldValue = "(uint64_t(1) << (sizeof(((JSJitInfo*)nullptr)->%s) * 8))" % jitInfoField
13220 return CGGeneric(declare="static_assert(%s < %s, \"%s\");\n\n"
13221 % (amount, maxFieldValue, message))
13223 idEnum.append(fieldSizeAssert("id::_ID_Count", "protoID",
13224 "Too many prototypes!"))
13226 # Wrap all of that in our namespaces.
13227 idEnum = CGNamespace.build(['mozilla', 'dom', 'prototypes'],
13228 CGWrapper(idEnum, pre='\n'))
13229 idEnum = CGWrapper(idEnum, post='\n')
13231 curr = CGList([CGGeneric(define="#include <stdint.h>\n\n"),
13232 idEnum])
13234 # Let things know the maximum length of the prototype chain.
13235 maxMacroName = "MAX_PROTOTYPE_CHAIN_LENGTH"
13236 maxMacro = CGGeneric(declare="#define " + maxMacroName + " " + str(config.maxProtoChainLength))
13237 curr.append(CGWrapper(maxMacro, post='\n\n'))
13238 curr.append(fieldSizeAssert(maxMacroName, "depth",
13239 "Some inheritance chain is too long!"))
13241 # Constructor ID enum.
13242 constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True)]
13243 idEnum = CGNamespacedEnum('id', 'ID', ['_ID_Start'] + constructors,
13244 ['prototypes::id::_ID_Count', '_ID_Start'])
13246 # Wrap all of that in our namespaces.
13247 idEnum = CGNamespace.build(['mozilla', 'dom', 'constructors'],
13248 CGWrapper(idEnum, pre='\n'))
13249 idEnum = CGWrapper(idEnum, post='\n')
13251 curr.append(idEnum)
13253 traitsDecls = [CGGeneric(declare=dedent("""
13254 template <prototypes::ID PrototypeID>
13255 struct PrototypeTraits;
13256 """))]
13257 traitsDecls.extend(CGPrototypeTraitsClass(d) for d in descriptorsWithPrototype)
13259 ifaceNamesWithProto = [d.interface.identifier.name
13260 for d in descriptorsWithPrototype]
13261 traitsDecls.append(CGStringTable("NamesOfInterfacesWithProtos",
13262 ifaceNamesWithProto))
13264 traitsDecl = CGNamespace.build(['mozilla', 'dom'],
13265 CGList(traitsDecls))
13267 curr.append(traitsDecl)
13269 # Add include guards.
13270 curr = CGIncludeGuard('PrototypeList', curr)
13272 # Add the auto-generated comment.
13273 curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
13275 # Done.
13276 return curr
13278 @staticmethod
13279 def RegisterBindings(config):
13281 # TODO - Generate the methods we want
13282 curr = CGRegisterProtos(config)
13284 # Wrap all of that in our namespaces.
13285 curr = CGNamespace.build(['mozilla', 'dom'],
13286 CGWrapper(curr, post='\n'))
13287 curr = CGWrapper(curr, post='\n')
13289 # Add the includes
13290 defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface)
13291 for desc in config.getDescriptors(hasInterfaceObject=True,
13292 workers=False,
13293 register=True)]
13294 defineIncludes.append('nsScriptNameSpaceManager.h')
13295 defineIncludes.extend([CGHeaders.getDeclarationFilename(desc.interface)
13296 for desc in config.getDescriptors(isNavigatorProperty=True,
13297 workers=False,
13298 register=True)])
13299 curr = CGHeaders([], [], [], [], [], defineIncludes, 'RegisterBindings',
13300 curr)
13302 # Add include guards.
13303 curr = CGIncludeGuard('RegisterBindings', curr)
13305 # Done.
13306 return curr
13308 @staticmethod
13309 def UnionTypes(config):
13311 (includes, implincludes,
13312 declarations, unions) = UnionTypes(config.getDescriptors(),
13313 config.getDictionaries(),
13314 config.getCallbacks(),
13315 config)
13316 includes.add("mozilla/dom/OwningNonNull.h")
13317 includes.add("mozilla/dom/UnionMember.h")
13318 includes.add("mozilla/dom/BindingDeclarations.h")
13319 # Need BindingUtils.h for FakeDependentString
13320 includes.add("mozilla/dom/BindingUtils.h")
13321 implincludes.add("mozilla/dom/PrimitiveConversions.h")
13323 # Wrap all of that in our namespaces.
13324 curr = CGNamespace.build(['mozilla', 'dom'], unions)
13326 curr = CGWrapper(curr, post='\n')
13328 namespaces = []
13329 stack = [CGList([])]
13330 for clazz, isStruct in sorted(declarations):
13331 elements = clazz.split("::")
13332 clazz = CGClassForwardDeclare(elements.pop(), isStruct=isStruct)
13333 i = 0
13334 if len(elements) > 0:
13335 common = min(len(namespaces), len(elements))
13336 while i < common and namespaces[i] == elements[i]:
13337 i += 1
13339 # pop all the namespaces that should be closed
13340 namespaces = namespaces[:i]
13342 # add all the namespaces that should be opened
13343 for j, namespace in enumerate(elements[i:]):
13344 namespaces.append(namespace)
13345 # every CGNamespace that we add holds a CGList
13346 list = CGList([])
13347 # add the new namespace to the list on top of the stack
13348 stack[i + j].append(CGNamespace(namespace, list))
13349 # set the top of the namespace stack to the list of the new
13350 # namespace
13351 stack[i + j + 1:] = [list]
13353 stack[len(elements)].append(clazz)
13355 curr = CGList([stack[0], curr], "\n")
13357 curr = CGHeaders([], [], [], [], includes, implincludes, 'UnionTypes',
13358 curr)
13360 # Add include guards.
13361 curr = CGIncludeGuard('UnionTypes', curr)
13363 # Done.
13364 return curr
13366 @staticmethod
13367 def UnionConversions(config):
13369 headers, unions = UnionConversions(config.getDescriptors(),
13370 config.getDictionaries(),
13371 config.getCallbacks(),
13372 config)
13374 # Wrap all of that in our namespaces.
13375 curr = CGNamespace.build(['mozilla', 'dom'], unions)
13377 curr = CGWrapper(curr, post='\n')
13379 headers.update(["nsDebug.h", "mozilla/dom/UnionTypes.h"])
13380 curr = CGHeaders([], [], [], [], headers, [], 'UnionConversions', curr)
13382 # Add include guards.
13383 curr = CGIncludeGuard('UnionConversions', curr)
13385 # Done.
13386 return curr
13389 # Code generator for simple events
13390 class CGEventGetter(CGNativeMember):
13391 def __init__(self, descriptor, attr):
13392 ea = descriptor.getExtendedAttributes(attr, getter=True)
13393 ea.append('resultNotAddRefed')
13394 CGNativeMember.__init__(self, descriptor, attr,
13395 CGSpecializedGetter.makeNativeName(descriptor,
13396 attr),
13397 (attr.type, []),
13398 ea)
13399 self.body = self.getMethodBody()
13401 def getArgs(self, returnType, argList):
13402 if 'infallible' not in self.extendedAttrs:
13403 raise TypeError("Event code generator does not support [Throws]!")
13404 if not self.member.isAttr():
13405 raise TypeError("Event code generator does not support methods")
13406 if self.member.isStatic():
13407 raise TypeError("Event code generators does not support static attributes")
13408 return CGNativeMember.getArgs(self, returnType, argList)
13410 def getMethodBody(self):
13411 type = self.member.type
13412 memberName = CGDictionary.makeMemberName(self.member.identifier.name)
13413 if (type.isPrimitive() and type.tag() in builtinNames) or type.isEnum() or type.isGeckoInterface():
13414 return "return " + memberName + ";\n"
13415 if type.isDOMString() or type.isByteString():
13416 return "aRetVal = " + memberName + ";\n"
13417 if type.isSpiderMonkeyInterface() or type.isObject():
13418 return fill(
13419 """
13420 if (${memberName}) {
13421 JS::ExposeObjectToActiveJS(${memberName});
13422 }
13423 aRetVal.set(${memberName});
13424 return;
13425 """,
13426 memberName=memberName)
13427 if type.isAny():
13428 return fill(
13429 """
13430 JS::ExposeValueToActiveJS(${memberName});
13431 aRetVal.set(${memberName});
13432 return;
13433 """,
13434 memberName=memberName)
13435 if type.isUnion():
13436 return "aRetVal = " + memberName + ";\n"
13437 raise TypeError("Event code generator does not support this type!")
13439 def declare(self, cgClass):
13440 if getattr(self.member, "originatingInterface",
13441 cgClass.descriptor.interface) != cgClass.descriptor.interface:
13442 return ""
13443 return CGNativeMember.declare(self, cgClass)
13445 def define(self, cgClass):
13446 if getattr(self.member, "originatingInterface",
13447 cgClass.descriptor.interface) != cgClass.descriptor.interface:
13448 return ""
13449 return CGNativeMember.define(self, cgClass)
13452 class CGEventSetter(CGNativeMember):
13453 def __init__(self):
13454 raise TypeError("Event code generator does not support setters!")
13457 class CGEventMethod(CGNativeMember):
13458 def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
13459 if not isConstructor:
13460 raise TypeError("Event code generator does not support methods!")
13461 self.wantsConstructorForNativeCaller = True
13462 CGNativeMember.__init__(self, descriptor, method,
13463 CGSpecializedMethod.makeNativeName(descriptor,
13464 method),
13465 signature,
13466 descriptor.getExtendedAttributes(method),
13467 breakAfter=breakAfter,
13468 variadicIsSequence=True)
13469 self.originalArgs = list(self.args)
13471 def getArgs(self, returnType, argList):
13472 args = [self.getArg(arg) for arg in argList]
13473 return args
13475 def getArg(self, arg):
13476 decl, ref = self.getArgType(arg.type,
13477 arg.optional and not arg.defaultValue,
13478 "Variadic" if arg.variadic else False)
13479 if ref:
13480 decl = CGWrapper(decl, pre="const ", post="&")
13482 name = arg.identifier.name
13483 name = "a" + name[0].upper() + name[1:]
13484 return Argument(decl.define(), name)
13486 def declare(self, cgClass):
13487 self.args = list(self.originalArgs)
13488 self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner"))
13489 constructorForNativeCaller = CGNativeMember.declare(self, cgClass) + "\n"
13490 self.args = list(self.originalArgs)
13491 if needCx(None, self.descriptorProvider.interface.members, [], True):
13492 self.args.insert(0, Argument("JSContext*", "aCx"))
13493 self.args.insert(0, Argument("const GlobalObject&", "aGlobal"))
13494 self.args.append(Argument('ErrorResult&', 'aRv'))
13495 return constructorForNativeCaller + CGNativeMember.declare(self, cgClass)
13497 def define(self, cgClass):
13498 self.args = list(self.originalArgs)
13499 members = ""
13500 holdJS = ""
13501 iface = self.descriptorProvider.interface
13502 while iface.identifier.name != "Event":
13503 for m in self.descriptorProvider.getDescriptor(iface.identifier.name).interface.members:
13504 if m.isAttr():
13505 # We initialize all the other member variables in the
13506 # Constructor except those ones coming from the Event.
13507 if getattr(m, "originatingInterface",
13508 cgClass.descriptor.interface).identifier.name == "Event":
13509 continue
13510 name = CGDictionary.makeMemberName(m.identifier.name)
13511 members += "e->%s = %s.%s;\n" % (name, self.args[1].name, name)
13512 if m.type.isAny() or m.type.isObject() or m.type.isSpiderMonkeyInterface():
13513 holdJS = "mozilla::HoldJSObjects(e.get());\n"
13514 iface = iface.parent
13516 self.body = fill(
13517 """
13518 nsRefPtr<${nativeType}> e = new ${nativeType}(aOwner);
13519 bool trusted = e->Init(aOwner);
13520 e->InitEvent(${eventType}, ${eventInit}.mBubbles, ${eventInit}.mCancelable);
13521 $*{members}
13522 e->SetTrusted(trusted);
13523 $*{holdJS}
13524 return e.forget();
13525 """,
13526 nativeType=self.descriptorProvider.nativeType.split('::')[-1],
13527 eventType=self.args[0].name,
13528 eventInit=self.args[1].name,
13529 members=members,
13530 holdJS=holdJS)
13532 self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner"))
13533 constructorForNativeCaller = CGNativeMember.define(self, cgClass) + "\n"
13534 self.args = list(self.originalArgs)
13535 self.body = fill(
13536 """
13537 nsCOMPtr<mozilla::dom::EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports());
13538 return Constructor(owner, ${arg0}, ${arg1});
13539 """,
13540 arg0=self.args[0].name,
13541 arg1=self.args[1].name)
13542 if needCx(None, self.descriptorProvider.interface.members, [], True):
13543 self.args.insert(0, Argument("JSContext*", "aCx"))
13544 self.args.insert(0, Argument("const GlobalObject&", "aGlobal"))
13545 self.args.append(Argument('ErrorResult&', 'aRv'))
13546 return constructorForNativeCaller + CGNativeMember.define(self, cgClass)
13549 class CGEventClass(CGBindingImplClass):
13550 """
13551 Codegen for the actual Event class implementation for this descriptor
13552 """
13553 def __init__(self, descriptor):
13554 CGBindingImplClass.__init__(self, descriptor, CGEventMethod, CGEventGetter, CGEventSetter, False)
13555 members = []
13556 for m in descriptor.interface.members:
13557 if m.isAttr():
13558 if getattr(m, "originatingInterface",
13559 descriptor.interface) != descriptor.interface:
13560 continue
13561 if m.type.isPrimitive() and m.type.tag() in builtinNames:
13562 nativeType = CGGeneric(builtinNames[m.type.tag()])
13563 if m.type.nullable():
13564 nativeType = CGTemplatedType("Nullable", nativeType)
13565 nativeType = nativeType.define()
13566 elif m.type.isEnum():
13567 nativeType = m.type.unroll().inner.identifier.name
13568 if m.type.nullable():
13569 nativeType = CGTemplatedType("Nullable",
13570 CGGeneric(nativeType)).define()
13571 elif m.type.isDOMString():
13572 nativeType = "nsString"
13573 elif m.type.isByteString():
13574 nativeType = "nsCString"
13575 elif m.type.isGeckoInterface():
13576 iface = m.type.unroll().inner
13577 nativeType = self.descriptor.getDescriptor(
13578 iface.identifier.name).nativeType
13579 # Now trim off unnecessary namespaces
13580 nativeType = nativeType.split("::")
13581 if nativeType[0] == "mozilla":
13582 nativeType.pop(0)
13583 if nativeType[0] == "dom":
13584 nativeType.pop(0)
13585 nativeType = CGWrapper(CGGeneric("::".join(nativeType)), pre="nsRefPtr<", post=">").define()
13586 elif m.type.isAny():
13587 nativeType = "JS::Heap<JS::Value>"
13588 elif m.type.isObject() or m.type.isSpiderMonkeyInterface():
13589 nativeType = "JS::Heap<JSObject*>"
13590 elif m.type.isUnion():
13591 nativeType = CGUnionStruct.unionTypeDecl(m.type, True)
13592 else:
13593 raise TypeError("Don't know how to declare member of type %s" %
13594 m.type)
13595 members.append(ClassMember(CGDictionary.makeMemberName(m.identifier.name),
13596 nativeType,
13597 visibility="private",
13598 body="body"))
13600 parent = self.descriptor.interface.parent
13601 self.parentType = self.descriptor.getDescriptor(parent.identifier.name).nativeType.split('::')[-1]
13602 baseDeclarations = fill(
13603 """
13604 public:
13605 NS_DECL_ISUPPORTS_INHERITED
13606 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(${nativeType}, ${parentType})
13607 virtual ~${nativeType}();
13608 protected:
13609 ${nativeType}(mozilla::dom::EventTarget* aOwner);
13611 """,
13612 nativeType=self.descriptor.nativeType.split('::')[-1],
13613 parentType=self.parentType)
13615 className = descriptor.nativeType.split('::')[-1]
13616 asConcreteTypeMethod = ClassMethod("As%s" % className,
13617 "%s*" % className,
13618 [],
13619 virtual=True,
13620 body="return this;\n",
13621 breakAfterReturnDecl=" ")
13623 CGClass.__init__(self, className,
13624 bases=[ClassBase(self.parentType)],
13625 methods=[asConcreteTypeMethod]+self.methodDecls,
13626 members=members,
13627 extradeclarations=baseDeclarations)
13629 def getWrapObjectBody(self):
13630 return "return %sBinding::Wrap(aCx, this);\n" % self.descriptor.name
13632 def implTraverse(self):
13633 retVal = ""
13634 for m in self.descriptor.interface.members:
13635 if m.isAttr() and m.type.isGeckoInterface():
13636 retVal += (" NS_IMPL_CYCLE_COLLECTION_TRAVERSE(" +
13637 CGDictionary.makeMemberName(m.identifier.name) +
13638 ")\n")
13639 return retVal
13641 def implUnlink(self):
13642 retVal = ""
13643 for m in self.descriptor.interface.members:
13644 if m.isAttr():
13645 name = CGDictionary.makeMemberName(m.identifier.name)
13646 if m.type.isGeckoInterface():
13647 retVal += " NS_IMPL_CYCLE_COLLECTION_UNLINK(" + name + ")\n"
13648 elif m.type.isAny():
13649 retVal += " tmp->" + name + ".setUndefined();\n"
13650 elif m.type.isObject() or m.type.isSpiderMonkeyInterface():
13651 retVal += " tmp->" + name + " = nullptr;\n"
13652 return retVal
13654 def implTrace(self):
13655 retVal = ""
13656 for m in self.descriptor.interface.members:
13657 if m.isAttr():
13658 name = CGDictionary.makeMemberName(m.identifier.name)
13659 if m.type.isAny():
13660 retVal += " NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(" + name + ")\n"
13661 elif m.type.isObject() or m.type.isSpiderMonkeyInterface():
13662 retVal += " NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(" + name + ")\n"
13663 elif typeNeedsRooting(m.type):
13664 raise TypeError("Need to implement tracing for event "
13665 "member of type %s" % m.type)
13666 return retVal
13668 def define(self):
13669 dropJS = ""
13670 for m in self.descriptor.interface.members:
13671 if m.isAttr():
13672 member = CGDictionary.makeMemberName(m.identifier.name)
13673 if m.type.isAny():
13674 dropJS += member + " = JS::UndefinedValue();\n"
13675 elif m.type.isObject() or m.type.isSpiderMonkeyInterface():
13676 dropJS += member + " = nullptr;\n"
13677 if dropJS != "":
13678 dropJS += "mozilla::DropJSObjects(this);\n"
13679 # Just override CGClass and do our own thing
13680 nativeType = self.descriptor.nativeType.split('::')[-1]
13681 ctorParams = ("aOwner, nullptr, nullptr" if self.parentType == "Event"
13682 else "aOwner")
13684 classImpl = fill(
13685 """
13687 NS_IMPL_CYCLE_COLLECTION_CLASS(${nativeType})
13689 NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType})
13690 NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType})
13692 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(${nativeType}, ${parentType})
13693 $*{traverse}
13694 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
13696 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(${nativeType}, ${parentType})
13697 $*{trace}
13698 NS_IMPL_CYCLE_COLLECTION_TRACE_END
13700 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(${nativeType}, ${parentType})
13701 $*{unlink}
13702 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
13704 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(${nativeType})
13705 NS_INTERFACE_MAP_END_INHERITING(${parentType})
13707 ${nativeType}::${nativeType}(mozilla::dom::EventTarget* aOwner)
13708 : ${parentType}(${ctorParams})
13709 {
13710 }
13712 ${nativeType}::~${nativeType}()
13713 {
13714 $*{dropJS}
13715 }
13717 """,
13718 ifaceName=self.descriptor.name,
13719 nativeType=nativeType,
13720 ctorParams=ctorParams,
13721 parentType=self.parentType,
13722 traverse=self.implTraverse(),
13723 unlink=self.implUnlink(),
13724 trace=self.implTrace(),
13725 dropJS=dropJS)
13726 return classImpl + CGBindingImplClass.define(self)
13729 class CGEventRoot(CGThing):
13730 def __init__(self, config, interfaceName):
13731 # Let's assume we're not doing workers stuff, for now
13732 descriptor = config.getDescriptor(interfaceName, False)
13734 self.root = CGWrapper(CGEventClass(descriptor),
13735 pre="\n", post="\n")
13737 self.root = CGNamespace.build(["mozilla", "dom"], self.root)
13739 self.root = CGList([CGClassForwardDeclare("JSContext", isStruct=True),
13740 self.root])
13742 parent = descriptor.interface.parent.identifier.name
13744 # Throw in our #includes
13745 self.root = CGHeaders([descriptor], [], [], [],
13746 [
13747 config.getDescriptor(parent, False).headerFile,
13748 "mozilla/Attributes.h",
13749 "mozilla/ErrorResult.h",
13750 "mozilla/dom/%sBinding.h" % interfaceName,
13751 'mozilla/dom/BindingUtils.h',
13752 ],
13753 [
13754 "%s.h" % interfaceName,
13755 "js/GCAPI.h",
13756 'mozilla/dom/Nullable.h',
13757 'nsDOMQS.h'
13758 ],
13759 "", self.root)
13761 # And now some include guards
13762 self.root = CGIncludeGuard(interfaceName, self.root)
13764 self.root = CGWrapper(self.root, pre=AUTOGENERATED_WARNING_COMMENT)
13766 self.root = CGWrapper(self.root, pre=dedent("""
13767 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
13768 /* vim:set ts=2 sw=2 sts=2 et cindent: */
13769 /* This Source Code Form is subject to the terms of the Mozilla Public
13770 * License, v. 2.0. If a copy of the MPL was not distributed with this
13771 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
13773 """))
13775 def declare(self):
13776 return self.root.declare()
13778 def define(self):
13779 return self.root.define()