dom/bindings/Codegen.py

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 # This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 3 # You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 4
michael@0 5 # Common codegen classes.
michael@0 6
michael@0 7 import os
michael@0 8 import re
michael@0 9 import string
michael@0 10 import math
michael@0 11 import textwrap
michael@0 12
michael@0 13 from WebIDL import BuiltinTypes, IDLBuiltinType, IDLNullValue, IDLSequenceType, IDLType, IDLAttribute, IDLUndefinedValue
michael@0 14 from Configuration import NoSuchDescriptorError, getTypesFromDescriptor, getTypesFromDictionary, getTypesFromCallback, Descriptor
michael@0 15
michael@0 16 AUTOGENERATED_WARNING_COMMENT = \
michael@0 17 "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n"
michael@0 18 ADDPROPERTY_HOOK_NAME = '_addProperty'
michael@0 19 FINALIZE_HOOK_NAME = '_finalize'
michael@0 20 CONSTRUCT_HOOK_NAME = '_constructor'
michael@0 21 LEGACYCALLER_HOOK_NAME = '_legacycaller'
michael@0 22 HASINSTANCE_HOOK_NAME = '_hasInstance'
michael@0 23 NEWRESOLVE_HOOK_NAME = '_newResolve'
michael@0 24 ENUMERATE_HOOK_NAME = '_enumerate'
michael@0 25 ENUM_ENTRY_VARIABLE_NAME = 'strings'
michael@0 26 INSTANCE_RESERVED_SLOTS = 3
michael@0 27
michael@0 28
michael@0 29 def memberReservedSlot(member):
michael@0 30 return "(DOM_INSTANCE_RESERVED_SLOTS + %d)" % member.slotIndex
michael@0 31
michael@0 32
michael@0 33 def toStringBool(arg):
michael@0 34 return str(not not arg).lower()
michael@0 35
michael@0 36
michael@0 37 def toBindingNamespace(arg):
michael@0 38 return re.sub("((_workers)?$)", "Binding\\1", arg)
michael@0 39
michael@0 40
michael@0 41 def isTypeCopyConstructible(type):
michael@0 42 # Nullable and sequence stuff doesn't affect copy-constructibility
michael@0 43 type = type.unroll()
michael@0 44 return (type.isPrimitive() or type.isString() or type.isEnum() or
michael@0 45 (type.isUnion() and
michael@0 46 CGUnionStruct.isUnionCopyConstructible(type)) or
michael@0 47 (type.isDictionary() and
michael@0 48 CGDictionary.isDictionaryCopyConstructible(type.inner)) or
michael@0 49 # Interface types are only copy-constructible if they're Gecko
michael@0 50 # interfaces. SpiderMonkey interfaces are not copy-constructible
michael@0 51 # because of rooting issues.
michael@0 52 (type.isInterface() and type.isGeckoInterface()))
michael@0 53
michael@0 54
michael@0 55 def wantsAddProperty(desc):
michael@0 56 return (desc.concrete and
michael@0 57 desc.wrapperCache and
michael@0 58 not (desc.workers and
michael@0 59 desc.interface.getExtendedAttribute("Global")))
michael@0 60
michael@0 61
michael@0 62 # We'll want to insert the indent at the beginnings of lines, but we
michael@0 63 # don't want to indent empty lines. So only indent lines that have a
michael@0 64 # non-newline character on them.
michael@0 65 lineStartDetector = re.compile("^(?=[^\n#])", re.MULTILINE)
michael@0 66
michael@0 67
michael@0 68 def indent(s, indentLevel=2):
michael@0 69 """
michael@0 70 Indent C++ code.
michael@0 71
michael@0 72 Weird secret feature: this doesn't indent lines that start with # (such as
michael@0 73 #include lines).
michael@0 74 """
michael@0 75 if s == "":
michael@0 76 return s
michael@0 77 return re.sub(lineStartDetector, indentLevel * " ", s)
michael@0 78
michael@0 79
michael@0 80 def dedent(s):
michael@0 81 """
michael@0 82 Remove all leading whitespace from s, and remove a blank line
michael@0 83 at the beginning.
michael@0 84 """
michael@0 85 if s.startswith('\n'):
michael@0 86 s = s[1:]
michael@0 87 return textwrap.dedent(s)
michael@0 88
michael@0 89 def fill(template, **args):
michael@0 90 """
michael@0 91 Convenience function for filling in a multiline template.
michael@0 92
michael@0 93 `fill(template, name1=v1, name2=v2)` is a lot like
michael@0 94 `string.Template(template).substitute({"name1": v1, "name2": v2})`.
michael@0 95
michael@0 96 However, it's shorter, and has a few nice features:
michael@0 97
michael@0 98 * If `template` is indented, fill() automatically dedents it!
michael@0 99 This makes code using fill() with Python's multiline strings
michael@0 100 much nicer to look at.
michael@0 101
michael@0 102 * If `template` starts with a blank line, fill() strips it off.
michael@0 103 (Again, convenient with multiline strings.)
michael@0 104
michael@0 105 * fill() recognizes a special kind of substitution
michael@0 106 of the form `$*{name}`.
michael@0 107
michael@0 108 Use this to paste in, and automatically indent, multiple lines.
michael@0 109 (Mnemonic: The `*` is for "multiple lines").
michael@0 110
michael@0 111 A `$*` substitution must appear by itself on a line, with optional
michael@0 112 preceding indentation (spaces only). The whole line is replaced by the
michael@0 113 corresponding keyword argument, indented appropriately. If the
michael@0 114 argument is an empty string, no output is generated, not even a blank
michael@0 115 line.
michael@0 116 """
michael@0 117
michael@0 118 # This works by transforming the fill()-template to an equivalent
michael@0 119 # string.Template.
michael@0 120 multiline_substitution_re = re.compile(r"( *)\$\*{(\w+)}(\n)?")
michael@0 121
michael@0 122 def replace(match):
michael@0 123 """
michael@0 124 Replaces a line like ' $*{xyz}\n' with '${xyz_n}',
michael@0 125 where n is the indent depth, and add a corresponding entry to args.
michael@0 126 """
michael@0 127 indentation, name, nl = match.groups()
michael@0 128 depth = len(indentation)
michael@0 129
michael@0 130 # Check that $*{xyz} appears by itself on a line.
michael@0 131 prev = match.string[:match.start()]
michael@0 132 if (prev and not prev.endswith("\n")) or nl is None:
michael@0 133 raise ValueError("Invalid fill() template: $*{%s} must appear by itself on a line" % name)
michael@0 134
michael@0 135 # Multiline text without a newline at the end is probably a mistake.
michael@0 136 if not (args[name] == "" or args[name].endswith("\n")):
michael@0 137 raise ValueError("Argument %s with value %r is missing a newline" % (name, args[name]))
michael@0 138
michael@0 139 # Now replace this whole line of template with the indented equivalent.
michael@0 140 modified_name = name + "_" + str(depth)
michael@0 141 indented_value = indent(args[name], depth)
michael@0 142 if modified_name in args:
michael@0 143 assert args[modified_name] == indented_value
michael@0 144 else:
michael@0 145 args[modified_name] = indented_value
michael@0 146 return "${" + modified_name + "}"
michael@0 147
michael@0 148 t = dedent(template)
michael@0 149 assert t.endswith("\n") or "\n" not in t
michael@0 150 t = re.sub(multiline_substitution_re, replace, t)
michael@0 151 t = string.Template(t)
michael@0 152 return t.substitute(args)
michael@0 153
michael@0 154
michael@0 155 class CGThing():
michael@0 156 """
michael@0 157 Abstract base class for things that spit out code.
michael@0 158 """
michael@0 159 def __init__(self):
michael@0 160 pass # Nothing for now
michael@0 161
michael@0 162 def declare(self):
michael@0 163 """Produce code for a header file."""
michael@0 164 assert False # Override me!
michael@0 165
michael@0 166 def define(self):
michael@0 167 """Produce code for a cpp file."""
michael@0 168 assert False # Override me!
michael@0 169
michael@0 170 def deps(self):
michael@0 171 """Produce the deps for a pp file"""
michael@0 172 assert False # Override me!
michael@0 173
michael@0 174
michael@0 175 class CGStringTable(CGThing):
michael@0 176 """
michael@0 177 Generate a string table for the given strings with a function accessor:
michael@0 178
michael@0 179 const char *accessorName(unsigned int index) {
michael@0 180 static const char table[] = "...";
michael@0 181 static const uint16_t indices = { ... };
michael@0 182 return &table[indices[index]];
michael@0 183 }
michael@0 184
michael@0 185 This is more efficient than the more natural:
michael@0 186
michael@0 187 const char *table[] = {
michael@0 188 ...
michael@0 189 };
michael@0 190
michael@0 191 The uint16_t indices are smaller than the pointer equivalents, and the
michael@0 192 string table requires no runtime relocations.
michael@0 193 """
michael@0 194 def __init__(self, accessorName, strings):
michael@0 195 CGThing.__init__(self)
michael@0 196 self.accessorName = accessorName
michael@0 197 self.strings = strings
michael@0 198
michael@0 199 def declare(self):
michael@0 200 return "extern const char *%s(unsigned int aIndex);\n" % self.accessorName
michael@0 201
michael@0 202 def define(self):
michael@0 203 table = ' "\\0" '.join('"%s"' % s for s in self.strings)
michael@0 204 indices = []
michael@0 205 currentIndex = 0
michael@0 206 for s in self.strings:
michael@0 207 indices.append(currentIndex)
michael@0 208 currentIndex += len(s) + 1 # for the null terminator
michael@0 209 return fill(
michael@0 210 """
michael@0 211 const char *${name}(unsigned int aIndex)
michael@0 212 {
michael@0 213 static const char table[] = ${table};
michael@0 214 static const uint16_t indices[] = { ${indices} };
michael@0 215 static_assert(${currentIndex} <= UINT16_MAX, "string table overflow!");
michael@0 216 return &table[indices[aIndex]];
michael@0 217 }
michael@0 218 """,
michael@0 219 name=self.accessorName,
michael@0 220 table=table,
michael@0 221 indices=", ".join("%d" % index for index in indices),
michael@0 222 currentIndex=currentIndex)
michael@0 223
michael@0 224
michael@0 225 class CGNativePropertyHooks(CGThing):
michael@0 226 """
michael@0 227 Generate a NativePropertyHooks for a given descriptor
michael@0 228 """
michael@0 229 def __init__(self, descriptor, properties):
michael@0 230 CGThing.__init__(self)
michael@0 231 self.descriptor = descriptor
michael@0 232 self.properties = properties
michael@0 233
michael@0 234 def declare(self):
michael@0 235 if self.descriptor.workers:
michael@0 236 return ""
michael@0 237 return dedent("""
michael@0 238 // We declare this as an array so that retrieving a pointer to this
michael@0 239 // binding's property hooks only requires compile/link-time resolvable
michael@0 240 // address arithmetic. Declaring it as a pointer instead would require
michael@0 241 // doing a run-time load to fetch a pointer to this binding's property
michael@0 242 // hooks. And then structures which embedded a pointer to this structure
michael@0 243 // would require a run-time load for proper initialization, which would
michael@0 244 // then induce static constructors. Lots of static constructors.
michael@0 245 extern const NativePropertyHooks sNativePropertyHooks[];
michael@0 246 """).rstrip() # BOGUS strip newline from the last line here (!)
michael@0 247
michael@0 248 def define(self):
michael@0 249 if self.descriptor.workers:
michael@0 250 return ""
michael@0 251 if self.descriptor.concrete and self.descriptor.proxy:
michael@0 252 resolveOwnProperty = "ResolveOwnProperty"
michael@0 253 enumerateOwnProperties = "EnumerateOwnProperties"
michael@0 254 elif self.descriptor.needsXrayResolveHooks():
michael@0 255 resolveOwnProperty = "ResolveOwnPropertyViaNewresolve"
michael@0 256 enumerateOwnProperties = "EnumerateOwnPropertiesViaGetOwnPropertyNames"
michael@0 257 else:
michael@0 258 resolveOwnProperty = "nullptr"
michael@0 259 enumerateOwnProperties = "nullptr"
michael@0 260 if self.properties.hasNonChromeOnly():
michael@0 261 regular = "&sNativeProperties"
michael@0 262 else:
michael@0 263 regular = "nullptr"
michael@0 264 if self.properties.hasChromeOnly():
michael@0 265 chrome = "&sChromeOnlyNativeProperties"
michael@0 266 else:
michael@0 267 chrome = "nullptr"
michael@0 268 constructorID = "constructors::id::"
michael@0 269 if self.descriptor.interface.hasInterfaceObject():
michael@0 270 constructorID += self.descriptor.name
michael@0 271 else:
michael@0 272 constructorID += "_ID_Count"
michael@0 273 prototypeID = "prototypes::id::"
michael@0 274 if self.descriptor.interface.hasInterfacePrototypeObject():
michael@0 275 prototypeID += self.descriptor.name
michael@0 276 else:
michael@0 277 prototypeID += "_ID_Count"
michael@0 278 parent = self.descriptor.interface.parent
michael@0 279 parentHooks = (toBindingNamespace(parent.identifier.name) + "::sNativePropertyHooks"
michael@0 280 if parent else 'nullptr')
michael@0 281
michael@0 282 return fill(
michael@0 283 """
michael@0 284 const NativePropertyHooks sNativePropertyHooks[] = { {
michael@0 285 ${resolveOwnProperty},
michael@0 286 ${enumerateOwnProperties},
michael@0 287 { ${regular}, ${chrome} },
michael@0 288 ${prototypeID},
michael@0 289 ${constructorID},
michael@0 290 ${parentHooks}
michael@0 291 } };
michael@0 292 """,
michael@0 293 resolveOwnProperty=resolveOwnProperty,
michael@0 294 enumerateOwnProperties=enumerateOwnProperties,
michael@0 295 regular=regular,
michael@0 296 chrome=chrome,
michael@0 297 prototypeID=prototypeID,
michael@0 298 constructorID=constructorID,
michael@0 299 parentHooks=parentHooks)
michael@0 300
michael@0 301
michael@0 302 def NativePropertyHooks(descriptor):
michael@0 303 return "&sWorkerNativePropertyHooks" if descriptor.workers else "sNativePropertyHooks"
michael@0 304
michael@0 305
michael@0 306 def DOMClass(descriptor):
michael@0 307 def make_name(d):
michael@0 308 return "%s%s" % (d.interface.identifier.name, '_workers' if d.workers else '')
michael@0 309
michael@0 310 protoList = ['prototypes::id::' + make_name(descriptor.getDescriptor(proto)) for proto in descriptor.prototypeChain]
michael@0 311 # Pad out the list to the right length with _ID_Count so we
michael@0 312 # guarantee that all the lists are the same length. _ID_Count
michael@0 313 # is never the ID of any prototype, so it's safe to use as
michael@0 314 # padding.
michael@0 315 protoList.extend(['prototypes::id::_ID_Count'] * (descriptor.config.maxProtoChainLength - len(protoList)))
michael@0 316
michael@0 317 return fill(
michael@0 318 """
michael@0 319 {
michael@0 320 { ${protoChain} },
michael@0 321 IsBaseOf<nsISupports, ${nativeType} >::value,
michael@0 322 ${hooks},
michael@0 323 GetParentObject<${nativeType}>::Get,
michael@0 324 GetProtoObject,
michael@0 325 GetCCParticipant<${nativeType}>::Get()
michael@0 326 }
michael@0 327 """,
michael@0 328 protoChain=', '.join(protoList),
michael@0 329 nativeType=descriptor.nativeType,
michael@0 330 hooks=NativePropertyHooks(descriptor))
michael@0 331
michael@0 332
michael@0 333 class CGDOMJSClass(CGThing):
michael@0 334 """
michael@0 335 Generate a DOMJSClass for a given descriptor
michael@0 336 """
michael@0 337 def __init__(self, descriptor):
michael@0 338 CGThing.__init__(self)
michael@0 339 self.descriptor = descriptor
michael@0 340
michael@0 341 def declare(self):
michael@0 342 return ""
michael@0 343
michael@0 344 def define(self):
michael@0 345 traceHook = 'nullptr'
michael@0 346 callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr'
michael@0 347 slotCount = INSTANCE_RESERVED_SLOTS + self.descriptor.interface.totalMembersInSlots
michael@0 348 classFlags = "JSCLASS_IS_DOMJSCLASS | "
michael@0 349 classExtensionAndObjectOps = """\
michael@0 350 JS_NULL_CLASS_EXT,
michael@0 351 JS_NULL_OBJECT_OPS
michael@0 352 """
michael@0 353 if self.descriptor.interface.getExtendedAttribute("Global"):
michael@0 354 classFlags += "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS) | JSCLASS_IMPLEMENTS_BARRIERS"
michael@0 355 traceHook = "JS_GlobalObjectTraceHook"
michael@0 356 if not self.descriptor.workers:
michael@0 357 classExtensionAndObjectOps = """\
michael@0 358 {
michael@0 359 nsGlobalWindow::OuterObject, /* outerObject */
michael@0 360 nullptr, /* innerObject */
michael@0 361 nullptr, /* iteratorObject */
michael@0 362 false, /* isWrappedNative */
michael@0 363 nullptr /* weakmapKeyDelegateOp */
michael@0 364 },
michael@0 365 {
michael@0 366 nullptr, /* lookupGeneric */
michael@0 367 nullptr, /* lookupProperty */
michael@0 368 nullptr, /* lookupElement */
michael@0 369 nullptr, /* defineGeneric */
michael@0 370 nullptr, /* defineProperty */
michael@0 371 nullptr, /* defineElement */
michael@0 372 nullptr, /* getGeneric */
michael@0 373 nullptr, /* getProperty */
michael@0 374 nullptr, /* getElement */
michael@0 375 nullptr, /* setGeneric */
michael@0 376 nullptr, /* setProperty */
michael@0 377 nullptr, /* setElement */
michael@0 378 nullptr, /* getGenericAttributes */
michael@0 379 nullptr, /* setGenericAttributes */
michael@0 380 nullptr, /* deleteProperty */
michael@0 381 nullptr, /* deleteElement */
michael@0 382 nullptr, /* watch */
michael@0 383 nullptr, /* unwatch */
michael@0 384 nullptr, /* slice */
michael@0 385 nullptr, /* enumerate */
michael@0 386 JS_ObjectToOuterObject /* thisObject */
michael@0 387 }
michael@0 388 """
michael@0 389 else:
michael@0 390 classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount
michael@0 391 if self.descriptor.interface.getExtendedAttribute("NeedNewResolve"):
michael@0 392 newResolveHook = "(JSResolveOp)" + NEWRESOLVE_HOOK_NAME
michael@0 393 classFlags += " | JSCLASS_NEW_RESOLVE"
michael@0 394 enumerateHook = ENUMERATE_HOOK_NAME
michael@0 395 elif self.descriptor.interface.getExtendedAttribute("Global"):
michael@0 396 newResolveHook = "(JSResolveOp) mozilla::dom::ResolveGlobal"
michael@0 397 classFlags += " | JSCLASS_NEW_RESOLVE"
michael@0 398 enumerateHook = "mozilla::dom::EnumerateGlobal"
michael@0 399 else:
michael@0 400 newResolveHook = "JS_ResolveStub"
michael@0 401 enumerateHook = "JS_EnumerateStub"
michael@0 402
michael@0 403 return fill( # BOGUS extra blank line at the top
michael@0 404 """
michael@0 405
michael@0 406 static const DOMJSClass Class = {
michael@0 407 { "${name}",
michael@0 408 ${flags},
michael@0 409 ${addProperty}, /* addProperty */
michael@0 410 JS_DeletePropertyStub, /* delProperty */
michael@0 411 JS_PropertyStub, /* getProperty */
michael@0 412 JS_StrictPropertyStub, /* setProperty */
michael@0 413 ${enumerate}, /* enumerate */
michael@0 414 ${resolve}, /* resolve */
michael@0 415 JS_ConvertStub,
michael@0 416 ${finalize}, /* finalize */
michael@0 417 ${call}, /* call */
michael@0 418 nullptr, /* hasInstance */
michael@0 419 nullptr, /* construct */
michael@0 420 ${trace}, /* trace */
michael@0 421 JS_NULL_CLASS_SPEC,
michael@0 422 $*{classExtensionAndObjectOps}
michael@0 423 },
michael@0 424 $*{descriptor}
michael@0 425 };
michael@0 426 """,
michael@0 427 name=self.descriptor.interface.identifier.name,
michael@0 428 flags=classFlags,
michael@0 429 addProperty=ADDPROPERTY_HOOK_NAME if wantsAddProperty(self.descriptor) else 'JS_PropertyStub',
michael@0 430 enumerate=enumerateHook,
michael@0 431 resolve=newResolveHook,
michael@0 432 finalize=FINALIZE_HOOK_NAME,
michael@0 433 call=callHook,
michael@0 434 trace=traceHook,
michael@0 435 classExtensionAndObjectOps=classExtensionAndObjectOps,
michael@0 436 descriptor=DOMClass(self.descriptor))
michael@0 437
michael@0 438
michael@0 439 class CGDOMProxyJSClass(CGThing):
michael@0 440 """
michael@0 441 Generate a DOMJSClass for a given proxy descriptor
michael@0 442 """
michael@0 443 def __init__(self, descriptor):
michael@0 444 CGThing.__init__(self)
michael@0 445 self.descriptor = descriptor
michael@0 446
michael@0 447 def declare(self):
michael@0 448 return ""
michael@0 449
michael@0 450 def define(self):
michael@0 451 flags = ["JSCLASS_IS_DOMJSCLASS"]
michael@0 452 # We don't use an IDL annotation for JSCLASS_EMULATES_UNDEFINED because
michael@0 453 # we don't want people ever adding that to any interface other than
michael@0 454 # HTMLAllCollection. So just hardcode it here.
michael@0 455 if self.descriptor.interface.identifier.name == "HTMLAllCollection":
michael@0 456 flags.append("JSCLASS_EMULATES_UNDEFINED")
michael@0 457 callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr'
michael@0 458 return fill( # BOGUS extra blank line at the top
michael@0 459 """
michael@0 460
michael@0 461 static const DOMJSClass Class = {
michael@0 462 PROXY_CLASS_DEF("${name}",
michael@0 463 0, /* extra slots */
michael@0 464 ${flags},
michael@0 465 ${call}, /* call */
michael@0 466 nullptr /* construct */),
michael@0 467 $*{descriptor}
michael@0 468 };
michael@0 469 """,
michael@0 470 name=self.descriptor.interface.identifier.name,
michael@0 471 flags=" | ".join(flags),
michael@0 472 call=callHook,
michael@0 473 descriptor=DOMClass(self.descriptor))
michael@0 474
michael@0 475
michael@0 476 def PrototypeIDAndDepth(descriptor):
michael@0 477 prototypeID = "prototypes::id::"
michael@0 478 if descriptor.interface.hasInterfacePrototypeObject():
michael@0 479 prototypeID += descriptor.interface.identifier.name
michael@0 480 if descriptor.workers:
michael@0 481 prototypeID += "_workers"
michael@0 482 depth = "PrototypeTraits<%s>::Depth" % prototypeID
michael@0 483 else:
michael@0 484 prototypeID += "_ID_Count"
michael@0 485 depth = "0"
michael@0 486 return (prototypeID, depth)
michael@0 487
michael@0 488
michael@0 489 def UseHolderForUnforgeable(descriptor):
michael@0 490 return (descriptor.concrete and
michael@0 491 descriptor.proxy and
michael@0 492 any(m for m in descriptor.interface.members if m.isAttr() and m.isUnforgeable()))
michael@0 493
michael@0 494
michael@0 495 def CallOnUnforgeableHolder(descriptor, code, isXrayCheck=None,
michael@0 496 useSharedRoot=False):
michael@0 497 """
michael@0 498 Generate the code to execute the code in "code" on an unforgeable holder if
michael@0 499 needed. code should be a string containing the code to execute. If it
michael@0 500 contains a ${holder} string parameter it will be replaced with the
michael@0 501 unforgeable holder object.
michael@0 502
michael@0 503 If isXrayCheck is not None it should be a string that contains a statement
michael@0 504 returning whether proxy is an Xray. If isXrayCheck is None the generated
michael@0 505 code won't try to unwrap Xrays.
michael@0 506
michael@0 507 If useSharedRoot is true, we will use an existing
michael@0 508 JS::Rooted<JSObject*> sharedRoot for storing our unforgeable holder instead
michael@0 509 of declaring a new Rooted.
michael@0 510 """
michael@0 511 if isXrayCheck is not None:
michael@0 512 pre = fill(
michael@0 513 """
michael@0 514 // Scope for 'global', 'ac' and 'unforgeableHolder'
michael@0 515 {
michael@0 516 JS::Rooted<JSObject*> global(cx);
michael@0 517 Maybe<JSAutoCompartment> ac;
michael@0 518 if (${isXrayCheck}) {
michael@0 519 global = js::GetGlobalForObjectCrossCompartment(js::UncheckedUnwrap(proxy));
michael@0 520 ac.construct(cx, global);
michael@0 521 } else {
michael@0 522 global = js::GetGlobalForObjectCrossCompartment(proxy);
michael@0 523 }
michael@0 524 """,
michael@0 525 isXrayCheck=isXrayCheck)
michael@0 526 else:
michael@0 527 pre = dedent("""
michael@0 528 // Scope for 'global' and 'unforgeableHolder'
michael@0 529 {
michael@0 530 JSObject* global = js::GetGlobalForObjectCrossCompartment(proxy);
michael@0 531 """)
michael@0 532
michael@0 533 if useSharedRoot:
michael@0 534 holderDecl = "JS::Rooted<JSObject*>& unforgeableHolder(sharedRoot);\n"
michael@0 535 else:
michael@0 536 holderDecl = "JS::Rooted<JSObject*> unforgeableHolder(cx);\n"
michael@0 537
michael@0 538 code = string.Template(code).substitute({"holder": "unforgeableHolder"})
michael@0 539 return fill(
michael@0 540 """
michael@0 541 $*{pre}
michael@0 542 $*{holderDecl}
michael@0 543 unforgeableHolder = GetUnforgeableHolder(global, prototypes::id::${name});
michael@0 544 $*{code}
michael@0 545 }
michael@0 546 """,
michael@0 547 pre=pre,
michael@0 548 holderDecl=holderDecl,
michael@0 549 name=descriptor.name,
michael@0 550 code=code)
michael@0 551
michael@0 552
michael@0 553 class CGPrototypeJSClass(CGThing):
michael@0 554 def __init__(self, descriptor, properties):
michael@0 555 CGThing.__init__(self)
michael@0 556 self.descriptor = descriptor
michael@0 557 self.properties = properties
michael@0 558
michael@0 559 def declare(self):
michael@0 560 # We're purely for internal consumption
michael@0 561 return ""
michael@0 562
michael@0 563 def define(self):
michael@0 564 prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
michael@0 565 slotCount = "DOM_INTERFACE_PROTO_SLOTS_BASE"
michael@0 566 if UseHolderForUnforgeable(self.descriptor):
michael@0 567 slotCount += " + 1 /* slot for the JSObject holding the unforgeable properties */"
michael@0 568 return fill(
michael@0 569 """
michael@0 570 static const DOMIfaceAndProtoJSClass PrototypeClass = {
michael@0 571 {
michael@0 572 "${name}Prototype",
michael@0 573 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
michael@0 574 JS_PropertyStub, /* addProperty */
michael@0 575 JS_DeletePropertyStub, /* delProperty */
michael@0 576 JS_PropertyStub, /* getProperty */
michael@0 577 JS_StrictPropertyStub, /* setProperty */
michael@0 578 JS_EnumerateStub,
michael@0 579 JS_ResolveStub,
michael@0 580 JS_ConvertStub,
michael@0 581 nullptr, /* finalize */
michael@0 582 nullptr, /* call */
michael@0 583 nullptr, /* hasInstance */
michael@0 584 nullptr, /* construct */
michael@0 585 nullptr, /* trace */
michael@0 586 JSCLASS_NO_INTERNAL_MEMBERS
michael@0 587 },
michael@0 588 eInterfacePrototype,
michael@0 589 ${hooks},
michael@0 590 "[object ${name}Prototype]",
michael@0 591 ${prototypeID},
michael@0 592 ${depth}
michael@0 593 };
michael@0 594 """,
michael@0 595 name=self.descriptor.interface.identifier.name,
michael@0 596 slotCount=slotCount,
michael@0 597 hooks=NativePropertyHooks(self.descriptor),
michael@0 598 prototypeID=prototypeID,
michael@0 599 depth=depth)
michael@0 600
michael@0 601
michael@0 602 def NeedsGeneratedHasInstance(descriptor):
michael@0 603 return descriptor.hasXPConnectImpls or descriptor.interface.isConsequential()
michael@0 604
michael@0 605
michael@0 606 class CGInterfaceObjectJSClass(CGThing):
michael@0 607 def __init__(self, descriptor, properties):
michael@0 608 CGThing.__init__(self)
michael@0 609 self.descriptor = descriptor
michael@0 610 self.properties = properties
michael@0 611
michael@0 612 def declare(self):
michael@0 613 # We're purely for internal consumption
michael@0 614 return ""
michael@0 615
michael@0 616 def define(self):
michael@0 617 if self.descriptor.interface.ctor():
michael@0 618 ctorname = CONSTRUCT_HOOK_NAME
michael@0 619 else:
michael@0 620 ctorname = "ThrowingConstructor"
michael@0 621 if NeedsGeneratedHasInstance(self.descriptor):
michael@0 622 hasinstance = HASINSTANCE_HOOK_NAME
michael@0 623 elif self.descriptor.interface.hasInterfacePrototypeObject():
michael@0 624 hasinstance = "InterfaceHasInstance"
michael@0 625 else:
michael@0 626 hasinstance = "nullptr"
michael@0 627 prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
michael@0 628 slotCount = "DOM_INTERFACE_SLOTS_BASE"
michael@0 629 if len(self.descriptor.interface.namedConstructors) > 0:
michael@0 630 slotCount += (" + %i /* slots for the named constructors */" %
michael@0 631 len(self.descriptor.interface.namedConstructors))
michael@0 632 return fill( # BOGUS extra newline at the top
michael@0 633 """
michael@0 634
michael@0 635 static const DOMIfaceAndProtoJSClass InterfaceObjectClass = {
michael@0 636 {
michael@0 637 "Function",
michael@0 638 JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}),
michael@0 639 JS_PropertyStub, /* addProperty */
michael@0 640 JS_DeletePropertyStub, /* delProperty */
michael@0 641 JS_PropertyStub, /* getProperty */
michael@0 642 JS_StrictPropertyStub, /* setProperty */
michael@0 643 JS_EnumerateStub,
michael@0 644 JS_ResolveStub,
michael@0 645 JS_ConvertStub,
michael@0 646 nullptr, /* finalize */
michael@0 647 ${ctorname}, /* call */
michael@0 648 ${hasInstance}, /* hasInstance */
michael@0 649 ${ctorname}, /* construct */
michael@0 650 nullptr, /* trace */
michael@0 651 JSCLASS_NO_INTERNAL_MEMBERS
michael@0 652 },
michael@0 653 eInterface,
michael@0 654 ${hooks},
michael@0 655 "function ${name}() {\\n [native code]\\n}",
michael@0 656 ${prototypeID},
michael@0 657 ${depth}
michael@0 658 };
michael@0 659 """,
michael@0 660 slotCount=slotCount,
michael@0 661 ctorname=ctorname,
michael@0 662 hasInstance=hasinstance,
michael@0 663 hooks=NativePropertyHooks(self.descriptor),
michael@0 664 name=self.descriptor.interface.identifier.name,
michael@0 665 prototypeID=prototypeID,
michael@0 666 depth=depth)
michael@0 667
michael@0 668
michael@0 669 class CGList(CGThing):
michael@0 670 """
michael@0 671 Generate code for a list of GCThings. Just concatenates them together, with
michael@0 672 an optional joiner string. "\n" is a common joiner.
michael@0 673 """
michael@0 674 def __init__(self, children, joiner=""):
michael@0 675 CGThing.__init__(self)
michael@0 676 # Make a copy of the kids into a list, because if someone passes in a
michael@0 677 # generator we won't be able to both declare and define ourselves, or
michael@0 678 # define ourselves more than once!
michael@0 679 self.children = list(children)
michael@0 680 self.joiner = joiner
michael@0 681
michael@0 682 def append(self, child):
michael@0 683 self.children.append(child)
michael@0 684
michael@0 685 def prepend(self, child):
michael@0 686 self.children.insert(0, child)
michael@0 687
michael@0 688 def extend(self, kids):
michael@0 689 self.children.extend(kids)
michael@0 690
michael@0 691 def join(self, iterable):
michael@0 692 return self.joiner.join(s for s in iterable if len(s) > 0)
michael@0 693
michael@0 694 def declare(self):
michael@0 695 return self.join(child.declare() for child in self.children if child is not None)
michael@0 696
michael@0 697 def define(self):
michael@0 698 return self.join(child.define() for child in self.children if child is not None)
michael@0 699
michael@0 700 def deps(self):
michael@0 701 deps = set()
michael@0 702 for child in self.children:
michael@0 703 if child is None:
michael@0 704 continue
michael@0 705 deps = deps.union(child.deps())
michael@0 706 return deps
michael@0 707
michael@0 708
michael@0 709 class CGGeneric(CGThing):
michael@0 710 """
michael@0 711 A class that spits out a fixed string into the codegen. Can spit out a
michael@0 712 separate string for the declaration too.
michael@0 713 """
michael@0 714 def __init__(self, define="", declare=""):
michael@0 715 self.declareText = declare
michael@0 716 self.defineText = define
michael@0 717
michael@0 718 def declare(self):
michael@0 719 return self.declareText
michael@0 720
michael@0 721 def define(self):
michael@0 722 return self.defineText
michael@0 723
michael@0 724 def deps(self):
michael@0 725 return set()
michael@0 726
michael@0 727
michael@0 728 class CGIndenter(CGThing):
michael@0 729 """
michael@0 730 A class that takes another CGThing and generates code that indents that
michael@0 731 CGThing by some number of spaces. The default indent is two spaces.
michael@0 732 """
michael@0 733 def __init__(self, child, indentLevel=2, declareOnly=False):
michael@0 734 assert isinstance(child, CGThing)
michael@0 735 CGThing.__init__(self)
michael@0 736 self.child = child
michael@0 737 self.indentLevel = indentLevel
michael@0 738 self.declareOnly = declareOnly
michael@0 739
michael@0 740 def declare(self):
michael@0 741 return indent(self.child.declare(), self.indentLevel)
michael@0 742
michael@0 743 def define(self):
michael@0 744 defn = self.child.define()
michael@0 745 if self.declareOnly:
michael@0 746 return defn
michael@0 747 else:
michael@0 748 return indent(defn, self.indentLevel)
michael@0 749
michael@0 750
michael@0 751 class CGWrapper(CGThing):
michael@0 752 """
michael@0 753 Generic CGThing that wraps other CGThings with pre and post text.
michael@0 754 """
michael@0 755 def __init__(self, child, pre="", post="", declarePre=None,
michael@0 756 declarePost=None, definePre=None, definePost=None,
michael@0 757 declareOnly=False, defineOnly=False, reindent=False):
michael@0 758 CGThing.__init__(self)
michael@0 759 self.child = child
michael@0 760 self.declarePre = declarePre or pre
michael@0 761 self.declarePost = declarePost or post
michael@0 762 self.definePre = definePre or pre
michael@0 763 self.definePost = definePost or post
michael@0 764 self.declareOnly = declareOnly
michael@0 765 self.defineOnly = defineOnly
michael@0 766 self.reindent = reindent
michael@0 767
michael@0 768 def declare(self):
michael@0 769 if self.defineOnly:
michael@0 770 return ''
michael@0 771 decl = self.child.declare()
michael@0 772 if self.reindent:
michael@0 773 decl = self.reindentString(decl, self.declarePre)
michael@0 774 return self.declarePre + decl + self.declarePost
michael@0 775
michael@0 776 def define(self):
michael@0 777 if self.declareOnly:
michael@0 778 return ''
michael@0 779 defn = self.child.define()
michael@0 780 if self.reindent:
michael@0 781 defn = self.reindentString(defn, self.definePre)
michael@0 782 return self.definePre + defn + self.definePost
michael@0 783
michael@0 784 @staticmethod
michael@0 785 def reindentString(stringToIndent, widthString):
michael@0 786 # We don't use lineStartDetector because we don't want to
michael@0 787 # insert whitespace at the beginning of our _first_ line.
michael@0 788 # Use the length of the last line of width string, in case
michael@0 789 # it is a multiline string.
michael@0 790 lastLineWidth = len(widthString.splitlines()[-1])
michael@0 791 return stripTrailingWhitespace(
michael@0 792 stringToIndent.replace("\n", "\n" + (" " * lastLineWidth)))
michael@0 793
michael@0 794 def deps(self):
michael@0 795 return self.child.deps()
michael@0 796
michael@0 797
michael@0 798 class CGIfWrapper(CGList):
michael@0 799 def __init__(self, child, condition):
michael@0 800 CGList.__init__(self, [
michael@0 801 CGWrapper(CGGeneric(condition), pre="if (", post=") {\n", reindent=True),
michael@0 802 CGIndenter(child),
michael@0 803 CGGeneric("}\n")
michael@0 804 ])
michael@0 805
michael@0 806
michael@0 807 class CGIfElseWrapper(CGList):
michael@0 808 def __init__(self, condition, ifTrue, ifFalse):
michael@0 809 CGList.__init__(self, [
michael@0 810 CGWrapper(CGGeneric(condition), pre="if (", post=") {\n", reindent=True),
michael@0 811 CGIndenter(ifTrue),
michael@0 812 CGGeneric("} else {\n"),
michael@0 813 CGIndenter(ifFalse),
michael@0 814 CGGeneric("}\n")
michael@0 815 ])
michael@0 816
michael@0 817
michael@0 818 class CGElseChain(CGThing):
michael@0 819 """
michael@0 820 Concatenate if statements in an if-else-if-else chain.
michael@0 821 """
michael@0 822 def __init__(self, children):
michael@0 823 self.children = [c for c in children if c is not None]
michael@0 824
michael@0 825 def declare(self):
michael@0 826 assert False
michael@0 827
michael@0 828 def define(self):
michael@0 829 if not self.children:
michael@0 830 return ""
michael@0 831 s = self.children[0].define()
michael@0 832 assert s.endswith("\n")
michael@0 833 for child in self.children[1:]:
michael@0 834 code = child.define()
michael@0 835 assert code.startswith("if") or code.startswith("{")
michael@0 836 assert code.endswith("\n")
michael@0 837 s = s.rstrip() + " else " + code
michael@0 838 return s
michael@0 839
michael@0 840
michael@0 841 class CGTemplatedType(CGWrapper):
michael@0 842 def __init__(self, templateName, child, isConst=False, isReference=False):
michael@0 843 const = "const " if isConst else ""
michael@0 844 pre = "%s%s<" % (const, templateName)
michael@0 845 ref = "&" if isReference else ""
michael@0 846 post = ">%s" % ref
michael@0 847 CGWrapper.__init__(self, child, pre=pre, post=post)
michael@0 848
michael@0 849
michael@0 850 class CGNamespace(CGWrapper):
michael@0 851 def __init__(self, namespace, child, declareOnly=False):
michael@0 852 pre = "namespace %s {\n" % namespace
michael@0 853 post = "} // namespace %s\n" % namespace
michael@0 854 CGWrapper.__init__(self, child, pre=pre, post=post,
michael@0 855 declareOnly=declareOnly)
michael@0 856
michael@0 857 @staticmethod
michael@0 858 def build(namespaces, child, declareOnly=False):
michael@0 859 """
michael@0 860 Static helper method to build multiple wrapped namespaces.
michael@0 861 """
michael@0 862 if not namespaces:
michael@0 863 return CGWrapper(child, declareOnly=declareOnly)
michael@0 864 inner = CGNamespace.build(namespaces[1:], child, declareOnly=declareOnly)
michael@0 865 return CGNamespace(namespaces[0], inner, declareOnly=declareOnly)
michael@0 866
michael@0 867
michael@0 868 class CGIncludeGuard(CGWrapper):
michael@0 869 """
michael@0 870 Generates include guards for a header.
michael@0 871 """
michael@0 872 def __init__(self, prefix, child):
michael@0 873 """|prefix| is the filename without the extension."""
michael@0 874 define = 'mozilla_dom_%s_h' % prefix
michael@0 875 CGWrapper.__init__(self, child,
michael@0 876 declarePre='#ifndef %s\n#define %s\n\n' % (define, define),
michael@0 877 declarePost='\n#endif // %s\n' % define)
michael@0 878
michael@0 879
michael@0 880 def getRelevantProviders(descriptor, config):
michael@0 881 if descriptor is not None:
michael@0 882 return [descriptor]
michael@0 883 # Do both the non-worker and worker versions
michael@0 884 return [
michael@0 885 config.getDescriptorProvider(False),
michael@0 886 config.getDescriptorProvider(True)
michael@0 887 ]
michael@0 888
michael@0 889
michael@0 890 def getAllTypes(descriptors, dictionaries, callbacks):
michael@0 891 """
michael@0 892 Generate all the types we're dealing with. For each type, a tuple
michael@0 893 containing type, descriptor, dictionary is yielded. The
michael@0 894 descriptor and dictionary can be None if the type does not come
michael@0 895 from a descriptor or dictionary; they will never both be non-None.
michael@0 896 """
michael@0 897 for d in descriptors:
michael@0 898 if d.interface.isExternal():
michael@0 899 continue
michael@0 900 for t in getTypesFromDescriptor(d):
michael@0 901 yield (t, d, None)
michael@0 902 for dictionary in dictionaries:
michael@0 903 for t in getTypesFromDictionary(dictionary):
michael@0 904 yield (t, None, dictionary)
michael@0 905 for callback in callbacks:
michael@0 906 for t in getTypesFromCallback(callback):
michael@0 907 yield (t, None, None)
michael@0 908
michael@0 909
michael@0 910 class CGHeaders(CGWrapper):
michael@0 911 """
michael@0 912 Generates the appropriate include statements.
michael@0 913 """
michael@0 914 def __init__(self, descriptors, dictionaries, callbacks,
michael@0 915 callbackDescriptors,
michael@0 916 declareIncludes, defineIncludes, prefix, child,
michael@0 917 config=None, jsImplementedDescriptors=[]):
michael@0 918 """
michael@0 919 Builds a set of includes to cover |descriptors|.
michael@0 920
michael@0 921 Also includes the files in |declareIncludes| in the header
michael@0 922 file and the files in |defineIncludes| in the .cpp.
michael@0 923
michael@0 924 |prefix| contains the basename of the file that we generate include
michael@0 925 statements for.
michael@0 926 """
michael@0 927
michael@0 928 # Determine the filenames for which we need headers.
michael@0 929 interfaceDeps = [d.interface for d in descriptors]
michael@0 930 ancestors = []
michael@0 931 for iface in interfaceDeps:
michael@0 932 if iface.parent:
michael@0 933 # We're going to need our parent's prototype, to use as the
michael@0 934 # prototype of our prototype object.
michael@0 935 ancestors.append(iface.parent)
michael@0 936 # And if we have an interface object, we'll need the nearest
michael@0 937 # ancestor with an interface object too, so we can use its
michael@0 938 # interface object as the proto of our interface object.
michael@0 939 if iface.hasInterfaceObject():
michael@0 940 parent = iface.parent
michael@0 941 while parent and not parent.hasInterfaceObject():
michael@0 942 parent = parent.parent
michael@0 943 if parent:
michael@0 944 ancestors.append(parent)
michael@0 945 interfaceDeps.extend(ancestors)
michael@0 946 bindingIncludes = set(self.getDeclarationFilename(d) for d in interfaceDeps)
michael@0 947
michael@0 948 # Grab all the implementation declaration files we need.
michael@0 949 implementationIncludes = set(d.headerFile for d in descriptors if d.needsHeaderInclude())
michael@0 950
michael@0 951 # Grab the includes for checking hasInstance
michael@0 952 interfacesImplementingSelf = set()
michael@0 953 for d in descriptors:
michael@0 954 interfacesImplementingSelf |= d.interface.interfacesImplementingSelf
michael@0 955 implementationIncludes |= set(self.getDeclarationFilename(i) for i in
michael@0 956 interfacesImplementingSelf)
michael@0 957
michael@0 958 # Grab the includes for the things that involve XPCOM interfaces
michael@0 959 hasInstanceIncludes = set("nsIDOM" + d.interface.identifier.name + ".h" for d
michael@0 960 in descriptors if
michael@0 961 NeedsGeneratedHasInstance(d) and
michael@0 962 d.interface.hasInterfacePrototypeObject())
michael@0 963
michael@0 964 # Now find all the things we'll need as arguments because we
michael@0 965 # need to wrap or unwrap them.
michael@0 966 bindingHeaders = set()
michael@0 967 declareIncludes = set(declareIncludes)
michael@0 968
michael@0 969 def addHeadersForType((t, descriptor, dictionary)):
michael@0 970 """
michael@0 971 Add the relevant headers for this type. We use descriptor and
michael@0 972 dictionary, if passed, to decide what to do with interface types.
michael@0 973 """
michael@0 974 assert not descriptor or not dictionary
michael@0 975 # Dictionaries have members that need to be actually
michael@0 976 # declared, not just forward-declared.
michael@0 977 if dictionary:
michael@0 978 headerSet = declareIncludes
michael@0 979 else:
michael@0 980 headerSet = bindingHeaders
michael@0 981 if t.nullable():
michael@0 982 # Need to make sure that Nullable as a dictionary
michael@0 983 # member works.
michael@0 984 headerSet.add("mozilla/dom/Nullable.h")
michael@0 985 unrolled = t.unroll()
michael@0 986 if unrolled.isUnion():
michael@0 987 # UnionConversions.h includes UnionTypes.h
michael@0 988 bindingHeaders.add("mozilla/dom/UnionConversions.h")
michael@0 989 if dictionary:
michael@0 990 # Our dictionary definition is in the header and
michael@0 991 # needs the union type.
michael@0 992 declareIncludes.add("mozilla/dom/UnionTypes.h")
michael@0 993 elif unrolled.isDate():
michael@0 994 if dictionary or jsImplementedDescriptors:
michael@0 995 declareIncludes.add("mozilla/dom/Date.h")
michael@0 996 else:
michael@0 997 bindingHeaders.add("mozilla/dom/Date.h")
michael@0 998 elif unrolled.isInterface():
michael@0 999 if unrolled.isSpiderMonkeyInterface():
michael@0 1000 bindingHeaders.add("jsfriendapi.h")
michael@0 1001 headerSet.add("mozilla/dom/TypedArray.h")
michael@0 1002 else:
michael@0 1003 providers = getRelevantProviders(descriptor, config)
michael@0 1004 for p in providers:
michael@0 1005 try:
michael@0 1006 typeDesc = p.getDescriptor(unrolled.inner.identifier.name)
michael@0 1007 except NoSuchDescriptorError:
michael@0 1008 continue
michael@0 1009 # Dictionaries with interface members rely on the
michael@0 1010 # actual class definition of that interface member
michael@0 1011 # being visible in the binding header, because they
michael@0 1012 # store them in nsRefPtr and have inline
michael@0 1013 # constructors/destructors.
michael@0 1014 #
michael@0 1015 # XXXbz maybe dictionaries with interface members
michael@0 1016 # should just have out-of-line constructors and
michael@0 1017 # destructors?
michael@0 1018 headerSet.add(typeDesc.headerFile)
michael@0 1019 elif unrolled.isDictionary():
michael@0 1020 headerSet.add(self.getDeclarationFilename(unrolled.inner))
michael@0 1021 elif unrolled.isCallback():
michael@0 1022 # Callbacks are both a type and an object
michael@0 1023 headerSet.add(self.getDeclarationFilename(unrolled))
michael@0 1024 elif unrolled.isFloat() and not unrolled.isUnrestricted():
michael@0 1025 # Restricted floats are tested for finiteness
michael@0 1026 bindingHeaders.add("mozilla/FloatingPoint.h")
michael@0 1027 bindingHeaders.add("mozilla/dom/PrimitiveConversions.h")
michael@0 1028 elif unrolled.isEnum():
michael@0 1029 filename = self.getDeclarationFilename(unrolled.inner)
michael@0 1030 declareIncludes.add(filename)
michael@0 1031 elif unrolled.isPrimitive():
michael@0 1032 bindingHeaders.add("mozilla/dom/PrimitiveConversions.h")
michael@0 1033 elif unrolled.isMozMap():
michael@0 1034 if dictionary or jsImplementedDescriptors:
michael@0 1035 declareIncludes.add("mozilla/dom/MozMap.h")
michael@0 1036 else:
michael@0 1037 bindingHeaders.add("mozilla/dom/MozMap.h")
michael@0 1038 # Also add headers for the type the MozMap is
michael@0 1039 # parametrized over, if needed.
michael@0 1040 addHeadersForType((t.inner, descriptor, dictionary))
michael@0 1041
michael@0 1042 map(addHeadersForType,
michael@0 1043 getAllTypes(descriptors + callbackDescriptors, dictionaries,
michael@0 1044 callbacks))
michael@0 1045
michael@0 1046 # Now make sure we're not trying to include the header from inside itself
michael@0 1047 declareIncludes.discard(prefix + ".h")
michael@0 1048
michael@0 1049 # Now for non-callback descriptors make sure we include any
michael@0 1050 # headers needed by Func declarations.
michael@0 1051 for desc in descriptors:
michael@0 1052 if desc.interface.isExternal():
michael@0 1053 continue
michael@0 1054
michael@0 1055 def addHeaderForFunc(func):
michael@0 1056 if func is None:
michael@0 1057 return
michael@0 1058 # Include the right class header, which we can only do
michael@0 1059 # if this is a class member function.
michael@0 1060 if not desc.headerIsDefault:
michael@0 1061 # An explicit header file was provided, assume that we know
michael@0 1062 # what we're doing.
michael@0 1063 return
michael@0 1064
michael@0 1065 if "::" in func:
michael@0 1066 # Strip out the function name and convert "::" to "/"
michael@0 1067 bindingHeaders.add("/".join(func.split("::")[:-1]) + ".h")
michael@0 1068
michael@0 1069 for m in desc.interface.members:
michael@0 1070 addHeaderForFunc(PropertyDefiner.getStringAttr(m, "Func"))
michael@0 1071 # getExtendedAttribute() returns a list, extract the entry.
michael@0 1072 funcList = desc.interface.getExtendedAttribute("Func")
michael@0 1073 if funcList is not None:
michael@0 1074 addHeaderForFunc(funcList[0])
michael@0 1075
michael@0 1076 for d in dictionaries:
michael@0 1077 if d.parent:
michael@0 1078 declareIncludes.add(self.getDeclarationFilename(d.parent))
michael@0 1079 bindingHeaders.add(self.getDeclarationFilename(d))
michael@0 1080
michael@0 1081 for c in callbacks:
michael@0 1082 bindingHeaders.add(self.getDeclarationFilename(c))
michael@0 1083
michael@0 1084 for c in callbackDescriptors:
michael@0 1085 bindingHeaders.add(self.getDeclarationFilename(c.interface))
michael@0 1086
michael@0 1087 if len(callbacks) != 0:
michael@0 1088 # We need CallbackFunction to serve as our parent class
michael@0 1089 declareIncludes.add("mozilla/dom/CallbackFunction.h")
michael@0 1090 # And we need BindingUtils.h so we can wrap "this" objects
michael@0 1091 declareIncludes.add("mozilla/dom/BindingUtils.h")
michael@0 1092
michael@0 1093 if len(callbackDescriptors) != 0 or len(jsImplementedDescriptors) != 0:
michael@0 1094 # We need CallbackInterface to serve as our parent class
michael@0 1095 declareIncludes.add("mozilla/dom/CallbackInterface.h")
michael@0 1096 # And we need BindingUtils.h so we can wrap "this" objects
michael@0 1097 declareIncludes.add("mozilla/dom/BindingUtils.h")
michael@0 1098
michael@0 1099 # Also need to include the headers for ancestors of
michael@0 1100 # JS-implemented interfaces.
michael@0 1101 for jsImplemented in jsImplementedDescriptors:
michael@0 1102 jsParent = jsImplemented.interface.parent
michael@0 1103 if jsParent:
michael@0 1104 parentDesc = jsImplemented.getDescriptor(jsParent.identifier.name)
michael@0 1105 declareIncludes.add(parentDesc.jsImplParentHeader)
michael@0 1106
michael@0 1107 # Let the machinery do its thing.
michael@0 1108 def _includeString(includes):
michael@0 1109 return ''.join(['#include "%s"\n' % i for i in includes]) + '\n'
michael@0 1110 CGWrapper.__init__(self, child,
michael@0 1111 declarePre=_includeString(sorted(declareIncludes)),
michael@0 1112 definePre=_includeString(sorted(set(defineIncludes) |
michael@0 1113 bindingIncludes |
michael@0 1114 bindingHeaders |
michael@0 1115 hasInstanceIncludes |
michael@0 1116 implementationIncludes)))
michael@0 1117
michael@0 1118 @staticmethod
michael@0 1119 def getDeclarationFilename(decl):
michael@0 1120 # Use our local version of the header, not the exported one, so that
michael@0 1121 # test bindings, which don't export, will work correctly.
michael@0 1122 basename = os.path.basename(decl.filename())
michael@0 1123 return basename.replace('.webidl', 'Binding.h')
michael@0 1124
michael@0 1125
michael@0 1126 def SortedDictValues(d):
michael@0 1127 """
michael@0 1128 Returns a list of values from the dict sorted by key.
michael@0 1129 """
michael@0 1130 return [v for k, v in sorted(d.items())]
michael@0 1131
michael@0 1132
michael@0 1133 def UnionTypes(descriptors, dictionaries, callbacks, config):
michael@0 1134 """
michael@0 1135 Returns a tuple containing a set of header filenames to include in
michael@0 1136 UnionTypes.h, a set of header filenames to include in UnionTypes.cpp, a set
michael@0 1137 of tuples containing a type declaration and a boolean if the type is a
michael@0 1138 struct for member types of the unions and a CGList containing CGUnionStructs
michael@0 1139 for every union.
michael@0 1140 """
michael@0 1141
michael@0 1142 # Now find all the things we'll need as arguments and return values because
michael@0 1143 # we need to wrap or unwrap them.
michael@0 1144 headers = set()
michael@0 1145 implheaders = set(["UnionTypes.h"])
michael@0 1146 declarations = set()
michael@0 1147 unionStructs = dict()
michael@0 1148 owningUnionStructs = dict()
michael@0 1149
michael@0 1150 for t, descriptor, dictionary in getAllTypes(descriptors, dictionaries, callbacks):
michael@0 1151 # Add info for the given type. descriptor and dictionary, if present, are
michael@0 1152 # used to figure out what to do with interface types.
michael@0 1153 assert not descriptor or not dictionary
michael@0 1154
michael@0 1155 t = t.unroll()
michael@0 1156 while t.isMozMap():
michael@0 1157 t = t.inner.unroll()
michael@0 1158 if not t.isUnion():
michael@0 1159 continue
michael@0 1160 name = str(t)
michael@0 1161 if name not in unionStructs:
michael@0 1162 providers = getRelevantProviders(descriptor, config)
michael@0 1163 # FIXME: Unions are broken in workers. See bug 809899.
michael@0 1164 unionStructs[name] = CGUnionStruct(t, providers[0])
michael@0 1165 owningUnionStructs[name] = CGUnionStruct(t, providers[0],
michael@0 1166 ownsMembers=True)
michael@0 1167
michael@0 1168 def addHeadersForType(f):
michael@0 1169 if f.nullable():
michael@0 1170 headers.add("mozilla/dom/Nullable.h")
michael@0 1171 f = f.unroll()
michael@0 1172 if f.isInterface():
michael@0 1173 if f.isSpiderMonkeyInterface():
michael@0 1174 headers.add("jsfriendapi.h")
michael@0 1175 headers.add("mozilla/dom/TypedArray.h")
michael@0 1176 else:
michael@0 1177 for p in providers:
michael@0 1178 try:
michael@0 1179 typeDesc = p.getDescriptor(f.inner.identifier.name)
michael@0 1180 except NoSuchDescriptorError:
michael@0 1181 continue
michael@0 1182 if typeDesc.interface.isCallback():
michael@0 1183 # Callback interfaces always use strong refs, so
michael@0 1184 # we need to include the right header to be able
michael@0 1185 # to Release() in our inlined code.
michael@0 1186 headers.add(typeDesc.headerFile)
michael@0 1187 else:
michael@0 1188 declarations.add((typeDesc.nativeType, False))
michael@0 1189 implheaders.add(typeDesc.headerFile)
michael@0 1190 elif f.isDictionary():
michael@0 1191 # For a dictionary, we need to see its declaration in
michael@0 1192 # UnionTypes.h so we have its sizeof and know how big to
michael@0 1193 # make our union.
michael@0 1194 headers.add(CGHeaders.getDeclarationFilename(f.inner))
michael@0 1195 # And if it needs rooting, we need RootedDictionary too
michael@0 1196 if typeNeedsRooting(f):
michael@0 1197 headers.add("mozilla/dom/RootedDictionary.h")
michael@0 1198 elif f.isEnum():
michael@0 1199 # Need to see the actual definition of the enum,
michael@0 1200 # unfortunately.
michael@0 1201 headers.add(CGHeaders.getDeclarationFilename(f.inner))
michael@0 1202 elif f.isCallback():
michael@0 1203 # Callbacks always use strong refs, so we need to include
michael@0 1204 # the right header to be able to Release() in our inlined
michael@0 1205 # code.
michael@0 1206 headers.add(CGHeaders.getDeclarationFilename(f))
michael@0 1207 elif f.isMozMap():
michael@0 1208 headers.add("mozilla/dom/MozMap.h")
michael@0 1209 # And add headers for the type we're parametrized over
michael@0 1210 addHeadersForType(f.inner)
michael@0 1211
michael@0 1212 for f in t.flatMemberTypes:
michael@0 1213 assert not f.nullable()
michael@0 1214 addHeadersForType(f)
michael@0 1215
michael@0 1216 return (headers, implheaders, declarations,
michael@0 1217 CGList(SortedDictValues(unionStructs) +
michael@0 1218 SortedDictValues(owningUnionStructs),
michael@0 1219 "\n"))
michael@0 1220
michael@0 1221
michael@0 1222 def UnionConversions(descriptors, dictionaries, callbacks, config):
michael@0 1223 """
michael@0 1224 Returns a CGThing to declare all union argument conversion helper structs.
michael@0 1225 """
michael@0 1226 # Now find all the things we'll need as arguments because we
michael@0 1227 # need to unwrap them.
michael@0 1228 headers = set()
michael@0 1229 unionConversions = dict()
michael@0 1230
michael@0 1231 for t, descriptor, dictionary in getAllTypes(descriptors, dictionaries, callbacks):
michael@0 1232 # Add info for the given type. descriptor and dictionary, if passed, are
michael@0 1233 # used to figure out what to do with interface types.
michael@0 1234 assert not descriptor or not dictionary
michael@0 1235
michael@0 1236 t = t.unroll()
michael@0 1237 if not t.isUnion():
michael@0 1238 continue
michael@0 1239 name = str(t)
michael@0 1240 if name not in unionConversions:
michael@0 1241 providers = getRelevantProviders(descriptor, config)
michael@0 1242 unionConversions[name] = CGUnionConversionStruct(t, providers[0])
michael@0 1243 def addHeadersForType(f, providers):
michael@0 1244 f = f.unroll()
michael@0 1245 if f.isInterface():
michael@0 1246 if f.isSpiderMonkeyInterface():
michael@0 1247 headers.add("jsfriendapi.h")
michael@0 1248 headers.add("mozilla/dom/TypedArray.h")
michael@0 1249 elif f.inner.isExternal():
michael@0 1250 providers = getRelevantProviders(descriptor, config)
michael@0 1251 for p in providers:
michael@0 1252 try:
michael@0 1253 typeDesc = p.getDescriptor(f.inner.identifier.name)
michael@0 1254 except NoSuchDescriptorError:
michael@0 1255 continue
michael@0 1256 headers.add(typeDesc.headerFile)
michael@0 1257 else:
michael@0 1258 headers.add(CGHeaders.getDeclarationFilename(f.inner))
michael@0 1259 # Check for whether we have a possibly-XPConnect-implemented
michael@0 1260 # interface. If we do, the right descriptor will come from
michael@0 1261 # providers[0], because that would be the non-worker
michael@0 1262 # descriptor provider, if we have one at all.
michael@0 1263 if (f.isGeckoInterface() and
michael@0 1264 providers[0].getDescriptor(f.inner.identifier.name).hasXPConnectImpls):
michael@0 1265 headers.add("nsDOMQS.h")
michael@0 1266 elif f.isDictionary():
michael@0 1267 headers.add(CGHeaders.getDeclarationFilename(f.inner))
michael@0 1268 elif f.isPrimitive():
michael@0 1269 headers.add("mozilla/dom/PrimitiveConversions.h")
michael@0 1270 elif f.isMozMap():
michael@0 1271 headers.add("mozilla/dom/MozMap.h")
michael@0 1272 # And the internal type of the MozMap
michael@0 1273 addHeadersForType(f.inner, providers)
michael@0 1274
michael@0 1275 for f in t.flatMemberTypes:
michael@0 1276 addHeadersForType(f, providers)
michael@0 1277
michael@0 1278 return (headers,
michael@0 1279 CGWrapper(CGList(SortedDictValues(unionConversions), "\n"),
michael@0 1280 post="\n\n"))
michael@0 1281
michael@0 1282
michael@0 1283 class Argument():
michael@0 1284 """
michael@0 1285 A class for outputting the type and name of an argument
michael@0 1286 """
michael@0 1287 def __init__(self, argType, name, default=None):
michael@0 1288 self.argType = argType
michael@0 1289 self.name = name
michael@0 1290 self.default = default
michael@0 1291
michael@0 1292 def declare(self):
michael@0 1293 string = self.argType + ' ' + self.name
michael@0 1294 if self.default is not None:
michael@0 1295 string += " = " + self.default
michael@0 1296 return string
michael@0 1297
michael@0 1298 def define(self):
michael@0 1299 return self.argType + ' ' + self.name
michael@0 1300
michael@0 1301
michael@0 1302 class CGAbstractMethod(CGThing):
michael@0 1303 """
michael@0 1304 An abstract class for generating code for a method. Subclasses
michael@0 1305 should override definition_body to create the actual code.
michael@0 1306
michael@0 1307 descriptor is the descriptor for the interface the method is associated with
michael@0 1308
michael@0 1309 name is the name of the method as a string
michael@0 1310
michael@0 1311 returnType is the IDLType of the return value
michael@0 1312
michael@0 1313 args is a list of Argument objects
michael@0 1314
michael@0 1315 inline should be True to generate an inline method, whose body is
michael@0 1316 part of the declaration.
michael@0 1317
michael@0 1318 alwaysInline should be True to generate an inline method annotated with
michael@0 1319 MOZ_ALWAYS_INLINE.
michael@0 1320
michael@0 1321 static should be True to generate a static method, which only has
michael@0 1322 a definition.
michael@0 1323
michael@0 1324 If templateArgs is not None it should be a list of strings containing
michael@0 1325 template arguments, and the function will be templatized using those
michael@0 1326 arguments.
michael@0 1327 """
michael@0 1328 def __init__(self, descriptor, name, returnType, args, inline=False, alwaysInline=False, static=False, templateArgs=None):
michael@0 1329 CGThing.__init__(self)
michael@0 1330 self.descriptor = descriptor
michael@0 1331 self.name = name
michael@0 1332 self.returnType = returnType
michael@0 1333 self.args = args
michael@0 1334 self.inline = inline
michael@0 1335 self.alwaysInline = alwaysInline
michael@0 1336 self.static = static
michael@0 1337 self.templateArgs = templateArgs
michael@0 1338
michael@0 1339 def _argstring(self, declare):
michael@0 1340 return ', '.join([a.declare() if declare else a.define() for a in self.args])
michael@0 1341
michael@0 1342 def _template(self):
michael@0 1343 if self.templateArgs is None:
michael@0 1344 return ''
michael@0 1345 return 'template <%s>\n' % ', '.join(self.templateArgs)
michael@0 1346
michael@0 1347 def _decorators(self):
michael@0 1348 decorators = []
michael@0 1349 if self.alwaysInline:
michael@0 1350 decorators.append('MOZ_ALWAYS_INLINE')
michael@0 1351 elif self.inline:
michael@0 1352 decorators.append('inline')
michael@0 1353 if self.static:
michael@0 1354 decorators.append('static')
michael@0 1355 decorators.append(self.returnType)
michael@0 1356 maybeNewline = " " if self.inline else "\n"
michael@0 1357 return ' '.join(decorators) + maybeNewline
michael@0 1358
michael@0 1359 def declare(self):
michael@0 1360 if self.inline:
michael@0 1361 return self._define(True)
michael@0 1362 return "%s%s%s(%s);\n" % (self._template(), self._decorators(), self.name, self._argstring(True))
michael@0 1363
michael@0 1364 def _define(self, fromDeclare=False):
michael@0 1365 return self.definition_prologue(fromDeclare) + self.definition_body() + self.definition_epilogue()
michael@0 1366
michael@0 1367 def define(self):
michael@0 1368 return "" if self.inline else self._define()
michael@0 1369
michael@0 1370 def definition_prologue(self, fromDeclare):
michael@0 1371 return "%s%s%s(%s)\n{\n" % (self._template(), self._decorators(),
michael@0 1372 self.name, self._argstring(fromDeclare))
michael@0 1373
michael@0 1374 def definition_epilogue(self):
michael@0 1375 return "}\n"
michael@0 1376
michael@0 1377 def definition_body(self):
michael@0 1378 assert False # Override me!
michael@0 1379
michael@0 1380
michael@0 1381 class CGAbstractStaticMethod(CGAbstractMethod):
michael@0 1382 """
michael@0 1383 Abstract base class for codegen of implementation-only (no
michael@0 1384 declaration) static methods.
michael@0 1385 """
michael@0 1386 def __init__(self, descriptor, name, returnType, args):
michael@0 1387 CGAbstractMethod.__init__(self, descriptor, name, returnType, args,
michael@0 1388 inline=False, static=True)
michael@0 1389
michael@0 1390 def declare(self):
michael@0 1391 # We only have implementation
michael@0 1392 return ""
michael@0 1393
michael@0 1394
michael@0 1395 class CGAbstractClassHook(CGAbstractStaticMethod):
michael@0 1396 """
michael@0 1397 Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw
michael@0 1398 'this' unwrapping as it assumes that the unwrapped type is always known.
michael@0 1399 """
michael@0 1400 def __init__(self, descriptor, name, returnType, args):
michael@0 1401 CGAbstractStaticMethod.__init__(self, descriptor, name, returnType,
michael@0 1402 args)
michael@0 1403
michael@0 1404 def definition_body_prologue(self):
michael@0 1405 return ("\n" # BOGUS extra blank line at start of function
michael@0 1406 " %s* self = UnwrapDOMObject<%s>(obj);\n" %
michael@0 1407 (self.descriptor.nativeType, self.descriptor.nativeType))
michael@0 1408
michael@0 1409 def definition_body(self):
michael@0 1410 return self.definition_body_prologue() + self.generate_code()
michael@0 1411
michael@0 1412 def generate_code(self):
michael@0 1413 assert False # Override me!
michael@0 1414
michael@0 1415
michael@0 1416 class CGGetJSClassMethod(CGAbstractMethod):
michael@0 1417 def __init__(self, descriptor):
michael@0 1418 CGAbstractMethod.__init__(self, descriptor, 'GetJSClass', 'const JSClass*',
michael@0 1419 [])
michael@0 1420
michael@0 1421 def definition_body(self):
michael@0 1422 return " return Class.ToJSClass();\n"
michael@0 1423
michael@0 1424
michael@0 1425 class CGAddPropertyHook(CGAbstractClassHook):
michael@0 1426 """
michael@0 1427 A hook for addProperty, used to preserve our wrapper from GC.
michael@0 1428 """
michael@0 1429 def __init__(self, descriptor):
michael@0 1430 args = [Argument('JSContext*', 'cx'),
michael@0 1431 Argument('JS::Handle<JSObject*>', 'obj'),
michael@0 1432 Argument('JS::Handle<jsid>', 'id'),
michael@0 1433 Argument('JS::MutableHandle<JS::Value>', 'vp')]
michael@0 1434 CGAbstractClassHook.__init__(self, descriptor, ADDPROPERTY_HOOK_NAME,
michael@0 1435 'bool', args)
michael@0 1436
michael@0 1437 def generate_code(self):
michael@0 1438 assert self.descriptor.wrapperCache
michael@0 1439 return indent(dedent("""
michael@0 1440 // We don't want to preserve if we don't have a wrapper.
michael@0 1441 if (self->GetWrapperPreserveColor()) {
michael@0 1442 PreserveWrapper(self);
michael@0 1443 }
michael@0 1444 return true;
michael@0 1445 """))
michael@0 1446
michael@0 1447
michael@0 1448 def DeferredFinalizeSmartPtr(descriptor):
michael@0 1449 if descriptor.nativeOwnership == 'owned':
michael@0 1450 smartPtr = 'nsAutoPtr'
michael@0 1451 else:
michael@0 1452 smartPtr = 'nsRefPtr'
michael@0 1453 return smartPtr
michael@0 1454
michael@0 1455
michael@0 1456 def finalizeHook(descriptor, hookName, freeOp):
michael@0 1457 finalize = "JSBindingFinalized<%s>::Finalized(self);\n" % descriptor.nativeType
michael@0 1458 if descriptor.wrapperCache:
michael@0 1459 finalize += "ClearWrapper(self, self);\n"
michael@0 1460 if descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
michael@0 1461 finalize += "self->mExpandoAndGeneration.expando = JS::UndefinedValue();\n"
michael@0 1462 if descriptor.interface.getExtendedAttribute("Global"):
michael@0 1463 finalize += "mozilla::dom::FinalizeGlobal(CastToJSFreeOp(%s), obj);\n" % freeOp
michael@0 1464 finalize += ("AddForDeferredFinalization<%s, %s >(self);\n" %
michael@0 1465 (descriptor.nativeType, DeferredFinalizeSmartPtr(descriptor)))
michael@0 1466 return CGIfWrapper(CGGeneric(finalize), "self")
michael@0 1467
michael@0 1468
michael@0 1469 class CGClassFinalizeHook(CGAbstractClassHook):
michael@0 1470 """
michael@0 1471 A hook for finalize, used to release our native object.
michael@0 1472 """
michael@0 1473 def __init__(self, descriptor):
michael@0 1474 args = [Argument('js::FreeOp*', 'fop'), Argument('JSObject*', 'obj')]
michael@0 1475 CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME,
michael@0 1476 'void', args)
michael@0 1477
michael@0 1478 def generate_code(self):
michael@0 1479 return indent(finalizeHook(self.descriptor, self.name, self.args[0].name).define())
michael@0 1480
michael@0 1481
michael@0 1482 class CGClassConstructor(CGAbstractStaticMethod):
michael@0 1483 """
michael@0 1484 JS-visible constructor for our objects
michael@0 1485 """
michael@0 1486 def __init__(self, descriptor, ctor, name=CONSTRUCT_HOOK_NAME):
michael@0 1487 args = [Argument('JSContext*', 'cx'),
michael@0 1488 Argument('unsigned', 'argc'),
michael@0 1489 Argument('JS::Value*', 'vp')]
michael@0 1490 CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args)
michael@0 1491 self._ctor = ctor
michael@0 1492
michael@0 1493 def define(self):
michael@0 1494 if not self._ctor:
michael@0 1495 return ""
michael@0 1496 return CGAbstractStaticMethod.define(self)
michael@0 1497
michael@0 1498 def definition_body(self):
michael@0 1499 return self.generate_code()
michael@0 1500
michael@0 1501 def generate_code(self):
michael@0 1502 # [ChromeOnly] interfaces may only be constructed by chrome.
michael@0 1503 chromeOnlyCheck = ""
michael@0 1504 if isChromeOnly(self._ctor):
michael@0 1505 chromeOnlyCheck = dedent("""
michael@0 1506 if (!nsContentUtils::ThreadsafeIsCallerChrome()) {
michael@0 1507 return ThrowingConstructor(cx, argc, vp);
michael@0 1508 }
michael@0 1509
michael@0 1510 """)
michael@0 1511
michael@0 1512 # Additionally, we want to throw if a caller does a bareword invocation
michael@0 1513 # of a constructor without |new|. We don't enforce this for chrome in
michael@0 1514 # realease builds to avoid the addon compat fallout of making that
michael@0 1515 # change. See bug 916644.
michael@0 1516 #
michael@0 1517 # Figure out the name of our constructor for error reporting purposes.
michael@0 1518 # For unnamed webidl constructors, identifier.name is "constructor" but
michael@0 1519 # the name JS sees is the interface name; for named constructors
michael@0 1520 # identifier.name is the actual name.
michael@0 1521 name = self._ctor.identifier.name
michael@0 1522 if name != "constructor":
michael@0 1523 ctorName = name
michael@0 1524 else:
michael@0 1525 ctorName = self.descriptor.interface.identifier.name
michael@0 1526
michael@0 1527 preamble = fill( # BOGUS extra blank line at beginning of function body
michael@0 1528 """
michael@0 1529
michael@0 1530 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
michael@0 1531 JS::Rooted<JSObject*> obj(cx, &args.callee());
michael@0 1532 $*{chromeOnlyCheck}
michael@0 1533 bool mayInvoke = args.isConstructing();
michael@0 1534 #ifdef RELEASE_BUILD
michael@0 1535 mayInvoke = mayInvoke || nsContentUtils::ThreadsafeIsCallerChrome();
michael@0 1536 #endif // RELEASE_BUILD
michael@0 1537 if (!mayInvoke) {
michael@0 1538 // XXXbz wish I could get the name from the callee instead of
michael@0 1539 // Adding more relocations
michael@0 1540 return ThrowConstructorWithoutNew(cx, "${ctorName}");
michael@0 1541 }
michael@0 1542 """,
michael@0 1543 chromeOnlyCheck=chromeOnlyCheck,
michael@0 1544 ctorName=ctorName)
michael@0 1545
michael@0 1546 name = self._ctor.identifier.name
michael@0 1547 nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
michael@0 1548 callGenerator = CGMethodCall(nativeName, True, self.descriptor,
michael@0 1549 self._ctor, isConstructor=True,
michael@0 1550 constructorName=ctorName)
michael@0 1551 return indent(preamble) + callGenerator.define()
michael@0 1552
michael@0 1553
michael@0 1554 # Encapsulate the constructor in a helper method to share genConstructorBody with CGJSImplMethod.
michael@0 1555 class CGConstructNavigatorObjectHelper(CGAbstractStaticMethod):
michael@0 1556 """
michael@0 1557 Construct a new JS-implemented WebIDL DOM object, for use on navigator.
michael@0 1558 """
michael@0 1559 def __init__(self, descriptor):
michael@0 1560 name = "ConstructNavigatorObjectHelper"
michael@0 1561 args = [Argument('JSContext*', 'cx'),
michael@0 1562 Argument('GlobalObject&', 'global'),
michael@0 1563 Argument('ErrorResult&', 'aRv')]
michael@0 1564 rtype = 'already_AddRefed<%s>' % descriptor.name
michael@0 1565 CGAbstractStaticMethod.__init__(self, descriptor, name, rtype, args)
michael@0 1566
michael@0 1567 def definition_body(self):
michael@0 1568 return indent(genConstructorBody(self.descriptor))
michael@0 1569
michael@0 1570
michael@0 1571 class CGConstructNavigatorObject(CGAbstractMethod):
michael@0 1572 """
michael@0 1573 Wrap a JS-implemented WebIDL object into a JS value, for use on navigator.
michael@0 1574 """
michael@0 1575 def __init__(self, descriptor):
michael@0 1576 name = 'ConstructNavigatorObject'
michael@0 1577 args = [Argument('JSContext*', 'aCx'), Argument('JS::Handle<JSObject*>', 'aObj')]
michael@0 1578 CGAbstractMethod.__init__(self, descriptor, name, 'JSObject*', args)
michael@0 1579
michael@0 1580 def definition_body(self):
michael@0 1581 if not self.descriptor.interface.isJSImplemented():
michael@0 1582 raise TypeError("Only JS-implemented classes are currently supported "
michael@0 1583 "on navigator. See bug 856820.")
michael@0 1584 return indent(fill(
michael@0 1585 """
michael@0 1586 GlobalObject global(aCx, aObj);
michael@0 1587 if (global.Failed()) {
michael@0 1588 return nullptr;
michael@0 1589 }
michael@0 1590 ErrorResult rv;
michael@0 1591 JS::Rooted<JS::Value> v(aCx);
michael@0 1592 { // Scope to make sure |result| goes out of scope while |v| is rooted
michael@0 1593 nsRefPtr<mozilla::dom::${descriptorName}> result = ConstructNavigatorObjectHelper(aCx, global, rv);
michael@0 1594 rv.WouldReportJSException();
michael@0 1595 if (rv.Failed()) {
michael@0 1596 ThrowMethodFailedWithDetails(aCx, rv, "${descriptorName}", "navigatorConstructor");
michael@0 1597 return nullptr;
michael@0 1598 }
michael@0 1599 if (!WrapNewBindingObject(aCx, result, &v)) {
michael@0 1600 //XXX Assertion disabled for now, see bug 991271.
michael@0 1601 MOZ_ASSERT(true || JS_IsExceptionPending(aCx));
michael@0 1602 return nullptr;
michael@0 1603 }
michael@0 1604 }
michael@0 1605 return &v.toObject();
michael@0 1606 """,
michael@0 1607 descriptorName=self.descriptor.name))
michael@0 1608
michael@0 1609
michael@0 1610 class CGClassConstructHookHolder(CGGeneric):
michael@0 1611 def __init__(self, descriptor):
michael@0 1612 if descriptor.interface.ctor():
michael@0 1613 constructHook = CONSTRUCT_HOOK_NAME
michael@0 1614 else:
michael@0 1615 constructHook = "ThrowingConstructor"
michael@0 1616 CGGeneric.__init__(self, fill(
michael@0 1617 """
michael@0 1618 static const JSNativeHolder ${CONSTRUCT_HOOK_NAME}_holder = {
michael@0 1619 ${constructHook},
michael@0 1620 ${hooks}
michael@0 1621 };
michael@0 1622 """,
michael@0 1623 CONSTRUCT_HOOK_NAME=CONSTRUCT_HOOK_NAME,
michael@0 1624 constructHook=constructHook,
michael@0 1625 hooks=NativePropertyHooks(descriptor)))
michael@0 1626
michael@0 1627
michael@0 1628 def NamedConstructorName(m):
michael@0 1629 return '_' + m.identifier.name
michael@0 1630
michael@0 1631
michael@0 1632 class CGNamedConstructors(CGThing):
michael@0 1633 def __init__(self, descriptor):
michael@0 1634 self.descriptor = descriptor
michael@0 1635 CGThing.__init__(self)
michael@0 1636
michael@0 1637 def declare(self):
michael@0 1638 return ""
michael@0 1639
michael@0 1640 def define(self):
michael@0 1641 if len(self.descriptor.interface.namedConstructors) == 0:
michael@0 1642 return ""
michael@0 1643
michael@0 1644 constructorID = "constructors::id::"
michael@0 1645 if self.descriptor.interface.hasInterfaceObject():
michael@0 1646 constructorID += self.descriptor.name
michael@0 1647 else:
michael@0 1648 constructorID += "_ID_Count"
michael@0 1649
michael@0 1650 namedConstructors = ""
michael@0 1651 for n in self.descriptor.interface.namedConstructors:
michael@0 1652 namedConstructors += (
michael@0 1653 "{ \"%s\", { %s, &sNamedConstructorNativePropertyHooks }, %i },\n" %
michael@0 1654 (n.identifier.name, NamedConstructorName(n), methodLength(n)))
michael@0 1655
michael@0 1656 return fill(
michael@0 1657 """
michael@0 1658 const NativePropertyHooks sNamedConstructorNativePropertyHooks = {
michael@0 1659 nullptr,
michael@0 1660 nullptr,
michael@0 1661 { nullptr, nullptr },
michael@0 1662 prototypes::id::${name},
michael@0 1663 ${constructorID},
michael@0 1664 nullptr
michael@0 1665 };
michael@0 1666
michael@0 1667 static const NamedConstructor namedConstructors[] = {
michael@0 1668 $*{namedConstructors}
michael@0 1669 { nullptr, { nullptr, nullptr }, 0 }
michael@0 1670 };
michael@0 1671 """,
michael@0 1672 name=self.descriptor.name,
michael@0 1673 constructorID=constructorID,
michael@0 1674 namedConstructors=namedConstructors)
michael@0 1675
michael@0 1676
michael@0 1677 class CGClassHasInstanceHook(CGAbstractStaticMethod):
michael@0 1678 def __init__(self, descriptor):
michael@0 1679 args = [Argument('JSContext*', 'cx'),
michael@0 1680 Argument('JS::Handle<JSObject*>', 'obj'),
michael@0 1681 Argument('JS::MutableHandle<JS::Value>', 'vp'),
michael@0 1682 Argument('bool*', 'bp')]
michael@0 1683 CGAbstractStaticMethod.__init__(self, descriptor, HASINSTANCE_HOOK_NAME,
michael@0 1684 'bool', args)
michael@0 1685
michael@0 1686 def define(self):
michael@0 1687 if not NeedsGeneratedHasInstance(self.descriptor):
michael@0 1688 return ""
michael@0 1689 return CGAbstractStaticMethod.define(self)
michael@0 1690
michael@0 1691 def definition_body(self):
michael@0 1692 return self.generate_code()
michael@0 1693
michael@0 1694 def generate_code(self):
michael@0 1695 # BOGUS extra blank line at start of function
michael@0 1696 header = dedent("""
michael@0 1697
michael@0 1698 if (!vp.isObject()) {
michael@0 1699 *bp = false;
michael@0 1700 return true;
michael@0 1701 }
michael@0 1702
michael@0 1703 JS::Rooted<JSObject*> instance(cx, &vp.toObject());
michael@0 1704 """)
michael@0 1705 if self.descriptor.interface.hasInterfacePrototypeObject():
michael@0 1706 return indent(
michael@0 1707 header +
michael@0 1708 fill(
michael@0 1709 """
michael@0 1710
michael@0 1711 static_assert(IsBaseOf<nsISupports, ${nativeType}>::value,
michael@0 1712 "HasInstance only works for nsISupports-based classes.");
michael@0 1713
michael@0 1714 bool ok = InterfaceHasInstance(cx, obj, instance, bp);
michael@0 1715 if (!ok || *bp) {
michael@0 1716 return ok;
michael@0 1717 }
michael@0 1718
michael@0 1719 // FIXME Limit this to chrome by checking xpc::AccessCheck::isChrome(obj).
michael@0 1720 nsISupports* native =
michael@0 1721 nsContentUtils::XPConnect()->GetNativeOfWrapper(cx,
michael@0 1722 js::UncheckedUnwrap(instance));
michael@0 1723 nsCOMPtr<nsIDOM${name}> qiResult = do_QueryInterface(native);
michael@0 1724 *bp = !!qiResult;
michael@0 1725 return true;
michael@0 1726
michael@0 1727 """, # BOGUS extra blank line at end of function
michael@0 1728 nativeType=self.descriptor.nativeType,
michael@0 1729 name=self.descriptor.interface.identifier.name))
michael@0 1730
michael@0 1731 hasInstanceCode = dedent("""
michael@0 1732
michael@0 1733 const DOMClass* domClass = GetDOMClass(js::UncheckedUnwrap(instance));
michael@0 1734 *bp = false;
michael@0 1735 if (!domClass) {
michael@0 1736 // Not a DOM object, so certainly not an instance of this interface
michael@0 1737 return true;
michael@0 1738 }
michael@0 1739 """)
michael@0 1740 if self.descriptor.interface.identifier.name == "ChromeWindow":
michael@0 1741 setBp = "*bp = UnwrapDOMObject<nsGlobalWindow>(js::UncheckedUnwrap(instance))->IsChromeWindow()"
michael@0 1742 else:
michael@0 1743 setBp = "*bp = true"
michael@0 1744 # Sort interaces implementing self by name so we get stable output.
michael@0 1745 for iface in sorted(self.descriptor.interface.interfacesImplementingSelf,
michael@0 1746 key=lambda iface: iface.identifier.name):
michael@0 1747 hasInstanceCode += fill(
michael@0 1748 """
michael@0 1749
michael@0 1750 if (domClass->mInterfaceChain[PrototypeTraits<prototypes::id::${name}>::Depth] == prototypes::id::${name}) {
michael@0 1751 ${setBp};
michael@0 1752 return true;
michael@0 1753 }
michael@0 1754 """,
michael@0 1755 name=iface.identifier.name,
michael@0 1756 setBp=setBp)
michael@0 1757 hasInstanceCode += "return true;\n"
michael@0 1758 return indent(header + hasInstanceCode)
michael@0 1759
michael@0 1760
michael@0 1761 def isChromeOnly(m):
michael@0 1762 return m.getExtendedAttribute("ChromeOnly")
michael@0 1763
michael@0 1764
michael@0 1765 def getAvailableInTestFunc(obj):
michael@0 1766 availableIn = obj.getExtendedAttribute("AvailableIn")
michael@0 1767 if availableIn is None:
michael@0 1768 return None
michael@0 1769 assert isinstance(availableIn, list) and len(availableIn) == 1
michael@0 1770 if availableIn[0] == "PrivilegedApps":
michael@0 1771 return "IsInPrivilegedApp"
michael@0 1772 if availableIn[0] == "CertifiedApps":
michael@0 1773 return "IsInCertifiedApp"
michael@0 1774 raise TypeError("Unknown AvailableIn value '%s'" % availableIn[0])
michael@0 1775
michael@0 1776
michael@0 1777 class MemberCondition:
michael@0 1778 """
michael@0 1779 An object representing the condition for a member to actually be
michael@0 1780 exposed. Any of pref, func, and available can be None. If not
michael@0 1781 None, they should be strings that have the pref name (for "pref")
michael@0 1782 or function name (for "func" and "available").
michael@0 1783 """
michael@0 1784 def __init__(self, pref, func, available=None):
michael@0 1785 assert pref is None or isinstance(pref, str)
michael@0 1786 assert func is None or isinstance(func, str)
michael@0 1787 assert available is None or isinstance(available, str)
michael@0 1788 self.pref = pref
michael@0 1789
michael@0 1790 def toFuncPtr(val):
michael@0 1791 if val is None:
michael@0 1792 return "nullptr"
michael@0 1793 return "&" + val
michael@0 1794 self.func = toFuncPtr(func)
michael@0 1795 self.available = toFuncPtr(available)
michael@0 1796
michael@0 1797 def __eq__(self, other):
michael@0 1798 return (self.pref == other.pref and self.func == other.func and
michael@0 1799 self.available == other.available)
michael@0 1800
michael@0 1801 def __ne__(self, other):
michael@0 1802 return not self.__eq__(other)
michael@0 1803
michael@0 1804
michael@0 1805 class PropertyDefiner:
michael@0 1806 """
michael@0 1807 A common superclass for defining things on prototype objects.
michael@0 1808
michael@0 1809 Subclasses should implement generateArray to generate the actual arrays of
michael@0 1810 things we're defining. They should also set self.chrome to the list of
michael@0 1811 things only exposed to chrome and self.regular to the list of things exposed
michael@0 1812 to both chrome and web pages.
michael@0 1813 """
michael@0 1814 def __init__(self, descriptor, name):
michael@0 1815 self.descriptor = descriptor
michael@0 1816 self.name = name
michael@0 1817 # self.prefCacheData will store an array of (prefname, bool*)
michael@0 1818 # pairs for our bool var caches. generateArray will fill it
michael@0 1819 # in as needed.
michael@0 1820 self.prefCacheData = []
michael@0 1821
michael@0 1822 def hasChromeOnly(self):
michael@0 1823 return len(self.chrome) > 0
michael@0 1824
michael@0 1825 def hasNonChromeOnly(self):
michael@0 1826 return len(self.regular) > 0
michael@0 1827
michael@0 1828 def variableName(self, chrome):
michael@0 1829 if chrome:
michael@0 1830 if self.hasChromeOnly():
michael@0 1831 return "sChrome" + self.name
michael@0 1832 else:
michael@0 1833 if self.hasNonChromeOnly():
michael@0 1834 return "s" + self.name
michael@0 1835 return "nullptr"
michael@0 1836
michael@0 1837 def usedForXrays(self):
michael@0 1838 # No Xrays in workers.
michael@0 1839 return not self.descriptor.workers
michael@0 1840
michael@0 1841 def __str__(self):
michael@0 1842 # We only need to generate id arrays for things that will end
michael@0 1843 # up used via ResolveProperty or EnumerateProperties.
michael@0 1844 str = self.generateArray(self.regular, self.variableName(False),
michael@0 1845 self.usedForXrays())
michael@0 1846 if self.hasChromeOnly():
michael@0 1847 str += self.generateArray(self.chrome, self.variableName(True),
michael@0 1848 self.usedForXrays())
michael@0 1849 return str
michael@0 1850
michael@0 1851 @staticmethod
michael@0 1852 def getStringAttr(member, name):
michael@0 1853 attr = member.getExtendedAttribute(name)
michael@0 1854 if attr is None:
michael@0 1855 return None
michael@0 1856 # It's a list of strings
michael@0 1857 assert len(attr) == 1
michael@0 1858 assert attr[0] is not None
michael@0 1859 return attr[0]
michael@0 1860
michael@0 1861 @staticmethod
michael@0 1862 def getControllingCondition(interfaceMember):
michael@0 1863 return MemberCondition(PropertyDefiner.getStringAttr(interfaceMember,
michael@0 1864 "Pref"),
michael@0 1865 PropertyDefiner.getStringAttr(interfaceMember,
michael@0 1866 "Func"),
michael@0 1867 getAvailableInTestFunc(interfaceMember))
michael@0 1868
michael@0 1869 def generatePrefableArray(self, array, name, specTemplate, specTerminator,
michael@0 1870 specType, getCondition, getDataTuple, doIdArrays):
michael@0 1871 """
michael@0 1872 This method generates our various arrays.
michael@0 1873
michael@0 1874 array is an array of interface members as passed to generateArray
michael@0 1875
michael@0 1876 name is the name as passed to generateArray
michael@0 1877
michael@0 1878 specTemplate is a template for each entry of the spec array
michael@0 1879
michael@0 1880 specTerminator is a terminator for the spec array (inserted every time
michael@0 1881 our controlling pref changes and at the end of the array)
michael@0 1882
michael@0 1883 specType is the actual typename of our spec
michael@0 1884
michael@0 1885 getCondition is a callback function that takes an array entry and
michael@0 1886 returns the corresponding MemberCondition.
michael@0 1887
michael@0 1888 getDataTuple is a callback function that takes an array entry and
michael@0 1889 returns a tuple suitable for substitution into specTemplate.
michael@0 1890 """
michael@0 1891
michael@0 1892 # We want to generate a single list of specs, but with specTerminator
michael@0 1893 # inserted at every point where the pref name controlling the member
michael@0 1894 # changes. That will make sure the order of the properties as exposed
michael@0 1895 # on the interface and interface prototype objects does not change when
michael@0 1896 # pref control is added to members while still allowing us to define all
michael@0 1897 # the members in the smallest number of JSAPI calls.
michael@0 1898 assert len(array) != 0
michael@0 1899 lastCondition = getCondition(array[0]) # So we won't put a specTerminator
michael@0 1900 # at the very front of the list.
michael@0 1901 specs = []
michael@0 1902 prefableSpecs = []
michael@0 1903
michael@0 1904 prefableTemplate = ' { true, %s, %s, &%s[%d] }'
michael@0 1905 prefCacheTemplate = '&%s[%d].enabled'
michael@0 1906
michael@0 1907 def switchToCondition(props, condition):
michael@0 1908 # Remember the info about where our pref-controlled
michael@0 1909 # booleans live.
michael@0 1910 if condition.pref is not None:
michael@0 1911 props.prefCacheData.append(
michael@0 1912 (condition.pref,
michael@0 1913 prefCacheTemplate % (name, len(prefableSpecs))))
michael@0 1914 # Set up pointers to the new sets of specs inside prefableSpecs
michael@0 1915 prefableSpecs.append(prefableTemplate %
michael@0 1916 (condition.func,
michael@0 1917 condition.available,
michael@0 1918 name + "_specs", len(specs)))
michael@0 1919
michael@0 1920 switchToCondition(self, lastCondition)
michael@0 1921
michael@0 1922 for member in array:
michael@0 1923 curCondition = getCondition(member)
michael@0 1924 if lastCondition != curCondition:
michael@0 1925 # Terminate previous list
michael@0 1926 specs.append(specTerminator)
michael@0 1927 # And switch to our new pref
michael@0 1928 switchToCondition(self, curCondition)
michael@0 1929 lastCondition = curCondition
michael@0 1930 # And the actual spec
michael@0 1931 specs.append(specTemplate % getDataTuple(member))
michael@0 1932 specs.append(specTerminator)
michael@0 1933 prefableSpecs.append(" { false, nullptr }")
michael@0 1934
michael@0 1935 specType = "const " + specType
michael@0 1936 arrays = fill(
michael@0 1937 """
michael@0 1938 static ${specType} ${name}_specs[] = {
michael@0 1939 ${specs}
michael@0 1940 };
michael@0 1941
michael@0 1942 // Can't be const because the pref-enabled boolean needs to be writable
michael@0 1943 static Prefable<${specType}> ${name}[] = {
michael@0 1944 ${prefableSpecs}
michael@0 1945 };
michael@0 1946
michael@0 1947 """,
michael@0 1948 specType=specType,
michael@0 1949 name=name,
michael@0 1950 specs=',\n'.join(specs),
michael@0 1951 prefableSpecs=',\n'.join(prefableSpecs))
michael@0 1952 if doIdArrays:
michael@0 1953 arrays += "static jsid %s_ids[%i];\n\n" % (name, len(specs))
michael@0 1954 return arrays
michael@0 1955
michael@0 1956
michael@0 1957 # The length of a method is the minimum of the lengths of the
michael@0 1958 # argument lists of all its overloads.
michael@0 1959 def overloadLength(arguments):
michael@0 1960 i = len(arguments)
michael@0 1961 while i > 0 and arguments[i - 1].optional:
michael@0 1962 i -= 1
michael@0 1963 return i
michael@0 1964
michael@0 1965
michael@0 1966 def methodLength(method):
michael@0 1967 signatures = method.signatures()
michael@0 1968 return min(overloadLength(arguments) for retType, arguments in signatures)
michael@0 1969
michael@0 1970
michael@0 1971 class MethodDefiner(PropertyDefiner):
michael@0 1972 """
michael@0 1973 A class for defining methods on a prototype object.
michael@0 1974 """
michael@0 1975 def __init__(self, descriptor, name, static):
michael@0 1976 PropertyDefiner.__init__(self, descriptor, name)
michael@0 1977
michael@0 1978 # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822
michael@0 1979 # We should be able to check for special operations without an
michael@0 1980 # identifier. For now we check if the name starts with __
michael@0 1981
michael@0 1982 # Ignore non-static methods for interfaces without a proto object
michael@0 1983 if descriptor.interface.hasInterfacePrototypeObject() or static:
michael@0 1984 methods = [m for m in descriptor.interface.members if
michael@0 1985 m.isMethod() and m.isStatic() == static and
michael@0 1986 not m.isIdentifierLess()]
michael@0 1987 else:
michael@0 1988 methods = []
michael@0 1989 self.chrome = []
michael@0 1990 self.regular = []
michael@0 1991 for m in methods:
michael@0 1992 if m.identifier.name == 'queryInterface':
michael@0 1993 if self.descriptor.workers:
michael@0 1994 continue
michael@0 1995 if m.isStatic():
michael@0 1996 raise TypeError("Legacy queryInterface member shouldn't be static")
michael@0 1997 signatures = m.signatures()
michael@0 1998
michael@0 1999 def argTypeIsIID(arg):
michael@0 2000 return arg.type.inner.isExternal() and arg.type.inner.identifier.name == 'IID'
michael@0 2001 if len(signatures) > 1 or len(signatures[0][1]) > 1 or not argTypeIsIID(signatures[0][1][0]):
michael@0 2002 raise TypeError("There should be only one queryInterface method with 1 argument of type IID")
michael@0 2003
michael@0 2004 # Make sure to not stick QueryInterface on abstract interfaces that
michael@0 2005 # have hasXPConnectImpls (like EventTarget). So only put it on
michael@0 2006 # interfaces that are concrete and all of whose ancestors are abstract.
michael@0 2007 def allAncestorsAbstract(iface):
michael@0 2008 if not iface.parent:
michael@0 2009 return True
michael@0 2010 desc = self.descriptor.getDescriptor(iface.parent.identifier.name)
michael@0 2011 if desc.concrete:
michael@0 2012 return False
michael@0 2013 return allAncestorsAbstract(iface.parent)
michael@0 2014 if (not self.descriptor.interface.hasInterfacePrototypeObject() or
michael@0 2015 not self.descriptor.concrete or
michael@0 2016 not allAncestorsAbstract(self.descriptor.interface)):
michael@0 2017 raise TypeError("QueryInterface is only supported on "
michael@0 2018 "interfaces that are concrete and all "
michael@0 2019 "of whose ancestors are abstract: " +
michael@0 2020 self.descriptor.name)
michael@0 2021 condition = "WantsQueryInterface<%s>::Enabled" % descriptor.nativeType
michael@0 2022 self.regular.append({
michael@0 2023 "name": 'QueryInterface',
michael@0 2024 "methodInfo": False,
michael@0 2025 "length": 1,
michael@0 2026 "flags": "0",
michael@0 2027 "condition": MemberCondition(None, condition)
michael@0 2028 })
michael@0 2029 continue
michael@0 2030
michael@0 2031 method = {
michael@0 2032 "name": m.identifier.name,
michael@0 2033 "methodInfo": not m.isStatic(),
michael@0 2034 "length": methodLength(m),
michael@0 2035 "flags": "JSPROP_ENUMERATE",
michael@0 2036 "condition": PropertyDefiner.getControllingCondition(m),
michael@0 2037 "allowCrossOriginThis": m.getExtendedAttribute("CrossOriginCallable"),
michael@0 2038 "returnsPromise": m.returnsPromise()
michael@0 2039 }
michael@0 2040 if isChromeOnly(m):
michael@0 2041 self.chrome.append(method)
michael@0 2042 else:
michael@0 2043 self.regular.append(method)
michael@0 2044
michael@0 2045 # FIXME Check for an existing iterator on the interface first.
michael@0 2046 if any(m.isGetter() and m.isIndexed() for m in methods):
michael@0 2047 self.regular.append({
michael@0 2048 "name": "@@iterator",
michael@0 2049 "methodInfo": False,
michael@0 2050 "selfHostedName": "ArrayValues",
michael@0 2051 "length": 0,
michael@0 2052 "flags": "JSPROP_ENUMERATE",
michael@0 2053 "condition": MemberCondition(None, None)
michael@0 2054 })
michael@0 2055
michael@0 2056 if not static:
michael@0 2057 stringifier = descriptor.operations['Stringifier']
michael@0 2058 if stringifier:
michael@0 2059 toStringDesc = {
michael@0 2060 "name": "toString",
michael@0 2061 "nativeName": stringifier.identifier.name,
michael@0 2062 "length": 0,
michael@0 2063 "flags": "JSPROP_ENUMERATE",
michael@0 2064 "condition": PropertyDefiner.getControllingCondition(stringifier)
michael@0 2065 }
michael@0 2066 if isChromeOnly(stringifier):
michael@0 2067 self.chrome.append(toStringDesc)
michael@0 2068 else:
michael@0 2069 self.regular.append(toStringDesc)
michael@0 2070 jsonifier = descriptor.operations['Jsonifier']
michael@0 2071 if jsonifier:
michael@0 2072 toJSONDesc = {
michael@0 2073 "name": "toJSON",
michael@0 2074 "nativeName": jsonifier.identifier.name,
michael@0 2075 "length": 0,
michael@0 2076 "flags": "JSPROP_ENUMERATE",
michael@0 2077 "condition": PropertyDefiner.getControllingCondition(jsonifier)
michael@0 2078 }
michael@0 2079 if isChromeOnly(jsonifier):
michael@0 2080 self.chrome.append(toJSONDesc)
michael@0 2081 else:
michael@0 2082 self.regular.append(toJSONDesc)
michael@0 2083 elif (descriptor.interface.isJSImplemented() and
michael@0 2084 descriptor.interface.hasInterfaceObject()):
michael@0 2085 self.chrome.append({
michael@0 2086 "name": '_create',
michael@0 2087 "nativeName": ("%s::_Create" % descriptor.name),
michael@0 2088 "methodInfo": False,
michael@0 2089 "length": 2,
michael@0 2090 "flags": "0",
michael@0 2091 "condition": MemberCondition(None, None)
michael@0 2092 })
michael@0 2093
michael@0 2094 if static:
michael@0 2095 if not descriptor.interface.hasInterfaceObject():
michael@0 2096 # static methods go on the interface object
michael@0 2097 assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
michael@0 2098 else:
michael@0 2099 if not descriptor.interface.hasInterfacePrototypeObject():
michael@0 2100 # non-static methods go on the interface prototype object
michael@0 2101 assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
michael@0 2102
michael@0 2103 def generateArray(self, array, name, doIdArrays):
michael@0 2104 if len(array) == 0:
michael@0 2105 return ""
michael@0 2106
michael@0 2107 def condition(m):
michael@0 2108 return m["condition"]
michael@0 2109
michael@0 2110 def specData(m):
michael@0 2111 if "selfHostedName" in m:
michael@0 2112 selfHostedName = '"%s"' % m["selfHostedName"]
michael@0 2113 assert not m.get("methodInfo", True)
michael@0 2114 accessor = "nullptr"
michael@0 2115 jitinfo = "nullptr"
michael@0 2116 else:
michael@0 2117 selfHostedName = "nullptr"
michael@0 2118 accessor = m.get("nativeName", m["name"])
michael@0 2119 if m.get("methodInfo", True):
michael@0 2120 # Cast this in case the methodInfo is a
michael@0 2121 # JSTypedMethodJitInfo.
michael@0 2122 jitinfo = ("reinterpret_cast<const JSJitInfo*>(&%s_methodinfo)" % accessor)
michael@0 2123 if m.get("allowCrossOriginThis", False):
michael@0 2124 if m.get("returnsPromise", False):
michael@0 2125 raise TypeError("%s returns a Promise but should "
michael@0 2126 "be allowed cross-origin?" %
michael@0 2127 accessor)
michael@0 2128 accessor = "genericCrossOriginMethod"
michael@0 2129 elif self.descriptor.needsSpecialGenericOps():
michael@0 2130 if m.get("returnsPromise", False):
michael@0 2131 raise TypeError("%s returns a Promise but needs "
michael@0 2132 "special generic ops?" %
michael@0 2133 accessor)
michael@0 2134 accessor = "genericMethod"
michael@0 2135 elif m.get("returnsPromise", False):
michael@0 2136 accessor = "GenericPromiseReturningBindingMethod"
michael@0 2137 else:
michael@0 2138 accessor = "GenericBindingMethod"
michael@0 2139 else:
michael@0 2140 if m.get("returnsPromise", False):
michael@0 2141 jitinfo = "&%s_methodinfo" % accessor
michael@0 2142 accessor = "StaticMethodPromiseWrapper"
michael@0 2143 else:
michael@0 2144 jitinfo = "nullptr"
michael@0 2145
michael@0 2146 return (m["name"], accessor, jitinfo, m["length"], m["flags"], selfHostedName)
michael@0 2147
michael@0 2148 return self.generatePrefableArray(
michael@0 2149 array, name,
michael@0 2150 ' JS_FNSPEC("%s", %s, %s, %s, %s, %s)',
michael@0 2151 ' JS_FS_END',
michael@0 2152 'JSFunctionSpec',
michael@0 2153 condition, specData, doIdArrays)
michael@0 2154
michael@0 2155
michael@0 2156 class AttrDefiner(PropertyDefiner):
michael@0 2157 def __init__(self, descriptor, name, static, unforgeable=False):
michael@0 2158 assert not (static and unforgeable)
michael@0 2159 PropertyDefiner.__init__(self, descriptor, name)
michael@0 2160 self.name = name
michael@0 2161 # Ignore non-static attributes for interfaces without a proto object
michael@0 2162 if descriptor.interface.hasInterfacePrototypeObject() or static:
michael@0 2163 attributes = [m for m in descriptor.interface.members if
michael@0 2164 m.isAttr() and m.isStatic() == static and
michael@0 2165 m.isUnforgeable() == unforgeable]
michael@0 2166 else:
michael@0 2167 attributes = []
michael@0 2168 self.chrome = [m for m in attributes if isChromeOnly(m)]
michael@0 2169 self.regular = [m for m in attributes if not isChromeOnly(m)]
michael@0 2170 self.static = static
michael@0 2171 self.unforgeable = unforgeable
michael@0 2172
michael@0 2173 if static:
michael@0 2174 if not descriptor.interface.hasInterfaceObject():
michael@0 2175 # static attributes go on the interface object
michael@0 2176 assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
michael@0 2177 else:
michael@0 2178 if not descriptor.interface.hasInterfacePrototypeObject():
michael@0 2179 # non-static attributes go on the interface prototype object
michael@0 2180 assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
michael@0 2181
michael@0 2182 def generateArray(self, array, name, doIdArrays):
michael@0 2183 if len(array) == 0:
michael@0 2184 return ""
michael@0 2185
michael@0 2186 def flags(attr):
michael@0 2187 unforgeable = " | JSPROP_PERMANENT" if self.unforgeable else ""
michael@0 2188 return ("JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS" +
michael@0 2189 unforgeable)
michael@0 2190
michael@0 2191 def getter(attr):
michael@0 2192 if self.static:
michael@0 2193 accessor = 'get_' + attr.identifier.name
michael@0 2194 jitinfo = "nullptr"
michael@0 2195 else:
michael@0 2196 if attr.hasLenientThis():
michael@0 2197 accessor = "genericLenientGetter"
michael@0 2198 elif attr.getExtendedAttribute("CrossOriginReadable"):
michael@0 2199 accessor = "genericCrossOriginGetter"
michael@0 2200 elif self.descriptor.needsSpecialGenericOps():
michael@0 2201 accessor = "genericGetter"
michael@0 2202 else:
michael@0 2203 accessor = "GenericBindingGetter"
michael@0 2204 jitinfo = "&%s_getterinfo" % attr.identifier.name
michael@0 2205 return "{ { JS_CAST_NATIVE_TO(%s, JSPropertyOp), %s } }" % \
michael@0 2206 (accessor, jitinfo)
michael@0 2207
michael@0 2208 def setter(attr):
michael@0 2209 if (attr.readonly and
michael@0 2210 attr.getExtendedAttribute("PutForwards") is None and
michael@0 2211 attr.getExtendedAttribute("Replaceable") is None):
michael@0 2212 return "JSOP_NULLWRAPPER"
michael@0 2213 if self.static:
michael@0 2214 accessor = 'set_' + attr.identifier.name
michael@0 2215 jitinfo = "nullptr"
michael@0 2216 else:
michael@0 2217 if attr.hasLenientThis():
michael@0 2218 accessor = "genericLenientSetter"
michael@0 2219 elif attr.getExtendedAttribute("CrossOriginWritable"):
michael@0 2220 accessor = "genericCrossOriginSetter"
michael@0 2221 elif self.descriptor.needsSpecialGenericOps():
michael@0 2222 accessor = "genericSetter"
michael@0 2223 else:
michael@0 2224 accessor = "GenericBindingSetter"
michael@0 2225 jitinfo = "&%s_setterinfo" % attr.identifier.name
michael@0 2226 return "{ { JS_CAST_NATIVE_TO(%s, JSStrictPropertyOp), %s } }" % \
michael@0 2227 (accessor, jitinfo)
michael@0 2228
michael@0 2229 def specData(attr):
michael@0 2230 return (attr.identifier.name, flags(attr), getter(attr),
michael@0 2231 setter(attr))
michael@0 2232
michael@0 2233 return self.generatePrefableArray(
michael@0 2234 array, name,
michael@0 2235 ' { "%s", %s, %s, %s}',
michael@0 2236 ' JS_PS_END',
michael@0 2237 'JSPropertySpec',
michael@0 2238 PropertyDefiner.getControllingCondition, specData, doIdArrays)
michael@0 2239
michael@0 2240
michael@0 2241 class ConstDefiner(PropertyDefiner):
michael@0 2242 """
michael@0 2243 A class for definining constants on the interface object
michael@0 2244 """
michael@0 2245 def __init__(self, descriptor, name):
michael@0 2246 PropertyDefiner.__init__(self, descriptor, name)
michael@0 2247 self.name = name
michael@0 2248 constants = [m for m in descriptor.interface.members if m.isConst()]
michael@0 2249 self.chrome = [m for m in constants if isChromeOnly(m)]
michael@0 2250 self.regular = [m for m in constants if not isChromeOnly(m)]
michael@0 2251
michael@0 2252 def generateArray(self, array, name, doIdArrays):
michael@0 2253 if len(array) == 0:
michael@0 2254 return ""
michael@0 2255
michael@0 2256 def specData(const):
michael@0 2257 return (const.identifier.name,
michael@0 2258 convertConstIDLValueToJSVal(const.value))
michael@0 2259
michael@0 2260 return self.generatePrefableArray(
michael@0 2261 array, name,
michael@0 2262 ' { "%s", %s }',
michael@0 2263 ' { 0, JS::UndefinedValue() }',
michael@0 2264 'ConstantSpec',
michael@0 2265 PropertyDefiner.getControllingCondition, specData, doIdArrays)
michael@0 2266
michael@0 2267
michael@0 2268 class PropertyArrays():
michael@0 2269 def __init__(self, descriptor):
michael@0 2270 self.staticMethods = MethodDefiner(descriptor, "StaticMethods",
michael@0 2271 static=True)
michael@0 2272 self.staticAttrs = AttrDefiner(descriptor, "StaticAttributes",
michael@0 2273 static=True)
michael@0 2274 self.methods = MethodDefiner(descriptor, "Methods", static=False)
michael@0 2275 self.attrs = AttrDefiner(descriptor, "Attributes", static=False)
michael@0 2276 self.unforgeableAttrs = AttrDefiner(descriptor, "UnforgeableAttributes",
michael@0 2277 static=False, unforgeable=True)
michael@0 2278 self.consts = ConstDefiner(descriptor, "Constants")
michael@0 2279
michael@0 2280 @staticmethod
michael@0 2281 def arrayNames():
michael@0 2282 return ["staticMethods", "staticAttrs", "methods", "attrs",
michael@0 2283 "unforgeableAttrs", "consts"]
michael@0 2284
michael@0 2285 def hasChromeOnly(self):
michael@0 2286 return any(getattr(self, a).hasChromeOnly() for a in self.arrayNames())
michael@0 2287
michael@0 2288 def hasNonChromeOnly(self):
michael@0 2289 return any(getattr(self, a).hasNonChromeOnly() for a in self.arrayNames())
michael@0 2290
michael@0 2291 def __str__(self):
michael@0 2292 define = ""
michael@0 2293 for array in self.arrayNames():
michael@0 2294 define += str(getattr(self, array))
michael@0 2295 return define
michael@0 2296
michael@0 2297
michael@0 2298 class CGNativeProperties(CGList):
michael@0 2299 def __init__(self, descriptor, properties):
michael@0 2300 def generateNativeProperties(name, chrome):
michael@0 2301 def check(p):
michael@0 2302 return p.hasChromeOnly() if chrome else p.hasNonChromeOnly()
michael@0 2303
michael@0 2304 nativeProps = []
michael@0 2305 for array in properties.arrayNames():
michael@0 2306 propertyArray = getattr(properties, array)
michael@0 2307 if check(propertyArray):
michael@0 2308 if propertyArray.usedForXrays():
michael@0 2309 ids = "%(name)s_ids"
michael@0 2310 else:
michael@0 2311 ids = "nullptr"
michael@0 2312 props = "%(name)s, " + ids + ", %(name)s_specs"
michael@0 2313 props = (props % {'name': propertyArray.variableName(chrome)})
michael@0 2314 else:
michael@0 2315 props = "nullptr, nullptr, nullptr"
michael@0 2316 nativeProps.append(CGGeneric(props))
michael@0 2317 return CGWrapper(CGIndenter(CGList(nativeProps, ",\n")),
michael@0 2318 pre="static const NativeProperties %s = {\n" % name,
michael@0 2319 post="\n};\n")
michael@0 2320
michael@0 2321 nativeProperties = []
michael@0 2322 if properties.hasNonChromeOnly():
michael@0 2323 nativeProperties.append(
michael@0 2324 generateNativeProperties("sNativeProperties", False))
michael@0 2325 if properties.hasChromeOnly():
michael@0 2326 nativeProperties.append(
michael@0 2327 generateNativeProperties("sChromeOnlyNativeProperties", True))
michael@0 2328
michael@0 2329 CGList.__init__(self, nativeProperties, "\n")
michael@0 2330
michael@0 2331 def declare(self):
michael@0 2332 return ""
michael@0 2333
michael@0 2334 def define(self):
michael@0 2335 # BOGUSly strip off a newline
michael@0 2336 return CGList.define(self).rstrip()
michael@0 2337
michael@0 2338
michael@0 2339 class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
michael@0 2340 """
michael@0 2341 Generate the CreateInterfaceObjects method for an interface descriptor.
michael@0 2342
michael@0 2343 properties should be a PropertyArrays instance.
michael@0 2344 """
michael@0 2345 def __init__(self, descriptor, properties):
michael@0 2346 args = [Argument('JSContext*', 'aCx'),
michael@0 2347 Argument('JS::Handle<JSObject*>', 'aGlobal'),
michael@0 2348 Argument('ProtoAndIfaceCache&', 'aProtoAndIfaceCache'),
michael@0 2349 Argument('bool', 'aDefineOnGlobal')]
michael@0 2350 CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'void', args)
michael@0 2351 self.properties = properties
michael@0 2352
michael@0 2353 def definition_body(self):
michael@0 2354 if len(self.descriptor.prototypeChain) == 1:
michael@0 2355 parentProtoType = "Rooted"
michael@0 2356 if self.descriptor.interface.getExtendedAttribute("ArrayClass"):
michael@0 2357 getParentProto = "aCx, JS_GetArrayPrototype(aCx, aGlobal)"
michael@0 2358 else:
michael@0 2359 getParentProto = "aCx, JS_GetObjectPrototype(aCx, aGlobal)"
michael@0 2360 else:
michael@0 2361 parentProtoName = self.descriptor.prototypeChain[-2]
michael@0 2362 parentDesc = self.descriptor.getDescriptor(parentProtoName)
michael@0 2363 if parentDesc.workers:
michael@0 2364 parentProtoName += '_workers'
michael@0 2365 getParentProto = ("%s::GetProtoObject(aCx, aGlobal)" %
michael@0 2366 toBindingNamespace(parentProtoName))
michael@0 2367 parentProtoType = "Handle"
michael@0 2368
michael@0 2369 parentWithInterfaceObject = self.descriptor.interface.parent
michael@0 2370 while (parentWithInterfaceObject and
michael@0 2371 not parentWithInterfaceObject.hasInterfaceObject()):
michael@0 2372 parentWithInterfaceObject = parentWithInterfaceObject.parent
michael@0 2373 if parentWithInterfaceObject:
michael@0 2374 parentIfaceName = parentWithInterfaceObject.identifier.name
michael@0 2375 parentDesc = self.descriptor.getDescriptor(parentIfaceName)
michael@0 2376 if parentDesc.workers:
michael@0 2377 parentIfaceName += "_workers"
michael@0 2378 getConstructorProto = ("%s::GetConstructorObject(aCx, aGlobal)" %
michael@0 2379 toBindingNamespace(parentIfaceName))
michael@0 2380 constructorProtoType = "Handle"
michael@0 2381 else:
michael@0 2382 getConstructorProto = "aCx, JS_GetFunctionPrototype(aCx, aGlobal)"
michael@0 2383 constructorProtoType = "Rooted"
michael@0 2384
michael@0 2385 needInterfaceObject = self.descriptor.interface.hasInterfaceObject()
michael@0 2386 needInterfacePrototypeObject = self.descriptor.interface.hasInterfacePrototypeObject()
michael@0 2387
michael@0 2388 # if we don't need to create anything, why are we generating this?
michael@0 2389 assert needInterfaceObject or needInterfacePrototypeObject
michael@0 2390
michael@0 2391 idsToInit = []
michael@0 2392 # There is no need to init any IDs in workers, because worker bindings
michael@0 2393 # don't have Xrays.
michael@0 2394 if not self.descriptor.workers:
michael@0 2395 for var in self.properties.arrayNames():
michael@0 2396 props = getattr(self.properties, var)
michael@0 2397 # We only have non-chrome ids to init if we have no chrome ids.
michael@0 2398 if props.hasChromeOnly():
michael@0 2399 idsToInit.append(props.variableName(True))
michael@0 2400 if props.hasNonChromeOnly():
michael@0 2401 idsToInit.append(props.variableName(False))
michael@0 2402 if len(idsToInit) > 0:
michael@0 2403 initIdCalls = ["!InitIds(aCx, %s, %s_ids)" % (varname, varname)
michael@0 2404 for varname in idsToInit]
michael@0 2405 idsInitedFlag = CGGeneric("static bool sIdsInited = false;\n")
michael@0 2406 setFlag = CGGeneric("sIdsInited = true;\n")
michael@0 2407 initIdConditionals = [CGIfWrapper(CGGeneric("return;\n"), call)
michael@0 2408 for call in initIdCalls]
michael@0 2409 initIds = CGList([idsInitedFlag,
michael@0 2410 CGIfWrapper(CGList(initIdConditionals + [setFlag]),
michael@0 2411 "!sIdsInited && NS_IsMainThread()")])
michael@0 2412 else:
michael@0 2413 initIds = None
michael@0 2414
michael@0 2415 prefCacheData = []
michael@0 2416 for var in self.properties.arrayNames():
michael@0 2417 props = getattr(self.properties, var)
michael@0 2418 prefCacheData.extend(props.prefCacheData)
michael@0 2419 if len(prefCacheData) != 0:
michael@0 2420 prefCacheData = [
michael@0 2421 CGGeneric('Preferences::AddBoolVarCache(%s, "%s");\n' % (ptr, pref))
michael@0 2422 for pref, ptr in prefCacheData]
michael@0 2423 prefCache = CGWrapper(CGIndenter(CGList(prefCacheData)),
michael@0 2424 pre=("static bool sPrefCachesInited = false;\n"
michael@0 2425 "if (!sPrefCachesInited) {\n"
michael@0 2426 " sPrefCachesInited = true;\n"),
michael@0 2427 post="}\n")
michael@0 2428 else:
michael@0 2429 prefCache = None
michael@0 2430
michael@0 2431 if UseHolderForUnforgeable(self.descriptor):
michael@0 2432 createUnforgeableHolder = CGGeneric(dedent("""
michael@0 2433 JS::Rooted<JSObject*> unforgeableHolder(aCx,
michael@0 2434 JS_NewObjectWithGivenProto(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
michael@0 2435 if (!unforgeableHolder) {
michael@0 2436 return;
michael@0 2437 }
michael@0 2438 """))
michael@0 2439 defineUnforgeables = InitUnforgeablePropertiesOnObject(self.descriptor,
michael@0 2440 "unforgeableHolder",
michael@0 2441 self.properties)
michael@0 2442 createUnforgeableHolder = CGList(
michael@0 2443 [createUnforgeableHolder, defineUnforgeables])
michael@0 2444 else:
michael@0 2445 createUnforgeableHolder = None
michael@0 2446
michael@0 2447 getParentProto = fill(
michael@0 2448 """
michael@0 2449 JS::${type}<JSObject*> parentProto(${getParentProto});
michael@0 2450 if (!parentProto) {
michael@0 2451 return;
michael@0 2452 }
michael@0 2453 """,
michael@0 2454 type=parentProtoType,
michael@0 2455 getParentProto=getParentProto)
michael@0 2456
michael@0 2457 getConstructorProto = fill(
michael@0 2458 """
michael@0 2459 JS::${type}<JSObject*> constructorProto(${getConstructorProto});
michael@0 2460 if (!constructorProto) {
michael@0 2461 return;
michael@0 2462 }
michael@0 2463 """,
michael@0 2464 type=constructorProtoType,
michael@0 2465 getConstructorProto=getConstructorProto)
michael@0 2466
michael@0 2467 if (needInterfaceObject and
michael@0 2468 self.descriptor.needsConstructHookHolder()):
michael@0 2469 constructHookHolder = "&" + CONSTRUCT_HOOK_NAME + "_holder"
michael@0 2470 else:
michael@0 2471 constructHookHolder = "nullptr"
michael@0 2472 if self.descriptor.interface.ctor():
michael@0 2473 constructArgs = methodLength(self.descriptor.interface.ctor())
michael@0 2474 else:
michael@0 2475 constructArgs = 0
michael@0 2476 if len(self.descriptor.interface.namedConstructors) > 0:
michael@0 2477 namedConstructors = "namedConstructors"
michael@0 2478 else:
michael@0 2479 namedConstructors = "nullptr"
michael@0 2480
michael@0 2481 if needInterfacePrototypeObject:
michael@0 2482 protoClass = "&PrototypeClass.mBase"
michael@0 2483 protoCache = "&aProtoAndIfaceCache.EntrySlotOrCreate(prototypes::id::%s)" % self.descriptor.name
michael@0 2484 else:
michael@0 2485 protoClass = "nullptr"
michael@0 2486 protoCache = "nullptr"
michael@0 2487 if needInterfaceObject:
michael@0 2488 interfaceClass = "&InterfaceObjectClass.mBase"
michael@0 2489 interfaceCache = "&aProtoAndIfaceCache.EntrySlotOrCreate(constructors::id::%s)" % self.descriptor.name
michael@0 2490 else:
michael@0 2491 # We don't have slots to store the named constructors.
michael@0 2492 assert len(self.descriptor.interface.namedConstructors) == 0
michael@0 2493 interfaceClass = "nullptr"
michael@0 2494 interfaceCache = "nullptr"
michael@0 2495
michael@0 2496 if self.descriptor.concrete:
michael@0 2497 domClass = "&Class.mClass"
michael@0 2498 else:
michael@0 2499 domClass = "nullptr"
michael@0 2500
michael@0 2501 if self.properties.hasNonChromeOnly():
michael@0 2502 properties = "&sNativeProperties"
michael@0 2503 else:
michael@0 2504 properties = "nullptr"
michael@0 2505 if self.properties.hasChromeOnly():
michael@0 2506 accessCheck = "nsContentUtils::ThreadsafeIsCallerChrome()"
michael@0 2507 chromeProperties = accessCheck + " ? &sChromeOnlyNativeProperties : nullptr"
michael@0 2508 else:
michael@0 2509 chromeProperties = "nullptr"
michael@0 2510
michael@0 2511 call = fill(
michael@0 2512 """
michael@0 2513 dom::CreateInterfaceObjects(aCx, aGlobal, parentProto,
michael@0 2514 ${protoClass}, ${protoCache},
michael@0 2515 constructorProto, ${interfaceClass}, ${constructHookHolder}, ${constructArgs}, ${namedConstructors},
michael@0 2516 ${interfaceCache},
michael@0 2517 ${domClass},
michael@0 2518 ${properties},
michael@0 2519 ${chromeProperties},
michael@0 2520 ${name}, aDefineOnGlobal);
michael@0 2521 """,
michael@0 2522 protoClass=protoClass,
michael@0 2523 protoCache=protoCache,
michael@0 2524 interfaceClass=interfaceClass,
michael@0 2525 constructHookHolder=constructHookHolder,
michael@0 2526 constructArgs=constructArgs,
michael@0 2527 namedConstructors=namedConstructors,
michael@0 2528 interfaceCache=interfaceCache,
michael@0 2529 domClass=domClass,
michael@0 2530 properties=properties,
michael@0 2531 chromeProperties=chromeProperties,
michael@0 2532 name='"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "nullptr")
michael@0 2533
michael@0 2534 if UseHolderForUnforgeable(self.descriptor):
michael@0 2535 assert needInterfacePrototypeObject
michael@0 2536 setUnforgeableHolder = CGGeneric(fill(
michael@0 2537 """
michael@0 2538 JSObject* proto = aProtoAndIfaceCache.EntrySlotOrCreate(prototypes::id::${name});
michael@0 2539 if (proto) {
michael@0 2540 js::SetReservedSlot(proto, DOM_INTERFACE_PROTO_SLOTS_BASE,
michael@0 2541 JS::ObjectValue(*unforgeableHolder));
michael@0 2542 }
michael@0 2543 """,
michael@0 2544 name=self.descriptor.name))
michael@0 2545 else:
michael@0 2546 setUnforgeableHolder = None
michael@0 2547 functionBody = CGList(
michael@0 2548 [CGGeneric(getParentProto), CGGeneric(getConstructorProto), initIds,
michael@0 2549 prefCache, createUnforgeableHolder, CGGeneric(call), setUnforgeableHolder],
michael@0 2550 "\n")
michael@0 2551 return CGIndenter(functionBody).define()
michael@0 2552
michael@0 2553
michael@0 2554 class CGGetPerInterfaceObject(CGAbstractMethod):
michael@0 2555 """
michael@0 2556 A method for getting a per-interface object (a prototype object or interface
michael@0 2557 constructor object).
michael@0 2558 """
michael@0 2559 def __init__(self, descriptor, name, idPrefix="", extraArgs=[]):
michael@0 2560 args = [Argument('JSContext*', 'aCx'),
michael@0 2561 Argument('JS::Handle<JSObject*>', 'aGlobal')] + extraArgs
michael@0 2562 CGAbstractMethod.__init__(self, descriptor, name,
michael@0 2563 'JS::Handle<JSObject*>', args)
michael@0 2564 self.id = idPrefix + "id::" + self.descriptor.name
michael@0 2565
michael@0 2566 def definition_body(self):
michael@0 2567 # BOGUS extra blank line at the beginning of the code below
michael@0 2568 # BOGUS - should be a blank line between an if-block and following comment below
michael@0 2569 return indent(fill(
michael@0 2570 """
michael@0 2571
michael@0 2572 /* Make sure our global is sane. Hopefully we can remove this sometime */
michael@0 2573 if (!(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL)) {
michael@0 2574 return JS::NullPtr();
michael@0 2575 }
michael@0 2576 /* Check to see whether the interface objects are already installed */
michael@0 2577 ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(aGlobal);
michael@0 2578 if (!protoAndIfaceCache.EntrySlotIfExists(${id})) {
michael@0 2579 CreateInterfaceObjects(aCx, aGlobal, protoAndIfaceCache, aDefineOnGlobal);
michael@0 2580 }
michael@0 2581
michael@0 2582 /*
michael@0 2583 * The object might _still_ be null, but that's OK.
michael@0 2584 *
michael@0 2585 * Calling fromMarkedLocation() is safe because protoAndIfaceCache is
michael@0 2586 * traced by TraceProtoAndIfaceCache() and its contents are never
michael@0 2587 * changed after they have been set.
michael@0 2588 */
michael@0 2589 return JS::Handle<JSObject*>::fromMarkedLocation(protoAndIfaceCache.EntrySlotMustExist(${id}).address());
michael@0 2590 """,
michael@0 2591 id=self.id))
michael@0 2592
michael@0 2593
michael@0 2594 class CGGetProtoObjectMethod(CGGetPerInterfaceObject):
michael@0 2595 """
michael@0 2596 A method for getting the interface prototype object.
michael@0 2597 """
michael@0 2598 def __init__(self, descriptor):
michael@0 2599 CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject",
michael@0 2600 "prototypes::")
michael@0 2601
michael@0 2602 def definition_body(self):
michael@0 2603 # BOGUS extra blank line at start of method
michael@0 2604 return indent(dedent("""
michael@0 2605
michael@0 2606 /* Get the interface prototype object for this class. This will create the
michael@0 2607 object as needed. */
michael@0 2608 bool aDefineOnGlobal = true;
michael@0 2609 """)) + CGGetPerInterfaceObject.definition_body(self)
michael@0 2610
michael@0 2611
michael@0 2612 class CGGetConstructorObjectMethod(CGGetPerInterfaceObject):
michael@0 2613 """
michael@0 2614 A method for getting the interface constructor object.
michael@0 2615 """
michael@0 2616 def __init__(self, descriptor):
michael@0 2617 CGGetPerInterfaceObject.__init__(
michael@0 2618 self, descriptor, "GetConstructorObject",
michael@0 2619 "constructors::",
michael@0 2620 extraArgs=[Argument("bool", "aDefineOnGlobal", "true")])
michael@0 2621
michael@0 2622 def definition_body(self):
michael@0 2623 # BOGUS extra blank line at start of method
michael@0 2624 return indent(dedent("""
michael@0 2625
michael@0 2626 /* Get the interface object for this class. This will create the object as
michael@0 2627 needed. */
michael@0 2628 """)) + CGGetPerInterfaceObject.definition_body(self)
michael@0 2629
michael@0 2630
michael@0 2631 class CGDefineDOMInterfaceMethod(CGAbstractMethod):
michael@0 2632 """
michael@0 2633 A method for resolve hooks to try to lazily define the interface object for
michael@0 2634 a given interface.
michael@0 2635 """
michael@0 2636 def __init__(self, descriptor):
michael@0 2637 args = [Argument('JSContext*', 'aCx'),
michael@0 2638 Argument('JS::Handle<JSObject*>', 'aGlobal'),
michael@0 2639 Argument('JS::Handle<jsid>', 'id'),
michael@0 2640 Argument('bool', 'aDefineOnGlobal')]
michael@0 2641 CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface', 'JSObject*', args)
michael@0 2642
michael@0 2643 def declare(self):
michael@0 2644 if self.descriptor.workers:
michael@0 2645 return ''
michael@0 2646 return CGAbstractMethod.declare(self)
michael@0 2647
michael@0 2648 def define(self):
michael@0 2649 if self.descriptor.workers:
michael@0 2650 return ''
michael@0 2651 return CGAbstractMethod.define(self)
michael@0 2652
michael@0 2653 def definition_body(self):
michael@0 2654 if len(self.descriptor.interface.namedConstructors) > 0:
michael@0 2655 getConstructor = indent(dedent("""
michael@0 2656 JSObject* interfaceObject = GetConstructorObject(aCx, aGlobal, aDefineOnGlobal);
michael@0 2657 if (!interfaceObject) {
michael@0 2658 return nullptr;
michael@0 2659 }
michael@0 2660 for (unsigned slot = DOM_INTERFACE_SLOTS_BASE; slot < JSCLASS_RESERVED_SLOTS(&InterfaceObjectClass.mBase); ++slot) {
michael@0 2661 JSObject* constructor = &js::GetReservedSlot(interfaceObject, slot).toObject();
michael@0 2662 if (JS_GetFunctionId(JS_GetObjectFunction(constructor)) == JSID_TO_STRING(id)) {
michael@0 2663 return constructor;
michael@0 2664 }
michael@0 2665 }
michael@0 2666 return interfaceObject;
michael@0 2667 """))
michael@0 2668 else:
michael@0 2669 getConstructor = " return GetConstructorObject(aCx, aGlobal, aDefineOnGlobal);\n"
michael@0 2670 return getConstructor
michael@0 2671
michael@0 2672
michael@0 2673 class CGConstructorEnabled(CGAbstractMethod):
michael@0 2674 """
michael@0 2675 A method for testing whether we should be exposing this interface
michael@0 2676 object or navigator property. This can perform various tests
michael@0 2677 depending on what conditions are specified on the interface.
michael@0 2678 """
michael@0 2679 def __init__(self, descriptor):
michael@0 2680 CGAbstractMethod.__init__(self, descriptor,
michael@0 2681 'ConstructorEnabled', 'bool',
michael@0 2682 [Argument("JSContext*", "aCx"),
michael@0 2683 Argument("JS::Handle<JSObject*>", "aObj")])
michael@0 2684
michael@0 2685 def definition_body(self):
michael@0 2686 conditions = []
michael@0 2687 iface = self.descriptor.interface
michael@0 2688 pref = iface.getExtendedAttribute("Pref")
michael@0 2689 if pref:
michael@0 2690 assert isinstance(pref, list) and len(pref) == 1
michael@0 2691 conditions.append('Preferences::GetBool("%s")' % pref[0])
michael@0 2692 if iface.getExtendedAttribute("ChromeOnly"):
michael@0 2693 conditions.append("nsContentUtils::ThreadsafeIsCallerChrome()")
michael@0 2694 func = iface.getExtendedAttribute("Func")
michael@0 2695 if func:
michael@0 2696 assert isinstance(func, list) and len(func) == 1
michael@0 2697 conditions.append("%s(aCx, aObj)" % func[0])
michael@0 2698 availableIn = getAvailableInTestFunc(iface)
michael@0 2699 if availableIn:
michael@0 2700 conditions.append("%s(aCx, aObj)" % availableIn)
michael@0 2701 # We should really have some conditions
michael@0 2702 assert len(conditions)
michael@0 2703 body = CGWrapper(CGList((CGGeneric(cond) for cond in conditions),
michael@0 2704 " &&\n"),
michael@0 2705 pre="return ", post=";\n", reindent=True)
michael@0 2706 return CGIndenter(body).define()
michael@0 2707
michael@0 2708
michael@0 2709 def CreateBindingJSObject(descriptor, properties, parent):
michael@0 2710 # We don't always need to root obj, but there are a variety
michael@0 2711 # of cases where we do, so for simplicity, just always root it.
michael@0 2712 objDecl = "JS::Rooted<JSObject*> obj(aCx);\n"
michael@0 2713 if descriptor.proxy:
michael@0 2714 create = fill(
michael@0 2715 """
michael@0 2716 JS::Rooted<JS::Value> proxyPrivateVal(aCx, JS::PrivateValue(aObject));
michael@0 2717 js::ProxyOptions options;
michael@0 2718 options.setClass(&Class.mBase);
michael@0 2719 obj = NewProxyObject(aCx, DOMProxyHandler::getInstance(),
michael@0 2720 proxyPrivateVal, proto, ${parent}, options);
michael@0 2721 if (!obj) {
michael@0 2722 return nullptr;
michael@0 2723 }
michael@0 2724
michael@0 2725 """,
michael@0 2726 parent=parent)
michael@0 2727 if descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
michael@0 2728 create += dedent("""
michael@0 2729 js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO,
michael@0 2730 JS::PrivateValue(&aObject->mExpandoAndGeneration));
michael@0 2731
michael@0 2732 """)
michael@0 2733 else:
michael@0 2734 create = fill(
michael@0 2735 """
michael@0 2736 obj = JS_NewObject(aCx, Class.ToJSClass(), proto, ${parent});
michael@0 2737 if (!obj) {
michael@0 2738 return nullptr;
michael@0 2739 }
michael@0 2740
michael@0 2741 js::SetReservedSlot(obj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(aObject));
michael@0 2742 """,
michael@0 2743 parent=parent)
michael@0 2744 if "Window" in descriptor.interface.identifier.name:
michael@0 2745 create = dedent("""
michael@0 2746 MOZ_ASSERT(false,
michael@0 2747 "Our current reserved slot situation is unsafe for globals. Fix "
michael@0 2748 "bug 760095!");
michael@0 2749 """) + create
michael@0 2750 create = objDecl + create
michael@0 2751
michael@0 2752 if descriptor.nativeOwnership == 'refcounted':
michael@0 2753 create += "NS_ADDREF(aObject);\n"
michael@0 2754 else:
michael@0 2755 create += dedent("""
michael@0 2756 // Make sure the native objects inherit from NonRefcountedDOMObject so that we
michael@0 2757 // log their ctor and dtor.
michael@0 2758 MustInheritFromNonRefcountedDOMObject(aObject);
michael@0 2759 *aTookOwnership = true;
michael@0 2760 """)
michael@0 2761 return create
michael@0 2762
michael@0 2763
michael@0 2764 def InitUnforgeablePropertiesOnObject(descriptor, obj, properties, failureReturnValue=""):
michael@0 2765 """
michael@0 2766 properties is a PropertyArrays instance
michael@0 2767 """
michael@0 2768 defineUnforgeables = fill(
michael@0 2769 """
michael@0 2770 if (!DefineUnforgeableAttributes(aCx, ${obj}, %s)) {
michael@0 2771 return${rv};
michael@0 2772 }
michael@0 2773 """,
michael@0 2774 obj=obj,
michael@0 2775 rv=" " + failureReturnValue if failureReturnValue else "")
michael@0 2776
michael@0 2777 unforgeableAttrs = properties.unforgeableAttrs
michael@0 2778 unforgeables = []
michael@0 2779 if unforgeableAttrs.hasNonChromeOnly():
michael@0 2780 unforgeables.append(CGGeneric(defineUnforgeables %
michael@0 2781 unforgeableAttrs.variableName(False)))
michael@0 2782 if unforgeableAttrs.hasChromeOnly():
michael@0 2783 unforgeables.append(
michael@0 2784 CGIfWrapper(CGGeneric(defineUnforgeables %
michael@0 2785 unforgeableAttrs.variableName(True)),
michael@0 2786 "nsContentUtils::ThreadsafeIsCallerChrome()"))
michael@0 2787 return CGList(unforgeables)
michael@0 2788
michael@0 2789
michael@0 2790 def InitUnforgeableProperties(descriptor, properties):
michael@0 2791 """
michael@0 2792 properties is a PropertyArrays instance
michael@0 2793 """
michael@0 2794 unforgeableAttrs = properties.unforgeableAttrs
michael@0 2795 if not unforgeableAttrs.hasNonChromeOnly() and not unforgeableAttrs.hasChromeOnly():
michael@0 2796 return ""
michael@0 2797
michael@0 2798 if descriptor.proxy:
michael@0 2799 unforgeableProperties = CGGeneric(
michael@0 2800 "// Unforgeable properties on proxy-based bindings are stored in an object held\n"
michael@0 2801 "// by the interface prototype object.\n"
michael@0 2802 "\n") # BOGUS extra blank line
michael@0 2803 else:
michael@0 2804 unforgeableProperties = CGWrapper(
michael@0 2805 InitUnforgeablePropertiesOnObject(descriptor, "obj", properties, "nullptr"),
michael@0 2806 pre=(
michael@0 2807 "// Important: do unforgeable property setup after we have handed\n"
michael@0 2808 "// over ownership of the C++ object to obj as needed, so that if\n"
michael@0 2809 "// we fail and it ends up GCed it won't have problems in the\n"
michael@0 2810 "// finalizer trying to drop its ownership of the C++ object.\n"))
michael@0 2811 return CGWrapper(unforgeableProperties, pre="\n").define()
michael@0 2812
michael@0 2813
michael@0 2814 def AssertInheritanceChain(descriptor):
michael@0 2815 asserts = ""
michael@0 2816 iface = descriptor.interface
michael@0 2817 while iface:
michael@0 2818 desc = descriptor.getDescriptor(iface.identifier.name)
michael@0 2819 asserts += (
michael@0 2820 " MOZ_ASSERT(static_cast<%s*>(aObject) == \n"
michael@0 2821 " reinterpret_cast<%s*>(aObject));\n" %
michael@0 2822 (desc.nativeType, desc.nativeType))
michael@0 2823 iface = iface.parent
michael@0 2824 asserts += " MOZ_ASSERT(ToSupportsIsCorrect(aObject));\n"
michael@0 2825 return asserts
michael@0 2826
michael@0 2827
michael@0 2828 def InitMemberSlots(descriptor, wrapperCache):
michael@0 2829 """
michael@0 2830 Initialize member slots on our JS object if we're supposed to have some.
michael@0 2831
michael@0 2832 Note that this is called after the SetWrapper() call in the
michael@0 2833 wrapperCache case, since that can affect how our getters behave
michael@0 2834 and we plan to invoke them here. So if we fail, we need to
michael@0 2835 ClearWrapper.
michael@0 2836 """
michael@0 2837 if not descriptor.interface.hasMembersInSlots():
michael@0 2838 return "\n" # BOGUS blank line only if this returns empty
michael@0 2839 if wrapperCache:
michael@0 2840 clearWrapper = " aCache->ClearWrapper();\n"
michael@0 2841 else:
michael@0 2842 clearWrapper = ""
michael@0 2843 return ("if (!UpdateMemberSlots(aCx, obj, aObject)) {\n"
michael@0 2844 "%s"
michael@0 2845 " return nullptr;\n"
michael@0 2846 "}\n" % clearWrapper)
michael@0 2847
michael@0 2848
michael@0 2849 class CGWrapWithCacheMethod(CGAbstractMethod):
michael@0 2850 """
michael@0 2851 Create a wrapper JSObject for a given native that implements nsWrapperCache.
michael@0 2852
michael@0 2853 properties should be a PropertyArrays instance.
michael@0 2854 """
michael@0 2855 def __init__(self, descriptor, properties):
michael@0 2856 assert descriptor.interface.hasInterfacePrototypeObject()
michael@0 2857 args = [Argument('JSContext*', 'aCx'),
michael@0 2858 Argument(descriptor.nativeType + '*', 'aObject'),
michael@0 2859 Argument('nsWrapperCache*', 'aCache')]
michael@0 2860 CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args)
michael@0 2861 self.properties = properties
michael@0 2862
michael@0 2863 def definition_body(self):
michael@0 2864 return fill(
michael@0 2865 """
michael@0 2866 ${assertion}
michael@0 2867 MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
michael@0 2868 "nsISupports must be on our primary inheritance chain");
michael@0 2869
michael@0 2870 JS::Rooted<JSObject*> parent(aCx,
michael@0 2871 GetRealParentObject(aObject,
michael@0 2872 WrapNativeParent(aCx, aObject->GetParentObject())));
michael@0 2873 if (!parent) {
michael@0 2874 return nullptr;
michael@0 2875 }
michael@0 2876
michael@0 2877 // That might have ended up wrapping us already, due to the wonders
michael@0 2878 // of XBL. Check for that, and bail out as needed. Scope so we don't
michael@0 2879 // collide with the "obj" we declare in CreateBindingJSObject.
michael@0 2880 {
michael@0 2881 JSObject* obj = aCache->GetWrapper();
michael@0 2882 if (obj) {
michael@0 2883 return obj;
michael@0 2884 }
michael@0 2885 }
michael@0 2886
michael@0 2887 JSAutoCompartment ac(aCx, parent);
michael@0 2888 JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForObject(aCx, parent));
michael@0 2889 JS::Handle<JSObject*> proto = GetProtoObject(aCx, global);
michael@0 2890 if (!proto) {
michael@0 2891 return nullptr;
michael@0 2892 }
michael@0 2893
michael@0 2894 $*{parent}
michael@0 2895
michael@0 2896 $*{unforgeable}
michael@0 2897
michael@0 2898 aCache->SetWrapper(obj);
michael@0 2899 $*{slots}
michael@0 2900 return obj;
michael@0 2901 """,
michael@0 2902 assertion=AssertInheritanceChain(self.descriptor),
michael@0 2903 parent=CreateBindingJSObject(self.descriptor, self.properties,
michael@0 2904 "parent"),
michael@0 2905 unforgeable=InitUnforgeableProperties(self.descriptor, self.properties),
michael@0 2906 slots=InitMemberSlots(self.descriptor, True))
michael@0 2907
michael@0 2908
michael@0 2909 class CGWrapMethod(CGAbstractMethod):
michael@0 2910 def __init__(self, descriptor):
michael@0 2911 # XXX can we wrap if we don't have an interface prototype object?
michael@0 2912 assert descriptor.interface.hasInterfacePrototypeObject()
michael@0 2913 args = [Argument('JSContext*', 'aCx'),
michael@0 2914 Argument('T*', 'aObject')]
michael@0 2915 CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args,
michael@0 2916 inline=True, templateArgs=["class T"])
michael@0 2917
michael@0 2918 def definition_body(self):
michael@0 2919 return " return Wrap(aCx, aObject, aObject);\n"
michael@0 2920
michael@0 2921
michael@0 2922 class CGWrapNonWrapperCacheMethod(CGAbstractMethod):
michael@0 2923 """
michael@0 2924 Create a wrapper JSObject for a given native that does not implement
michael@0 2925 nsWrapperCache.
michael@0 2926
michael@0 2927 properties should be a PropertyArrays instance.
michael@0 2928 """
michael@0 2929 def __init__(self, descriptor, properties):
michael@0 2930 # XXX can we wrap if we don't have an interface prototype object?
michael@0 2931 assert descriptor.interface.hasInterfacePrototypeObject()
michael@0 2932 args = [Argument('JSContext*', 'aCx'),
michael@0 2933 Argument(descriptor.nativeType + '*', 'aObject')]
michael@0 2934 if descriptor.nativeOwnership == 'owned':
michael@0 2935 args.append(Argument('bool*', 'aTookOwnership'))
michael@0 2936 CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args)
michael@0 2937 self.properties = properties
michael@0 2938
michael@0 2939 def definition_body(self):
michael@0 2940 return fill(
michael@0 2941 """
michael@0 2942 ${assertions}
michael@0 2943 JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
michael@0 2944 JS::Handle<JSObject*> proto = GetProtoObject(aCx, global);
michael@0 2945 if (!proto) {
michael@0 2946 return nullptr;
michael@0 2947 }
michael@0 2948
michael@0 2949 $*{global_}
michael@0 2950
michael@0 2951 $*{unforgeable}
michael@0 2952
michael@0 2953 $*{slots}
michael@0 2954 return obj;
michael@0 2955 """,
michael@0 2956 assertions=AssertInheritanceChain(self.descriptor),
michael@0 2957 global_=CreateBindingJSObject(self.descriptor, self.properties,
michael@0 2958 "global"),
michael@0 2959 unforgeable=InitUnforgeableProperties(self.descriptor, self.properties),
michael@0 2960 slots=InitMemberSlots(self.descriptor, False))
michael@0 2961
michael@0 2962
michael@0 2963 class CGWrapGlobalMethod(CGAbstractMethod):
michael@0 2964 """
michael@0 2965 Create a wrapper JSObject for a global. The global must implement
michael@0 2966 nsWrapperCache.
michael@0 2967
michael@0 2968 properties should be a PropertyArrays instance.
michael@0 2969 """
michael@0 2970 def __init__(self, descriptor, properties):
michael@0 2971 assert descriptor.interface.hasInterfacePrototypeObject()
michael@0 2972 args = [Argument('JSContext*', 'aCx'),
michael@0 2973 Argument(descriptor.nativeType + '*', 'aObject'),
michael@0 2974 Argument('nsWrapperCache*', 'aCache'),
michael@0 2975 Argument('JS::CompartmentOptions&', 'aOptions'),
michael@0 2976 Argument('JSPrincipals*', 'aPrincipal')]
michael@0 2977 CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args)
michael@0 2978 self.descriptor = descriptor
michael@0 2979 self.properties = properties
michael@0 2980
michael@0 2981 def definition_body(self):
michael@0 2982 return fill(
michael@0 2983 """
michael@0 2984 ${assertions}
michael@0 2985 MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
michael@0 2986 "nsISupports must be on our primary inheritance chain");
michael@0 2987
michael@0 2988 JS::Rooted<JSObject*> obj(aCx);
michael@0 2989 obj = CreateGlobal<${nativeType}, GetProtoObject>(aCx,
michael@0 2990 aObject,
michael@0 2991 aCache,
michael@0 2992 Class.ToJSClass(),
michael@0 2993 aOptions,
michael@0 2994 aPrincipal);
michael@0 2995
michael@0 2996 $*{unforgeable}
michael@0 2997
michael@0 2998 $*{slots}
michael@0 2999
michael@0 3000 // XXXkhuey can't do this yet until workers can lazy resolve.
michael@0 3001 // JS_FireOnNewGlobalObject(aCx, obj);
michael@0 3002
michael@0 3003 return obj;
michael@0 3004 """,
michael@0 3005 assertions=AssertInheritanceChain(self.descriptor),
michael@0 3006 nativeType=self.descriptor.nativeType,
michael@0 3007 unforgeable=InitUnforgeableProperties(self.descriptor, self.properties),
michael@0 3008 slots=InitMemberSlots(self.descriptor, True))
michael@0 3009
michael@0 3010
michael@0 3011 class CGUpdateMemberSlotsMethod(CGAbstractStaticMethod):
michael@0 3012 def __init__(self, descriptor):
michael@0 3013 args = [Argument('JSContext*', 'aCx'),
michael@0 3014 Argument('JS::Handle<JSObject*>', 'aWrapper'),
michael@0 3015 Argument(descriptor.nativeType + '*', 'aObject')]
michael@0 3016 CGAbstractStaticMethod.__init__(self, descriptor, 'UpdateMemberSlots', 'bool', args)
michael@0 3017
michael@0 3018 def definition_body(self):
michael@0 3019 body = ("JS::Rooted<JS::Value> temp(aCx);\n"
michael@0 3020 "JSJitGetterCallArgs args(&temp);\n")
michael@0 3021 for m in self.descriptor.interface.members:
michael@0 3022 if m.isAttr() and m.getExtendedAttribute("StoreInSlot"):
michael@0 3023 body += fill(
michael@0 3024 """
michael@0 3025
michael@0 3026 static_assert(${slot} < js::shadow::Object::MAX_FIXED_SLOTS,
michael@0 3027 "Not enough fixed slots to fit '${interface}.${member}'");
michael@0 3028 if (!get_${member}(aCx, aWrapper, aObject, args)) {
michael@0 3029 return false;
michael@0 3030 }
michael@0 3031 // Getter handled setting our reserved slots
michael@0 3032 """,
michael@0 3033 slot=memberReservedSlot(m),
michael@0 3034 interface=self.descriptor.interface.identifier.name,
michael@0 3035 member=m.identifier.name)
michael@0 3036
michael@0 3037 body += "\nreturn true;\n"
michael@0 3038 return indent(body)
michael@0 3039
michael@0 3040
michael@0 3041 class CGClearCachedValueMethod(CGAbstractMethod):
michael@0 3042 def __init__(self, descriptor, member):
michael@0 3043 self.member = member
michael@0 3044 # If we're StoreInSlot, we'll need to call the getter
michael@0 3045 if member.getExtendedAttribute("StoreInSlot"):
michael@0 3046 args = [Argument('JSContext*', 'aCx')]
michael@0 3047 returnType = 'bool'
michael@0 3048 else:
michael@0 3049 args = []
michael@0 3050 returnType = 'void'
michael@0 3051 args.append(Argument(descriptor.nativeType + '*', 'aObject'))
michael@0 3052 name = ("ClearCached%sValue" % MakeNativeName(member.identifier.name))
michael@0 3053 CGAbstractMethod.__init__(self, descriptor, name, returnType, args)
michael@0 3054
michael@0 3055 def definition_body(self):
michael@0 3056 slotIndex = memberReservedSlot(self.member)
michael@0 3057 if self.member.getExtendedAttribute("StoreInSlot"):
michael@0 3058 # We have to root things and save the old value in case
michael@0 3059 # regetting fails, so we can restore it.
michael@0 3060 declObj = "JS::Rooted<JSObject*> obj(aCx);\n"
michael@0 3061 noopRetval = " true"
michael@0 3062 saveMember = (
michael@0 3063 "JS::Rooted<JS::Value> oldValue(aCx, js::GetReservedSlot(obj, %s));\n" %
michael@0 3064 slotIndex)
michael@0 3065 regetMember = fill(
michael@0 3066 """
michael@0 3067 JS::Rooted<JS::Value> temp(aCx);
michael@0 3068 JSJitGetterCallArgs args(&temp);
michael@0 3069 JSAutoCompartment ac(aCx, obj);
michael@0 3070 if (!get_${name}(aCx, obj, aObject, args)) {
michael@0 3071 js::SetReservedSlot(obj, ${slotIndex}, oldValue);
michael@0 3072 nsJSUtils::ReportPendingException(aCx);
michael@0 3073 return false;
michael@0 3074 }
michael@0 3075 return true;
michael@0 3076 """,
michael@0 3077 name=self.member.identifier.name,
michael@0 3078 slotIndex=slotIndex)
michael@0 3079 else:
michael@0 3080 declObj = "JSObject* obj;\n"
michael@0 3081 noopRetval = ""
michael@0 3082 saveMember = ""
michael@0 3083 regetMember = ""
michael@0 3084
michael@0 3085 return indent(fill(
michael@0 3086 """
michael@0 3087 $*{declObj}
michael@0 3088 obj = aObject->GetWrapper();
michael@0 3089 if (!obj) {
michael@0 3090 return${noopRetval};
michael@0 3091 }
michael@0 3092 $*{saveMember}
michael@0 3093 js::SetReservedSlot(obj, ${slotIndex}, JS::UndefinedValue());
michael@0 3094 $*{regetMember}
michael@0 3095 """,
michael@0 3096 declObj=declObj,
michael@0 3097 noopRetval=noopRetval,
michael@0 3098 saveMember=saveMember,
michael@0 3099 slotIndex=slotIndex,
michael@0 3100 regetMember=regetMember))
michael@0 3101
michael@0 3102
michael@0 3103 class CGIsPermittedMethod(CGAbstractMethod):
michael@0 3104 """
michael@0 3105 crossOriginGetters/Setters/Methods are sets of names of the relevant members.
michael@0 3106 """
michael@0 3107 def __init__(self, descriptor, crossOriginGetters, crossOriginSetters,
michael@0 3108 crossOriginMethods):
michael@0 3109 self.crossOriginGetters = crossOriginGetters
michael@0 3110 self.crossOriginSetters = crossOriginSetters
michael@0 3111 self.crossOriginMethods = crossOriginMethods
michael@0 3112 args = [Argument("JSFlatString*", "prop"),
michael@0 3113 Argument("jschar", "propFirstChar"),
michael@0 3114 Argument("bool", "set")]
michael@0 3115 CGAbstractMethod.__init__(self, descriptor, "IsPermitted", "bool", args,
michael@0 3116 inline=True)
michael@0 3117
michael@0 3118 def definition_body(self):
michael@0 3119 allNames = self.crossOriginGetters | self.crossOriginSetters | self.crossOriginMethods
michael@0 3120 readwrite = self.crossOriginGetters & self.crossOriginSetters
michael@0 3121 readonly = (self.crossOriginGetters - self.crossOriginSetters) | self.crossOriginMethods
michael@0 3122 writeonly = self.crossOriginSetters - self.crossOriginGetters
michael@0 3123 cases = {}
michael@0 3124 for name in sorted(allNames):
michael@0 3125 cond = 'JS_FlatStringEqualsAscii(prop, "%s")' % name
michael@0 3126 if name in readonly:
michael@0 3127 cond = "!set && %s" % cond
michael@0 3128 elif name in writeonly:
michael@0 3129 cond = "set && %s" % cond
michael@0 3130 else:
michael@0 3131 assert name in readwrite
michael@0 3132 firstLetter = name[0]
michael@0 3133 case = cases.get(firstLetter, CGList([]))
michael@0 3134 case.append(CGGeneric("if (%s) {\n"
michael@0 3135 " return true;\n"
michael@0 3136 "}\n" % cond))
michael@0 3137 cases[firstLetter] = case
michael@0 3138 caseList = []
michael@0 3139 for firstLetter in sorted(cases.keys()):
michael@0 3140 caseList.append(CGCase("'%s'" % firstLetter, cases[firstLetter]))
michael@0 3141 switch = CGSwitch("propFirstChar", caseList)
michael@0 3142 return indent(switch.define() + "\nreturn false;\n")
michael@0 3143
michael@0 3144 builtinNames = {
michael@0 3145 IDLType.Tags.bool: 'bool',
michael@0 3146 IDLType.Tags.int8: 'int8_t',
michael@0 3147 IDLType.Tags.int16: 'int16_t',
michael@0 3148 IDLType.Tags.int32: 'int32_t',
michael@0 3149 IDLType.Tags.int64: 'int64_t',
michael@0 3150 IDLType.Tags.uint8: 'uint8_t',
michael@0 3151 IDLType.Tags.uint16: 'uint16_t',
michael@0 3152 IDLType.Tags.uint32: 'uint32_t',
michael@0 3153 IDLType.Tags.uint64: 'uint64_t',
michael@0 3154 IDLType.Tags.unrestricted_float: 'float',
michael@0 3155 IDLType.Tags.float: 'float',
michael@0 3156 IDLType.Tags.unrestricted_double: 'double',
michael@0 3157 IDLType.Tags.double: 'double'
michael@0 3158 }
michael@0 3159
michael@0 3160 numericSuffixes = {
michael@0 3161 IDLType.Tags.int8: '',
michael@0 3162 IDLType.Tags.uint8: '',
michael@0 3163 IDLType.Tags.int16: '',
michael@0 3164 IDLType.Tags.uint16: '',
michael@0 3165 IDLType.Tags.int32: '',
michael@0 3166 IDLType.Tags.uint32: 'U',
michael@0 3167 IDLType.Tags.int64: 'LL',
michael@0 3168 IDLType.Tags.uint64: 'ULL',
michael@0 3169 IDLType.Tags.unrestricted_float: 'F',
michael@0 3170 IDLType.Tags.float: 'F',
michael@0 3171 IDLType.Tags.unrestricted_double: '',
michael@0 3172 IDLType.Tags.double: ''
michael@0 3173 }
michael@0 3174
michael@0 3175
michael@0 3176 def numericValue(t, v):
michael@0 3177 if (t == IDLType.Tags.unrestricted_double or
michael@0 3178 t == IDLType.Tags.unrestricted_float):
michael@0 3179 typeName = builtinNames[t]
michael@0 3180 if v == float("inf"):
michael@0 3181 return "mozilla::PositiveInfinity<%s>()" % typeName
michael@0 3182 if v == float("-inf"):
michael@0 3183 return "mozilla::NegativeInfinity<%s>()" % typeName
michael@0 3184 if math.isnan(v):
michael@0 3185 return "mozilla::UnspecifiedNaN<%s>()" % typeName
michael@0 3186 return "%s%s" % (v, numericSuffixes[t])
michael@0 3187
michael@0 3188
michael@0 3189 class CastableObjectUnwrapper():
michael@0 3190 """
michael@0 3191 A class for unwrapping an object named by the "source" argument
michael@0 3192 based on the passed-in descriptor and storing it in a variable
michael@0 3193 called by the name in the "target" argument.
michael@0 3194
michael@0 3195 codeOnFailure is the code to run if unwrapping fails.
michael@0 3196
michael@0 3197 If isCallbackReturnValue is "JSImpl" and our descriptor is also
michael@0 3198 JS-implemented, fall back to just creating the right object if what we
michael@0 3199 have isn't one already.
michael@0 3200
michael@0 3201 If allowCrossOriginObj is True, then we'll first do an
michael@0 3202 UncheckedUnwrap and then operate on the result.
michael@0 3203 """
michael@0 3204 def __init__(self, descriptor, source, target, codeOnFailure,
michael@0 3205 exceptionCode=None, isCallbackReturnValue=False,
michael@0 3206 allowCrossOriginObj=False):
michael@0 3207 self.substitution = {
michael@0 3208 "type": descriptor.nativeType,
michael@0 3209 "protoID": "prototypes::id::" + descriptor.name,
michael@0 3210 "target": target,
michael@0 3211 "codeOnFailure": codeOnFailure,
michael@0 3212 }
michael@0 3213 if allowCrossOriginObj:
michael@0 3214 self.substitution["uncheckedObjDecl"] = (
michael@0 3215 "JS::Rooted<JSObject*> uncheckedObj(cx, js::UncheckedUnwrap(%s));\n" % source)
michael@0 3216 self.substitution["source"] = "uncheckedObj"
michael@0 3217 xpconnectUnwrap = dedent("""
michael@0 3218 nsresult rv;
michael@0 3219 { // Scope for the JSAutoCompartment, because we only
michael@0 3220 // want to be in that compartment for the UnwrapArg call.
michael@0 3221 JSAutoCompartment ac(cx, ${source});
michael@0 3222 rv = UnwrapArg<${type}>(cx, val, &objPtr, &objRef.ptr, &val);
michael@0 3223 }
michael@0 3224 """)
michael@0 3225 else:
michael@0 3226 self.substitution["uncheckedObjDecl"] = ""
michael@0 3227 self.substitution["source"] = source
michael@0 3228 xpconnectUnwrap = "nsresult rv = UnwrapArg<${type}>(cx, val, &objPtr, &objRef.ptr, &val);\n"
michael@0 3229
michael@0 3230 if descriptor.hasXPConnectImpls:
michael@0 3231 # We don't use xpc_qsUnwrapThis because it will always throw on
michael@0 3232 # unwrap failure, whereas we want to control whether we throw or
michael@0 3233 # not.
michael@0 3234 self.substitution["codeOnFailure"] = string.Template(
michael@0 3235 "${type} *objPtr;\n"
michael@0 3236 "SelfRef objRef;\n"
michael@0 3237 "JS::Rooted<JS::Value> val(cx, JS::ObjectValue(*${source}));\n" +
michael@0 3238 xpconnectUnwrap +
michael@0 3239 "if (NS_FAILED(rv)) {\n"
michael@0 3240 "${indentedCodeOnFailure}"
michael@0 3241 "}\n"
michael@0 3242 "// We should be castable!\n"
michael@0 3243 "MOZ_ASSERT(!objRef.ptr);\n"
michael@0 3244 "// We should have an object, too!\n"
michael@0 3245 "MOZ_ASSERT(objPtr);\n"
michael@0 3246 "${target} = objPtr;\n"
michael@0 3247 ).substitute(self.substitution,
michael@0 3248 indentedCodeOnFailure=indent(codeOnFailure))
michael@0 3249 elif (isCallbackReturnValue == "JSImpl" and
michael@0 3250 descriptor.interface.isJSImplemented()):
michael@0 3251 exceptionCode = exceptionCode or codeOnFailure
michael@0 3252 self.substitution["codeOnFailure"] = fill(
michael@0 3253 """
michael@0 3254 // Be careful to not wrap random DOM objects here, even if
michael@0 3255 // they're wrapped in opaque security wrappers for some reason.
michael@0 3256 // XXXbz Wish we could check for a JS-implemented object
michael@0 3257 // that already has a content reflection...
michael@0 3258 if (!IsDOMObject(js::UncheckedUnwrap(${source}))) {
michael@0 3259 nsCOMPtr<nsPIDOMWindow> ourWindow;
michael@0 3260 if (!GetWindowForJSImplementedObject(cx, Callback(), getter_AddRefs(ourWindow))) {
michael@0 3261 $*{exceptionCode}
michael@0 3262 }
michael@0 3263 JS::Rooted<JSObject*> jsImplSourceObj(cx, ${source});
michael@0 3264 ${target} = new ${type}(jsImplSourceObj, ourWindow);
michael@0 3265 } else {
michael@0 3266 $*{codeOnFailure}
michael@0 3267 }
michael@0 3268 """,
michael@0 3269 exceptionCode=exceptionCode,
michael@0 3270 **self.substitution)
michael@0 3271 else:
michael@0 3272 self.substitution["codeOnFailure"] = codeOnFailure
michael@0 3273
michael@0 3274 def __str__(self):
michael@0 3275 substitution = self.substitution.copy()
michael@0 3276 substitution["codeOnFailure"] %= {
michael@0 3277 'securityError': 'rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO'
michael@0 3278 }
michael@0 3279 return fill(
michael@0 3280 """
michael@0 3281 {
michael@0 3282 $*{uncheckedObjDecl}
michael@0 3283 nsresult rv = UnwrapObject<${protoID}, ${type}>(${source}, ${target});
michael@0 3284 if (NS_FAILED(rv)) {
michael@0 3285 $*{codeOnFailure}
michael@0 3286 }
michael@0 3287 }
michael@0 3288 """,
michael@0 3289 **substitution)
michael@0 3290
michael@0 3291
michael@0 3292 class FailureFatalCastableObjectUnwrapper(CastableObjectUnwrapper):
michael@0 3293 """
michael@0 3294 As CastableObjectUnwrapper, but defaulting to throwing if unwrapping fails
michael@0 3295 """
michael@0 3296 def __init__(self, descriptor, source, target, exceptionCode,
michael@0 3297 isCallbackReturnValue, sourceDescription):
michael@0 3298 CastableObjectUnwrapper.__init__(
michael@0 3299 self, descriptor, source, target,
michael@0 3300 'ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s", "%s");\n'
michael@0 3301 '%s' % (sourceDescription, descriptor.interface.identifier.name,
michael@0 3302 exceptionCode),
michael@0 3303 exceptionCode,
michael@0 3304 isCallbackReturnValue)
michael@0 3305
michael@0 3306
michael@0 3307 class CGCallbackTempRoot(CGGeneric):
michael@0 3308 def __init__(self, name):
michael@0 3309 define = dedent("""
michael@0 3310 { // Scope for tempRoot
michael@0 3311 JS::Rooted<JSObject*> tempRoot(cx, &${val}.toObject());
michael@0 3312 ${declName} = new %s(tempRoot, mozilla::dom::GetIncumbentGlobal());
michael@0 3313 }
michael@0 3314 """) % name
michael@0 3315 CGGeneric.__init__(self, define=define)
michael@0 3316
michael@0 3317
michael@0 3318 class JSToNativeConversionInfo():
michael@0 3319 """
michael@0 3320 An object representing information about a JS-to-native conversion.
michael@0 3321 """
michael@0 3322 def __init__(self, template, declType=None, holderType=None,
michael@0 3323 dealWithOptional=False, declArgs=None,
michael@0 3324 holderArgs=None):
michael@0 3325 """
michael@0 3326 template: A string representing the conversion code. This will have
michael@0 3327 template substitution performed on it as follows:
michael@0 3328
michael@0 3329 ${val} is a handle to the JS::Value in question
michael@0 3330 ${mutableVal} is a mutable handle to the JS::Value in question
michael@0 3331 ${holderName} replaced by the holder's name, if any
michael@0 3332 ${declName} replaced by the declaration's name
michael@0 3333 ${haveValue} replaced by an expression that evaluates to a boolean
michael@0 3334 for whether we have a JS::Value. Only used when
michael@0 3335 defaultValue is not None or when True is passed for
michael@0 3336 checkForValue to instantiateJSToNativeConversion.
michael@0 3337
michael@0 3338 declType: A CGThing representing the native C++ type we're converting
michael@0 3339 to. This is allowed to be None if the conversion code is
michael@0 3340 supposed to be used as-is.
michael@0 3341
michael@0 3342 holderType: A CGThing representing the type of a "holder" which will
michael@0 3343 hold a possible reference to the C++ thing whose type we
michael@0 3344 returned in declType, or None if no such holder is needed.
michael@0 3345
michael@0 3346 dealWithOptional: A boolean indicating whether the caller has to do
michael@0 3347 optional-argument handling. This should only be set
michael@0 3348 to true if the JS-to-native conversion is being done
michael@0 3349 for an optional argument or dictionary member with no
michael@0 3350 default value and if the returned template expects
michael@0 3351 both declType and holderType to be wrapped in
michael@0 3352 Optional<>, with ${declName} and ${holderName}
michael@0 3353 adjusted to point to the Value() of the Optional, and
michael@0 3354 Construct() calls to be made on the Optional<>s as
michael@0 3355 needed.
michael@0 3356
michael@0 3357 declArgs: If not None, the arguments to pass to the ${declName}
michael@0 3358 constructor. These will have template substitution performed
michael@0 3359 on them so you can use things like ${val}. This is a
michael@0 3360 single string, not a list of strings.
michael@0 3361
michael@0 3362 holderArgs: If not None, the arguments to pass to the ${holderName}
michael@0 3363 constructor. These will have template substitution
michael@0 3364 performed on them so you can use things like ${val}.
michael@0 3365 This is a single string, not a list of strings.
michael@0 3366
michael@0 3367 ${declName} must be in scope before the code from 'template' is entered.
michael@0 3368
michael@0 3369 If holderType is not None then ${holderName} must be in scope before
michael@0 3370 the code from 'template' is entered.
michael@0 3371 """
michael@0 3372 assert isinstance(template, str)
michael@0 3373 assert declType is None or isinstance(declType, CGThing)
michael@0 3374 assert holderType is None or isinstance(holderType, CGThing)
michael@0 3375 self.template = template
michael@0 3376 self.declType = declType
michael@0 3377 self.holderType = holderType
michael@0 3378 self.dealWithOptional = dealWithOptional
michael@0 3379 self.declArgs = declArgs
michael@0 3380 self.holderArgs = holderArgs
michael@0 3381
michael@0 3382
michael@0 3383 def getHandleDefault(defaultValue):
michael@0 3384 tag = defaultValue.type.tag()
michael@0 3385 if tag in numericSuffixes:
michael@0 3386 # Some numeric literals require a suffix to compile without warnings
michael@0 3387 return numericValue(tag, defaultValue.value)
michael@0 3388 assert tag == IDLType.Tags.bool
michael@0 3389 return toStringBool(defaultValue.value)
michael@0 3390
michael@0 3391
michael@0 3392 def handleDefaultStringValue(defaultValue, method):
michael@0 3393 """
michael@0 3394 Returns a string which ends up calling 'method' with a (char16_t*, length)
michael@0 3395 pair that sets this string default value. This string is suitable for
michael@0 3396 passing as the second argument of handleDefault; in particular it does not
michael@0 3397 end with a ';'
michael@0 3398 """
michael@0 3399 assert defaultValue.type.isDOMString()
michael@0 3400 return ("static const char16_t data[] = { %s };\n"
michael@0 3401 "%s(data, ArrayLength(data) - 1)" %
michael@0 3402 (", ".join(["'" + char + "'" for char in
michael@0 3403 defaultValue.value] + ["0"]),
michael@0 3404 method))
michael@0 3405
michael@0 3406
michael@0 3407 # If this function is modified, modify CGNativeMember.getArg and
michael@0 3408 # CGNativeMember.getRetvalInfo accordingly. The latter cares about the decltype
michael@0 3409 # and holdertype we end up using, because it needs to be able to return the code
michael@0 3410 # that will convert those to the actual return value of the callback function.
michael@0 3411 def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
michael@0 3412 isDefinitelyObject=False,
michael@0 3413 isMember=False,
michael@0 3414 isOptional=False,
michael@0 3415 invalidEnumValueFatal=True,
michael@0 3416 defaultValue=None,
michael@0 3417 treatNullAs="Default",
michael@0 3418 isEnforceRange=False,
michael@0 3419 isClamp=False,
michael@0 3420 isNullOrUndefined=False,
michael@0 3421 exceptionCode=None,
michael@0 3422 lenientFloatCode=None,
michael@0 3423 allowTreatNonCallableAsNull=False,
michael@0 3424 isCallbackReturnValue=False,
michael@0 3425 sourceDescription="value"):
michael@0 3426 """
michael@0 3427 Get a template for converting a JS value to a native object based on the
michael@0 3428 given type and descriptor. If failureCode is given, then we're actually
michael@0 3429 testing whether we can convert the argument to the desired type. That
michael@0 3430 means that failures to convert due to the JS value being the wrong type of
michael@0 3431 value need to use failureCode instead of throwing exceptions. Failures to
michael@0 3432 convert that are due to JS exceptions (from toString or valueOf methods) or
michael@0 3433 out of memory conditions need to throw exceptions no matter what
michael@0 3434 failureCode is. However what actually happens when throwing an exception
michael@0 3435 can be controlled by exceptionCode. The only requirement on that is that
michael@0 3436 exceptionCode must end up doing a return, and every return from this
michael@0 3437 function must happen via exceptionCode if exceptionCode is not None.
michael@0 3438
michael@0 3439 If isDefinitelyObject is True, that means we know the value
michael@0 3440 isObject() and we have no need to recheck that.
michael@0 3441
michael@0 3442 if isMember is not False, we're being converted from a property of some JS
michael@0 3443 object, not from an actual method argument, so we can't rely on our jsval
michael@0 3444 being rooted or outliving us in any way. Callers can pass "Dictionary",
michael@0 3445 "Variadic", "Sequence", or "OwningUnion" to indicate that the conversion is
michael@0 3446 for something that is a dictionary member, a variadic argument, a sequence,
michael@0 3447 or an owning union respectively.
michael@0 3448
michael@0 3449 If isOptional is true, then we are doing conversion of an optional
michael@0 3450 argument with no default value.
michael@0 3451
michael@0 3452 invalidEnumValueFatal controls whether an invalid enum value conversion
michael@0 3453 attempt will throw (if true) or simply return without doing anything (if
michael@0 3454 false).
michael@0 3455
michael@0 3456 If defaultValue is not None, it's the IDL default value for this conversion
michael@0 3457
michael@0 3458 If isEnforceRange is true, we're converting an integer and throwing if the
michael@0 3459 value is out of range.
michael@0 3460
michael@0 3461 If isClamp is true, we're converting an integer and clamping if the
michael@0 3462 value is out of range.
michael@0 3463
michael@0 3464 If lenientFloatCode is not None, it should be used in cases when
michael@0 3465 we're a non-finite float that's not unrestricted.
michael@0 3466
michael@0 3467 If allowTreatNonCallableAsNull is true, then [TreatNonCallableAsNull] and
michael@0 3468 [TreatNonObjectAsNull] extended attributes on nullable callback functions
michael@0 3469 will be honored.
michael@0 3470
michael@0 3471 If isCallbackReturnValue is "JSImpl" or "Callback", then the declType may be
michael@0 3472 adjusted to make it easier to return from a callback. Since that type is
michael@0 3473 never directly observable by any consumers of the callback code, this is OK.
michael@0 3474 Furthermore, if isCallbackReturnValue is "JSImpl", that affects the behavior
michael@0 3475 of the FailureFatalCastableObjectUnwrapper conversion; this is used for
michael@0 3476 implementing auto-wrapping of JS-implemented return values from a
michael@0 3477 JS-implemented interface.
michael@0 3478
michael@0 3479 sourceDescription is a description of what this JS value represents, to be
michael@0 3480 used in error reporting. Callers should assume that it might get placed in
michael@0 3481 the middle of a sentence. If it ends up at the beginning of a sentence, its
michael@0 3482 first character will be automatically uppercased.
michael@0 3483
michael@0 3484 The return value from this function is a JSToNativeConversionInfo.
michael@0 3485 """
michael@0 3486 # If we have a defaultValue then we're not actually optional for
michael@0 3487 # purposes of what we need to be declared as.
michael@0 3488 assert defaultValue is None or not isOptional
michael@0 3489
michael@0 3490 # Also, we should not have a defaultValue if we know we're an object
michael@0 3491 assert not isDefinitelyObject or defaultValue is None
michael@0 3492
michael@0 3493 # And we can't both be an object and be null or undefined
michael@0 3494 assert not isDefinitelyObject or not isNullOrUndefined
michael@0 3495
michael@0 3496 # If exceptionCode is not set, we'll just rethrow the exception we got.
michael@0 3497 # Note that we can't just set failureCode to exceptionCode, because setting
michael@0 3498 # failureCode will prevent pending exceptions from being set in cases when
michael@0 3499 # they really should be!
michael@0 3500 if exceptionCode is None:
michael@0 3501 exceptionCode = "return false;\n"
michael@0 3502 # We often want exceptionCode to be indented, since it often appears in an
michael@0 3503 # if body.
michael@0 3504 exceptionCodeIndented = CGIndenter(CGGeneric(exceptionCode))
michael@0 3505
michael@0 3506 # Unfortunately, .capitalize() on a string will lowercase things inside the
michael@0 3507 # string, which we do not want.
michael@0 3508 def firstCap(string):
michael@0 3509 return string[0].upper() + string[1:]
michael@0 3510
michael@0 3511 # Helper functions for dealing with failures due to the JS value being the
michael@0 3512 # wrong type of value
michael@0 3513 def onFailureNotAnObject(failureCode):
michael@0 3514 return CGGeneric(
michael@0 3515 failureCode or
michael@0 3516 ('ThrowErrorMessage(cx, MSG_NOT_OBJECT, "%s");\n'
michael@0 3517 '%s' % (firstCap(sourceDescription), exceptionCode)))
michael@0 3518
michael@0 3519 def onFailureBadType(failureCode, typeName):
michael@0 3520 return CGGeneric(
michael@0 3521 failureCode or
michael@0 3522 ('ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s", "%s");\n'
michael@0 3523 '%s' % (firstCap(sourceDescription), typeName, exceptionCode)))
michael@0 3524
michael@0 3525 def onFailureNotCallable(failureCode):
michael@0 3526 return CGGeneric(
michael@0 3527 failureCode or
michael@0 3528 ('ThrowErrorMessage(cx, MSG_NOT_CALLABLE, "%s");\n'
michael@0 3529 '%s' % (firstCap(sourceDescription), exceptionCode)))
michael@0 3530
michael@0 3531 # A helper function for handling default values. Takes a template
michael@0 3532 # body and the C++ code to set the default value and wraps the
michael@0 3533 # given template body in handling for the default value.
michael@0 3534 def handleDefault(template, setDefault):
michael@0 3535 if defaultValue is None:
michael@0 3536 return template
michael@0 3537 return (
michael@0 3538 "if (${haveValue}) {\n" +
michael@0 3539 indent(template) +
michael@0 3540 "} else {\n" +
michael@0 3541 indent(setDefault) +
michael@0 3542 "}\n")
michael@0 3543
michael@0 3544 # A helper function for handling null default values. Much like
michael@0 3545 # handleDefault, but checks that the default value, if it exists, is null.
michael@0 3546 def handleDefaultNull(template, codeToSetNull):
michael@0 3547 if (defaultValue is not None and
michael@0 3548 not isinstance(defaultValue, IDLNullValue)):
michael@0 3549 raise TypeError("Can't handle non-null default value here")
michael@0 3550 return handleDefault(template, codeToSetNull)
michael@0 3551
michael@0 3552 # A helper function for wrapping up the template body for
michael@0 3553 # possibly-nullable objecty stuff
michael@0 3554 def wrapObjectTemplate(templateBody, type, codeToSetNull, failureCode=None):
michael@0 3555 if isNullOrUndefined and type.nullable():
michael@0 3556 # Just ignore templateBody and set ourselves to null.
michael@0 3557 # Note that we don't have to worry about default values
michael@0 3558 # here either, since we already examined this value.
michael@0 3559 return codeToSetNull
michael@0 3560
michael@0 3561 if not isDefinitelyObject:
michael@0 3562 # Handle the non-object cases by wrapping up the whole
michael@0 3563 # thing in an if cascade.
michael@0 3564 if type.nullable():
michael@0 3565 elifLine = "} else if (${val}.isNullOrUndefined()) {\n"
michael@0 3566 elifBody = codeToSetNull
michael@0 3567 else:
michael@0 3568 elifLine = ""
michael@0 3569 elifBody = ""
michael@0 3570
michael@0 3571 # Note that $${val} below expands to ${val}. This string is
michael@0 3572 # used as a template later, and val will be filled in then.
michael@0 3573 templateBody = fill(
michael@0 3574 """
michael@0 3575 if ($${val}.isObject()) {
michael@0 3576 $*{templateBody}
michael@0 3577 $*{elifLine}
michael@0 3578 $*{elifBody}
michael@0 3579 } else {
michael@0 3580 $*{failureBody}
michael@0 3581 }
michael@0 3582 """,
michael@0 3583 templateBody=templateBody,
michael@0 3584 elifLine=elifLine,
michael@0 3585 elifBody=elifBody,
michael@0 3586 failureBody=onFailureNotAnObject(failureCode).define())
michael@0 3587
michael@0 3588 if type.nullable():
michael@0 3589 templateBody = handleDefaultNull(templateBody, codeToSetNull)
michael@0 3590 else:
michael@0 3591 assert defaultValue is None
michael@0 3592
michael@0 3593 return templateBody
michael@0 3594
michael@0 3595 # A helper function for converting things that look like a JSObject*.
michael@0 3596 def handleJSObjectType(type, isMember, failureCode):
michael@0 3597 if not isMember:
michael@0 3598 if isOptional:
michael@0 3599 # We have a specialization of Optional that will use a
michael@0 3600 # Rooted for the storage here.
michael@0 3601 declType = CGGeneric("JS::Handle<JSObject*>")
michael@0 3602 else:
michael@0 3603 declType = CGGeneric("JS::Rooted<JSObject*>")
michael@0 3604 declArgs = "cx"
michael@0 3605 else:
michael@0 3606 assert (isMember in
michael@0 3607 ("Sequence", "Variadic", "Dictionary", "OwningUnion", "MozMap"))
michael@0 3608 # We'll get traced by the sequence or dictionary or union tracer
michael@0 3609 declType = CGGeneric("JSObject*")
michael@0 3610 declArgs = None
michael@0 3611 templateBody = "${declName} = &${val}.toObject();\n"
michael@0 3612 setToNullCode = "${declName} = nullptr;\n"
michael@0 3613 template = wrapObjectTemplate(templateBody, type, setToNullCode,
michael@0 3614 failureCode)
michael@0 3615 return JSToNativeConversionInfo(template, declType=declType,
michael@0 3616 dealWithOptional=isOptional,
michael@0 3617 declArgs=declArgs)
michael@0 3618
michael@0 3619 assert not (isEnforceRange and isClamp) # These are mutually exclusive
michael@0 3620
michael@0 3621 if type.isArray():
michael@0 3622 raise TypeError("Can't handle array arguments yet")
michael@0 3623
michael@0 3624 if type.isSequence():
michael@0 3625 assert not isEnforceRange and not isClamp
michael@0 3626
michael@0 3627 if failureCode is None:
michael@0 3628 notSequence = ('ThrowErrorMessage(cx, MSG_NOT_SEQUENCE, "%s");\n'
michael@0 3629 "%s" % (firstCap(sourceDescription), exceptionCode))
michael@0 3630 else:
michael@0 3631 notSequence = failureCode
michael@0 3632
michael@0 3633 nullable = type.nullable()
michael@0 3634 # Be very careful not to change "type": we need it later
michael@0 3635 if nullable:
michael@0 3636 elementType = type.inner.inner
michael@0 3637 else:
michael@0 3638 elementType = type.inner
michael@0 3639
michael@0 3640 # We want to use auto arrays if we can, but we have to be careful with
michael@0 3641 # reallocation behavior for arrays. In particular, if we use auto
michael@0 3642 # arrays for sequences and have a sequence of elements which are
michael@0 3643 # themselves sequences or have sequences as members, we have a problem.
michael@0 3644 # In that case, resizing the outermost nsAutoTarray to the right size
michael@0 3645 # will memmove its elements, but nsAutoTArrays are not memmovable and
michael@0 3646 # hence will end up with pointers to bogus memory, which is bad. To
michael@0 3647 # deal with this, we typically map WebIDL sequences to our Sequence
michael@0 3648 # type, which is in fact memmovable. The one exception is when we're
michael@0 3649 # passing in a sequence directly as an argument without any sort of
michael@0 3650 # optional or nullable complexity going on. In that situation, we can
michael@0 3651 # use an AutoSequence instead. We have to keep using Sequence in the
michael@0 3652 # nullable and optional cases because we don't want to leak the
michael@0 3653 # AutoSequence type to consumers, which would be unavoidable with
michael@0 3654 # Nullable<AutoSequence> or Optional<AutoSequence>.
michael@0 3655 if isMember or isOptional or nullable or isCallbackReturnValue:
michael@0 3656 sequenceClass = "Sequence"
michael@0 3657 else:
michael@0 3658 sequenceClass = "binding_detail::AutoSequence"
michael@0 3659
michael@0 3660 # XXXbz we can't include the index in the sourceDescription, because
michael@0 3661 # we don't really have a way to pass one in dynamically at runtime...
michael@0 3662 elementInfo = getJSToNativeConversionInfo(
michael@0 3663 elementType, descriptorProvider, isMember="Sequence",
michael@0 3664 exceptionCode=exceptionCode, lenientFloatCode=lenientFloatCode,
michael@0 3665 isCallbackReturnValue=isCallbackReturnValue,
michael@0 3666 sourceDescription="element of %s" % sourceDescription)
michael@0 3667 if elementInfo.dealWithOptional:
michael@0 3668 raise TypeError("Shouldn't have optional things in sequences")
michael@0 3669 if elementInfo.holderType is not None:
michael@0 3670 raise TypeError("Shouldn't need holders for sequences")
michael@0 3671
michael@0 3672 typeName = CGTemplatedType(sequenceClass, elementInfo.declType)
michael@0 3673 sequenceType = typeName.define()
michael@0 3674 if nullable:
michael@0 3675 typeName = CGTemplatedType("Nullable", typeName)
michael@0 3676 arrayRef = "${declName}.SetValue()"
michael@0 3677 else:
michael@0 3678 arrayRef = "${declName}"
michael@0 3679
michael@0 3680 elementConversion = string.Template(elementInfo.template).substitute({
michael@0 3681 "val": "temp",
michael@0 3682 "mutableVal": "&temp",
michael@0 3683 "declName": "slot",
michael@0 3684 # We only need holderName here to handle isExternal()
michael@0 3685 # interfaces, which use an internal holder for the
michael@0 3686 # conversion even when forceOwningType ends up true.
michael@0 3687 "holderName": "tempHolder"
michael@0 3688 })
michael@0 3689
michael@0 3690 # NOTE: Keep this in sync with variadic conversions as needed
michael@0 3691 templateBody = fill(
michael@0 3692 """
michael@0 3693 JS::ForOfIterator iter(cx);
michael@0 3694 if (!iter.init($${val}, JS::ForOfIterator::AllowNonIterable)) {
michael@0 3695 $*{exceptionCode}
michael@0 3696 }
michael@0 3697 if (!iter.valueIsIterable()) {
michael@0 3698 $*{notSequence}
michael@0 3699 }
michael@0 3700 ${sequenceType} &arr = ${arrayRef};
michael@0 3701 JS::Rooted<JS::Value> temp(cx);
michael@0 3702 while (true) {
michael@0 3703 bool done;
michael@0 3704 if (!iter.next(&temp, &done)) {
michael@0 3705 $*{exceptionCode}
michael@0 3706 }
michael@0 3707 if (done) {
michael@0 3708 break;
michael@0 3709 }
michael@0 3710 ${elementType}* slotPtr = arr.AppendElement();
michael@0 3711 if (!slotPtr) {
michael@0 3712 JS_ReportOutOfMemory(cx);
michael@0 3713 $*{exceptionCode}
michael@0 3714 }
michael@0 3715 ${elementType}& slot = *slotPtr;
michael@0 3716 $*{elementConversion}
michael@0 3717 }
michael@0 3718 """,
michael@0 3719 exceptionCode=exceptionCode,
michael@0 3720 notSequence=notSequence,
michael@0 3721 sequenceType=sequenceType,
michael@0 3722 arrayRef=arrayRef,
michael@0 3723 elementType=elementInfo.declType.define(),
michael@0 3724 elementConversion=elementConversion)
michael@0 3725
michael@0 3726 templateBody = wrapObjectTemplate(templateBody, type,
michael@0 3727 "${declName}.SetNull();\n", notSequence)
michael@0 3728 # Sequence arguments that might contain traceable things need
michael@0 3729 # to get traced
michael@0 3730 if not isMember and typeNeedsRooting(elementType):
michael@0 3731 holderType = CGTemplatedType("SequenceRooter", elementInfo.declType)
michael@0 3732 # If our sequence is nullable, this will set the Nullable to be
michael@0 3733 # not-null, but that's ok because we make an explicit SetNull() call
michael@0 3734 # on it as needed if our JS value is actually null.
michael@0 3735 holderArgs = "cx, &%s" % arrayRef
michael@0 3736 else:
michael@0 3737 holderType = None
michael@0 3738 holderArgs = None
michael@0 3739
michael@0 3740 return JSToNativeConversionInfo(templateBody, declType=typeName,
michael@0 3741 holderType=holderType,
michael@0 3742 dealWithOptional=isOptional,
michael@0 3743 holderArgs=holderArgs)
michael@0 3744
michael@0 3745 if type.isMozMap():
michael@0 3746 assert not isEnforceRange and not isClamp
michael@0 3747 if failureCode is None:
michael@0 3748 notMozMap = ('ThrowErrorMessage(cx, MSG_NOT_OBJECT, "%s");\n'
michael@0 3749 "%s" % (firstCap(sourceDescription), exceptionCode))
michael@0 3750 else:
michael@0 3751 notMozMap = failureCode
michael@0 3752
michael@0 3753 nullable = type.nullable()
michael@0 3754 # Be very careful not to change "type": we need it later
michael@0 3755 if nullable:
michael@0 3756 valueType = type.inner.inner
michael@0 3757 else:
michael@0 3758 valueType = type.inner
michael@0 3759
michael@0 3760 valueInfo = getJSToNativeConversionInfo(
michael@0 3761 valueType, descriptorProvider, isMember="MozMap",
michael@0 3762 exceptionCode=exceptionCode, lenientFloatCode=lenientFloatCode,
michael@0 3763 isCallbackReturnValue=isCallbackReturnValue,
michael@0 3764 sourceDescription="value in %s" % sourceDescription)
michael@0 3765 if valueInfo.dealWithOptional:
michael@0 3766 raise TypeError("Shouldn't have optional things in MozMap")
michael@0 3767 if valueInfo.holderType is not None:
michael@0 3768 raise TypeError("Shouldn't need holders for MozMap")
michael@0 3769
michael@0 3770 typeName = CGTemplatedType("MozMap", valueInfo.declType)
michael@0 3771 mozMapType = typeName.define()
michael@0 3772 if nullable:
michael@0 3773 typeName = CGTemplatedType("Nullable", typeName)
michael@0 3774 mozMapRef = "${declName}.SetValue()"
michael@0 3775 else:
michael@0 3776 mozMapRef = "${declName}"
michael@0 3777
michael@0 3778 valueConversion = string.Template(valueInfo.template).substitute({
michael@0 3779 "val": "temp",
michael@0 3780 "mutableVal": "&temp",
michael@0 3781 "declName": "slot",
michael@0 3782 # We only need holderName here to handle isExternal()
michael@0 3783 # interfaces, which use an internal holder for the
michael@0 3784 # conversion even when forceOwningType ends up true.
michael@0 3785 "holderName": "tempHolder"
michael@0 3786 })
michael@0 3787
michael@0 3788 templateBody = fill(
michael@0 3789 """
michael@0 3790 ${mozMapType} &mozMap = ${mozMapRef};
michael@0 3791
michael@0 3792 JS::Rooted<JSObject*> mozMapObj(cx, &$${val}.toObject());
michael@0 3793 JS::AutoIdArray ids(cx, JS_Enumerate(cx, mozMapObj));
michael@0 3794 if (!ids) {
michael@0 3795 $*{exceptionCode}
michael@0 3796 }
michael@0 3797 JS::Rooted<JS::Value> propNameValue(cx);
michael@0 3798 JS::Rooted<JS::Value> temp(cx);
michael@0 3799 JS::Rooted<jsid> curId(cx);
michael@0 3800 for (size_t i = 0; i < ids.length(); ++i) {
michael@0 3801 // Make sure we get the value before converting the name, since
michael@0 3802 // getting the value can trigger GC but our name is a dependent
michael@0 3803 // string.
michael@0 3804 curId = ids[i];
michael@0 3805 binding_detail::FakeDependentString propName;
michael@0 3806 if (!JS_GetPropertyById(cx, mozMapObj, curId, &temp) ||
michael@0 3807 !JS_IdToValue(cx, curId, &propNameValue) ||
michael@0 3808 !ConvertJSValueToString(cx, propNameValue, &propNameValue,
michael@0 3809 eStringify, eStringify, propName)) {
michael@0 3810 $*{exceptionCode}
michael@0 3811 }
michael@0 3812
michael@0 3813 ${valueType}* slotPtr = mozMap.AddEntry(propName);
michael@0 3814 if (!slotPtr) {
michael@0 3815 JS_ReportOutOfMemory(cx);
michael@0 3816 $*{exceptionCode}
michael@0 3817 }
michael@0 3818 ${valueType}& slot = *slotPtr;
michael@0 3819 $*{valueConversion}
michael@0 3820 }
michael@0 3821 """,
michael@0 3822 exceptionCode=exceptionCode,
michael@0 3823 mozMapType=mozMapType,
michael@0 3824 mozMapRef=mozMapRef,
michael@0 3825 valueType=valueInfo.declType.define(),
michael@0 3826 valueConversion=valueConversion)
michael@0 3827
michael@0 3828 templateBody = wrapObjectTemplate(templateBody, type,
michael@0 3829 "${declName}.SetNull();\n",
michael@0 3830 notMozMap)
michael@0 3831
michael@0 3832 declType = typeName
michael@0 3833 declArgs = None
michael@0 3834 holderType = None
michael@0 3835 holderArgs = None
michael@0 3836 # MozMap arguments that might contain traceable things need
michael@0 3837 # to get traced
michael@0 3838 if not isMember and isCallbackReturnValue:
michael@0 3839 # Go ahead and just convert directly into our actual return value
michael@0 3840 declType = CGWrapper(declType, post="&")
michael@0 3841 declArgs = "aRetVal"
michael@0 3842 elif not isMember and typeNeedsRooting(valueType):
michael@0 3843 holderType = CGTemplatedType("MozMapRooter", valueInfo.declType)
michael@0 3844 # If our MozMap is nullable, this will set the Nullable to be
michael@0 3845 # not-null, but that's ok because we make an explicit SetNull() call
michael@0 3846 # on it as needed if our JS value is actually null.
michael@0 3847 holderArgs = "cx, &%s" % mozMapRef
michael@0 3848
michael@0 3849 return JSToNativeConversionInfo(templateBody, declType=declType,
michael@0 3850 declArgs=declArgs,
michael@0 3851 holderType=holderType,
michael@0 3852 dealWithOptional=isOptional,
michael@0 3853 holderArgs=holderArgs)
michael@0 3854
michael@0 3855 if type.isUnion():
michael@0 3856 nullable = type.nullable()
michael@0 3857 if nullable:
michael@0 3858 type = type.inner
michael@0 3859
michael@0 3860 unionArgumentObj = "${declName}" if isMember else "${holderName}"
michael@0 3861 if nullable:
michael@0 3862 # If we're a member, we're a Nullable, which hasn't been told it has
michael@0 3863 # a value. Otherwise we're an already-constructed Maybe.
michael@0 3864 unionArgumentObj += ".SetValue()" if isMember else ".ref()"
michael@0 3865
michael@0 3866 memberTypes = type.flatMemberTypes
michael@0 3867 names = []
michael@0 3868
michael@0 3869 interfaceMemberTypes = filter(lambda t: t.isNonCallbackInterface(), memberTypes)
michael@0 3870 if len(interfaceMemberTypes) > 0:
michael@0 3871 interfaceObject = []
michael@0 3872 for memberType in interfaceMemberTypes:
michael@0 3873 if type.isGeckoInterface():
michael@0 3874 name = memberType.inner.identifier.name
michael@0 3875 else:
michael@0 3876 name = memberType.name
michael@0 3877 interfaceObject.append(
michael@0 3878 CGGeneric("(failed = !%s.TrySetTo%s(cx, ${val}, ${mutableVal}, tryNext)) || !tryNext" %
michael@0 3879 (unionArgumentObj, name)))
michael@0 3880 names.append(name)
michael@0 3881 interfaceObject = CGWrapper(CGList(interfaceObject, " ||\n"),
michael@0 3882 pre="done = ", post=";\n\n", reindent=True)
michael@0 3883 else:
michael@0 3884 interfaceObject = None
michael@0 3885
michael@0 3886 arrayObjectMemberTypes = filter(lambda t: t.isArray() or t.isSequence(), memberTypes)
michael@0 3887 if len(arrayObjectMemberTypes) > 0:
michael@0 3888 raise TypeError("Bug 767924: We don't support sequences in unions yet")
michael@0 3889 else:
michael@0 3890 arrayObject = None
michael@0 3891
michael@0 3892 dateObjectMemberTypes = filter(lambda t: t.isDate(), memberTypes)
michael@0 3893 if len(dateObjectMemberTypes) > 0:
michael@0 3894 assert len(dateObjectMemberTypes) == 1
michael@0 3895 memberType = dateObjectMemberTypes[0]
michael@0 3896 name = memberType.name
michael@0 3897 dateObject = CGGeneric("%s.SetTo%s(cx, ${val}, ${mutableVal});\n"
michael@0 3898 "done = true;\n" % (unionArgumentObj, name))
michael@0 3899 dateObject = CGIfWrapper(dateObject, "JS_ObjectIsDate(cx, argObj)")
michael@0 3900 names.append(name)
michael@0 3901 else:
michael@0 3902 dateObject = None
michael@0 3903
michael@0 3904 callbackMemberTypes = filter(lambda t: t.isCallback() or t.isCallbackInterface(), memberTypes)
michael@0 3905 if len(callbackMemberTypes) > 0:
michael@0 3906 assert len(callbackMemberTypes) == 1
michael@0 3907 memberType = callbackMemberTypes[0]
michael@0 3908 name = memberType.name
michael@0 3909 callbackObject = CGGeneric(
michael@0 3910 "done = (failed = !%s.TrySetTo%s(cx, ${val}, ${mutableVal}, tryNext)) || !tryNext;\n" %
michael@0 3911 (unionArgumentObj, name))
michael@0 3912 names.append(name)
michael@0 3913 else:
michael@0 3914 callbackObject = None
michael@0 3915
michael@0 3916 dictionaryMemberTypes = filter(lambda t: t.isDictionary(), memberTypes)
michael@0 3917 if len(dictionaryMemberTypes) > 0:
michael@0 3918 assert len(dictionaryMemberTypes) == 1
michael@0 3919 name = dictionaryMemberTypes[0].inner.identifier.name
michael@0 3920 setDictionary = CGGeneric(
michael@0 3921 "done = (failed = !%s.TrySetTo%s(cx, ${val}, ${mutableVal}, tryNext)) || !tryNext;\n" %
michael@0 3922 (unionArgumentObj, name))
michael@0 3923 names.append(name)
michael@0 3924 else:
michael@0 3925 setDictionary = None
michael@0 3926
michael@0 3927 mozMapMemberTypes = filter(lambda t: t.isMozMap(), memberTypes)
michael@0 3928 if len(mozMapMemberTypes) > 0:
michael@0 3929 raise TypeError("We don't support MozMap in unions yet")
michael@0 3930
michael@0 3931 objectMemberTypes = filter(lambda t: t.isObject(), memberTypes)
michael@0 3932 if len(objectMemberTypes) > 0:
michael@0 3933 assert len(objectMemberTypes) == 1
michael@0 3934 # Very important to NOT construct a temporary Rooted here, since the
michael@0 3935 # SetToObject call can call a Rooted constructor and we need to keep
michael@0 3936 # stack discipline for Rooted.
michael@0 3937 object = CGGeneric("%s.SetToObject(cx, &${val}.toObject());\n"
michael@0 3938 "done = true;\n" % unionArgumentObj)
michael@0 3939 names.append(objectMemberTypes[0].name)
michael@0 3940 else:
michael@0 3941 object = None
michael@0 3942
michael@0 3943 hasObjectTypes = interfaceObject or arrayObject or dateObject or callbackObject or object
michael@0 3944 if hasObjectTypes:
michael@0 3945 # "object" is not distinguishable from other types
michael@0 3946 assert not object or not (interfaceObject or arrayObject or dateObject or callbackObject)
michael@0 3947 if arrayObject or dateObject or callbackObject:
michael@0 3948 # An object can be both an array object and a callback or
michael@0 3949 # dictionary, but we shouldn't have both in the union's members
michael@0 3950 # because they are not distinguishable.
michael@0 3951 assert not (arrayObject and callbackObject)
michael@0 3952 templateBody = CGElseChain([arrayObject, dateObject, callbackObject])
michael@0 3953 else:
michael@0 3954 templateBody = None
michael@0 3955 if interfaceObject:
michael@0 3956 assert not object
michael@0 3957 if templateBody:
michael@0 3958 templateBody = CGIfWrapper(templateBody, "!done")
michael@0 3959 templateBody = CGList([interfaceObject, templateBody])
michael@0 3960 else:
michael@0 3961 templateBody = CGList([templateBody, object])
michael@0 3962
michael@0 3963 if dateObject:
michael@0 3964 templateBody.prepend(CGGeneric("JS::Rooted<JSObject*> argObj(cx, &${val}.toObject());\n"))
michael@0 3965 templateBody = CGIfWrapper(templateBody, "${val}.isObject()")
michael@0 3966 else:
michael@0 3967 templateBody = CGGeneric()
michael@0 3968
michael@0 3969 if setDictionary:
michael@0 3970 assert not object
michael@0 3971 templateBody = CGList([templateBody,
michael@0 3972 CGIfWrapper(setDictionary, "!done")])
michael@0 3973
michael@0 3974 stringTypes = [t for t in memberTypes if t.isString() or t.isEnum()]
michael@0 3975 numericTypes = [t for t in memberTypes if t.isNumeric()]
michael@0 3976 booleanTypes = [t for t in memberTypes if t.isBoolean()]
michael@0 3977 if stringTypes or numericTypes or booleanTypes:
michael@0 3978 assert len(stringTypes) <= 1
michael@0 3979 assert len(numericTypes) <= 1
michael@0 3980 assert len(booleanTypes) <= 1
michael@0 3981
michael@0 3982 # We will wrap all this stuff in a do { } while (0); so we
michael@0 3983 # can use "break" for flow control.
michael@0 3984 def getStringOrPrimitiveConversion(memberType):
michael@0 3985 if memberType.isEnum():
michael@0 3986 name = memberType.inner.identifier.name
michael@0 3987 else:
michael@0 3988 name = memberType.name
michael@0 3989 return CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${mutableVal}, tryNext)) || !tryNext;\n"
michael@0 3990 "break;\n" % (unionArgumentObj, name))
michael@0 3991 other = CGList([])
michael@0 3992 stringConversion = map(getStringOrPrimitiveConversion, stringTypes)
michael@0 3993 numericConversion = map(getStringOrPrimitiveConversion, numericTypes)
michael@0 3994 booleanConversion = map(getStringOrPrimitiveConversion, booleanTypes)
michael@0 3995 if stringConversion:
michael@0 3996 if booleanConversion:
michael@0 3997 other.append(CGIfWrapper(booleanConversion[0],
michael@0 3998 "${val}.isBoolean()"))
michael@0 3999 if numericConversion:
michael@0 4000 other.append(CGIfWrapper(numericConversion[0],
michael@0 4001 "${val}.isNumber()"))
michael@0 4002 other.append(stringConversion[0])
michael@0 4003 elif numericConversion:
michael@0 4004 if booleanConversion:
michael@0 4005 other.append(CGIfWrapper(booleanConversion[0],
michael@0 4006 "${val}.isBoolean()"))
michael@0 4007 other.append(numericConversion[0])
michael@0 4008 else:
michael@0 4009 assert booleanConversion
michael@0 4010 other.append(booleanConversion[0])
michael@0 4011
michael@0 4012 other = CGWrapper(CGIndenter(other), pre="do {\n", post="} while (0);\n")
michael@0 4013 if hasObjectTypes or setDictionary:
michael@0 4014 other = CGWrapper(CGIndenter(other), "{\n", post="}\n")
michael@0 4015 if object:
michael@0 4016 templateBody = CGElseChain([templateBody, other])
michael@0 4017 else:
michael@0 4018 other = CGWrapper(other, pre="if (!done) ")
michael@0 4019 templateBody = CGList([templateBody, other])
michael@0 4020 else:
michael@0 4021 assert templateBody.define() == ""
michael@0 4022 templateBody = other
michael@0 4023 else:
michael@0 4024 other = None
michael@0 4025
michael@0 4026 templateBody = CGWrapper(templateBody, pre="bool done = false, failed = false, tryNext;\n")
michael@0 4027 throw = CGGeneric(fill(
michael@0 4028 """
michael@0 4029 if (failed) {
michael@0 4030 $*{exceptionCode}
michael@0 4031 }
michael@0 4032 if (!done) {
michael@0 4033 ThrowErrorMessage(cx, MSG_NOT_IN_UNION, "${desc}", "${names}");
michael@0 4034 $*{exceptionCode}
michael@0 4035 }
michael@0 4036 """,
michael@0 4037 exceptionCode=exceptionCode,
michael@0 4038 desc=firstCap(sourceDescription),
michael@0 4039 names=", ".join(names)))
michael@0 4040
michael@0 4041 templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw])), pre="{\n", post="}\n")
michael@0 4042
michael@0 4043 typeName = CGUnionStruct.unionTypeDecl(type, isMember)
michael@0 4044 argumentTypeName = typeName + "Argument"
michael@0 4045 if nullable:
michael@0 4046 typeName = "Nullable<" + typeName + " >"
michael@0 4047
michael@0 4048 def handleNull(templateBody, setToNullVar, extraConditionForNull=""):
michael@0 4049 nullTest = "%s${val}.isNullOrUndefined()" % extraConditionForNull
michael@0 4050 return CGIfElseWrapper(nullTest,
michael@0 4051 CGGeneric("%s.SetNull();\n" % setToNullVar),
michael@0 4052 templateBody)
michael@0 4053
michael@0 4054 if type.hasNullableType:
michael@0 4055 assert not nullable
michael@0 4056 # Make sure to handle a null default value here
michael@0 4057 if defaultValue and isinstance(defaultValue, IDLNullValue):
michael@0 4058 assert defaultValue.type == type
michael@0 4059 extraConditionForNull = "!(${haveValue}) || "
michael@0 4060 else:
michael@0 4061 extraConditionForNull = ""
michael@0 4062 templateBody = handleNull(templateBody, unionArgumentObj,
michael@0 4063 extraConditionForNull=extraConditionForNull)
michael@0 4064
michael@0 4065 declType = CGGeneric(typeName)
michael@0 4066 if isMember:
michael@0 4067 holderType = None
michael@0 4068 else:
michael@0 4069 holderType = CGGeneric(argumentTypeName)
michael@0 4070 if nullable:
michael@0 4071 holderType = CGTemplatedType("Maybe", holderType)
michael@0 4072
michael@0 4073 # If we're isOptional and not nullable the normal optional handling will
michael@0 4074 # handle lazy construction of our holder. If we're nullable and not
michael@0 4075 # isMember we do it all by hand because we do not want our holder
michael@0 4076 # constructed if we're null. But if we're isMember we don't have a
michael@0 4077 # holder anyway, so we can do the normal Optional codepath.
michael@0 4078 declLoc = "${declName}"
michael@0 4079 constructDecl = None
michael@0 4080 if nullable:
michael@0 4081 if isOptional and not isMember:
michael@0 4082 holderArgs = "${declName}.Value().SetValue()"
michael@0 4083 declType = CGTemplatedType("Optional", declType)
michael@0 4084 constructDecl = CGGeneric("${declName}.Construct();\n")
michael@0 4085 declLoc = "${declName}.Value()"
michael@0 4086 else:
michael@0 4087 holderArgs = "${declName}.SetValue()"
michael@0 4088 if holderType is not None:
michael@0 4089 constructHolder = CGGeneric("${holderName}.construct(%s);\n" % holderArgs)
michael@0 4090 else:
michael@0 4091 constructHolder = None
michael@0 4092 # Don't need to pass those args when the holder is being constructed
michael@0 4093 holderArgs = None
michael@0 4094 else:
michael@0 4095 holderArgs = "${declName}"
michael@0 4096 constructHolder = None
michael@0 4097
michael@0 4098 if defaultValue and not isinstance(defaultValue, IDLNullValue):
michael@0 4099 tag = defaultValue.type.tag()
michael@0 4100
michael@0 4101 if tag in numericSuffixes or tag is IDLType.Tags.bool:
michael@0 4102 defaultStr = getHandleDefault(defaultValue)
michael@0 4103 value = declLoc + (".Value()" if nullable else "")
michael@0 4104 default = CGGeneric("%s.RawSetAs%s() = %s;\n" %
michael@0 4105 (value, defaultValue.type, defaultStr))
michael@0 4106 else:
michael@0 4107 default = CGGeneric(
michael@0 4108 handleDefaultStringValue(
michael@0 4109 defaultValue, "%s.SetStringData" % unionArgumentObj) +
michael@0 4110 ";\n")
michael@0 4111
michael@0 4112 templateBody = CGIfElseWrapper("!(${haveValue})", default, templateBody)
michael@0 4113
michael@0 4114 templateBody = CGList([constructHolder, templateBody])
michael@0 4115
michael@0 4116 if nullable:
michael@0 4117 if defaultValue:
michael@0 4118 if isinstance(defaultValue, IDLNullValue):
michael@0 4119 extraConditionForNull = "!(${haveValue}) || "
michael@0 4120 else:
michael@0 4121 extraConditionForNull = "${haveValue} && "
michael@0 4122 else:
michael@0 4123 extraConditionForNull = ""
michael@0 4124 templateBody = handleNull(templateBody, declLoc,
michael@0 4125 extraConditionForNull=extraConditionForNull)
michael@0 4126 elif (not type.hasNullableType and defaultValue and
michael@0 4127 isinstance(defaultValue, IDLNullValue)):
michael@0 4128 assert type.hasDictionaryType
michael@0 4129 assert defaultValue.type.isDictionary()
michael@0 4130 if not isMember and typeNeedsRooting(defaultValue.type):
michael@0 4131 ctorArgs = "cx"
michael@0 4132 else:
michael@0 4133 ctorArgs = ""
michael@0 4134 initDictionaryWithNull = CGIfWrapper(
michael@0 4135 CGGeneric("return false;\n"),
michael@0 4136 ('!%s.RawSetAs%s(%s).Init(cx, JS::NullHandleValue, "Member of %s")'
michael@0 4137 % (declLoc, getUnionMemberName(defaultValue.type),
michael@0 4138 ctorArgs, type)))
michael@0 4139 templateBody = CGIfElseWrapper("!(${haveValue})",
michael@0 4140 initDictionaryWithNull,
michael@0 4141 templateBody)
michael@0 4142
michael@0 4143 templateBody = CGList([constructDecl, templateBody])
michael@0 4144
michael@0 4145 return JSToNativeConversionInfo(templateBody.define(),
michael@0 4146 declType=declType,
michael@0 4147 holderType=holderType,
michael@0 4148 holderArgs=holderArgs,
michael@0 4149 dealWithOptional=isOptional and (not nullable or isMember))
michael@0 4150
michael@0 4151 if type.isGeckoInterface():
michael@0 4152 assert not isEnforceRange and not isClamp
michael@0 4153
michael@0 4154 descriptor = descriptorProvider.getDescriptor(
michael@0 4155 type.unroll().inner.identifier.name)
michael@0 4156
michael@0 4157 if descriptor.nativeType == 'JSObject':
michael@0 4158 # XXXbz Workers code does this sometimes
michael@0 4159 assert descriptor.workers
michael@0 4160 return handleJSObjectType(type, isMember, failureCode)
michael@0 4161
michael@0 4162 if descriptor.interface.isCallback():
michael@0 4163 name = descriptor.interface.identifier.name
michael@0 4164 if type.nullable() or isCallbackReturnValue:
michael@0 4165 declType = CGGeneric("nsRefPtr<%s>" % name)
michael@0 4166 else:
michael@0 4167 declType = CGGeneric("OwningNonNull<%s>" % name)
michael@0 4168 # BOGUS extra blank line here turns out to be at the end of a block:
michael@0 4169 conversion = indent(CGCallbackTempRoot(name).define()) + "\n"
michael@0 4170
michael@0 4171 template = wrapObjectTemplate(conversion, type,
michael@0 4172 "${declName} = nullptr;\n",
michael@0 4173 failureCode)
michael@0 4174 return JSToNativeConversionInfo(template, declType=declType,
michael@0 4175 dealWithOptional=isOptional)
michael@0 4176
michael@0 4177 # This is an interface that we implement as a concrete class
michael@0 4178 # or an XPCOM interface.
michael@0 4179
michael@0 4180 # Allow null pointers for nullable types and old-binding classes, and
michael@0 4181 # use an nsRefPtr or raw pointer for callback return values to make
michael@0 4182 # them easier to return.
michael@0 4183 argIsPointer = (type.nullable() or type.unroll().inner.isExternal() or
michael@0 4184 isCallbackReturnValue)
michael@0 4185
michael@0 4186 # Sequences and non-worker callbacks have to hold a strong ref to the
michael@0 4187 # thing being passed down. Union return values must hold a strong ref
michael@0 4188 # because they may be returning an addrefed pointer.
michael@0 4189 # Also, callback return values always end up
michael@0 4190 # addrefing anyway, so there is no point trying to avoid it here and it
michael@0 4191 # makes other things simpler since we can assume the return value is a
michael@0 4192 # strong ref.
michael@0 4193 forceOwningType = ((descriptor.interface.isCallback() and
michael@0 4194 not descriptor.workers) or
michael@0 4195 isMember or
michael@0 4196 isCallbackReturnValue)
michael@0 4197
michael@0 4198 if forceOwningType and descriptor.nativeOwnership == 'owned':
michael@0 4199 raise TypeError("Interface %s has 'owned' nativeOwnership, so we "
michael@0 4200 "don't know how to keep it alive in %s" %
michael@0 4201 (descriptor.interface.identifier.name,
michael@0 4202 sourceDescription))
michael@0 4203
michael@0 4204 typeName = descriptor.nativeType
michael@0 4205 typePtr = typeName + "*"
michael@0 4206
michael@0 4207 # Compute a few things:
michael@0 4208 # - declType is the type we want to return as the first element of our
michael@0 4209 # tuple.
michael@0 4210 # - holderType is the type we want to return as the third element
michael@0 4211 # of our tuple.
michael@0 4212
michael@0 4213 # Set up some sensible defaults for these things insofar as we can.
michael@0 4214 holderType = None
michael@0 4215 if argIsPointer:
michael@0 4216 if forceOwningType:
michael@0 4217 declType = "nsRefPtr<" + typeName + ">"
michael@0 4218 else:
michael@0 4219 declType = typePtr
michael@0 4220 else:
michael@0 4221 if forceOwningType:
michael@0 4222 declType = "OwningNonNull<" + typeName + ">"
michael@0 4223 else:
michael@0 4224 declType = "NonNull<" + typeName + ">"
michael@0 4225
michael@0 4226 templateBody = ""
michael@0 4227 if not descriptor.skipGen and not descriptor.interface.isConsequential() and not descriptor.interface.isExternal():
michael@0 4228 if failureCode is not None:
michael@0 4229 templateBody += str(CastableObjectUnwrapper(
michael@0 4230 descriptor,
michael@0 4231 "&${val}.toObject()",
michael@0 4232 "${declName}",
michael@0 4233 failureCode))
michael@0 4234 else:
michael@0 4235 templateBody += str(FailureFatalCastableObjectUnwrapper(
michael@0 4236 descriptor,
michael@0 4237 "&${val}.toObject()",
michael@0 4238 "${declName}",
michael@0 4239 exceptionCode,
michael@0 4240 isCallbackReturnValue,
michael@0 4241 firstCap(sourceDescription)))
michael@0 4242 elif descriptor.workers:
michael@0 4243 return handleJSObjectType(type, isMember, failureCode)
michael@0 4244 else:
michael@0 4245 # Either external, or new-binding non-castable. We always have a
michael@0 4246 # holder for these, because we don't actually know whether we have
michael@0 4247 # to addref when unwrapping or not. So we just pass an
michael@0 4248 # getter_AddRefs(nsRefPtr) to XPConnect and if we'll need a release
michael@0 4249 # it'll put a non-null pointer in there.
michael@0 4250 if forceOwningType:
michael@0 4251 # Don't return a holderType in this case; our declName
michael@0 4252 # will just own stuff.
michael@0 4253 templateBody += "nsRefPtr<" + typeName + "> ${holderName};\n"
michael@0 4254 else:
michael@0 4255 holderType = "nsRefPtr<" + typeName + ">"
michael@0 4256 templateBody += (
michael@0 4257 "JS::Rooted<JS::Value> tmpVal(cx, ${val});\n" +
michael@0 4258 typePtr + " tmp;\n"
michael@0 4259 "if (NS_FAILED(UnwrapArg<" + typeName + ">(cx, ${val}, &tmp, static_cast<" + typeName + "**>(getter_AddRefs(${holderName})), &tmpVal))) {\n")
michael@0 4260 templateBody += CGIndenter(onFailureBadType(failureCode,
michael@0 4261 descriptor.interface.identifier.name)).define()
michael@0 4262 templateBody += ("}\n"
michael@0 4263 "MOZ_ASSERT(tmp);\n")
michael@0 4264
michael@0 4265 if not isDefinitelyObject and not forceOwningType:
michael@0 4266 # Our tmpVal will go out of scope, so we can't rely on it
michael@0 4267 # for rooting
michael@0 4268 templateBody += dedent("""
michael@0 4269 if (tmpVal != ${val} && !${holderName}) {
michael@0 4270 // We have to have a strong ref, because we got this off
michael@0 4271 // some random object that might get GCed
michael@0 4272 ${holderName} = tmp;
michael@0 4273 }
michael@0 4274 """)
michael@0 4275
michael@0 4276 # And store our tmp, before it goes out of scope.
michael@0 4277 templateBody += "${declName} = tmp;\n"
michael@0 4278
michael@0 4279 # Just pass failureCode, not onFailureBadType, here, so we'll report the
michael@0 4280 # thing as not an object as opposed to not implementing whatever our
michael@0 4281 # interface is.
michael@0 4282 templateBody = wrapObjectTemplate(templateBody, type,
michael@0 4283 "${declName} = nullptr;\n", failureCode)
michael@0 4284
michael@0 4285 declType = CGGeneric(declType)
michael@0 4286 if holderType is not None:
michael@0 4287 holderType = CGGeneric(holderType)
michael@0 4288 return JSToNativeConversionInfo(templateBody,
michael@0 4289 declType=declType,
michael@0 4290 holderType=holderType,
michael@0 4291 dealWithOptional=isOptional)
michael@0 4292
michael@0 4293 if type.isSpiderMonkeyInterface():
michael@0 4294 assert not isEnforceRange and not isClamp
michael@0 4295 name = type.name
michael@0 4296 arrayType = CGGeneric(name)
michael@0 4297 declType = arrayType
michael@0 4298 if type.nullable():
michael@0 4299 declType = CGTemplatedType("Nullable", declType)
michael@0 4300 objRef = "${declName}.SetValue()"
michael@0 4301 else:
michael@0 4302 objRef = "${declName}"
michael@0 4303
michael@0 4304 # Again, this is a bit strange since we are actually building a
michael@0 4305 # template string here. ${objRef} and $*{badType} below are filled in
michael@0 4306 # right now; $${val} expands to ${val}, to be filled in later.
michael@0 4307 template = fill(
michael@0 4308 """
michael@0 4309 if (!${objRef}.Init(&$${val}.toObject())) {
michael@0 4310 $*{badType}
michael@0 4311 }
michael@0 4312
michael@0 4313 """, # BOGUS extra blank line
michael@0 4314 objRef=objRef,
michael@0 4315 badType=onFailureBadType(failureCode, type.name).define())
michael@0 4316 template = wrapObjectTemplate(template, type, "${declName}.SetNull();\n",
michael@0 4317 failureCode)
michael@0 4318 if not isMember:
michael@0 4319 # This is a bit annoying. In a union we don't want to have a
michael@0 4320 # holder, since unions don't support that. But if we're optional we
michael@0 4321 # want to have a holder, so that the callee doesn't see
michael@0 4322 # Optional<RootedTypedArray<ArrayType> >. So do a holder if we're
michael@0 4323 # optional and use a RootedTypedArray otherwise.
michael@0 4324 if isOptional:
michael@0 4325 holderType = CGTemplatedType("TypedArrayRooter", arrayType)
michael@0 4326 # If our typed array is nullable, this will set the Nullable to
michael@0 4327 # be not-null, but that's ok because we make an explicit
michael@0 4328 # SetNull() call on it as needed if our JS value is actually
michael@0 4329 # null. XXXbz Because "Maybe" takes const refs for constructor
michael@0 4330 # arguments, we can't pass a reference here; have to pass a
michael@0 4331 # pointer.
michael@0 4332 holderArgs = "cx, &%s" % objRef
michael@0 4333 declArgs = None
michael@0 4334 else:
michael@0 4335 holderType = None
michael@0 4336 holderArgs = None
michael@0 4337 declType = CGTemplatedType("RootedTypedArray", declType)
michael@0 4338 declArgs = "cx"
michael@0 4339 else:
michael@0 4340 holderType = None
michael@0 4341 holderArgs = None
michael@0 4342 declArgs = None
michael@0 4343 return JSToNativeConversionInfo(template,
michael@0 4344 declType=declType,
michael@0 4345 holderType=holderType,
michael@0 4346 dealWithOptional=isOptional,
michael@0 4347 declArgs=declArgs,
michael@0 4348 holderArgs=holderArgs)
michael@0 4349
michael@0 4350 if type.isDOMString():
michael@0 4351 assert not isEnforceRange and not isClamp
michael@0 4352
michael@0 4353 treatAs = {
michael@0 4354 "Default": "eStringify",
michael@0 4355 "EmptyString": "eEmpty",
michael@0 4356 "Null": "eNull",
michael@0 4357 }
michael@0 4358 if type.nullable():
michael@0 4359 # For nullable strings null becomes a null string.
michael@0 4360 treatNullAs = "Null"
michael@0 4361 # For nullable strings undefined also becomes a null string.
michael@0 4362 undefinedBehavior = "eNull"
michael@0 4363 else:
michael@0 4364 undefinedBehavior = "eStringify"
michael@0 4365 nullBehavior = treatAs[treatNullAs]
michael@0 4366
michael@0 4367 def getConversionCode(varName):
michael@0 4368 conversionCode = (
michael@0 4369 "if (!ConvertJSValueToString(cx, ${val}, ${mutableVal}, %s, %s, %s)) {\n"
michael@0 4370 "%s"
michael@0 4371 "}\n" % (nullBehavior, undefinedBehavior, varName,
michael@0 4372 exceptionCodeIndented.define()))
michael@0 4373 if defaultValue is None:
michael@0 4374 return conversionCode
michael@0 4375
michael@0 4376 if isinstance(defaultValue, IDLNullValue):
michael@0 4377 assert(type.nullable())
michael@0 4378 defaultCode = "%s.SetNull()" % varName
michael@0 4379 else:
michael@0 4380 defaultCode = handleDefaultStringValue(defaultValue,
michael@0 4381 "%s.SetData" % varName)
michael@0 4382 return handleDefault(conversionCode, defaultCode + ";\n")
michael@0 4383
michael@0 4384 if isMember:
michael@0 4385 # We have to make a copy, except in the variadic case, because our
michael@0 4386 # jsval may well not live as long as our string needs to.
michael@0 4387 declType = CGGeneric("nsString")
michael@0 4388 if isMember == "Variadic":
michael@0 4389 # The string is kept alive by the argument, so we can just
michael@0 4390 # depend on it.
michael@0 4391 assignString = "${declName}.Rebind(str.Data(), str.Length());\n"
michael@0 4392 else:
michael@0 4393 assignString = "${declName} = str;\n"
michael@0 4394 return JSToNativeConversionInfo(
michael@0 4395 fill(
michael@0 4396 """
michael@0 4397 {
michael@0 4398 binding_detail::FakeDependentString str;
michael@0 4399 $*{convert}
michael@0 4400 $*{assign}
michael@0 4401 }
michael@0 4402
michael@0 4403 """, # BOGUS extra newline
michael@0 4404 convert=getConversionCode("str"),
michael@0 4405 assign=assignString),
michael@0 4406 declType=declType,
michael@0 4407 dealWithOptional=isOptional)
michael@0 4408
michael@0 4409 if isOptional:
michael@0 4410 declType = "Optional<nsAString>"
michael@0 4411 holderType = CGGeneric("binding_detail::FakeDependentString")
michael@0 4412 conversionCode = ("%s"
michael@0 4413 "${declName} = &${holderName};\n" %
michael@0 4414 getConversionCode("${holderName}"))
michael@0 4415 else:
michael@0 4416 declType = "binding_detail::FakeDependentString"
michael@0 4417 holderType = None
michael@0 4418 conversionCode = getConversionCode("${declName}")
michael@0 4419
michael@0 4420 # No need to deal with optional here; we handled it already
michael@0 4421 return JSToNativeConversionInfo(
michael@0 4422 conversionCode,
michael@0 4423 declType=CGGeneric(declType),
michael@0 4424 holderType=holderType)
michael@0 4425
michael@0 4426 if type.isByteString():
michael@0 4427 assert not isEnforceRange and not isClamp
michael@0 4428
michael@0 4429 nullable = toStringBool(type.nullable())
michael@0 4430
michael@0 4431 conversionCode = (
michael@0 4432 "if (!ConvertJSValueToByteString(cx, ${val}, ${mutableVal}, %s, ${declName})) {\n"
michael@0 4433 "%s"
michael@0 4434 "}\n" % (nullable, exceptionCodeIndented.define()))
michael@0 4435 # ByteString arguments cannot have a default value.
michael@0 4436 assert defaultValue is None
michael@0 4437
michael@0 4438 return JSToNativeConversionInfo(
michael@0 4439 conversionCode,
michael@0 4440 declType=CGGeneric("nsCString"),
michael@0 4441 dealWithOptional=isOptional)
michael@0 4442
michael@0 4443 if type.isEnum():
michael@0 4444 assert not isEnforceRange and not isClamp
michael@0 4445
michael@0 4446 enumName = type.unroll().inner.identifier.name
michael@0 4447 declType = CGGeneric(enumName)
michael@0 4448 if type.nullable():
michael@0 4449 declType = CGTemplatedType("Nullable", declType)
michael@0 4450 declType = declType.define()
michael@0 4451 enumLoc = "${declName}.SetValue()"
michael@0 4452 else:
michael@0 4453 enumLoc = "${declName}"
michael@0 4454 declType = declType.define()
michael@0 4455
michael@0 4456 if invalidEnumValueFatal:
michael@0 4457 handleInvalidEnumValueCode = "MOZ_ASSERT(index >= 0);\n"
michael@0 4458 else:
michael@0 4459 # invalidEnumValueFatal is false only for attributes. So we won't
michael@0 4460 # have a non-default exceptionCode here unless attribute "arg
michael@0 4461 # conversion" code starts passing in an exceptionCode. At which
michael@0 4462 # point we'll need to figure out what that even means.
michael@0 4463 assert exceptionCode == "return false;\n"
michael@0 4464 handleInvalidEnumValueCode = dedent("""
michael@0 4465 if (index < 0) {
michael@0 4466 return true;
michael@0 4467 }
michael@0 4468 """)
michael@0 4469
michael@0 4470 template = fill(
michael@0 4471 """
michael@0 4472 {
michael@0 4473 bool ok;
michael@0 4474 int index = FindEnumStringIndex<${invalidEnumValueFatal}>(cx, $${val}, ${values}, "${enumtype}", "${sourceDescription}", &ok);
michael@0 4475 if (!ok) {
michael@0 4476 $*{exceptionCode}
michael@0 4477 }
michael@0 4478 $*{handleInvalidEnumValueCode}
michael@0 4479 ${enumLoc} = static_cast<${enumtype}>(index);
michael@0 4480 }
michael@0 4481 """,
michael@0 4482 enumtype=enumName,
michael@0 4483 values=enumName + "Values::" + ENUM_ENTRY_VARIABLE_NAME,
michael@0 4484 invalidEnumValueFatal=toStringBool(invalidEnumValueFatal),
michael@0 4485 handleInvalidEnumValueCode=handleInvalidEnumValueCode,
michael@0 4486 exceptionCode=exceptionCode,
michael@0 4487 enumLoc=enumLoc,
michael@0 4488 sourceDescription=firstCap(sourceDescription))
michael@0 4489
michael@0 4490 setNull = "${declName}.SetNull();\n"
michael@0 4491
michael@0 4492 if type.nullable():
michael@0 4493 template = CGIfElseWrapper("${val}.isNullOrUndefined()",
michael@0 4494 CGGeneric(setNull),
michael@0 4495 CGGeneric(template)).define()
michael@0 4496
michael@0 4497 if defaultValue is not None:
michael@0 4498 if isinstance(defaultValue, IDLNullValue):
michael@0 4499 assert type.nullable()
michael@0 4500 template = handleDefault(template, setNull)
michael@0 4501 else:
michael@0 4502 assert(defaultValue.type.tag() == IDLType.Tags.domstring)
michael@0 4503 template = handleDefault(template,
michael@0 4504 ("%s = %s::%s;\n" %
michael@0 4505 (enumLoc, enumName,
michael@0 4506 getEnumValueName(defaultValue.value))))
michael@0 4507 return JSToNativeConversionInfo(template, declType=CGGeneric(declType),
michael@0 4508 dealWithOptional=isOptional)
michael@0 4509
michael@0 4510 if type.isCallback():
michael@0 4511 assert not isEnforceRange and not isClamp
michael@0 4512 assert not type.treatNonCallableAsNull() or type.nullable()
michael@0 4513 assert not type.treatNonObjectAsNull() or type.nullable()
michael@0 4514 assert not type.treatNonObjectAsNull() or not type.treatNonCallableAsNull()
michael@0 4515
michael@0 4516 name = type.unroll().identifier.name
michael@0 4517 if type.nullable():
michael@0 4518 declType = CGGeneric("nsRefPtr<%s>" % name)
michael@0 4519 else:
michael@0 4520 declType = CGGeneric("OwningNonNull<%s>" % name)
michael@0 4521 conversion = indent(CGCallbackTempRoot(name).define())
michael@0 4522
michael@0 4523 if allowTreatNonCallableAsNull and type.treatNonCallableAsNull():
michael@0 4524 haveCallable = "JS_ObjectIsCallable(cx, &${val}.toObject())"
michael@0 4525 if not isDefinitelyObject:
michael@0 4526 haveCallable = "${val}.isObject() && " + haveCallable
michael@0 4527 if defaultValue is not None:
michael@0 4528 assert(isinstance(defaultValue, IDLNullValue))
michael@0 4529 haveCallable = "${haveValue} && " + haveCallable
michael@0 4530 template = (
michael@0 4531 ("if (%s) {\n" % haveCallable) +
michael@0 4532 conversion +
michael@0 4533 "} else {\n"
michael@0 4534 " ${declName} = nullptr;\n"
michael@0 4535 "}\n")
michael@0 4536 elif allowTreatNonCallableAsNull and type.treatNonObjectAsNull():
michael@0 4537 if not isDefinitelyObject:
michael@0 4538 haveObject = "${val}.isObject()"
michael@0 4539 if defaultValue is not None:
michael@0 4540 assert(isinstance(defaultValue, IDLNullValue))
michael@0 4541 haveObject = "${haveValue} && " + haveObject
michael@0 4542 template = CGIfElseWrapper(haveObject,
michael@0 4543 CGGeneric(conversion + "\n"), # BOGUS extra blank line
michael@0 4544 CGGeneric("${declName} = nullptr;\n")).define()
michael@0 4545 else:
michael@0 4546 template = conversion
michael@0 4547 else:
michael@0 4548 template = wrapObjectTemplate(
michael@0 4549 "if (JS_ObjectIsCallable(cx, &${val}.toObject())) {\n" +
michael@0 4550 conversion +
michael@0 4551 "} else {\n" +
michael@0 4552 indent(onFailureNotCallable(failureCode).define()) +
michael@0 4553 "}\n",
michael@0 4554 type,
michael@0 4555 "${declName} = nullptr;\n",
michael@0 4556 failureCode)
michael@0 4557 return JSToNativeConversionInfo(template, declType=declType,
michael@0 4558 dealWithOptional=isOptional)
michael@0 4559
michael@0 4560 if type.isAny():
michael@0 4561 assert not isEnforceRange and not isClamp
michael@0 4562
michael@0 4563 declArgs = None
michael@0 4564 if isMember in ("Variadic", "Sequence", "Dictionary", "MozMap"):
michael@0 4565 # Rooting is handled by the sequence and dictionary tracers.
michael@0 4566 declType = "JS::Value"
michael@0 4567 else:
michael@0 4568 assert not isMember
michael@0 4569 declType = "JS::Rooted<JS::Value>"
michael@0 4570 declArgs = "cx"
michael@0 4571
michael@0 4572 assert not isOptional
michael@0 4573 templateBody = "${declName} = ${val};\n"
michael@0 4574 # We may not have a default value if we're being converted for
michael@0 4575 # a setter, say.
michael@0 4576 if defaultValue:
michael@0 4577 if isinstance(defaultValue, IDLNullValue):
michael@0 4578 defaultHandling = "${declName} = JS::NullValue();\n"
michael@0 4579 else:
michael@0 4580 assert isinstance(defaultValue, IDLUndefinedValue)
michael@0 4581 defaultHandling = "${declName} = JS::UndefinedValue();\n"
michael@0 4582 templateBody = handleDefault(templateBody, defaultHandling)
michael@0 4583 return JSToNativeConversionInfo(templateBody,
michael@0 4584 declType=CGGeneric(declType),
michael@0 4585 declArgs=declArgs)
michael@0 4586
michael@0 4587 if type.isObject():
michael@0 4588 assert not isEnforceRange and not isClamp
michael@0 4589 return handleJSObjectType(type, isMember, failureCode)
michael@0 4590
michael@0 4591 if type.isDictionary():
michael@0 4592 # There are no nullable dictionaries
michael@0 4593 assert not type.nullable() or isCallbackReturnValue
michael@0 4594 # All optional dictionaries always have default values, so we
michael@0 4595 # should be able to assume not isOptional here.
michael@0 4596 assert not isOptional
michael@0 4597 # In the callback return value case we never have to worry
michael@0 4598 # about a default value; we always have a value.
michael@0 4599 assert not isCallbackReturnValue or defaultValue is None
michael@0 4600
michael@0 4601 typeName = CGDictionary.makeDictionaryName(type.unroll().inner)
michael@0 4602 if not isMember and not isCallbackReturnValue:
michael@0 4603 # Since we're not a member and not nullable or optional, no one will
michael@0 4604 # see our real type, so we can do the fast version of the dictionary
michael@0 4605 # that doesn't pre-initialize members.
michael@0 4606 typeName = "binding_detail::Fast" + typeName
michael@0 4607
michael@0 4608 declType = CGGeneric(typeName)
michael@0 4609
michael@0 4610 # We do manual default value handling here, because we
michael@0 4611 # actually do want a jsval, and we only handle null anyway
michael@0 4612 # NOTE: if isNullOrUndefined or isDefinitelyObject are true,
michael@0 4613 # we know we have a value, so we don't have to worry about the
michael@0 4614 # default value.
michael@0 4615 if (not isNullOrUndefined and not isDefinitelyObject and
michael@0 4616 defaultValue is not None):
michael@0 4617 assert(isinstance(defaultValue, IDLNullValue))
michael@0 4618 val = "(${haveValue}) ? ${val} : JS::NullHandleValue"
michael@0 4619 else:
michael@0 4620 val = "${val}"
michael@0 4621
michael@0 4622 if failureCode is not None:
michael@0 4623 if isDefinitelyObject:
michael@0 4624 dictionaryTest = "IsObjectValueConvertibleToDictionary"
michael@0 4625 else:
michael@0 4626 dictionaryTest = "IsConvertibleToDictionary"
michael@0 4627 # Check that the value we have can in fact be converted to
michael@0 4628 # a dictionary, and return failureCode if not.
michael@0 4629 template = CGIfWrapper(
michael@0 4630 CGGeneric(failureCode),
michael@0 4631 "!%s(cx, ${val})" % dictionaryTest).define() + "\n"
michael@0 4632 else:
michael@0 4633 template = ""
michael@0 4634
michael@0 4635 dictLoc = "${declName}"
michael@0 4636 if type.nullable():
michael@0 4637 dictLoc += ".SetValue()"
michael@0 4638
michael@0 4639 template += ('if (!%s.Init(cx, %s, "%s")) {\n'
michael@0 4640 "%s"
michael@0 4641 "}\n" % (dictLoc, val, firstCap(sourceDescription),
michael@0 4642 exceptionCodeIndented.define()))
michael@0 4643
michael@0 4644 if type.nullable():
michael@0 4645 declType = CGTemplatedType("Nullable", declType)
michael@0 4646 template = CGIfElseWrapper("${val}.isNullOrUndefined()",
michael@0 4647 CGGeneric("${declName}.SetNull();\n"),
michael@0 4648 CGGeneric(template)).define()
michael@0 4649
michael@0 4650 # Dictionary arguments that might contain traceable things need to get
michael@0 4651 # traced
michael@0 4652 if not isMember and isCallbackReturnValue:
michael@0 4653 # Go ahead and just convert directly into our actual return value
michael@0 4654 declType = CGWrapper(declType, post="&")
michael@0 4655 declArgs = "aRetVal"
michael@0 4656 elif not isMember and typeNeedsRooting(type):
michael@0 4657 declType = CGTemplatedType("RootedDictionary", declType)
michael@0 4658 declArgs = "cx"
michael@0 4659 else:
michael@0 4660 declArgs = None
michael@0 4661
michael@0 4662 return JSToNativeConversionInfo(template, declType=declType,
michael@0 4663 declArgs=declArgs)
michael@0 4664
michael@0 4665 if type.isVoid():
michael@0 4666 assert not isOptional
michael@0 4667 # This one only happens for return values, and its easy: Just
michael@0 4668 # ignore the jsval.
michael@0 4669 return JSToNativeConversionInfo("")
michael@0 4670
michael@0 4671 if type.isDate():
michael@0 4672 assert not isEnforceRange and not isClamp
michael@0 4673
michael@0 4674 declType = CGGeneric("Date")
michael@0 4675 if type.nullable():
michael@0 4676 declType = CGTemplatedType("Nullable", declType)
michael@0 4677 dateVal = "${declName}.SetValue()"
michael@0 4678 else:
michael@0 4679 dateVal = "${declName}"
michael@0 4680
michael@0 4681 if failureCode is None:
michael@0 4682 notDate = ('ThrowErrorMessage(cx, MSG_NOT_DATE, "%s");\n'
michael@0 4683 "%s" % (firstCap(sourceDescription), exceptionCode))
michael@0 4684 else:
michael@0 4685 notDate = failureCode
michael@0 4686
michael@0 4687 conversion = fill(
michael@0 4688 """
michael@0 4689 JS::Rooted<JSObject*> possibleDateObject(cx, &$${val}.toObject());
michael@0 4690 if (!JS_ObjectIsDate(cx, possibleDateObject) ||
michael@0 4691 !${dateVal}.SetTimeStamp(cx, possibleDateObject)) {
michael@0 4692 $*{notDate}
michael@0 4693 }
michael@0 4694 """,
michael@0 4695 dateVal=dateVal,
michael@0 4696 notDate=notDate)
michael@0 4697
michael@0 4698 conversion = wrapObjectTemplate(conversion, type,
michael@0 4699 "${declName}.SetNull();\n", notDate)
michael@0 4700 return JSToNativeConversionInfo(conversion,
michael@0 4701 declType=declType,
michael@0 4702 dealWithOptional=isOptional)
michael@0 4703
michael@0 4704 if not type.isPrimitive():
michael@0 4705 raise TypeError("Need conversion for argument type '%s'" % str(type))
michael@0 4706
michael@0 4707 typeName = builtinNames[type.tag()]
michael@0 4708
michael@0 4709 conversionBehavior = "eDefault"
michael@0 4710 if isEnforceRange:
michael@0 4711 assert type.isInteger()
michael@0 4712 conversionBehavior = "eEnforceRange"
michael@0 4713 elif isClamp:
michael@0 4714 assert type.isInteger()
michael@0 4715 conversionBehavior = "eClamp"
michael@0 4716
michael@0 4717 if type.nullable():
michael@0 4718 declType = CGGeneric("Nullable<" + typeName + ">")
michael@0 4719 writeLoc = "${declName}.SetValue()"
michael@0 4720 readLoc = "${declName}.Value()"
michael@0 4721 nullCondition = "${val}.isNullOrUndefined()"
michael@0 4722 if defaultValue is not None and isinstance(defaultValue, IDLNullValue):
michael@0 4723 nullCondition = "!(${haveValue}) || " + nullCondition
michael@0 4724 template = (
michael@0 4725 "if (%s) {\n"
michael@0 4726 " ${declName}.SetNull();\n"
michael@0 4727 "} else if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n"
michael@0 4728 "%s"
michael@0 4729 "}\n" % (nullCondition, typeName, conversionBehavior,
michael@0 4730 writeLoc, exceptionCodeIndented.define()))
michael@0 4731 else:
michael@0 4732 assert(defaultValue is None or
michael@0 4733 not isinstance(defaultValue, IDLNullValue))
michael@0 4734 writeLoc = "${declName}"
michael@0 4735 readLoc = writeLoc
michael@0 4736 template = (
michael@0 4737 "if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n"
michael@0 4738 "%s"
michael@0 4739 "}\n" % (typeName, conversionBehavior, writeLoc,
michael@0 4740 exceptionCodeIndented.define()))
michael@0 4741 declType = CGGeneric(typeName)
michael@0 4742
michael@0 4743 if type.isFloat() and not type.isUnrestricted():
michael@0 4744 if lenientFloatCode is not None:
michael@0 4745 nonFiniteCode = lenientFloatCode
michael@0 4746 else:
michael@0 4747 nonFiniteCode = ('ThrowErrorMessage(cx, MSG_NOT_FINITE, "%s");\n'
michael@0 4748 "%s" % (firstCap(sourceDescription), exceptionCode))
michael@0 4749 template = template.rstrip()
michael@0 4750 template += fill(
michael@0 4751 """
michael@0 4752 else if (!mozilla::IsFinite(${readLoc})) {
michael@0 4753 // Note: mozilla::IsFinite will do the right thing
michael@0 4754 // when passed a non-finite float too.
michael@0 4755 $*{nonFiniteCode}
michael@0 4756 }
michael@0 4757 """,
michael@0 4758 readLoc=readLoc,
michael@0 4759 nonFiniteCode=nonFiniteCode)
michael@0 4760
michael@0 4761 if (defaultValue is not None and
michael@0 4762 # We already handled IDLNullValue, so just deal with the other ones
michael@0 4763 not isinstance(defaultValue, IDLNullValue)):
michael@0 4764 tag = defaultValue.type.tag()
michael@0 4765 defaultStr = getHandleDefault(defaultValue)
michael@0 4766 template = CGIfElseWrapper(
michael@0 4767 "${haveValue}",
michael@0 4768 CGGeneric(template),
michael@0 4769 CGGeneric("%s = %s;\n" % (writeLoc, defaultStr))).define()
michael@0 4770
michael@0 4771 return JSToNativeConversionInfo(template, declType=declType,
michael@0 4772 dealWithOptional=isOptional)
michael@0 4773
michael@0 4774
michael@0 4775 def instantiateJSToNativeConversion(info, replacements, checkForValue=False):
michael@0 4776 """
michael@0 4777 Take a JSToNativeConversionInfo as returned by getJSToNativeConversionInfo
michael@0 4778 and a set of replacements as required by the strings in such an object, and
michael@0 4779 generate code to convert into stack C++ types.
michael@0 4780
michael@0 4781 If checkForValue is True, then the conversion will get wrapped in
michael@0 4782 a check for ${haveValue}.
michael@0 4783 """
michael@0 4784 templateBody, declType, holderType, dealWithOptional = (
michael@0 4785 info.template, info.declType, info.holderType, info.dealWithOptional)
michael@0 4786
michael@0 4787 if dealWithOptional and not checkForValue:
michael@0 4788 raise TypeError("Have to deal with optional things, but don't know how")
michael@0 4789 if checkForValue and declType is None:
michael@0 4790 raise TypeError("Need to predeclare optional things, so they will be "
michael@0 4791 "outside the check for big enough arg count!")
michael@0 4792
michael@0 4793 # We can't precompute our holder constructor arguments, since
michael@0 4794 # those might depend on ${declName}, which we change below. Just
michael@0 4795 # compute arguments at the point when we need them as we go.
michael@0 4796 def getArgsCGThing(args):
michael@0 4797 return CGGeneric(string.Template(args).substitute(replacements))
michael@0 4798
michael@0 4799 result = CGList([])
michael@0 4800 # Make a copy of "replacements" since we may be about to start modifying it
michael@0 4801 replacements = dict(replacements)
michael@0 4802 originalDeclName = replacements["declName"]
michael@0 4803 if declType is not None:
michael@0 4804 if dealWithOptional:
michael@0 4805 replacements["declName"] = "%s.Value()" % originalDeclName
michael@0 4806 declType = CGTemplatedType("Optional", declType)
michael@0 4807 declCtorArgs = None
michael@0 4808 elif info.declArgs is not None:
michael@0 4809 declCtorArgs = CGWrapper(getArgsCGThing(info.declArgs),
michael@0 4810 pre="(", post=")")
michael@0 4811 else:
michael@0 4812 declCtorArgs = None
michael@0 4813 result.append(
michael@0 4814 CGList([declType, CGGeneric(" "),
michael@0 4815 CGGeneric(originalDeclName),
michael@0 4816 declCtorArgs, CGGeneric(";\n")]))
michael@0 4817
michael@0 4818 originalHolderName = replacements["holderName"]
michael@0 4819 if holderType is not None:
michael@0 4820 if dealWithOptional:
michael@0 4821 replacements["holderName"] = "%s.ref()" % originalHolderName
michael@0 4822 holderType = CGTemplatedType("Maybe", holderType)
michael@0 4823 holderCtorArgs = None
michael@0 4824 elif info.holderArgs is not None:
michael@0 4825 holderCtorArgs = CGWrapper(getArgsCGThing(info.holderArgs),
michael@0 4826 pre="(", post=")")
michael@0 4827 else:
michael@0 4828 holderCtorArgs = None
michael@0 4829 result.append(
michael@0 4830 CGList([holderType, CGGeneric(" "),
michael@0 4831 CGGeneric(originalHolderName),
michael@0 4832 holderCtorArgs, CGGeneric(";\n")]))
michael@0 4833
michael@0 4834 conversion = CGGeneric(
michael@0 4835 string.Template(templateBody).substitute(replacements))
michael@0 4836
michael@0 4837 if checkForValue:
michael@0 4838 if dealWithOptional:
michael@0 4839 declConstruct = CGIndenter(
michael@0 4840 CGGeneric("%s.Construct(%s);\n" %
michael@0 4841 (originalDeclName,
michael@0 4842 getArgsCGThing(info.declArgs).define() if
michael@0 4843 info.declArgs else "")))
michael@0 4844 if holderType is not None:
michael@0 4845 holderConstruct = CGIndenter(
michael@0 4846 CGGeneric("%s.construct(%s);\n" %
michael@0 4847 (originalHolderName,
michael@0 4848 getArgsCGThing(info.holderArgs).define() if
michael@0 4849 info.holderArgs else "")))
michael@0 4850 else:
michael@0 4851 holderConstruct = None
michael@0 4852 else:
michael@0 4853 declConstruct = None
michael@0 4854 holderConstruct = None
michael@0 4855
michael@0 4856 conversion = CGList([
michael@0 4857 CGGeneric(
michael@0 4858 string.Template("if (${haveValue}) {\n").substitute(replacements)),
michael@0 4859 declConstruct,
michael@0 4860 holderConstruct,
michael@0 4861 CGIndenter(conversion),
michael@0 4862 CGGeneric("}\n")
michael@0 4863 ])
michael@0 4864
michael@0 4865 result.append(conversion)
michael@0 4866 return result
michael@0 4867
michael@0 4868
michael@0 4869 def convertConstIDLValueToJSVal(value):
michael@0 4870 if isinstance(value, IDLNullValue):
michael@0 4871 return "JS::NullValue()"
michael@0 4872 if isinstance(value, IDLUndefinedValue):
michael@0 4873 return "JS::UndefinedValue()"
michael@0 4874 tag = value.type.tag()
michael@0 4875 if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
michael@0 4876 IDLType.Tags.uint16, IDLType.Tags.int32]:
michael@0 4877 return "INT_TO_JSVAL(%s)" % (value.value)
michael@0 4878 if tag == IDLType.Tags.uint32:
michael@0 4879 return "UINT_TO_JSVAL(%sU)" % (value.value)
michael@0 4880 if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]:
michael@0 4881 return "DOUBLE_TO_JSVAL(%s)" % numericValue(tag, value.value)
michael@0 4882 if tag == IDLType.Tags.bool:
michael@0 4883 return "JSVAL_TRUE" if value.value else "JSVAL_FALSE"
michael@0 4884 if tag in [IDLType.Tags.float, IDLType.Tags.double]:
michael@0 4885 return "DOUBLE_TO_JSVAL(%s)" % (value.value)
michael@0 4886 raise TypeError("Const value of unhandled type: %s" % value.type)
michael@0 4887
michael@0 4888
michael@0 4889 class CGArgumentConverter(CGThing):
michael@0 4890 """
michael@0 4891 A class that takes an IDL argument object and its index in the
michael@0 4892 argument list and generates code to unwrap the argument to the
michael@0 4893 right native type.
michael@0 4894
michael@0 4895 argDescription is a description of the argument for error-reporting
michael@0 4896 purposes. Callers should assume that it might get placed in the middle of a
michael@0 4897 sentence. If it ends up at the beginning of a sentence, its first character
michael@0 4898 will be automatically uppercased.
michael@0 4899 """
michael@0 4900 def __init__(self, argument, index, descriptorProvider,
michael@0 4901 argDescription,
michael@0 4902 invalidEnumValueFatal=True, lenientFloatCode=None):
michael@0 4903 CGThing.__init__(self)
michael@0 4904 self.argument = argument
michael@0 4905 self.argDescription = argDescription
michael@0 4906 assert(not argument.defaultValue or argument.optional)
michael@0 4907
michael@0 4908 replacer = {
michael@0 4909 "index": index,
michael@0 4910 "argc": "args.length()"
michael@0 4911 }
michael@0 4912 self.replacementVariables = {
michael@0 4913 "declName": "arg%d" % index,
michael@0 4914 "holderName": ("arg%d" % index) + "_holder",
michael@0 4915 "obj": "obj"
michael@0 4916 }
michael@0 4917 self.replacementVariables["val"] = string.Template(
michael@0 4918 "args[${index}]").substitute(replacer)
michael@0 4919 self.replacementVariables["mutableVal"] = self.replacementVariables["val"]
michael@0 4920 haveValueCheck = string.Template(
michael@0 4921 "args.hasDefined(${index})").substitute(replacer)
michael@0 4922 self.replacementVariables["haveValue"] = haveValueCheck
michael@0 4923 self.descriptorProvider = descriptorProvider
michael@0 4924 if self.argument.optional and not self.argument.defaultValue:
michael@0 4925 self.argcAndIndex = replacer
michael@0 4926 else:
michael@0 4927 self.argcAndIndex = None
michael@0 4928 self.invalidEnumValueFatal = invalidEnumValueFatal
michael@0 4929 self.lenientFloatCode = lenientFloatCode
michael@0 4930
michael@0 4931 def define(self):
michael@0 4932 typeConversion = getJSToNativeConversionInfo(
michael@0 4933 self.argument.type,
michael@0 4934 self.descriptorProvider,
michael@0 4935 isOptional=(self.argcAndIndex is not None and
michael@0 4936 not self.argument.variadic),
michael@0 4937 invalidEnumValueFatal=self.invalidEnumValueFatal,
michael@0 4938 defaultValue=self.argument.defaultValue,
michael@0 4939 treatNullAs=self.argument.treatNullAs,
michael@0 4940 isEnforceRange=self.argument.enforceRange,
michael@0 4941 isClamp=self.argument.clamp,
michael@0 4942 lenientFloatCode=self.lenientFloatCode,
michael@0 4943 isMember="Variadic" if self.argument.variadic else False,
michael@0 4944 allowTreatNonCallableAsNull=self.argument.allowTreatNonCallableAsNull(),
michael@0 4945 sourceDescription=self.argDescription)
michael@0 4946
michael@0 4947 if not self.argument.variadic:
michael@0 4948 return instantiateJSToNativeConversion(
michael@0 4949 typeConversion,
michael@0 4950 self.replacementVariables,
michael@0 4951 self.argcAndIndex is not None).define()
michael@0 4952
michael@0 4953 # Variadic arguments get turned into a sequence.
michael@0 4954 if typeConversion.dealWithOptional:
michael@0 4955 raise TypeError("Shouldn't have optional things in variadics")
michael@0 4956 if typeConversion.holderType is not None:
michael@0 4957 raise TypeError("Shouldn't need holders for variadics")
michael@0 4958
michael@0 4959 replacer = dict(self.argcAndIndex, **self.replacementVariables)
michael@0 4960 replacer["seqType"] = CGTemplatedType("binding_detail::AutoSequence",
michael@0 4961 typeConversion.declType).define()
michael@0 4962 if typeNeedsRooting(self.argument.type):
michael@0 4963 rooterDecl = ("SequenceRooter<%s> ${holderName}(cx, &${declName});\n" %
michael@0 4964 typeConversion.declType.define())
michael@0 4965 else:
michael@0 4966 rooterDecl = ""
michael@0 4967 replacer["elemType"] = typeConversion.declType.define()
michael@0 4968
michael@0 4969 # NOTE: Keep this in sync with sequence conversions as needed
michael@0 4970 variadicConversion = string.Template(
michael@0 4971 "${seqType} ${declName};\n" +
michael@0 4972 rooterDecl +
michael@0 4973 dedent("""
michael@0 4974 if (${argc} > ${index}) {
michael@0 4975 if (!${declName}.SetCapacity(${argc} - ${index})) {
michael@0 4976 JS_ReportOutOfMemory(cx);
michael@0 4977 return false;
michael@0 4978 }
michael@0 4979 for (uint32_t variadicArg = ${index}; variadicArg < ${argc}; ++variadicArg) {
michael@0 4980 ${elemType}& slot = *${declName}.AppendElement();
michael@0 4981 """)
michael@0 4982 ).substitute(replacer)
michael@0 4983
michael@0 4984 val = string.Template("args[variadicArg]").substitute(replacer)
michael@0 4985 variadicConversion += indent(
michael@0 4986 string.Template(typeConversion.template).substitute({
michael@0 4987 "val": val,
michael@0 4988 "mutableVal": val,
michael@0 4989 "declName": "slot",
michael@0 4990 # We only need holderName here to handle isExternal()
michael@0 4991 # interfaces, which use an internal holder for the
michael@0 4992 # conversion even when forceOwningType ends up true.
michael@0 4993 "holderName": "tempHolder",
michael@0 4994 # Use the same ${obj} as for the variadic arg itself
michael@0 4995 "obj": replacer["obj"]
michael@0 4996 }), 4)
michael@0 4997
michael@0 4998 variadicConversion += (" }\n"
michael@0 4999 "}\n")
michael@0 5000 return variadicConversion
michael@0 5001
michael@0 5002
michael@0 5003 def getMaybeWrapValueFuncForType(type):
michael@0 5004 # Callbacks might actually be DOM objects; nothing prevents a page from
michael@0 5005 # doing that.
michael@0 5006 if type.isCallback() or type.isCallbackInterface() or type.isObject():
michael@0 5007 if type.nullable():
michael@0 5008 return "MaybeWrapObjectOrNullValue"
michael@0 5009 return "MaybeWrapObjectValue"
michael@0 5010 # Spidermonkey interfaces are never DOM objects. Neither are sequences or
michael@0 5011 # dictionaries, since those are always plain JS objects.
michael@0 5012 if type.isSpiderMonkeyInterface() or type.isDictionary() or type.isSequence():
michael@0 5013 if type.nullable():
michael@0 5014 return "MaybeWrapNonDOMObjectOrNullValue"
michael@0 5015 return "MaybeWrapNonDOMObjectValue"
michael@0 5016 if type.isAny():
michael@0 5017 return "MaybeWrapValue"
michael@0 5018
michael@0 5019 # For other types, just go ahead an fall back on MaybeWrapValue for now:
michael@0 5020 # it's always safe to do, and shouldn't be particularly slow for any of
michael@0 5021 # them
michael@0 5022 return "MaybeWrapValue"
michael@0 5023
michael@0 5024
michael@0 5025 sequenceWrapLevel = 0
michael@0 5026 mozMapWrapLevel = 0
michael@0 5027
michael@0 5028
michael@0 5029 def getWrapTemplateForType(type, descriptorProvider, result, successCode,
michael@0 5030 returnsNewObject, exceptionCode, typedArraysAreStructs):
michael@0 5031 """
michael@0 5032 Reflect a C++ value stored in "result", of IDL type "type" into JS. The
michael@0 5033 "successCode" is the code to run once we have successfully done the
michael@0 5034 conversion and must guarantee that execution of the conversion template
michael@0 5035 stops once the successCode has executed (e.g. by doing a 'return', or by
michael@0 5036 doing a 'break' if the entire conversion template is inside a block that
michael@0 5037 the 'break' will exit).
michael@0 5038
michael@0 5039 If typedArraysAreStructs is true, then if the type is a typed array,
michael@0 5040 "result" is one of the dom::TypedArray subclasses, not a JSObject*.
michael@0 5041
michael@0 5042 The resulting string should be used with string.Template. It
michael@0 5043 needs the following keys when substituting:
michael@0 5044
michael@0 5045 jsvalHandle: something that can be passed to methods taking a
michael@0 5046 JS::MutableHandle<JS::Value>. This can be a
michael@0 5047 JS::MutableHandle<JS::Value> or a JS::Rooted<JS::Value>*.
michael@0 5048 jsvalRef: something that can have .address() called on it to get a
michael@0 5049 JS::Value* and .set() called on it to set it to a JS::Value.
michael@0 5050 This can be a JS::MutableHandle<JS::Value> or a
michael@0 5051 JS::Rooted<JS::Value>.
michael@0 5052 obj: a JS::Handle<JSObject*>.
michael@0 5053
michael@0 5054 Returns (templateString, infallibility of conversion template)
michael@0 5055 """
michael@0 5056 if successCode is None:
michael@0 5057 successCode = "return true;\n"
michael@0 5058
michael@0 5059 def setUndefined():
michael@0 5060 return _setValue("", setter="setUndefined")
michael@0 5061
michael@0 5062 def setNull():
michael@0 5063 return _setValue("", setter="setNull")
michael@0 5064
michael@0 5065 def setInt32(value):
michael@0 5066 return _setValue(value, setter="setInt32")
michael@0 5067
michael@0 5068 def setString(value):
michael@0 5069 return _setValue(value, setter="setString")
michael@0 5070
michael@0 5071 def setObject(value, wrapAsType=None):
michael@0 5072 return _setValue(value, wrapAsType=wrapAsType, setter="setObject")
michael@0 5073
michael@0 5074 def setObjectOrNull(value, wrapAsType=None):
michael@0 5075 return _setValue(value, wrapAsType=wrapAsType, setter="setObjectOrNull")
michael@0 5076
michael@0 5077 def setUint32(value):
michael@0 5078 return _setValue(value, setter="setNumber")
michael@0 5079
michael@0 5080 def setDouble(value):
michael@0 5081 return _setValue("JS_NumberValue(%s)" % value)
michael@0 5082
michael@0 5083 def setBoolean(value):
michael@0 5084 return _setValue(value, setter="setBoolean")
michael@0 5085
michael@0 5086 def _setValue(value, wrapAsType=None, setter="set"):
michael@0 5087 """
michael@0 5088 Returns the code to set the jsval to value.
michael@0 5089
michael@0 5090 If wrapAsType is not None, then will wrap the resulting value using the
michael@0 5091 function that getMaybeWrapValueFuncForType(wrapAsType) returns.
michael@0 5092 Otherwise, no wrapping will be done.
michael@0 5093 """
michael@0 5094 if wrapAsType is None:
michael@0 5095 tail = successCode
michael@0 5096 else:
michael@0 5097 tail = fill(
michael@0 5098 """
michael@0 5099 if (!${maybeWrap}(cx, $${jsvalHandle})) {
michael@0 5100 $*{exceptionCode}
michael@0 5101 }
michael@0 5102 $*{successCode}
michael@0 5103 """,
michael@0 5104 maybeWrap=getMaybeWrapValueFuncForType(wrapAsType),
michael@0 5105 exceptionCode=exceptionCode,
michael@0 5106 successCode=successCode)
michael@0 5107 return ("${jsvalRef}.%s(%s);\n" % (setter, value)) + tail
michael@0 5108
michael@0 5109 def wrapAndSetPtr(wrapCall, failureCode=None):
michael@0 5110 """
michael@0 5111 Returns the code to set the jsval by calling "wrapCall". "failureCode"
michael@0 5112 is the code to run if calling "wrapCall" fails
michael@0 5113 """
michael@0 5114 if failureCode is None:
michael@0 5115 failureCode = exceptionCode
michael@0 5116 return fill(
michael@0 5117 """
michael@0 5118 if (!${wrapCall}) {
michael@0 5119 $*{failureCode}
michael@0 5120 }
michael@0 5121 $*{successCode}
michael@0 5122 """,
michael@0 5123 wrapCall=wrapCall,
michael@0 5124 failureCode=failureCode,
michael@0 5125 successCode=successCode)
michael@0 5126
michael@0 5127 if type is None or type.isVoid():
michael@0 5128 return (setUndefined(), True)
michael@0 5129
michael@0 5130 if type.isArray():
michael@0 5131 raise TypeError("Can't handle array return values yet")
michael@0 5132
michael@0 5133 if (type.isSequence() or type.isMozMap()) and type.nullable():
michael@0 5134 # These are both wrapped in Nullable<>
michael@0 5135 recTemplate, recInfall = getWrapTemplateForType(type.inner, descriptorProvider,
michael@0 5136 "%s.Value()" % result, successCode,
michael@0 5137 returnsNewObject, exceptionCode,
michael@0 5138 typedArraysAreStructs)
michael@0 5139 code = fill(
michael@0 5140 """
michael@0 5141
michael@0 5142 if (${result}.IsNull()) {
michael@0 5143 $*{setNull}
michael@0 5144 }
michael@0 5145 $*{recTemplate}
michael@0 5146 """,
michael@0 5147 result=result,
michael@0 5148 setNull=setNull(),
michael@0 5149 recTemplate=recTemplate)
michael@0 5150 return code, recInfall
michael@0 5151
michael@0 5152 if type.isSequence():
michael@0 5153 # Now do non-nullable sequences. Our success code is just to break to
michael@0 5154 # where we set the element in the array. Note that we bump the
michael@0 5155 # sequenceWrapLevel around this call so that nested sequence conversions
michael@0 5156 # will use different iteration variables.
michael@0 5157 global sequenceWrapLevel
michael@0 5158 index = "sequenceIdx%d" % sequenceWrapLevel
michael@0 5159 sequenceWrapLevel += 1
michael@0 5160 innerTemplate = wrapForType(
michael@0 5161 type.inner, descriptorProvider,
michael@0 5162 {
michael@0 5163 'result': "%s[%s]" % (result, index),
michael@0 5164 'successCode': "break;\n",
michael@0 5165 'jsvalRef': "tmp",
michael@0 5166 'jsvalHandle': "&tmp",
michael@0 5167 'returnsNewObject': returnsNewObject,
michael@0 5168 'exceptionCode': exceptionCode,
michael@0 5169 'obj': "returnArray"
michael@0 5170 })
michael@0 5171 sequenceWrapLevel -= 1
michael@0 5172 code = fill(
michael@0 5173 """
michael@0 5174
michael@0 5175 uint32_t length = ${result}.Length();
michael@0 5176 JS::Rooted<JSObject*> returnArray(cx, JS_NewArrayObject(cx, length));
michael@0 5177 if (!returnArray) {
michael@0 5178 $*{exceptionCode}
michael@0 5179 }
michael@0 5180 // Scope for 'tmp'
michael@0 5181 {
michael@0 5182 JS::Rooted<JS::Value> tmp(cx);
michael@0 5183 for (uint32_t ${index} = 0; ${index} < length; ++${index}) {
michael@0 5184 // Control block to let us common up the JS_DefineElement calls when there
michael@0 5185 // are different ways to succeed at wrapping the object.
michael@0 5186 do {
michael@0 5187 $*{innerTemplate}
michael@0 5188 } while (0);
michael@0 5189 if (!JS_DefineElement(cx, returnArray, ${index}, tmp,
michael@0 5190 nullptr, nullptr, JSPROP_ENUMERATE)) {
michael@0 5191 $*{exceptionCode}
michael@0 5192 }
michael@0 5193 }
michael@0 5194 }
michael@0 5195 $*{set}
michael@0 5196 """,
michael@0 5197 result=result,
michael@0 5198 exceptionCode=exceptionCode,
michael@0 5199 index=index,
michael@0 5200 innerTemplate=innerTemplate,
michael@0 5201 set=setObject("*returnArray"))
michael@0 5202
michael@0 5203 return (code, False)
michael@0 5204
michael@0 5205 if type.isMozMap():
michael@0 5206 # Now do non-nullable MozMap. Our success code is just to break to
michael@0 5207 # where we define the property on the object. Note that we bump the
michael@0 5208 # mozMapWrapLevel around this call so that nested MozMap conversions
michael@0 5209 # will use different temp value names.
michael@0 5210 global mozMapWrapLevel
michael@0 5211 valueName = "mozMapValue%d" % mozMapWrapLevel
michael@0 5212 mozMapWrapLevel += 1
michael@0 5213 innerTemplate = wrapForType(
michael@0 5214 type.inner, descriptorProvider,
michael@0 5215 {
michael@0 5216 'result': valueName,
michael@0 5217 'successCode': "break;\n",
michael@0 5218 'jsvalRef': "tmp",
michael@0 5219 'jsvalHandle': "&tmp",
michael@0 5220 'returnsNewObject': returnsNewObject,
michael@0 5221 'exceptionCode': exceptionCode,
michael@0 5222 'obj': "returnObj"
michael@0 5223 })
michael@0 5224 mozMapWrapLevel -= 1
michael@0 5225 code = fill(
michael@0 5226 """
michael@0 5227
michael@0 5228 nsTArray<nsString> keys;
michael@0 5229 ${result}.GetKeys(keys);
michael@0 5230 JS::Rooted<JSObject*> returnObj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
michael@0 5231 if (!returnObj) {
michael@0 5232 $*{exceptionCode}
michael@0 5233 }
michael@0 5234 // Scope for 'tmp'
michael@0 5235 {
michael@0 5236 JS::Rooted<JS::Value> tmp(cx);
michael@0 5237 for (size_t idx = 0; idx < keys.Length(); ++idx) {
michael@0 5238 auto& ${valueName} = ${result}.Get(keys[idx]);
michael@0 5239 // Control block to let us common up the JS_DefineUCProperty calls when there
michael@0 5240 // are different ways to succeed at wrapping the value.
michael@0 5241 do {
michael@0 5242 $*{innerTemplate}
michael@0 5243 } while (0);
michael@0 5244 if (!JS_DefineUCProperty(cx, returnObj, keys[idx].get(),
michael@0 5245 keys[idx].Length(), tmp,
michael@0 5246 JS_PropertyStub, JS_StrictPropertyStub,
michael@0 5247 JSPROP_ENUMERATE)) {
michael@0 5248 $*{exceptionCode}
michael@0 5249 }
michael@0 5250 }
michael@0 5251 }
michael@0 5252 $*{set}
michael@0 5253 """,
michael@0 5254 result=result,
michael@0 5255 exceptionCode=exceptionCode,
michael@0 5256 valueName=valueName,
michael@0 5257 innerTemplate=innerTemplate,
michael@0 5258 set=setObject("*returnObj"))
michael@0 5259
michael@0 5260 return (code, False)
michael@0 5261
michael@0 5262 if type.isGeckoInterface() and not type.isCallbackInterface():
michael@0 5263 descriptor = descriptorProvider.getDescriptor(type.unroll().inner.identifier.name)
michael@0 5264 if type.nullable():
michael@0 5265 wrappingCode = ("if (!%s) {\n" % (result) +
michael@0 5266 indent(setNull()) +
michael@0 5267 "}\n")
michael@0 5268 else:
michael@0 5269 wrappingCode = ""
michael@0 5270
michael@0 5271 if not descriptor.interface.isExternal() and not descriptor.skipGen:
michael@0 5272 if descriptor.wrapperCache:
michael@0 5273 assert descriptor.nativeOwnership != 'owned'
michael@0 5274 wrapMethod = "WrapNewBindingObject"
michael@0 5275 else:
michael@0 5276 if not returnsNewObject:
michael@0 5277 raise MethodNotNewObjectError(descriptor.interface.identifier.name)
michael@0 5278 if descriptor.nativeOwnership == 'owned':
michael@0 5279 wrapMethod = "WrapNewBindingNonWrapperCachedOwnedObject"
michael@0 5280 else:
michael@0 5281 wrapMethod = "WrapNewBindingNonWrapperCachedObject"
michael@0 5282 wrap = "%s(cx, ${obj}, %s, ${jsvalHandle})" % (wrapMethod, result)
michael@0 5283 if not descriptor.hasXPConnectImpls:
michael@0 5284 # Can only fail to wrap as a new-binding object
michael@0 5285 # if they already threw an exception.
michael@0 5286 #XXX Assertion disabled for now, see bug 991271.
michael@0 5287 failed = ("MOZ_ASSERT(true || JS_IsExceptionPending(cx));\n" +
michael@0 5288 exceptionCode)
michael@0 5289 else:
michael@0 5290 if descriptor.notflattened:
michael@0 5291 raise TypeError("%s has XPConnect impls but not flattened; "
michael@0 5292 "fallback won't work correctly" %
michael@0 5293 descriptor.interface.identifier.name)
michael@0 5294 # Try old-style wrapping for bindings which might be XPConnect impls.
michael@0 5295 failed = wrapAndSetPtr("HandleNewBindingWrappingFailure(cx, ${obj}, %s, ${jsvalHandle})" % result)
michael@0 5296 else:
michael@0 5297 if descriptor.notflattened:
michael@0 5298 getIID = "&NS_GET_IID(%s), " % descriptor.nativeType
michael@0 5299 else:
michael@0 5300 getIID = ""
michael@0 5301 wrap = "WrapObject(cx, %s, %s${jsvalHandle})" % (result, getIID)
michael@0 5302 failed = None
michael@0 5303
michael@0 5304 wrappingCode += wrapAndSetPtr(wrap, failed)
michael@0 5305 return (wrappingCode, False)
michael@0 5306
michael@0 5307 if type.isDOMString():
michael@0 5308 if type.nullable():
michael@0 5309 return (wrapAndSetPtr("xpc::StringToJsval(cx, %s, ${jsvalHandle})" % result), False)
michael@0 5310 else:
michael@0 5311 return (wrapAndSetPtr("xpc::NonVoidStringToJsval(cx, %s, ${jsvalHandle})" % result), False)
michael@0 5312
michael@0 5313 if type.isByteString():
michael@0 5314 if type.nullable():
michael@0 5315 return (wrapAndSetPtr("ByteStringToJsval(cx, %s, ${jsvalHandle})" % result), False)
michael@0 5316 else:
michael@0 5317 return (wrapAndSetPtr("NonVoidByteStringToJsval(cx, %s, ${jsvalHandle})" % result), False)
michael@0 5318
michael@0 5319 if type.isEnum():
michael@0 5320 if type.nullable():
michael@0 5321 resultLoc = "%s.Value()" % result
michael@0 5322 else:
michael@0 5323 resultLoc = result
michael@0 5324 conversion = fill(
michael@0 5325 """
michael@0 5326 {
michael@0 5327 // Scope for resultStr
michael@0 5328 MOZ_ASSERT(uint32_t(${result}) < ArrayLength(${strings}));
michael@0 5329 JSString* resultStr = JS_NewStringCopyN(cx, ${strings}[uint32_t(${result})].value, ${strings}[uint32_t(${result})].length);
michael@0 5330 if (!resultStr) {
michael@0 5331 $*{exceptionCode}
michael@0 5332 }
michael@0 5333 $*{setResultStr}
michael@0 5334 }
michael@0 5335 """,
michael@0 5336 result=resultLoc,
michael@0 5337 strings=(type.unroll().inner.identifier.name + "Values::" +
michael@0 5338 ENUM_ENTRY_VARIABLE_NAME),
michael@0 5339 exceptionCode=exceptionCode,
michael@0 5340 setResultStr=setString("resultStr"))
michael@0 5341
michael@0 5342 if type.nullable():
michael@0 5343 conversion = CGIfElseWrapper(
michael@0 5344 "%s.IsNull()" % result,
michael@0 5345 CGGeneric(setNull()),
michael@0 5346 CGGeneric(conversion)).define()
michael@0 5347 return conversion, False
michael@0 5348
michael@0 5349 if type.isCallback() or type.isCallbackInterface():
michael@0 5350 wrapCode = setObject(
michael@0 5351 "*GetCallbackFromCallbackObject(%(result)s)",
michael@0 5352 wrapAsType=type)
michael@0 5353 if type.nullable():
michael@0 5354 wrapCode = (
michael@0 5355 "if (%(result)s) {\n" +
michael@0 5356 indent(wrapCode) +
michael@0 5357 "} else {\n" +
michael@0 5358 indent(setNull()) +
michael@0 5359 "}\n")
michael@0 5360 wrapCode = wrapCode % {"result": result}
michael@0 5361 return wrapCode, False
michael@0 5362
michael@0 5363 if type.isAny():
michael@0 5364 # See comments in WrapNewBindingObject explaining why we need
michael@0 5365 # to wrap here.
michael@0 5366 # NB: _setValue(..., type-that-is-any) calls JS_WrapValue(), so is fallible
michael@0 5367 return (_setValue(result, wrapAsType=type), False)
michael@0 5368
michael@0 5369 if (type.isObject() or (type.isSpiderMonkeyInterface() and
michael@0 5370 not typedArraysAreStructs)):
michael@0 5371 # See comments in WrapNewBindingObject explaining why we need
michael@0 5372 # to wrap here.
michael@0 5373 if type.nullable():
michael@0 5374 toValue = "%s"
michael@0 5375 setter = setObjectOrNull
michael@0 5376 else:
michael@0 5377 toValue = "*%s"
michael@0 5378 setter = setObject
michael@0 5379 # NB: setObject{,OrNull}(..., some-object-type) calls JS_WrapValue(), so is fallible
michael@0 5380 return (setter(toValue % result, wrapAsType=type), False)
michael@0 5381
michael@0 5382 if not (type.isUnion() or type.isPrimitive() or type.isDictionary() or
michael@0 5383 type.isDate() or
michael@0 5384 (type.isSpiderMonkeyInterface() and typedArraysAreStructs)):
michael@0 5385 raise TypeError("Need to learn to wrap %s" % type)
michael@0 5386
michael@0 5387 if type.nullable():
michael@0 5388 recTemplate, recInfal = getWrapTemplateForType(type.inner, descriptorProvider,
michael@0 5389 "%s.Value()" % result, successCode,
michael@0 5390 returnsNewObject, exceptionCode,
michael@0 5391 typedArraysAreStructs)
michael@0 5392 return ("if (%s.IsNull()) {\n" % result +
michael@0 5393 indent(setNull()) +
michael@0 5394 "}\n" +
michael@0 5395 recTemplate, recInfal)
michael@0 5396
michael@0 5397 if type.isSpiderMonkeyInterface():
michael@0 5398 assert typedArraysAreStructs
michael@0 5399 # See comments in WrapNewBindingObject explaining why we need
michael@0 5400 # to wrap here.
michael@0 5401 # NB: setObject(..., some-object-type) calls JS_WrapValue(), so is fallible
michael@0 5402 return (setObject("*%s.Obj()" % result,
michael@0 5403 wrapAsType=type), False)
michael@0 5404
michael@0 5405 if type.isUnion():
michael@0 5406 return (wrapAndSetPtr("%s.ToJSVal(cx, ${obj}, ${jsvalHandle})" % result),
michael@0 5407 False)
michael@0 5408
michael@0 5409 if type.isDictionary():
michael@0 5410 return (wrapAndSetPtr("%s.ToObject(cx, ${jsvalHandle})" % result),
michael@0 5411 False)
michael@0 5412
michael@0 5413 if type.isDate():
michael@0 5414 return (wrapAndSetPtr("%s.ToDateObject(cx, ${jsvalHandle})" % result),
michael@0 5415 False)
michael@0 5416
michael@0 5417 tag = type.tag()
michael@0 5418
michael@0 5419 if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
michael@0 5420 IDLType.Tags.uint16, IDLType.Tags.int32]:
michael@0 5421 return (setInt32("int32_t(%s)" % result), True)
michael@0 5422
michael@0 5423 elif tag in [IDLType.Tags.int64, IDLType.Tags.uint64,
michael@0 5424 IDLType.Tags.unrestricted_float, IDLType.Tags.float,
michael@0 5425 IDLType.Tags.unrestricted_double, IDLType.Tags.double]:
michael@0 5426 # XXXbz will cast to double do the "even significand" thing that webidl
michael@0 5427 # calls for for 64-bit ints? Do we care?
michael@0 5428 return (setDouble("double(%s)" % result), True)
michael@0 5429
michael@0 5430 elif tag == IDLType.Tags.uint32:
michael@0 5431 return (setUint32(result), True)
michael@0 5432
michael@0 5433 elif tag == IDLType.Tags.bool:
michael@0 5434 return (setBoolean(result), True)
michael@0 5435
michael@0 5436 else:
michael@0 5437 raise TypeError("Need to learn to wrap primitive: %s" % type)
michael@0 5438
michael@0 5439
michael@0 5440 def wrapForType(type, descriptorProvider, templateValues):
michael@0 5441 """
michael@0 5442 Reflect a C++ value of IDL type "type" into JS. TemplateValues is a dict
michael@0 5443 that should contain:
michael@0 5444
michael@0 5445 * 'jsvalRef': something that can have .address() called on it to get a
michael@0 5446 JS::Value* and .set() called on it to set it to a JS::Value.
michael@0 5447 This can be a JS::MutableHandle<JS::Value> or a
michael@0 5448 JS::Rooted<JS::Value>.
michael@0 5449 * 'jsvalHandle': something that can be passed to methods taking a
michael@0 5450 JS::MutableHandle<JS::Value>. This can be a
michael@0 5451 JS::MutableHandle<JS::Value> or a JS::Rooted<JS::Value>*.
michael@0 5452 * 'obj' (optional): the name of the variable that contains the JSObject to
michael@0 5453 use as a scope when wrapping, if not supplied 'obj'
michael@0 5454 will be used as the name
michael@0 5455 * 'result' (optional): the name of the variable in which the C++ value is
michael@0 5456 stored, if not supplied 'result' will be used as
michael@0 5457 the name
michael@0 5458 * 'successCode' (optional): the code to run once we have successfully
michael@0 5459 done the conversion, if not supplied 'return
michael@0 5460 true;' will be used as the code. The
michael@0 5461 successCode must ensure that once it runs no
michael@0 5462 more of the conversion template will be
michael@0 5463 executed (e.g. by doing a 'return' or 'break'
michael@0 5464 as appropriate).
michael@0 5465 * 'returnsNewObject' (optional): If true, we're wrapping for the return
michael@0 5466 value of a [NewObject] method. Assumed
michael@0 5467 false if not set.
michael@0 5468 * 'exceptionCode' (optional): Code to run when a JS exception is thrown.
michael@0 5469 The default is "return false;". The code
michael@0 5470 passed here must return.
michael@0 5471 """
michael@0 5472 wrap = getWrapTemplateForType(type, descriptorProvider,
michael@0 5473 templateValues.get('result', 'result'),
michael@0 5474 templateValues.get('successCode', None),
michael@0 5475 templateValues.get('returnsNewObject', False),
michael@0 5476 templateValues.get('exceptionCode',
michael@0 5477 "return false;\n"),
michael@0 5478 templateValues.get('typedArraysAreStructs',
michael@0 5479 False))[0]
michael@0 5480
michael@0 5481 defaultValues = {'obj': 'obj'}
michael@0 5482 return string.Template(wrap).substitute(defaultValues, **templateValues)
michael@0 5483
michael@0 5484
michael@0 5485 def infallibleForMember(member, type, descriptorProvider):
michael@0 5486 """
michael@0 5487 Determine the fallibility of changing a C++ value of IDL type "type" into
michael@0 5488 JS for the given attribute. Apart from returnsNewObject, all the defaults
michael@0 5489 are used, since the fallbility does not change based on the boolean values,
michael@0 5490 and the template will be discarded.
michael@0 5491
michael@0 5492 CURRENT ASSUMPTIONS:
michael@0 5493 We assume that successCode for wrapping up return values cannot contain
michael@0 5494 failure conditions.
michael@0 5495 """
michael@0 5496 return getWrapTemplateForType(type, descriptorProvider, 'result', None,
michael@0 5497 memberReturnsNewObject(member), "return false;\n",
michael@0 5498 False)[1]
michael@0 5499
michael@0 5500
michael@0 5501 def leafTypeNeedsCx(type, retVal):
michael@0 5502 return (type.isAny() or type.isObject() or
michael@0 5503 (retVal and type.isSpiderMonkeyInterface()))
michael@0 5504
michael@0 5505
michael@0 5506 def leafTypeNeedsScopeObject(type, retVal):
michael@0 5507 return retVal and type.isSpiderMonkeyInterface()
michael@0 5508
michael@0 5509
michael@0 5510 def leafTypeNeedsRooting(type):
michael@0 5511 return leafTypeNeedsCx(type, False) or type.isSpiderMonkeyInterface()
michael@0 5512
michael@0 5513
michael@0 5514 def typeNeedsRooting(type):
michael@0 5515 return typeMatchesLambda(type,
michael@0 5516 lambda t: leafTypeNeedsRooting(t))
michael@0 5517
michael@0 5518
michael@0 5519 def typeNeedsCx(type, retVal=False):
michael@0 5520 return typeMatchesLambda(type,
michael@0 5521 lambda t: leafTypeNeedsCx(t, retVal))
michael@0 5522
michael@0 5523
michael@0 5524 def typeNeedsScopeObject(type, retVal=False):
michael@0 5525 return typeMatchesLambda(type,
michael@0 5526 lambda t: leafTypeNeedsScopeObject(t, retVal))
michael@0 5527
michael@0 5528
michael@0 5529 def typeMatchesLambda(type, func):
michael@0 5530 if type is None:
michael@0 5531 return False
michael@0 5532 if type.nullable():
michael@0 5533 return typeMatchesLambda(type.inner, func)
michael@0 5534 if type.isSequence() or type.isMozMap() or type.isArray():
michael@0 5535 return typeMatchesLambda(type.inner, func)
michael@0 5536 if type.isUnion():
michael@0 5537 return any(typeMatchesLambda(t, func) for t in
michael@0 5538 type.unroll().flatMemberTypes)
michael@0 5539 if type.isDictionary():
michael@0 5540 return dictionaryMatchesLambda(type.inner, func)
michael@0 5541 return func(type)
michael@0 5542
michael@0 5543
michael@0 5544 def dictionaryMatchesLambda(dictionary, func):
michael@0 5545 return (any(typeMatchesLambda(m.type, func) for m in dictionary.members) or
michael@0 5546 (dictionary.parent and dictionaryMatchesLambda(dictionary.parent, func)))
michael@0 5547
michael@0 5548
michael@0 5549 # Whenever this is modified, please update CGNativeMember.getRetvalInfo as
michael@0 5550 # needed to keep the types compatible.
michael@0 5551 def getRetvalDeclarationForType(returnType, descriptorProvider,
michael@0 5552 resultAlreadyAddRefed,
michael@0 5553 isMember=False):
michael@0 5554 """
michael@0 5555 Returns a tuple containing four things:
michael@0 5556
michael@0 5557 1) A CGThing for the type of the return value, or None if there is no need
michael@0 5558 for a return value.
michael@0 5559
michael@0 5560 2) A value indicating the kind of ourparam to pass the value as. Valid
michael@0 5561 options are None to not pass as an out param at all, "ref" (to pass a
michael@0 5562 reference as an out param), and "ptr" (to pass a pointer as an out
michael@0 5563 param).
michael@0 5564
michael@0 5565 3) A CGThing for a tracer for the return value, or None if no tracing is
michael@0 5566 needed.
michael@0 5567
michael@0 5568 4) An argument string to pass to the retval declaration
michael@0 5569 constructor or None if there are no arguments.
michael@0 5570 """
michael@0 5571 if returnType is None or returnType.isVoid():
michael@0 5572 # Nothing to declare
michael@0 5573 return None, None, None, None
michael@0 5574 if returnType.isPrimitive() and returnType.tag() in builtinNames:
michael@0 5575 result = CGGeneric(builtinNames[returnType.tag()])
michael@0 5576 if returnType.nullable():
michael@0 5577 result = CGTemplatedType("Nullable", result)
michael@0 5578 return result, None, None, None
michael@0 5579 if returnType.isDOMString():
michael@0 5580 if isMember:
michael@0 5581 return CGGeneric("nsString"), "ref", None, None
michael@0 5582 return CGGeneric("DOMString"), "ref", None, None
michael@0 5583 if returnType.isByteString():
michael@0 5584 return CGGeneric("nsCString"), "ref", None, None
michael@0 5585 if returnType.isEnum():
michael@0 5586 result = CGGeneric(returnType.unroll().inner.identifier.name)
michael@0 5587 if returnType.nullable():
michael@0 5588 result = CGTemplatedType("Nullable", result)
michael@0 5589 return result, None, None, None
michael@0 5590 if returnType.isGeckoInterface():
michael@0 5591 result = CGGeneric(descriptorProvider.getDescriptor(
michael@0 5592 returnType.unroll().inner.identifier.name).nativeType)
michael@0 5593 if descriptorProvider.getDescriptor(
michael@0 5594 returnType.unroll().inner.identifier.name).nativeOwnership == 'owned':
michael@0 5595 result = CGTemplatedType("nsAutoPtr", result)
michael@0 5596 elif resultAlreadyAddRefed:
michael@0 5597 result = CGTemplatedType("nsRefPtr", result)
michael@0 5598 else:
michael@0 5599 result = CGWrapper(result, post="*")
michael@0 5600 return result, None, None, None
michael@0 5601 if returnType.isCallback():
michael@0 5602 name = returnType.unroll().identifier.name
michael@0 5603 return CGGeneric("nsRefPtr<%s>" % name), None, None, None
michael@0 5604 if returnType.isAny():
michael@0 5605 if isMember:
michael@0 5606 return CGGeneric("JS::Value"), None, None, None
michael@0 5607 return CGGeneric("JS::Rooted<JS::Value>"), "ptr", None, "cx"
michael@0 5608 if returnType.isObject() or returnType.isSpiderMonkeyInterface():
michael@0 5609 if isMember:
michael@0 5610 return CGGeneric("JSObject*"), None, None, None
michael@0 5611 return CGGeneric("JS::Rooted<JSObject*>"), "ptr", None, "cx"
michael@0 5612 if returnType.isSequence():
michael@0 5613 nullable = returnType.nullable()
michael@0 5614 if nullable:
michael@0 5615 returnType = returnType.inner
michael@0 5616 # If our result is already addrefed, use the right type in the
michael@0 5617 # sequence argument here.
michael@0 5618 result, _, _, _ = getRetvalDeclarationForType(returnType.inner,
michael@0 5619 descriptorProvider,
michael@0 5620 resultAlreadyAddRefed,
michael@0 5621 isMember="Sequence")
michael@0 5622 # While we have our inner type, set up our rooter, if needed
michael@0 5623 if not isMember and typeNeedsRooting(returnType):
michael@0 5624 rooter = CGGeneric("SequenceRooter<%s > resultRooter(cx, &result);\n" %
michael@0 5625 result.define())
michael@0 5626 else:
michael@0 5627 rooter = None
michael@0 5628 result = CGTemplatedType("nsTArray", result)
michael@0 5629 if nullable:
michael@0 5630 result = CGTemplatedType("Nullable", result)
michael@0 5631 return result, "ref", rooter, None
michael@0 5632 if returnType.isMozMap():
michael@0 5633 nullable = returnType.nullable()
michael@0 5634 if nullable:
michael@0 5635 returnType = returnType.inner
michael@0 5636 # If our result is already addrefed, use the right type in the
michael@0 5637 # MozMap argument here.
michael@0 5638 result, _, _, _ = getRetvalDeclarationForType(returnType.inner,
michael@0 5639 descriptorProvider,
michael@0 5640 resultAlreadyAddRefed,
michael@0 5641 isMember="MozMap")
michael@0 5642 # While we have our inner type, set up our rooter, if needed
michael@0 5643 if not isMember and typeNeedsRooting(returnType):
michael@0 5644 rooter = CGGeneric("MozMapRooter<%s> resultRooter(cx, &result);\n" %
michael@0 5645 result.define())
michael@0 5646 else:
michael@0 5647 rooter = None
michael@0 5648 result = CGTemplatedType("MozMap", result)
michael@0 5649 if nullable:
michael@0 5650 result = CGTemplatedType("Nullable", result)
michael@0 5651 return result, "ref", rooter, None
michael@0 5652 if returnType.isDictionary():
michael@0 5653 nullable = returnType.nullable()
michael@0 5654 dictName = CGDictionary.makeDictionaryName(returnType.unroll().inner)
michael@0 5655 result = CGGeneric(dictName)
michael@0 5656 if not isMember and typeNeedsRooting(returnType):
michael@0 5657 if nullable:
michael@0 5658 result = CGTemplatedType("NullableRootedDictionary", result)
michael@0 5659 else:
michael@0 5660 result = CGTemplatedType("RootedDictionary", result)
michael@0 5661 resultArgs = "cx"
michael@0 5662 else:
michael@0 5663 if nullable:
michael@0 5664 result = CGTemplatedType("Nullable", result)
michael@0 5665 resultArgs = None
michael@0 5666 return result, "ref", None, resultArgs
michael@0 5667 if returnType.isUnion():
michael@0 5668 result = CGGeneric(CGUnionStruct.unionTypeName(returnType.unroll(), True))
michael@0 5669 if not isMember and typeNeedsRooting(returnType):
michael@0 5670 if returnType.nullable():
michael@0 5671 result = CGTemplatedType("NullableRootedUnion", result)
michael@0 5672 else:
michael@0 5673 result = CGTemplatedType("RootedUnion", result)
michael@0 5674 resultArgs = "cx"
michael@0 5675 else:
michael@0 5676 if returnType.nullable():
michael@0 5677 result = CGTemplatedType("Nullable", result)
michael@0 5678 resultArgs = None
michael@0 5679 return result, "ref", None, resultArgs
michael@0 5680 if returnType.isDate():
michael@0 5681 result = CGGeneric("Date")
michael@0 5682 if returnType.nullable():
michael@0 5683 result = CGTemplatedType("Nullable", result)
michael@0 5684 return result, None, None, None
michael@0 5685 raise TypeError("Don't know how to declare return value for %s" %
michael@0 5686 returnType)
michael@0 5687
michael@0 5688
michael@0 5689 def isResultAlreadyAddRefed(extendedAttributes):
michael@0 5690 return 'resultNotAddRefed' not in extendedAttributes
michael@0 5691
michael@0 5692
michael@0 5693 def needCx(returnType, arguments, extendedAttributes, considerTypes):
michael@0 5694 return (considerTypes and
michael@0 5695 (typeNeedsCx(returnType, True) or
michael@0 5696 any(typeNeedsCx(a.type) for a in arguments)) or
michael@0 5697 'implicitJSContext' in extendedAttributes)
michael@0 5698
michael@0 5699
michael@0 5700 def needScopeObject(returnType, arguments, extendedAttributes,
michael@0 5701 isWrapperCached, considerTypes, isMember):
michael@0 5702 """
michael@0 5703 isMember should be true if we're dealing with an attribute
michael@0 5704 annotated as [StoreInSlot].
michael@0 5705 """
michael@0 5706 return (considerTypes and not isWrapperCached and
michael@0 5707 ((not isMember and typeNeedsScopeObject(returnType, True)) or
michael@0 5708 any(typeNeedsScopeObject(a.type) for a in arguments)))
michael@0 5709
michael@0 5710
michael@0 5711 class CGCallGenerator(CGThing):
michael@0 5712 """
michael@0 5713 A class to generate an actual call to a C++ object. Assumes that the C++
michael@0 5714 object is stored in a variable whose name is given by the |object| argument.
michael@0 5715
michael@0 5716 errorReport should be a CGThing for an error report or None if no
michael@0 5717 error reporting is needed.
michael@0 5718 """
michael@0 5719 def __init__(self, errorReport, arguments, argsPre, returnType,
michael@0 5720 extendedAttributes, descriptorProvider, nativeMethodName,
michael@0 5721 static, object="self", argsPost=[]):
michael@0 5722 CGThing.__init__(self)
michael@0 5723
michael@0 5724 assert errorReport is None or isinstance(errorReport, CGThing)
michael@0 5725
michael@0 5726 isFallible = errorReport is not None
michael@0 5727
michael@0 5728 resultAlreadyAddRefed = isResultAlreadyAddRefed(extendedAttributes)
michael@0 5729 result, resultOutParam, resultRooter, resultArgs = \
michael@0 5730 getRetvalDeclarationForType(
michael@0 5731 returnType, descriptorProvider, resultAlreadyAddRefed)
michael@0 5732
michael@0 5733 args = CGList([CGGeneric(arg) for arg in argsPre], ", ")
michael@0 5734 for a, name in arguments:
michael@0 5735 arg = CGGeneric(name)
michael@0 5736
michael@0 5737 # Now constify the things that need it
michael@0 5738 def needsConst(a):
michael@0 5739 if a.type.isDictionary():
michael@0 5740 return True
michael@0 5741 if a.type.isSequence():
michael@0 5742 return True
michael@0 5743 if a.type.isMozMap():
michael@0 5744 return True
michael@0 5745 # isObject() types are always a JS::Rooted, whether
michael@0 5746 # nullable or not, and it turns out a const JS::Rooted
michael@0 5747 # is not very helpful at all (in particular, it won't
michael@0 5748 # even convert to a JS::Handle).
michael@0 5749 # XXX bz Well, why not???
michael@0 5750 if a.type.nullable() and not a.type.isObject():
michael@0 5751 return True
michael@0 5752 if a.type.isString():
michael@0 5753 return True
michael@0 5754 if a.optional and not a.defaultValue:
michael@0 5755 # If a.defaultValue, then it's not going to use an Optional,
michael@0 5756 # so doesn't need to be const just due to being optional.
michael@0 5757 # This also covers variadic arguments.
michael@0 5758 return True
michael@0 5759 if a.type.isUnion():
michael@0 5760 return True
michael@0 5761 if a.type.isSpiderMonkeyInterface():
michael@0 5762 return True
michael@0 5763 return False
michael@0 5764 if needsConst(a):
michael@0 5765 arg = CGWrapper(arg, pre="Constify(", post=")")
michael@0 5766 # And convert NonNull<T> to T&
michael@0 5767 if (((a.type.isGeckoInterface() or a.type.isCallback()) and not a.type.nullable()) or
michael@0 5768 a.type.isDOMString()):
michael@0 5769 arg = CGWrapper(arg, pre="NonNullHelper(", post=")")
michael@0 5770 args.append(arg)
michael@0 5771
michael@0 5772 # Return values that go in outparams go here
michael@0 5773 if resultOutParam is not None:
michael@0 5774 if resultOutParam is "ref":
michael@0 5775 args.append(CGGeneric("result"))
michael@0 5776 else:
michael@0 5777 assert resultOutParam is "ptr"
michael@0 5778 args.append(CGGeneric("&result"))
michael@0 5779
michael@0 5780 if isFallible:
michael@0 5781 args.append(CGGeneric("rv"))
michael@0 5782 args.extend(CGGeneric(arg) for arg in argsPost)
michael@0 5783
michael@0 5784 # Build up our actual call
michael@0 5785 self.cgRoot = CGList([])
michael@0 5786
michael@0 5787 call = CGGeneric(nativeMethodName)
michael@0 5788 if not static:
michael@0 5789 call = CGWrapper(call, pre="%s->" % object)
michael@0 5790 call = CGList([call, CGWrapper(args, pre="(", post=");\n")])
michael@0 5791 if result is not None:
michael@0 5792 if resultRooter is not None:
michael@0 5793 self.cgRoot.prepend(resultRooter)
michael@0 5794 if resultArgs is not None:
michael@0 5795 resultArgs = "(%s)" % resultArgs
michael@0 5796 else:
michael@0 5797 resultArgs = ""
michael@0 5798 result = CGWrapper(result, post=(" result%s;\n" % resultArgs))
michael@0 5799 self.cgRoot.prepend(result)
michael@0 5800 if not resultOutParam:
michael@0 5801 call = CGWrapper(call, pre="result = ")
michael@0 5802
michael@0 5803 call = CGWrapper(call)
michael@0 5804 self.cgRoot.append(call)
michael@0 5805
michael@0 5806 if isFallible:
michael@0 5807 self.cgRoot.prepend(CGGeneric("ErrorResult rv;\n"))
michael@0 5808 self.cgRoot.append(CGGeneric("rv.WouldReportJSException();\n"))
michael@0 5809 self.cgRoot.append(CGGeneric("if (rv.Failed()) {\n"))
michael@0 5810 self.cgRoot.append(CGIndenter(errorReport))
michael@0 5811 self.cgRoot.append(CGGeneric("}\n"))
michael@0 5812
michael@0 5813 def define(self):
michael@0 5814 return self.cgRoot.define()
michael@0 5815
michael@0 5816
michael@0 5817 def getUnionMemberName(type):
michael@0 5818 if type.isGeckoInterface():
michael@0 5819 return type.inner.identifier.name
michael@0 5820 if type.isEnum():
michael@0 5821 return type.inner.identifier.name
michael@0 5822 if type.isArray() or type.isSequence():
michael@0 5823 return str(type)
michael@0 5824 return type.name
michael@0 5825
michael@0 5826
michael@0 5827 class MethodNotNewObjectError(Exception):
michael@0 5828 def __init__(self, typename):
michael@0 5829 self.typename = typename
michael@0 5830
michael@0 5831 # A counter for making sure that when we're wrapping up things in
michael@0 5832 # nested sequences we don't use the same variable name to iterate over
michael@0 5833 # different sequences.
michael@0 5834 sequenceWrapLevel = 0
michael@0 5835
michael@0 5836
michael@0 5837 def wrapTypeIntoCurrentCompartment(type, value, isMember=True):
michael@0 5838 """
michael@0 5839 Take the thing named by "value" and if it contains "any",
michael@0 5840 "object", or spidermonkey-interface types inside return a CGThing
michael@0 5841 that will wrap them into the current compartment.
michael@0 5842 """
michael@0 5843 if type.isAny():
michael@0 5844 assert not type.nullable()
michael@0 5845 if isMember:
michael@0 5846 value = "JS::MutableHandle<JS::Value>::fromMarkedLocation(&%s)" % value
michael@0 5847 else:
michael@0 5848 value = "&" + value
michael@0 5849 return CGGeneric("if (!JS_WrapValue(cx, %s)) {\n"
michael@0 5850 " return false;\n"
michael@0 5851 "}\n" % value)
michael@0 5852
michael@0 5853 if type.isObject():
michael@0 5854 if isMember:
michael@0 5855 value = "JS::MutableHandle<JSObject*>::fromMarkedLocation(&%s)" % value
michael@0 5856 else:
michael@0 5857 value = "&" + value
michael@0 5858 return CGGeneric("if (!JS_WrapObject(cx, %s)) {\n"
michael@0 5859 " return false;\n"
michael@0 5860 "}\n" % value)
michael@0 5861
michael@0 5862 if type.isSpiderMonkeyInterface():
michael@0 5863 origValue = value
michael@0 5864 if type.nullable():
michael@0 5865 value = "%s.Value()" % value
michael@0 5866 wrapCode = CGGeneric("if (!%s.WrapIntoNewCompartment(cx)) {\n"
michael@0 5867 " return false;\n"
michael@0 5868 "}\n" % value)
michael@0 5869 if type.nullable():
michael@0 5870 wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue)
michael@0 5871 return wrapCode
michael@0 5872
michael@0 5873 if type.isSequence():
michael@0 5874 origValue = value
michael@0 5875 origType = type
michael@0 5876 if type.nullable():
michael@0 5877 type = type.inner
michael@0 5878 value = "%s.Value()" % value
michael@0 5879 global sequenceWrapLevel
michael@0 5880 index = "indexName%d" % sequenceWrapLevel
michael@0 5881 sequenceWrapLevel += 1
michael@0 5882 wrapElement = wrapTypeIntoCurrentCompartment(type.inner,
michael@0 5883 "%s[%s]" % (value, index))
michael@0 5884 sequenceWrapLevel -= 1
michael@0 5885 if not wrapElement:
michael@0 5886 return None
michael@0 5887 wrapCode = CGWrapper(CGIndenter(wrapElement),
michael@0 5888 pre=("for (uint32_t %s = 0; %s < %s.Length(); ++%s) {\n" %
michael@0 5889 (index, index, value, index)),
michael@0 5890 post="}\n")
michael@0 5891 if origType.nullable():
michael@0 5892 wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue)
michael@0 5893 return wrapCode
michael@0 5894
michael@0 5895 if type.isDictionary():
michael@0 5896 assert not type.nullable()
michael@0 5897 myDict = type.inner
michael@0 5898 memberWraps = []
michael@0 5899 while myDict:
michael@0 5900 for member in myDict.members:
michael@0 5901 memberWrap = wrapArgIntoCurrentCompartment(
michael@0 5902 member,
michael@0 5903 "%s.%s" % (value, CGDictionary.makeMemberName(member.identifier.name)))
michael@0 5904 if memberWrap:
michael@0 5905 memberWraps.append(memberWrap)
michael@0 5906 myDict = myDict.parent
michael@0 5907 return CGList(memberWraps) if len(memberWraps) != 0 else None
michael@0 5908
michael@0 5909 if type.isUnion():
michael@0 5910 memberWraps = []
michael@0 5911 if type.nullable():
michael@0 5912 type = type.inner
michael@0 5913 value = "%s.Value()" % value
michael@0 5914 for member in type.flatMemberTypes:
michael@0 5915 memberName = getUnionMemberName(member)
michael@0 5916 memberWrap = wrapTypeIntoCurrentCompartment(
michael@0 5917 member, "%s.GetAs%s()" % (value, memberName))
michael@0 5918 if memberWrap:
michael@0 5919 memberWrap = CGIfWrapper(
michael@0 5920 memberWrap, "%s.Is%s()" % (value, memberName))
michael@0 5921 memberWraps.append(memberWrap)
michael@0 5922 return CGList(memberWraps, "else ") if len(memberWraps) != 0 else None
michael@0 5923
michael@0 5924 if (type.isString() or type.isPrimitive() or type.isEnum() or
michael@0 5925 type.isGeckoInterface() or type.isCallback() or type.isDate()):
michael@0 5926 # All of these don't need wrapping
michael@0 5927 return None
michael@0 5928
michael@0 5929 raise TypeError("Unknown type; we don't know how to wrap it in constructor "
michael@0 5930 "arguments: %s" % type)
michael@0 5931
michael@0 5932
michael@0 5933 def wrapArgIntoCurrentCompartment(arg, value, isMember=True):
michael@0 5934 """
michael@0 5935 As wrapTypeIntoCurrentCompartment but handles things being optional
michael@0 5936 """
michael@0 5937 origValue = value
michael@0 5938 isOptional = arg.optional and not arg.defaultValue
michael@0 5939 if isOptional:
michael@0 5940 value = value + ".Value()"
michael@0 5941 wrap = wrapTypeIntoCurrentCompartment(arg.type, value, isMember)
michael@0 5942 if wrap and isOptional:
michael@0 5943 wrap = CGIfWrapper(wrap, "%s.WasPassed()" % origValue)
michael@0 5944 return wrap
michael@0 5945
michael@0 5946
michael@0 5947 class CGPerSignatureCall(CGThing):
michael@0 5948 """
michael@0 5949 This class handles the guts of generating code for a particular
michael@0 5950 call signature. A call signature consists of four things:
michael@0 5951
michael@0 5952 1) A return type, which can be None to indicate that there is no
michael@0 5953 actual return value (e.g. this is an attribute setter) or an
michael@0 5954 IDLType if there's an IDL type involved (including |void|).
michael@0 5955 2) An argument list, which is allowed to be empty.
michael@0 5956 3) A name of a native method to call.
michael@0 5957 4) Whether or not this method is static.
michael@0 5958
michael@0 5959 We also need to know whether this is a method or a getter/setter
michael@0 5960 to do error reporting correctly.
michael@0 5961
michael@0 5962 The idlNode parameter can be either a method or an attr. We can query
michael@0 5963 |idlNode.identifier| in both cases, so we can be agnostic between the two.
michael@0 5964 """
michael@0 5965 # XXXbz For now each entry in the argument list is either an
michael@0 5966 # IDLArgument or a FakeArgument, but longer-term we may want to
michael@0 5967 # have ways of flagging things like JSContext* or optional_argc in
michael@0 5968 # there.
michael@0 5969
michael@0 5970 def __init__(self, returnType, arguments, nativeMethodName, static,
michael@0 5971 descriptor, idlNode, argConversionStartsAt=0, getter=False,
michael@0 5972 setter=False, isConstructor=False):
michael@0 5973 assert idlNode.isMethod() == (not getter and not setter)
michael@0 5974 assert idlNode.isAttr() == (getter or setter)
michael@0 5975 # Constructors are always static
michael@0 5976 assert not isConstructor or static
michael@0 5977
michael@0 5978 CGThing.__init__(self)
michael@0 5979 self.returnType = returnType
michael@0 5980 self.descriptor = descriptor
michael@0 5981 self.idlNode = idlNode
michael@0 5982 self.extendedAttributes = descriptor.getExtendedAttributes(idlNode,
michael@0 5983 getter=getter,
michael@0 5984 setter=setter)
michael@0 5985 self.arguments = arguments
michael@0 5986 self.argCount = len(arguments)
michael@0 5987 cgThings = []
michael@0 5988 lenientFloatCode = None
michael@0 5989 if idlNode.getExtendedAttribute('LenientFloat') is not None:
michael@0 5990 if setter:
michael@0 5991 lenientFloatCode = "return true;\n"
michael@0 5992 elif idlNode.isMethod():
michael@0 5993 lenientFloatCode = ("args.rval().setUndefined();\n"
michael@0 5994 "return true;\n")
michael@0 5995
michael@0 5996 argsPre = []
michael@0 5997 if static:
michael@0 5998 nativeMethodName = "%s::%s" % (descriptor.nativeType,
michael@0 5999 nativeMethodName)
michael@0 6000 # If we're a constructor, "obj" may not be a function, so calling
michael@0 6001 # XrayAwareCalleeGlobal() on it is not safe. Of course in the
michael@0 6002 # constructor case either "obj" is an Xray or we're already in the
michael@0 6003 # content compartment, not the Xray compartment, so just
michael@0 6004 # constructing the GlobalObject from "obj" is fine.
michael@0 6005 if isConstructor:
michael@0 6006 objForGlobalObject = "obj"
michael@0 6007 else:
michael@0 6008 objForGlobalObject = "xpc::XrayAwareCalleeGlobal(obj)"
michael@0 6009 cgThings.append(CGGeneric(fill(
michael@0 6010 """
michael@0 6011 GlobalObject global(cx, ${obj});
michael@0 6012 if (global.Failed()) {
michael@0 6013 return false;
michael@0 6014 }
michael@0 6015
michael@0 6016 """,
michael@0 6017 obj=objForGlobalObject)))
michael@0 6018 argsPre.append("global")
michael@0 6019
michael@0 6020 # For JS-implemented interfaces we do not want to base the
michael@0 6021 # needsCx decision on the types involved, just on our extended
michael@0 6022 # attributes.
michael@0 6023 needsCx = needCx(returnType, arguments, self.extendedAttributes,
michael@0 6024 not descriptor.interface.isJSImplemented())
michael@0 6025 if needsCx and not (static and descriptor.workers):
michael@0 6026 argsPre.append("cx")
michael@0 6027
michael@0 6028 needsUnwrap = False
michael@0 6029 argsPost = []
michael@0 6030 if isConstructor:
michael@0 6031 needsUnwrap = True
michael@0 6032 needsUnwrappedVar = False
michael@0 6033 unwrappedVar = "obj"
michael@0 6034 elif descriptor.interface.isJSImplemented():
michael@0 6035 needsUnwrap = True
michael@0 6036 needsUnwrappedVar = True
michael@0 6037 argsPost.append("js::GetObjectCompartment(unwrappedObj.empty() ? obj : unwrappedObj.ref())")
michael@0 6038 elif needScopeObject(returnType, arguments, self.extendedAttributes,
michael@0 6039 descriptor.wrapperCache, True,
michael@0 6040 idlNode.getExtendedAttribute("StoreInSlot")):
michael@0 6041 needsUnwrap = True
michael@0 6042 needsUnwrappedVar = True
michael@0 6043 argsPre.append("unwrappedObj.empty() ? obj : unwrappedObj.ref()")
michael@0 6044
michael@0 6045 if needsUnwrap and needsUnwrappedVar:
michael@0 6046 # We cannot assign into obj because it's a Handle, not a
michael@0 6047 # MutableHandle, so we need a separate Rooted.
michael@0 6048 cgThings.append(CGGeneric("Maybe<JS::Rooted<JSObject*> > unwrappedObj;\n"))
michael@0 6049 unwrappedVar = "unwrappedObj.ref()"
michael@0 6050
michael@0 6051 if idlNode.isMethod() and idlNode.isLegacycaller():
michael@0 6052 # If we can have legacycaller with identifier, we can't
michael@0 6053 # just use the idlNode to determine whether we're
michael@0 6054 # generating code for the legacycaller or not.
michael@0 6055 assert idlNode.isIdentifierLess()
michael@0 6056 # Pass in our thisVal
michael@0 6057 argsPre.append("args.thisv()")
michael@0 6058
michael@0 6059 ourName = "%s.%s" % (descriptor.interface.identifier.name,
michael@0 6060 idlNode.identifier.name)
michael@0 6061 if idlNode.isMethod():
michael@0 6062 argDescription = "argument %(index)d of " + ourName
michael@0 6063 elif setter:
michael@0 6064 argDescription = "value being assigned to %s" % ourName
michael@0 6065 else:
michael@0 6066 assert self.argCount == 0
michael@0 6067
michael@0 6068 if needsUnwrap:
michael@0 6069 # It's very important that we construct our unwrappedObj, if we need
michael@0 6070 # to do it, before we might start setting up Rooted things for our
michael@0 6071 # arguments, so that we don't violate the stack discipline Rooted
michael@0 6072 # depends on.
michael@0 6073 cgThings.append(CGGeneric(
michael@0 6074 "bool objIsXray = xpc::WrapperFactory::IsXrayWrapper(obj);\n"))
michael@0 6075 if needsUnwrappedVar:
michael@0 6076 cgThings.append(CGIfWrapper(
michael@0 6077 CGGeneric("unwrappedObj.construct(cx, obj);\n"),
michael@0 6078 "objIsXray"))
michael@0 6079
michael@0 6080 for i in range(argConversionStartsAt, self.argCount):
michael@0 6081 cgThings.append(
michael@0 6082 CGArgumentConverter(arguments[i], i, self.descriptor,
michael@0 6083 argDescription % {"index": i + 1},
michael@0 6084 invalidEnumValueFatal=not setter,
michael@0 6085 lenientFloatCode=lenientFloatCode))
michael@0 6086
michael@0 6087 if needsUnwrap:
michael@0 6088 # Something depends on having the unwrapped object, so unwrap it now.
michael@0 6089 xraySteps = []
michael@0 6090 # XXXkhuey we should be able to MOZ_ASSERT that ${obj} is
michael@0 6091 # not null.
michael@0 6092 xraySteps.append(
michael@0 6093 CGGeneric(string.Template(dedent("""
michael@0 6094 ${obj} = js::CheckedUnwrap(${obj});
michael@0 6095 if (!${obj}) {
michael@0 6096 return false;
michael@0 6097 }
michael@0 6098 """)).substitute({'obj': unwrappedVar})))
michael@0 6099 if isConstructor:
michael@0 6100 # If we're called via an xray, we need to enter the underlying
michael@0 6101 # object's compartment and then wrap up all of our arguments into
michael@0 6102 # that compartment as needed. This is all happening after we've
michael@0 6103 # already done the conversions from JS values to WebIDL (C++)
michael@0 6104 # values, so we only need to worry about cases where there are 'any'
michael@0 6105 # or 'object' types, or other things that we represent as actual
michael@0 6106 # JSAPI types, present. Effectively, we're emulating a
michael@0 6107 # CrossCompartmentWrapper, but working with the C++ types, not the
michael@0 6108 # original list of JS::Values.
michael@0 6109 cgThings.append(CGGeneric("Maybe<JSAutoCompartment> ac;\n"))
michael@0 6110 xraySteps.append(CGGeneric("ac.construct(cx, obj);\n"))
michael@0 6111 xraySteps.extend(
michael@0 6112 wrapArgIntoCurrentCompartment(arg, argname, isMember=False)
michael@0 6113 for arg, argname in self.getArguments())
michael@0 6114
michael@0 6115 cgThings.append(
michael@0 6116 CGIfWrapper(CGList(xraySteps),
michael@0 6117 "objIsXray"))
michael@0 6118
michael@0 6119 cgThings.append(CGCallGenerator(
michael@0 6120 self.getErrorReport() if self.isFallible() else None,
michael@0 6121 self.getArguments(), argsPre, returnType,
michael@0 6122 self.extendedAttributes, descriptor, nativeMethodName,
michael@0 6123 static, argsPost=argsPost))
michael@0 6124 self.cgRoot = CGList(cgThings)
michael@0 6125
michael@0 6126 def getArguments(self):
michael@0 6127 return [(a, "arg" + str(i)) for i, a in enumerate(self.arguments)]
michael@0 6128
michael@0 6129 def isFallible(self):
michael@0 6130 return 'infallible' not in self.extendedAttributes
michael@0 6131
michael@0 6132 def wrap_return_value(self):
michael@0 6133 returnsNewObject = memberReturnsNewObject(self.idlNode)
michael@0 6134 if returnsNewObject:
michael@0 6135 # We better be returning addrefed things!
michael@0 6136 assert(isResultAlreadyAddRefed(self.extendedAttributes) or
michael@0 6137 # NewObject can return raw pointers to owned objects
michael@0 6138 (self.returnType.isGeckoInterface() and
michael@0 6139 self.descriptor.getDescriptor(self.returnType.unroll().inner.identifier.name).nativeOwnership == 'owned'))
michael@0 6140
michael@0 6141 setSlot = self.idlNode.isAttr() and self.idlNode.slotIndex is not None
michael@0 6142 if setSlot:
michael@0 6143 # For attributes in slots, we want to do some
michael@0 6144 # post-processing once we've wrapped them.
michael@0 6145 successCode = "break;\n"
michael@0 6146 else:
michael@0 6147 successCode = None
michael@0 6148
michael@0 6149 resultTemplateValues = {
michael@0 6150 'jsvalRef': 'args.rval()',
michael@0 6151 'jsvalHandle': 'args.rval()',
michael@0 6152 'returnsNewObject': returnsNewObject,
michael@0 6153 'successCode': successCode,
michael@0 6154 'obj': "reflector" if setSlot else "obj"
michael@0 6155 }
michael@0 6156 try:
michael@0 6157 wrapCode = wrapForType(self.returnType, self.descriptor, resultTemplateValues)
michael@0 6158 except MethodNotNewObjectError, err:
michael@0 6159 assert not returnsNewObject
michael@0 6160 raise TypeError("%s being returned from non-NewObject method or property %s.%s" %
michael@0 6161 (err.typename,
michael@0 6162 self.descriptor.interface.identifier.name,
michael@0 6163 self.idlNode.identifier.name))
michael@0 6164 if setSlot:
michael@0 6165 # We need to make sure that our initial wrapping is done in the
michael@0 6166 # reflector compartment, but that we finally set args.rval() in the
michael@0 6167 # caller compartment. We also need to make sure that the actual
michael@0 6168 # wrapping steps happen inside a do/while that they can break out
michael@0 6169 # of.
michael@0 6170
michael@0 6171 # postSteps are the steps that run while we're still in the
michael@0 6172 # reflector compartment but after we've finished the initial
michael@0 6173 # wrapping into args.rval().
michael@0 6174 postSteps = ""
michael@0 6175 if self.idlNode.getExtendedAttribute("Frozen"):
michael@0 6176 assert self.idlNode.type.isSequence() or self.idlNode.type.isDictionary()
michael@0 6177 freezeValue = CGGeneric(
michael@0 6178 "JS::Rooted<JSObject*> rvalObj(cx, &args.rval().toObject());\n"
michael@0 6179 "if (!JS_FreezeObject(cx, rvalObj)) {\n"
michael@0 6180 " return false;\n"
michael@0 6181 "}\n")
michael@0 6182 if self.idlNode.type.nullable():
michael@0 6183 freezeValue = CGIfWrapper(freezeValue,
michael@0 6184 "args.rval().isObject()")
michael@0 6185 postSteps += freezeValue.define()
michael@0 6186 postSteps += ("js::SetReservedSlot(reflector, %s, args.rval());\n" %
michael@0 6187 memberReservedSlot(self.idlNode))
michael@0 6188 # For the case of Cached attributes, go ahead and preserve our
michael@0 6189 # wrapper if needed. We need to do this because otherwise the
michael@0 6190 # wrapper could get garbage-collected and the cached value would
michael@0 6191 # suddenly disappear, but the whole premise of cached values is that
michael@0 6192 # they never change without explicit action on someone's part. We
michael@0 6193 # don't do this for StoreInSlot, since those get dealt with during
michael@0 6194 # wrapper setup, and failure would involve us trying to clear an
michael@0 6195 # already-preserved wrapper.
michael@0 6196 if (self.idlNode.getExtendedAttribute("Cached") and
michael@0 6197 self.descriptor.wrapperCache):
michael@0 6198 postSteps += "PreserveWrapper(self);\n"
michael@0 6199
michael@0 6200 wrapCode = fill(
michael@0 6201 """
michael@0 6202 { // Make sure we wrap and store in the slot in reflector's compartment
michael@0 6203 JSAutoCompartment ac(cx, reflector);
michael@0 6204 do { // block we break out of when done wrapping
michael@0 6205 $*{wrapCode}
michael@0 6206 } while (0);
michael@0 6207 $*{postSteps}
michael@0 6208 }
michael@0 6209 // And now make sure args.rval() is in the caller compartment
michael@0 6210 return ${maybeWrap}(cx, args.rval());
michael@0 6211 """,
michael@0 6212 wrapCode=wrapCode,
michael@0 6213 postSteps=postSteps,
michael@0 6214 maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type))
michael@0 6215 return wrapCode
michael@0 6216
michael@0 6217 def getErrorReport(self):
michael@0 6218 jsImplemented = ""
michael@0 6219 if self.descriptor.interface.isJSImplemented():
michael@0 6220 jsImplemented = ", true"
michael@0 6221 return CGGeneric('return ThrowMethodFailedWithDetails(cx, rv, "%s", "%s"%s);\n'
michael@0 6222 % (self.descriptor.interface.identifier.name,
michael@0 6223 self.idlNode.identifier.name,
michael@0 6224 jsImplemented))
michael@0 6225
michael@0 6226 def define(self):
michael@0 6227 return (self.cgRoot.define() + self.wrap_return_value())
michael@0 6228
michael@0 6229
michael@0 6230 class CGSwitch(CGList):
michael@0 6231 """
michael@0 6232 A class to generate code for a switch statement.
michael@0 6233
michael@0 6234 Takes three constructor arguments: an expression, a list of cases,
michael@0 6235 and an optional default.
michael@0 6236
michael@0 6237 Each case is a CGCase. The default is a CGThing for the body of
michael@0 6238 the default case, if any.
michael@0 6239 """
michael@0 6240 def __init__(self, expression, cases, default=None):
michael@0 6241 CGList.__init__(self, [CGIndenter(c) for c in cases])
michael@0 6242 self.prepend(CGGeneric("switch (" + expression + ") {\n"))
michael@0 6243 if default is not None:
michael@0 6244 self.append(
michael@0 6245 CGIndenter(
michael@0 6246 CGWrapper(
michael@0 6247 CGIndenter(default),
michael@0 6248 pre="default: {\n",
michael@0 6249 post=" break;\n}\n")))
michael@0 6250
michael@0 6251 self.append(CGGeneric("}\n"))
michael@0 6252
michael@0 6253
michael@0 6254 class CGCase(CGList):
michael@0 6255 """
michael@0 6256 A class to generate code for a case statement.
michael@0 6257
michael@0 6258 Takes three constructor arguments: an expression, a CGThing for
michael@0 6259 the body (allowed to be None if there is no body), and an optional
michael@0 6260 argument (defaulting to False) for whether to fall through.
michael@0 6261 """
michael@0 6262 def __init__(self, expression, body, fallThrough=False):
michael@0 6263 CGList.__init__(self, [])
michael@0 6264 self.append(CGGeneric("case " + expression + ": {\n"))
michael@0 6265 bodyList = CGList([body])
michael@0 6266 if fallThrough:
michael@0 6267 bodyList.append(CGGeneric("/* Fall through */\n"))
michael@0 6268 else:
michael@0 6269 bodyList.append(CGGeneric("break;\n"))
michael@0 6270 self.append(CGIndenter(bodyList))
michael@0 6271 self.append(CGGeneric("}\n"))
michael@0 6272
michael@0 6273
michael@0 6274 class CGMethodCall(CGThing):
michael@0 6275 """
michael@0 6276 A class to generate selection of a method signature from a set of
michael@0 6277 signatures and generation of a call to that signature.
michael@0 6278 """
michael@0 6279 def __init__(self, nativeMethodName, static, descriptor, method,
michael@0 6280 isConstructor=False, constructorName=None):
michael@0 6281 CGThing.__init__(self)
michael@0 6282
michael@0 6283 if isConstructor:
michael@0 6284 assert constructorName is not None
michael@0 6285 methodName = constructorName
michael@0 6286 else:
michael@0 6287 methodName = "%s.%s" % (descriptor.interface.identifier.name, method.identifier.name)
michael@0 6288 argDesc = "argument %d of " + methodName
michael@0 6289
michael@0 6290 def requiredArgCount(signature):
michael@0 6291 arguments = signature[1]
michael@0 6292 if len(arguments) == 0:
michael@0 6293 return 0
michael@0 6294 requiredArgs = len(arguments)
michael@0 6295 while requiredArgs and arguments[requiredArgs-1].optional:
michael@0 6296 requiredArgs -= 1
michael@0 6297 return requiredArgs
michael@0 6298
michael@0 6299 def getPerSignatureCall(signature, argConversionStartsAt=0):
michael@0 6300 return CGPerSignatureCall(signature[0], signature[1],
michael@0 6301 nativeMethodName, static, descriptor,
michael@0 6302 method,
michael@0 6303 argConversionStartsAt=argConversionStartsAt,
michael@0 6304 isConstructor=isConstructor)
michael@0 6305
michael@0 6306 signatures = method.signatures()
michael@0 6307 if len(signatures) == 1:
michael@0 6308 # Special case: we can just do a per-signature method call
michael@0 6309 # here for our one signature and not worry about switching
michael@0 6310 # on anything.
michael@0 6311 signature = signatures[0]
michael@0 6312 self.cgRoot = CGList([CGIndenter(getPerSignatureCall(signature))])
michael@0 6313 requiredArgs = requiredArgCount(signature)
michael@0 6314
michael@0 6315 if requiredArgs > 0:
michael@0 6316 code = indent(fill( # BOGUS extra blank line
michael@0 6317 """
michael@0 6318
michael@0 6319 if (args.length() < ${requiredArgs}) {
michael@0 6320 return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${methodName}");
michael@0 6321 }
michael@0 6322 """,
michael@0 6323 requiredArgs=requiredArgs,
michael@0 6324 methodName=methodName))
michael@0 6325 self.cgRoot.prepend(CGGeneric(code))
michael@0 6326 return
michael@0 6327
michael@0 6328 # Need to find the right overload
michael@0 6329 maxArgCount = method.maxArgCount
michael@0 6330 allowedArgCounts = method.allowedArgCounts
michael@0 6331
michael@0 6332 argCountCases = []
michael@0 6333 for argCountIdx, argCount in enumerate(allowedArgCounts):
michael@0 6334 possibleSignatures = method.signaturesForArgCount(argCount)
michael@0 6335
michael@0 6336 # Try to optimize away cases when the next argCount in the list
michael@0 6337 # will have the same code as us; if it does, we can fall through to
michael@0 6338 # that case.
michael@0 6339 if argCountIdx+1 < len(allowedArgCounts):
michael@0 6340 nextPossibleSignatures = \
michael@0 6341 method.signaturesForArgCount(allowedArgCounts[argCountIdx+1])
michael@0 6342 else:
michael@0 6343 nextPossibleSignatures = None
michael@0 6344 if possibleSignatures == nextPossibleSignatures:
michael@0 6345 # Same set of signatures means we better have the same
michael@0 6346 # distinguishing index. So we can in fact just fall through to
michael@0 6347 # the next case here.
michael@0 6348 assert (len(possibleSignatures) == 1 or
michael@0 6349 (method.distinguishingIndexForArgCount(argCount) ==
michael@0 6350 method.distinguishingIndexForArgCount(allowedArgCounts[argCountIdx+1])))
michael@0 6351 argCountCases.append(CGCase(str(argCount), None, True))
michael@0 6352 continue
michael@0 6353
michael@0 6354 if len(possibleSignatures) == 1:
michael@0 6355 # easy case!
michael@0 6356 signature = possibleSignatures[0]
michael@0 6357 argCountCases.append(
michael@0 6358 CGCase(str(argCount), getPerSignatureCall(signature)))
michael@0 6359 continue
michael@0 6360
michael@0 6361 distinguishingIndex = method.distinguishingIndexForArgCount(argCount)
michael@0 6362
michael@0 6363 def distinguishingArgument(signature):
michael@0 6364 args = signature[1]
michael@0 6365 if distinguishingIndex < len(args):
michael@0 6366 return args[distinguishingIndex]
michael@0 6367 assert args[-1].variadic
michael@0 6368 return args[-1]
michael@0 6369
michael@0 6370 def distinguishingType(signature):
michael@0 6371 return distinguishingArgument(signature).type
michael@0 6372
michael@0 6373 for sig in possibleSignatures:
michael@0 6374 # We should not have "any" args at distinguishingIndex,
michael@0 6375 # since we have multiple possible signatures remaining,
michael@0 6376 # but "any" is never distinguishable from anything else.
michael@0 6377 assert not distinguishingType(sig).isAny()
michael@0 6378 # We can't handle unions at the distinguishing index.
michael@0 6379 if distinguishingType(sig).isUnion():
michael@0 6380 raise TypeError("No support for unions as distinguishing "
michael@0 6381 "arguments yet: %s" %
michael@0 6382 distinguishingArgument(sig).location)
michael@0 6383 # We don't support variadics as the distinguishingArgument yet.
michael@0 6384 # If you want to add support, consider this case:
michael@0 6385 #
michael@0 6386 # void(long... foo);
michael@0 6387 # void(long bar, Int32Array baz);
michael@0 6388 #
michael@0 6389 # in which we have to convert argument 0 to long before picking
michael@0 6390 # an overload... but all the variadic stuff needs to go into a
michael@0 6391 # single array in case we pick that overload, so we have to have
michael@0 6392 # machinery for converting argument 0 to long and then either
michael@0 6393 # placing it in the variadic bit or not. Or something. We may
michael@0 6394 # be able to loosen this restriction if the variadic arg is in
michael@0 6395 # fact at distinguishingIndex, perhaps. Would need to
michael@0 6396 # double-check.
michael@0 6397 if distinguishingArgument(sig).variadic:
michael@0 6398 raise TypeError("No support for variadics as distinguishing "
michael@0 6399 "arguments yet: %s" %
michael@0 6400 distinguishingArgument(sig).location)
michael@0 6401
michael@0 6402 # Convert all our arguments up to the distinguishing index.
michael@0 6403 # Doesn't matter which of the possible signatures we use, since
michael@0 6404 # they all have the same types up to that point; just use
michael@0 6405 # possibleSignatures[0]
michael@0 6406 caseBody = [CGArgumentConverter(possibleSignatures[0][1][i],
michael@0 6407 i, descriptor,
michael@0 6408 argDesc % (i + 1))
michael@0 6409 for i in range(0, distinguishingIndex)]
michael@0 6410
michael@0 6411 # Select the right overload from our set.
michael@0 6412 distinguishingArg = "args[%d]" % distinguishingIndex
michael@0 6413
michael@0 6414 def tryCall(signature, indent, isDefinitelyObject=False,
michael@0 6415 isNullOrUndefined=False):
michael@0 6416 assert not isDefinitelyObject or not isNullOrUndefined
michael@0 6417 assert isDefinitelyObject or isNullOrUndefined
michael@0 6418 if isDefinitelyObject:
michael@0 6419 failureCode = "break;\n"
michael@0 6420 else:
michael@0 6421 failureCode = None
michael@0 6422 type = distinguishingType(signature)
michael@0 6423 # The argument at index distinguishingIndex can't possibly be
michael@0 6424 # unset here, because we've already checked that argc is large
michael@0 6425 # enough that we can examine this argument. But note that we
michael@0 6426 # still want to claim that optional arguments are optional, in
michael@0 6427 # case undefined was passed in.
michael@0 6428 argIsOptional = (distinguishingArgument(signature).optional and
michael@0 6429 not distinguishingArgument(signature).defaultValue)
michael@0 6430 testCode = instantiateJSToNativeConversion(
michael@0 6431 getJSToNativeConversionInfo(type, descriptor,
michael@0 6432 failureCode=failureCode,
michael@0 6433 isDefinitelyObject=isDefinitelyObject,
michael@0 6434 isNullOrUndefined=isNullOrUndefined,
michael@0 6435 isOptional=argIsOptional,
michael@0 6436 sourceDescription=(argDesc % (distinguishingIndex + 1))),
michael@0 6437 {
michael@0 6438 "declName": "arg%d" % distinguishingIndex,
michael@0 6439 "holderName": ("arg%d" % distinguishingIndex) + "_holder",
michael@0 6440 "val": distinguishingArg,
michael@0 6441 "mutableVal": distinguishingArg,
michael@0 6442 "obj": "obj",
michael@0 6443 "haveValue": "args.hasDefined(%d)" % distinguishingIndex
michael@0 6444 },
michael@0 6445 checkForValue=argIsOptional)
michael@0 6446 caseBody.append(CGIndenter(testCode, indent))
michael@0 6447
michael@0 6448 # If we got this far, we know we unwrapped to the right
michael@0 6449 # C++ type, so just do the call. Start conversion with
michael@0 6450 # distinguishingIndex + 1, since we already converted
michael@0 6451 # distinguishingIndex.
michael@0 6452 caseBody.append(CGIndenter(
michael@0 6453 getPerSignatureCall(signature, distinguishingIndex + 1),
michael@0 6454 indent))
michael@0 6455
michael@0 6456 def hasConditionalConversion(type):
michael@0 6457 """
michael@0 6458 Return whether the argument conversion for this type will be
michael@0 6459 conditional on the type of incoming JS value. For example, for
michael@0 6460 interface types the conversion is conditional on the incoming
michael@0 6461 value being isObject().
michael@0 6462
michael@0 6463 For the types for which this returns false, we do not have to
michael@0 6464 output extra isUndefined() or isNullOrUndefined() cases, because
michael@0 6465 null/undefined values will just fall through into our
michael@0 6466 unconditional conversion.
michael@0 6467 """
michael@0 6468 if type.isString() or type.isEnum():
michael@0 6469 return False
michael@0 6470 if type.isBoolean():
michael@0 6471 distinguishingTypes = (distinguishingType(s) for s in
michael@0 6472 possibleSignatures)
michael@0 6473 return any(t.isString() or t.isEnum() or t.isNumeric()
michael@0 6474 for t in distinguishingTypes)
michael@0 6475 if type.isNumeric():
michael@0 6476 distinguishingTypes = (distinguishingType(s) for s in
michael@0 6477 possibleSignatures)
michael@0 6478 return any(t.isString() or t.isEnum()
michael@0 6479 for t in distinguishingTypes)
michael@0 6480 return True
michael@0 6481
michael@0 6482 def needsNullOrUndefinedCase(type):
michael@0 6483 """
michael@0 6484 Return true if the type needs a special isNullOrUndefined() case
michael@0 6485 """
michael@0 6486 return ((type.nullable() and
michael@0 6487 hasConditionalConversion(type)) or
michael@0 6488 type.isDictionary())
michael@0 6489
michael@0 6490 # First check for undefined and optional distinguishing arguments
michael@0 6491 # and output a special branch for that case. Note that we don't
michael@0 6492 # use distinguishingArgument here because we actualy want to
michael@0 6493 # exclude variadic arguments. Also note that we skip this check if
michael@0 6494 # we plan to output a isNullOrUndefined() special case for this
michael@0 6495 # argument anyway, since that will subsume our isUndefined() check.
michael@0 6496 # This is safe, because there can be at most one nullable
michael@0 6497 # distinguishing argument, so if we're it we'll definitely get
michael@0 6498 # picked up by the nullable handling. Also, we can skip this check
michael@0 6499 # if the argument has an unconditional conversion later on.
michael@0 6500 undefSigs = [s for s in possibleSignatures if
michael@0 6501 distinguishingIndex < len(s[1]) and
michael@0 6502 s[1][distinguishingIndex].optional and
michael@0 6503 hasConditionalConversion(s[1][distinguishingIndex].type) and
michael@0 6504 not needsNullOrUndefinedCase(s[1][distinguishingIndex].type)]
michael@0 6505 # Can't have multiple signatures with an optional argument at the
michael@0 6506 # same index.
michael@0 6507 assert len(undefSigs) < 2
michael@0 6508 if len(undefSigs) > 0:
michael@0 6509 caseBody.append(CGGeneric("if (%s.isUndefined()) {\n" %
michael@0 6510 distinguishingArg))
michael@0 6511 tryCall(undefSigs[0], 2, isNullOrUndefined=True)
michael@0 6512 caseBody.append(CGGeneric("}\n"))
michael@0 6513
michael@0 6514 # Next, check for null or undefined. That means looking for
michael@0 6515 # nullable arguments at the distinguishing index and outputting a
michael@0 6516 # separate branch for them. But if the nullable argument has an
michael@0 6517 # unconditional conversion, we don't need to do that. The reason
michael@0 6518 # for that is that at most one argument at the distinguishing index
michael@0 6519 # is nullable (since two nullable arguments are not
michael@0 6520 # distinguishable), and null/undefined values will always fall
michael@0 6521 # through to the unconditional conversion we have, if any, since
michael@0 6522 # they will fail whatever the conditions on the input value are for
michael@0 6523 # our other conversions.
michael@0 6524 nullOrUndefSigs = [s for s in possibleSignatures
michael@0 6525 if needsNullOrUndefinedCase(distinguishingType(s))]
michael@0 6526 # Can't have multiple nullable types here
michael@0 6527 assert len(nullOrUndefSigs) < 2
michael@0 6528 if len(nullOrUndefSigs) > 0:
michael@0 6529 caseBody.append(CGGeneric("if (%s.isNullOrUndefined()) {\n" %
michael@0 6530 distinguishingArg))
michael@0 6531 tryCall(nullOrUndefSigs[0], 2, isNullOrUndefined=True)
michael@0 6532 caseBody.append(CGGeneric("}\n"))
michael@0 6533
michael@0 6534 # Now check for distinguishingArg being various kinds of objects.
michael@0 6535 # The spec says to check for the following things in order:
michael@0 6536 # 1) A platform object that's not a platform array object, being
michael@0 6537 # passed to an interface or "object" arg.
michael@0 6538 # 2) A Date object being passed to a Date or "object" arg.
michael@0 6539 # 3) A RegExp object being passed to a RegExp or "object" arg.
michael@0 6540 # 4) A callable object being passed to a callback or "object" arg.
michael@0 6541 # 5) Any non-Date and non-RegExp object being passed to a
michael@0 6542 # array or sequence or callback interface dictionary or
michael@0 6543 # "object" arg.
michael@0 6544 #
michael@0 6545 # We can can coalesce these five cases together, as long as we make
michael@0 6546 # sure to check whether our object works as an interface argument
michael@0 6547 # before checking whether it works as an arraylike or dictionary or
michael@0 6548 # callback function or callback interface.
michael@0 6549
michael@0 6550 # First grab all the overloads that have a non-callback interface
michael@0 6551 # (which includes typed arrays and arraybuffers) at the
michael@0 6552 # distinguishing index. We can also include the ones that have an
michael@0 6553 # "object" here, since if those are present no other object-typed
michael@0 6554 # argument will be.
michael@0 6555 objectSigs = [
michael@0 6556 s for s in possibleSignatures
michael@0 6557 if (distinguishingType(s).isObject() or
michael@0 6558 distinguishingType(s).isNonCallbackInterface())]
michael@0 6559
michael@0 6560 # And all the overloads that take Date
michael@0 6561 objectSigs.extend(s for s in possibleSignatures
michael@0 6562 if distinguishingType(s).isDate())
michael@0 6563
michael@0 6564 # And all the overloads that take callbacks
michael@0 6565 objectSigs.extend(s for s in possibleSignatures
michael@0 6566 if distinguishingType(s).isCallback())
michael@0 6567
michael@0 6568 # Now append all the overloads that take an array or sequence or
michael@0 6569 # dictionary or callback interface:
michael@0 6570 objectSigs.extend(s for s in possibleSignatures
michael@0 6571 if (distinguishingType(s).isArray() or
michael@0 6572 distinguishingType(s).isSequence() or
michael@0 6573 distinguishingType(s).isDictionary() or
michael@0 6574 distinguishingType(s).isCallbackInterface()))
michael@0 6575
michael@0 6576 # There might be more than one thing in objectSigs; we need to check
michael@0 6577 # which ones we unwrap to.
michael@0 6578 if len(objectSigs) > 0:
michael@0 6579 # Here it's enough to guard on our argument being an object. The
michael@0 6580 # code for unwrapping non-callback interfaces, typed arrays,
michael@0 6581 # sequences, arrays, and Dates will just bail out and move on to
michael@0 6582 # the next overload if the object fails to unwrap correctly,
michael@0 6583 # while "object" accepts any object anyway. We could even not
michael@0 6584 # do the isObject() check up front here, but in cases where we
michael@0 6585 # have multiple object overloads it makes sense to do it only
michael@0 6586 # once instead of for each overload. That will also allow the
michael@0 6587 # unwrapping test to skip having to do codegen for the
michael@0 6588 # null-or-undefined case, which we already handled above.
michael@0 6589 caseBody.append(CGGeneric("if (%s.isObject()) {\n" %
michael@0 6590 distinguishingArg))
michael@0 6591 for sig in objectSigs:
michael@0 6592 caseBody.append(CGIndenter(CGGeneric("do {\n")))
michael@0 6593 # Indent by 4, since we need to indent further
michael@0 6594 # than our "do" statement
michael@0 6595 tryCall(sig, 4, isDefinitelyObject=True)
michael@0 6596 caseBody.append(CGIndenter(CGGeneric("} while (0);\n")))
michael@0 6597
michael@0 6598 caseBody.append(CGGeneric("}\n"))
michael@0 6599
michael@0 6600 # Now we only have to consider booleans, numerics, and strings. If
michael@0 6601 # we only have one of them, then we can just output it. But if not,
michael@0 6602 # then we need to output some of the cases conditionally: if we have
michael@0 6603 # a string overload, then boolean and numeric are conditional, and
michael@0 6604 # if not then boolean is conditional if we have a numeric overload.
michael@0 6605 def findUniqueSignature(filterLambda):
michael@0 6606 sigs = filter(filterLambda, possibleSignatures)
michael@0 6607 assert len(sigs) < 2
michael@0 6608 if len(sigs) > 0:
michael@0 6609 return sigs[0]
michael@0 6610 return None
michael@0 6611
michael@0 6612 stringSignature = findUniqueSignature(
michael@0 6613 lambda s: (distinguishingType(s).isString() or
michael@0 6614 distinguishingType(s).isEnum()))
michael@0 6615 numericSignature = findUniqueSignature(
michael@0 6616 lambda s: distinguishingType(s).isNumeric())
michael@0 6617 booleanSignature = findUniqueSignature(
michael@0 6618 lambda s: distinguishingType(s).isBoolean())
michael@0 6619
michael@0 6620 if stringSignature or numericSignature:
michael@0 6621 booleanCondition = "%s.isBoolean()"
michael@0 6622 else:
michael@0 6623 booleanCondition = None
michael@0 6624
michael@0 6625 if stringSignature:
michael@0 6626 numericCondition = "%s.isNumber()"
michael@0 6627 else:
michael@0 6628 numericCondition = None
michael@0 6629
michael@0 6630 def addCase(sig, condition):
michael@0 6631 sigCode = getPerSignatureCall(sig, distinguishingIndex)
michael@0 6632 if condition:
michael@0 6633 sigCode = CGIfWrapper(sigCode,
michael@0 6634 condition % distinguishingArg)
michael@0 6635 caseBody.append(sigCode)
michael@0 6636
michael@0 6637 if booleanSignature:
michael@0 6638 addCase(booleanSignature, booleanCondition)
michael@0 6639 if numericSignature:
michael@0 6640 addCase(numericSignature, numericCondition)
michael@0 6641 if stringSignature:
michael@0 6642 addCase(stringSignature, None)
michael@0 6643
michael@0 6644 if (not booleanSignature and not numericSignature and
michael@0 6645 not stringSignature):
michael@0 6646 # Just throw; we have no idea what we're supposed to
michael@0 6647 # do with this.
michael@0 6648 caseBody.append(CGGeneric(
michael@0 6649 'return ThrowErrorMessage(cx, MSG_OVERLOAD_RESOLUTION_FAILED, "%d", "%d", "%s");\n' %
michael@0 6650 (distinguishingIndex + 1, argCount, methodName)))
michael@0 6651
michael@0 6652 argCountCases.append(CGCase(str(argCount), CGList(caseBody)))
michael@0 6653
michael@0 6654 overloadCGThings = []
michael@0 6655 overloadCGThings.append(
michael@0 6656 CGGeneric("unsigned argcount = std::min(args.length(), %du);\n" %
michael@0 6657 maxArgCount))
michael@0 6658 overloadCGThings.append(
michael@0 6659 CGSwitch("argcount",
michael@0 6660 argCountCases,
michael@0 6661 # BOGUS extra blank line at end of default block
michael@0 6662 CGGeneric('return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "%s");\n\n' %
michael@0 6663 methodName)))
michael@0 6664 overloadCGThings.append(
michael@0 6665 CGGeneric('MOZ_CRASH("We have an always-returning default case");\n'
michael@0 6666 'return false;\n'))
michael@0 6667 self.cgRoot = CGWrapper(CGIndenter(CGList(overloadCGThings)),
michael@0 6668 pre="\n")
michael@0 6669
michael@0 6670 def define(self):
michael@0 6671 return self.cgRoot.define()
michael@0 6672
michael@0 6673
michael@0 6674 class CGGetterCall(CGPerSignatureCall):
michael@0 6675 """
michael@0 6676 A class to generate a native object getter call for a particular IDL
michael@0 6677 getter.
michael@0 6678 """
michael@0 6679 def __init__(self, returnType, nativeMethodName, descriptor, attr):
michael@0 6680 CGPerSignatureCall.__init__(self, returnType, [], nativeMethodName,
michael@0 6681 attr.isStatic(), descriptor, attr,
michael@0 6682 getter=True)
michael@0 6683
michael@0 6684
michael@0 6685 class FakeArgument():
michael@0 6686 """
michael@0 6687 A class that quacks like an IDLArgument. This is used to make
michael@0 6688 setters look like method calls or for special operations.
michael@0 6689 """
michael@0 6690 def __init__(self, type, interfaceMember, name="arg", allowTreatNonCallableAsNull=False):
michael@0 6691 self.type = type
michael@0 6692 self.optional = False
michael@0 6693 self.variadic = False
michael@0 6694 self.defaultValue = None
michael@0 6695 self._allowTreatNonCallableAsNull = allowTreatNonCallableAsNull
michael@0 6696 self.treatNullAs = interfaceMember.treatNullAs
michael@0 6697 if isinstance(interfaceMember, IDLAttribute):
michael@0 6698 self.enforceRange = interfaceMember.enforceRange
michael@0 6699 self.clamp = interfaceMember.clamp
michael@0 6700 else:
michael@0 6701 self.enforceRange = False
michael@0 6702 self.clamp = False
michael@0 6703
michael@0 6704 class FakeIdentifier():
michael@0 6705 def __init__(self):
michael@0 6706 self.name = name
michael@0 6707 self.identifier = FakeIdentifier()
michael@0 6708
michael@0 6709 def allowTreatNonCallableAsNull(self):
michael@0 6710 return self._allowTreatNonCallableAsNull
michael@0 6711
michael@0 6712
michael@0 6713 class CGSetterCall(CGPerSignatureCall):
michael@0 6714 """
michael@0 6715 A class to generate a native object setter call for a particular IDL
michael@0 6716 setter.
michael@0 6717 """
michael@0 6718 def __init__(self, argType, nativeMethodName, descriptor, attr):
michael@0 6719 CGPerSignatureCall.__init__(self, None, [FakeArgument(argType, attr, allowTreatNonCallableAsNull=True)],
michael@0 6720 nativeMethodName, attr.isStatic(),
michael@0 6721 descriptor, attr, setter=True)
michael@0 6722
michael@0 6723 def wrap_return_value(self):
michael@0 6724 attr = self.idlNode
michael@0 6725 if self.descriptor.wrapperCache and attr.slotIndex is not None:
michael@0 6726 if attr.getExtendedAttribute("StoreInSlot"):
michael@0 6727 args = "cx, self"
michael@0 6728 else:
michael@0 6729 args = "self"
michael@0 6730 clearSlot = ("ClearCached%sValue(%s);\n" %
michael@0 6731 (MakeNativeName(self.idlNode.identifier.name), args))
michael@0 6732 else:
michael@0 6733 clearSlot = ""
michael@0 6734
michael@0 6735 # We have no return value
michael@0 6736 return ("\n"
michael@0 6737 "%s"
michael@0 6738 "return true;\n" % clearSlot)
michael@0 6739
michael@0 6740
michael@0 6741 class CGAbstractBindingMethod(CGAbstractStaticMethod):
michael@0 6742 """
michael@0 6743 Common class to generate the JSNatives for all our methods, getters, and
michael@0 6744 setters. This will generate the function declaration and unwrap the
michael@0 6745 |this| object. Subclasses are expected to override the generate_code
michael@0 6746 function to do the rest of the work. This function should return a
michael@0 6747 CGThing which is already properly indented.
michael@0 6748
michael@0 6749 getThisObj should be code for getting a JSObject* for the binding
michael@0 6750 object. If this is None, we will auto-generate code based on
michael@0 6751 descriptor to do the right thing. "" can be passed in if the
michael@0 6752 binding object is already stored in 'obj'.
michael@0 6753
michael@0 6754 callArgs should be code for getting a JS::CallArgs into a variable
michael@0 6755 called 'args'. This can be "" if there is already such a variable
michael@0 6756 around.
michael@0 6757
michael@0 6758 If allowCrossOriginThis is true, then this-unwrapping will first do an
michael@0 6759 UncheckedUnwrap and after that operate on the result.
michael@0 6760 """
michael@0 6761 def __init__(self, descriptor, name, args, unwrapFailureCode=None,
michael@0 6762 getThisObj=None,
michael@0 6763 callArgs="JS::CallArgs args = JS::CallArgsFromVp(argc, vp);\n",
michael@0 6764 allowCrossOriginThis=False):
michael@0 6765 CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
michael@0 6766
michael@0 6767 if unwrapFailureCode is None:
michael@0 6768 self.unwrapFailureCode = 'return ThrowErrorMessage(cx, MSG_THIS_DOES_NOT_IMPLEMENT_INTERFACE, "Value", "%s");\n' % descriptor.interface.identifier.name
michael@0 6769 else:
michael@0 6770 self.unwrapFailureCode = unwrapFailureCode
michael@0 6771
michael@0 6772 if getThisObj == "":
michael@0 6773 self.getThisObj = None
michael@0 6774 else:
michael@0 6775 if getThisObj is None:
michael@0 6776 if descriptor.interface.isOnGlobalProtoChain():
michael@0 6777 ensureCondition = "!args.thisv().isNullOrUndefined() && !args.thisv().isObject()"
michael@0 6778 getThisObj = "args.thisv().isObject() ? &args.thisv().toObject() : js::GetGlobalForObjectCrossCompartment(&args.callee())"
michael@0 6779 else:
michael@0 6780 ensureCondition = "!args.thisv().isObject()"
michael@0 6781 getThisObj = "&args.thisv().toObject()"
michael@0 6782 unwrapFailureCode = self.unwrapFailureCode % {'securityError': 'false'}
michael@0 6783 ensureThisObj = CGIfWrapper(CGGeneric(unwrapFailureCode),
michael@0 6784 ensureCondition)
michael@0 6785 else:
michael@0 6786 ensureThisObj = None
michael@0 6787 self.getThisObj = CGList(
michael@0 6788 [ensureThisObj,
michael@0 6789 CGGeneric("JS::Rooted<JSObject*> obj(cx, %s);\n" %
michael@0 6790 getThisObj)])
michael@0 6791 self.callArgs = callArgs
michael@0 6792 self.allowCrossOriginThis = allowCrossOriginThis
michael@0 6793
michael@0 6794 def definition_body(self):
michael@0 6795 body = self.callArgs
michael@0 6796 if self.getThisObj is not None:
michael@0 6797 body += self.getThisObj.define() + "\n"
michael@0 6798 body += "%s* self;\n" % self.descriptor.nativeType
michael@0 6799
michael@0 6800 # Our descriptor might claim that we're not castable, simply because
michael@0 6801 # we're someone's consequential interface. But for this-unwrapping, we
michael@0 6802 # know that we're the real deal. So fake a descriptor here for
michael@0 6803 # consumption by CastableObjectUnwrapper.
michael@0 6804 body += str(CastableObjectUnwrapper(
michael@0 6805 self.descriptor,
michael@0 6806 "obj", "self", self.unwrapFailureCode,
michael@0 6807 allowCrossOriginObj=self.allowCrossOriginThis))
michael@0 6808
michael@0 6809 return indent(body) + self.generate_code().define()
michael@0 6810
michael@0 6811 def generate_code(self):
michael@0 6812 assert False # Override me
michael@0 6813
michael@0 6814
michael@0 6815 class CGAbstractStaticBindingMethod(CGAbstractStaticMethod):
michael@0 6816 """
michael@0 6817 Common class to generate the JSNatives for all our static methods, getters
michael@0 6818 and setters. This will generate the function declaration and unwrap the
michael@0 6819 global object. Subclasses are expected to override the generate_code
michael@0 6820 function to do the rest of the work. This function should return a
michael@0 6821 CGThing which is already properly indented.
michael@0 6822 """
michael@0 6823 def __init__(self, descriptor, name):
michael@0 6824 args = [Argument('JSContext*', 'cx'),
michael@0 6825 Argument('unsigned', 'argc'),
michael@0 6826 Argument('JS::Value*', 'vp')]
michael@0 6827 CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
michael@0 6828
michael@0 6829 def definition_body(self):
michael@0 6830 # Make sure that "obj" is in the same compartment as "cx", since we'll
michael@0 6831 # later use it to wrap return values.
michael@0 6832 unwrap = indent(dedent("""
michael@0 6833 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
michael@0 6834 JS::Rooted<JSObject*> obj(cx, &args.callee());
michael@0 6835
michael@0 6836 """))
michael@0 6837 return unwrap + self.generate_code().define()
michael@0 6838
michael@0 6839 def generate_code(self):
michael@0 6840 assert False # Override me
michael@0 6841
michael@0 6842
michael@0 6843 def MakeNativeName(name):
michael@0 6844 return name[0].upper() + name[1:]
michael@0 6845
michael@0 6846
michael@0 6847 class CGGenericMethod(CGAbstractBindingMethod):
michael@0 6848 """
michael@0 6849 A class for generating the C++ code for an IDL method.
michael@0 6850
michael@0 6851 If allowCrossOriginThis is true, then this-unwrapping will first do an
michael@0 6852 UncheckedUnwrap and after that operate on the result.
michael@0 6853 """
michael@0 6854 def __init__(self, descriptor, allowCrossOriginThis=False):
michael@0 6855 args = [Argument('JSContext*', 'cx'),
michael@0 6856 Argument('unsigned', 'argc'),
michael@0 6857 Argument('JS::Value*', 'vp')]
michael@0 6858 unwrapFailureCode = (
michael@0 6859 'return ThrowInvalidThis(cx, args, GetInvalidThisErrorForMethod(%%(securityError)s), "%s");\n' %
michael@0 6860 descriptor.interface.identifier.name)
michael@0 6861 name = "genericCrossOriginMethod" if allowCrossOriginThis else "genericMethod"
michael@0 6862 CGAbstractBindingMethod.__init__(self, descriptor, name,
michael@0 6863 args,
michael@0 6864 unwrapFailureCode=unwrapFailureCode,
michael@0 6865 allowCrossOriginThis=allowCrossOriginThis)
michael@0 6866
michael@0 6867 def generate_code(self):
michael@0 6868 return CGGeneric(indent(dedent("""
michael@0 6869 const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
michael@0 6870 MOZ_ASSERT(info->type() == JSJitInfo::Method);
michael@0 6871 JSJitMethodOp method = info->method;
michael@0 6872 return method(cx, obj, self, JSJitMethodCallArgs(args));
michael@0 6873 """)))
michael@0 6874
michael@0 6875
michael@0 6876 class CGSpecializedMethod(CGAbstractStaticMethod):
michael@0 6877 """
michael@0 6878 A class for generating the C++ code for a specialized method that the JIT
michael@0 6879 can call with lower overhead.
michael@0 6880 """
michael@0 6881 def __init__(self, descriptor, method):
michael@0 6882 self.method = method
michael@0 6883 name = CppKeywords.checkMethodName(method.identifier.name)
michael@0 6884 args = [Argument('JSContext*', 'cx'),
michael@0 6885 Argument('JS::Handle<JSObject*>', 'obj'),
michael@0 6886 Argument('%s*' % descriptor.nativeType, 'self'),
michael@0 6887 Argument('const JSJitMethodCallArgs&', 'args')]
michael@0 6888 CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args)
michael@0 6889
michael@0 6890 def definition_body(self):
michael@0 6891 nativeName = CGSpecializedMethod.makeNativeName(self.descriptor,
michael@0 6892 self.method)
michael@0 6893 return CGMethodCall(nativeName, self.method.isStatic(), self.descriptor,
michael@0 6894 self.method).define()
michael@0 6895
michael@0 6896 @staticmethod
michael@0 6897 def makeNativeName(descriptor, method):
michael@0 6898 name = method.identifier.name
michael@0 6899 return MakeNativeName(descriptor.binaryNames.get(name, name))
michael@0 6900
michael@0 6901
michael@0 6902 class CGMethodPromiseWrapper(CGAbstractStaticMethod):
michael@0 6903 """
michael@0 6904 A class for generating a wrapper around another method that will
michael@0 6905 convert exceptions to promises.
michael@0 6906 """
michael@0 6907 def __init__(self, descriptor, methodToWrap):
michael@0 6908 self.method = methodToWrap
michael@0 6909 name = self.makeName(methodToWrap.name)
michael@0 6910 args = list(methodToWrap.args)
michael@0 6911 CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args)
michael@0 6912
michael@0 6913 def definition_body(self):
michael@0 6914 return indent(fill(
michael@0 6915 """
michael@0 6916 // Make sure to save the callee before someone maybe messes
michael@0 6917 // with rval().
michael@0 6918 JS::Rooted<JSObject*> callee(cx, &args.callee());
michael@0 6919 bool ok = ${methodName}(${args});
michael@0 6920 if (ok) {
michael@0 6921 return true;
michael@0 6922 }
michael@0 6923 return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
michael@0 6924 args.rval());
michael@0 6925 """,
michael@0 6926 methodName=self.method.name,
michael@0 6927 args=", ".join(arg.name for arg in self.args)))
michael@0 6928
michael@0 6929 @staticmethod
michael@0 6930 def makeName(methodName):
michael@0 6931 return methodName + "_promiseWrapper"
michael@0 6932
michael@0 6933
michael@0 6934 class CGJsonifierMethod(CGSpecializedMethod):
michael@0 6935 def __init__(self, descriptor, method):
michael@0 6936 assert method.isJsonifier()
michael@0 6937 CGSpecializedMethod.__init__(self, descriptor, method)
michael@0 6938
michael@0 6939 def definition_body(self):
michael@0 6940 ret = dedent("""
michael@0 6941 JS::Rooted<JSObject*> result(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
michael@0 6942 if (!result) {
michael@0 6943 return false;
michael@0 6944 }
michael@0 6945 """)
michael@0 6946 for m in self.descriptor.interface.members:
michael@0 6947 if m.isAttr() and not m.isStatic() and m.type.isSerializable():
michael@0 6948 ret += fill(
michael@0 6949 """
michael@0 6950 { // scope for "temp"
michael@0 6951 JS::Rooted<JS::Value> temp(cx);
michael@0 6952 if (!get_${name}(cx, obj, self, JSJitGetterCallArgs(&temp))) {
michael@0 6953 return false;
michael@0 6954 }
michael@0 6955 if (!JS_DefineProperty(cx, result, "${name}", temp, JSPROP_ENUMERATE)) {
michael@0 6956 return false;
michael@0 6957 }
michael@0 6958 }
michael@0 6959 """,
michael@0 6960 name=m.identifier.name)
michael@0 6961
michael@0 6962 ret += ('args.rval().setObject(*result);\n'
michael@0 6963 'return true;\n')
michael@0 6964 return indent(ret)
michael@0 6965
michael@0 6966
michael@0 6967 class CGLegacyCallHook(CGAbstractBindingMethod):
michael@0 6968 """
michael@0 6969 Call hook for our object
michael@0 6970 """
michael@0 6971 def __init__(self, descriptor):
michael@0 6972 self._legacycaller = descriptor.operations["LegacyCaller"]
michael@0 6973 args = [Argument('JSContext*', 'cx'),
michael@0 6974 Argument('unsigned', 'argc'),
michael@0 6975 Argument('JS::Value*', 'vp')]
michael@0 6976 # Our "self" is actually the callee in this case, not the thisval.
michael@0 6977 CGAbstractBindingMethod.__init__(
michael@0 6978 self, descriptor, LEGACYCALLER_HOOK_NAME,
michael@0 6979 args, getThisObj="&args.callee()")
michael@0 6980
michael@0 6981 def define(self):
michael@0 6982 if not self._legacycaller:
michael@0 6983 return ""
michael@0 6984 return CGAbstractBindingMethod.define(self)
michael@0 6985
michael@0 6986 def generate_code(self):
michael@0 6987 name = self._legacycaller.identifier.name
michael@0 6988 nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
michael@0 6989 return CGMethodCall(nativeName, False, self.descriptor,
michael@0 6990 self._legacycaller)
michael@0 6991
michael@0 6992
michael@0 6993 class CGNewResolveHook(CGAbstractBindingMethod):
michael@0 6994 """
michael@0 6995 NewResolve hook for objects with custom hooks.
michael@0 6996 """
michael@0 6997 def __init__(self, descriptor):
michael@0 6998 assert descriptor.interface.getExtendedAttribute("NeedNewResolve")
michael@0 6999
michael@0 7000 args = [Argument('JSContext*', 'cx'),
michael@0 7001 Argument('JS::Handle<JSObject*>', 'obj'),
michael@0 7002 Argument('JS::Handle<jsid>', 'id'),
michael@0 7003 Argument('JS::MutableHandle<JSObject*>', 'objp')]
michael@0 7004 # Our "self" is actually the "obj" argument in this case, not the thisval.
michael@0 7005 CGAbstractBindingMethod.__init__(
michael@0 7006 self, descriptor, NEWRESOLVE_HOOK_NAME,
michael@0 7007 args, getThisObj="", callArgs="")
michael@0 7008
michael@0 7009 def generate_code(self):
michael@0 7010 return CGGeneric(indent(dedent("""
michael@0 7011 JS::Rooted<JSPropertyDescriptor> desc(cx);
michael@0 7012 if (!self->DoNewResolve(cx, obj, id, &desc)) {
michael@0 7013 return false;
michael@0 7014 }
michael@0 7015 if (!desc.object()) {
michael@0 7016 return true;
michael@0 7017 }
michael@0 7018 // If desc.value() is undefined, then the DoNewResolve call
michael@0 7019 // has already defined it on the object. Don't try to also
michael@0 7020 // define it.
michael@0 7021 if (!desc.value().isUndefined() &&
michael@0 7022 !JS_DefinePropertyById(cx, obj, id, desc.value(),
michael@0 7023 desc.getter(), desc.setter(),
michael@0 7024 desc.attributes())) {
michael@0 7025 return false;
michael@0 7026 }
michael@0 7027 objp.set(obj);
michael@0 7028 return true;
michael@0 7029 """)))
michael@0 7030
michael@0 7031 def definition_body(self):
michael@0 7032 if self.descriptor.interface.getExtendedAttribute("Global"):
michael@0 7033 # Resolve standard classes
michael@0 7034 prefix = indent(dedent("""
michael@0 7035 if (!ResolveGlobal(cx, obj, id, objp)) {
michael@0 7036 return false;
michael@0 7037 }
michael@0 7038 if (objp) {
michael@0 7039 return true;
michael@0 7040 }
michael@0 7041
michael@0 7042 """))
michael@0 7043 else:
michael@0 7044 prefix = ""
michael@0 7045 return prefix + CGAbstractBindingMethod.definition_body(self)
michael@0 7046
michael@0 7047
michael@0 7048 class CGEnumerateHook(CGAbstractBindingMethod):
michael@0 7049 """
michael@0 7050 Enumerate hook for objects with custom hooks.
michael@0 7051 """
michael@0 7052 def __init__(self, descriptor):
michael@0 7053 assert descriptor.interface.getExtendedAttribute("NeedNewResolve")
michael@0 7054
michael@0 7055 args = [Argument('JSContext*', 'cx'),
michael@0 7056 Argument('JS::Handle<JSObject*>', 'obj')]
michael@0 7057 # Our "self" is actually the "obj" argument in this case, not the thisval.
michael@0 7058 CGAbstractBindingMethod.__init__(
michael@0 7059 self, descriptor, ENUMERATE_HOOK_NAME,
michael@0 7060 args, getThisObj="", callArgs="")
michael@0 7061
michael@0 7062 def generate_code(self):
michael@0 7063 return CGGeneric(indent(dedent("""
michael@0 7064 nsAutoTArray<nsString, 8> names;
michael@0 7065 ErrorResult rv;
michael@0 7066 self->GetOwnPropertyNames(cx, names, rv);
michael@0 7067 rv.WouldReportJSException();
michael@0 7068 if (rv.Failed()) {
michael@0 7069 return ThrowMethodFailedWithDetails(cx, rv, "%s", "enumerate");
michael@0 7070 }
michael@0 7071 JS::Rooted<JS::Value> dummy(cx);
michael@0 7072 for (uint32_t i = 0; i < names.Length(); ++i) {
michael@0 7073 if (!JS_LookupUCProperty(cx, obj, names[i].get(), names[i].Length(), &dummy)) {
michael@0 7074 return false;
michael@0 7075 }
michael@0 7076 }
michael@0 7077 return true;
michael@0 7078 """)))
michael@0 7079
michael@0 7080 def definition_body(self):
michael@0 7081 if self.descriptor.interface.getExtendedAttribute("Global"):
michael@0 7082 # Enumerate standard classes
michael@0 7083 prefix = indent(dedent("""
michael@0 7084 if (!EnumerateGlobal(cx, obj)) {
michael@0 7085 return false;
michael@0 7086 }
michael@0 7087
michael@0 7088 """))
michael@0 7089 else:
michael@0 7090 prefix = ""
michael@0 7091 return prefix + CGAbstractBindingMethod.definition_body(self)
michael@0 7092
michael@0 7093
michael@0 7094 class CppKeywords():
michael@0 7095 """
michael@0 7096 A class for checking if method names declared in webidl
michael@0 7097 are not in conflict with C++ keywords.
michael@0 7098 """
michael@0 7099 keywords = frozenset([
michael@0 7100 'alignas', 'alignof', 'and', 'and_eq', 'asm', 'assert', 'auto', 'bitand', 'bitor', 'bool',
michael@0 7101 'break', 'case', 'catch', 'char', 'char16_t', 'char32_t', 'class', 'compl', 'const',
michael@0 7102 'constexpr', 'const_cast', 'continue', 'decltype', 'default', 'delete', 'do', 'double',
michael@0 7103 'dynamic_cast', 'else', 'enum', 'explicit', 'export', 'extern', 'false', 'final', 'float',
michael@0 7104 'for', 'friend', 'goto', 'if', 'inline', 'int', 'long', 'mutable', 'namespace', 'new',
michael@0 7105 'noexcept', 'not', 'not_eq', 'nullptr', 'operator', 'or', 'or_eq', 'override', 'private',
michael@0 7106 'protected', 'public', 'register', 'reinterpret_cast', 'return', 'short', 'signed',
michael@0 7107 'sizeof', 'static', 'static_assert', 'static_cast', 'struct', 'switch', 'template', 'this',
michael@0 7108 'thread_local', 'throw', 'true', 'try', 'typedef', 'typeid', 'typename', 'union',
michael@0 7109 'unsigned', 'using', 'virtual', 'void', 'volatile', 'wchar_t', 'while', 'xor', 'xor_eq'])
michael@0 7110
michael@0 7111 @staticmethod
michael@0 7112 def checkMethodName(name):
michael@0 7113 # Double '_' because 'assert' and '_assert' cannot be used in MS2013 compiler.
michael@0 7114 # Bug 964892 and bug 963560.
michael@0 7115 if name in CppKeywords.keywords:
michael@0 7116 name = '_' + name + '_'
michael@0 7117 return name
michael@0 7118
michael@0 7119
michael@0 7120 class CGStaticMethod(CGAbstractStaticBindingMethod):
michael@0 7121 """
michael@0 7122 A class for generating the C++ code for an IDL static method.
michael@0 7123 """
michael@0 7124 def __init__(self, descriptor, method):
michael@0 7125 self.method = method
michael@0 7126 name = method.identifier.name
michael@0 7127 CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
michael@0 7128
michael@0 7129 def generate_code(self):
michael@0 7130 nativeName = CGSpecializedMethod.makeNativeName(self.descriptor,
michael@0 7131 self.method)
michael@0 7132 return CGMethodCall(nativeName, True, self.descriptor, self.method)
michael@0 7133
michael@0 7134
michael@0 7135 class CGGenericGetter(CGAbstractBindingMethod):
michael@0 7136 """
michael@0 7137 A class for generating the C++ code for an IDL attribute getter.
michael@0 7138 """
michael@0 7139 def __init__(self, descriptor, lenientThis=False, allowCrossOriginThis=False):
michael@0 7140 args = [Argument('JSContext*', 'cx'),
michael@0 7141 Argument('unsigned', 'argc'),
michael@0 7142 Argument('JS::Value*', 'vp')]
michael@0 7143 if lenientThis:
michael@0 7144 name = "genericLenientGetter"
michael@0 7145 unwrapFailureCode = dedent("""
michael@0 7146 MOZ_ASSERT(!JS_IsExceptionPending(cx));
michael@0 7147 if (!ReportLenientThisUnwrappingFailure(cx, &args.callee())) {
michael@0 7148 return false;
michael@0 7149 }
michael@0 7150 args.rval().set(JS::UndefinedValue());
michael@0 7151 return true;
michael@0 7152 """)
michael@0 7153 else:
michael@0 7154 if allowCrossOriginThis:
michael@0 7155 name = "genericCrossOriginGetter"
michael@0 7156 else:
michael@0 7157 name = "genericGetter"
michael@0 7158 unwrapFailureCode = (
michael@0 7159 'return ThrowInvalidThis(cx, args, GetInvalidThisErrorForGetter(%%(securityError)s), "%s");\n' %
michael@0 7160 descriptor.interface.identifier.name)
michael@0 7161 CGAbstractBindingMethod.__init__(self, descriptor, name, args,
michael@0 7162 unwrapFailureCode,
michael@0 7163 allowCrossOriginThis=allowCrossOriginThis)
michael@0 7164
michael@0 7165 def generate_code(self):
michael@0 7166 return CGGeneric(indent(dedent("""
michael@0 7167 const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
michael@0 7168 MOZ_ASSERT(info->type() == JSJitInfo::Getter);
michael@0 7169 JSJitGetterOp getter = info->getter;
michael@0 7170 return getter(cx, obj, self, JSJitGetterCallArgs(args));
michael@0 7171 """)))
michael@0 7172
michael@0 7173
michael@0 7174 class CGSpecializedGetter(CGAbstractStaticMethod):
michael@0 7175 """
michael@0 7176 A class for generating the code for a specialized attribute getter
michael@0 7177 that the JIT can call with lower overhead.
michael@0 7178 """
michael@0 7179 def __init__(self, descriptor, attr):
michael@0 7180 self.attr = attr
michael@0 7181 name = 'get_' + attr.identifier.name
michael@0 7182 args = [
michael@0 7183 Argument('JSContext*', 'cx'),
michael@0 7184 Argument('JS::Handle<JSObject*>', 'obj'),
michael@0 7185 Argument('%s*' % descriptor.nativeType, 'self'),
michael@0 7186 Argument('JSJitGetterCallArgs', 'args')
michael@0 7187 ]
michael@0 7188 CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
michael@0 7189
michael@0 7190 def definition_body(self):
michael@0 7191 nativeName = CGSpecializedGetter.makeNativeName(self.descriptor,
michael@0 7192 self.attr)
michael@0 7193 if self.attr.slotIndex is not None:
michael@0 7194 if (self.descriptor.hasXPConnectImpls and
michael@0 7195 (self.descriptor.interface.identifier.name != 'Window' or
michael@0 7196 self.attr.identifier.name != 'document')):
michael@0 7197 raise TypeError("Interface '%s' has XPConnect impls, so we "
michael@0 7198 "can't use our slot for property '%s'!" %
michael@0 7199 (self.descriptor.interface.identifier.name,
michael@0 7200 self.attr.identifier.name))
michael@0 7201 prefix = indent(fill(
michael@0 7202 """
michael@0 7203 // Have to either root across the getter call or reget after.
michael@0 7204 JS::Rooted<JSObject*> reflector(cx);
michael@0 7205 // Safe to do an unchecked unwrap, since we've gotten this far.
michael@0 7206 // Also make sure to unwrap outer windows, since we want the
michael@0 7207 // real DOM object.
michael@0 7208 reflector = IsDOMObject(obj) ? obj : js::UncheckedUnwrap(obj, /* stopAtOuter = */ false);
michael@0 7209 {
michael@0 7210 // Scope for cachedVal
michael@0 7211 JS::Value cachedVal = js::GetReservedSlot(reflector, ${slot});
michael@0 7212 if (!cachedVal.isUndefined()) {
michael@0 7213 args.rval().set(cachedVal);
michael@0 7214 // The cached value is in the compartment of reflector,
michael@0 7215 // so wrap into the caller compartment as needed.
michael@0 7216 return ${maybeWrap}(cx, args.rval());
michael@0 7217 }
michael@0 7218 }
michael@0 7219
michael@0 7220 """,
michael@0 7221 slot=memberReservedSlot(self.attr),
michael@0 7222 maybeWrap=getMaybeWrapValueFuncForType(self.attr.type)))
michael@0 7223 else:
michael@0 7224 prefix = ""
michael@0 7225
michael@0 7226 return (prefix +
michael@0 7227 indent(CGGetterCall(self.attr.type, nativeName,
michael@0 7228 self.descriptor, self.attr).define()))
michael@0 7229
michael@0 7230 @staticmethod
michael@0 7231 def makeNativeName(descriptor, attr):
michael@0 7232 name = attr.identifier.name
michael@0 7233 nativeName = MakeNativeName(descriptor.binaryNames.get(name, name))
michael@0 7234 # resultOutParam does not depend on whether resultAlreadyAddRefed is set
michael@0 7235 _, resultOutParam, _, _ = getRetvalDeclarationForType(attr.type,
michael@0 7236 descriptor,
michael@0 7237 False)
michael@0 7238 infallible = ('infallible' in
michael@0 7239 descriptor.getExtendedAttributes(attr, getter=True))
michael@0 7240 if resultOutParam or attr.type.nullable() or not infallible:
michael@0 7241 nativeName = "Get" + nativeName
michael@0 7242 return nativeName
michael@0 7243
michael@0 7244
michael@0 7245 class CGStaticGetter(CGAbstractStaticBindingMethod):
michael@0 7246 """
michael@0 7247 A class for generating the C++ code for an IDL static attribute getter.
michael@0 7248 """
michael@0 7249 def __init__(self, descriptor, attr):
michael@0 7250 self.attr = attr
michael@0 7251 name = 'get_' + attr.identifier.name
michael@0 7252 CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
michael@0 7253
michael@0 7254 def generate_code(self):
michael@0 7255 nativeName = CGSpecializedGetter.makeNativeName(self.descriptor,
michael@0 7256 self.attr)
michael@0 7257 return CGIndenter(CGGetterCall(self.attr.type, nativeName,
michael@0 7258 self.descriptor, self.attr))
michael@0 7259
michael@0 7260
michael@0 7261 class CGGenericSetter(CGAbstractBindingMethod):
michael@0 7262 """
michael@0 7263 A class for generating the C++ code for an IDL attribute setter.
michael@0 7264 """
michael@0 7265 def __init__(self, descriptor, lenientThis=False, allowCrossOriginThis=False):
michael@0 7266 args = [Argument('JSContext*', 'cx'),
michael@0 7267 Argument('unsigned', 'argc'),
michael@0 7268 Argument('JS::Value*', 'vp')]
michael@0 7269 if lenientThis:
michael@0 7270 name = "genericLenientSetter"
michael@0 7271 unwrapFailureCode = dedent("""
michael@0 7272 MOZ_ASSERT(!JS_IsExceptionPending(cx));
michael@0 7273 if (!ReportLenientThisUnwrappingFailure(cx, &args.callee())) {
michael@0 7274 return false;
michael@0 7275 }
michael@0 7276 args.rval().set(JS::UndefinedValue());
michael@0 7277 return true;
michael@0 7278 """)
michael@0 7279 else:
michael@0 7280 if allowCrossOriginThis:
michael@0 7281 name = "genericCrossOriginSetter"
michael@0 7282 else:
michael@0 7283 name = "genericSetter"
michael@0 7284 unwrapFailureCode = (
michael@0 7285 'return ThrowInvalidThis(cx, args, GetInvalidThisErrorForSetter(%%(securityError)s), "%s");\n' %
michael@0 7286 descriptor.interface.identifier.name)
michael@0 7287
michael@0 7288 CGAbstractBindingMethod.__init__(self, descriptor, name, args,
michael@0 7289 unwrapFailureCode,
michael@0 7290 allowCrossOriginThis=allowCrossOriginThis)
michael@0 7291
michael@0 7292 def generate_code(self):
michael@0 7293 return CGGeneric(indent(fill(
michael@0 7294 """
michael@0 7295 if (args.length() == 0) {
michael@0 7296 return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${name} attribute setter");
michael@0 7297 }
michael@0 7298 const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
michael@0 7299 MOZ_ASSERT(info->type() == JSJitInfo::Setter);
michael@0 7300 JSJitSetterOp setter = info->setter;
michael@0 7301 if (!setter(cx, obj, self, JSJitSetterCallArgs(args))) {
michael@0 7302 return false;
michael@0 7303 }
michael@0 7304 args.rval().set(JSVAL_VOID);
michael@0 7305 return true;
michael@0 7306 """,
michael@0 7307 name=self.descriptor.interface.identifier.name)))
michael@0 7308
michael@0 7309
michael@0 7310 class CGSpecializedSetter(CGAbstractStaticMethod):
michael@0 7311 """
michael@0 7312 A class for generating the code for a specialized attribute setter
michael@0 7313 that the JIT can call with lower overhead.
michael@0 7314 """
michael@0 7315 def __init__(self, descriptor, attr):
michael@0 7316 self.attr = attr
michael@0 7317 name = 'set_' + attr.identifier.name
michael@0 7318 args = [Argument('JSContext*', 'cx'),
michael@0 7319 Argument('JS::Handle<JSObject*>', 'obj'),
michael@0 7320 Argument('%s*' % descriptor.nativeType, 'self'),
michael@0 7321 Argument('JSJitSetterCallArgs', 'args')]
michael@0 7322 CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
michael@0 7323
michael@0 7324 def definition_body(self):
michael@0 7325 nativeName = CGSpecializedSetter.makeNativeName(self.descriptor,
michael@0 7326 self.attr)
michael@0 7327 return CGIndenter(CGSetterCall(self.attr.type, nativeName,
michael@0 7328 self.descriptor, self.attr)).define()
michael@0 7329
michael@0 7330 @staticmethod
michael@0 7331 def makeNativeName(descriptor, attr):
michael@0 7332 name = attr.identifier.name
michael@0 7333 return "Set" + MakeNativeName(descriptor.binaryNames.get(name, name))
michael@0 7334
michael@0 7335
michael@0 7336 class CGStaticSetter(CGAbstractStaticBindingMethod):
michael@0 7337 """
michael@0 7338 A class for generating the C++ code for an IDL static attribute setter.
michael@0 7339 """
michael@0 7340 def __init__(self, descriptor, attr):
michael@0 7341 self.attr = attr
michael@0 7342 name = 'set_' + attr.identifier.name
michael@0 7343 CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
michael@0 7344
michael@0 7345 def generate_code(self):
michael@0 7346 nativeName = CGSpecializedSetter.makeNativeName(self.descriptor,
michael@0 7347 self.attr)
michael@0 7348 checkForArg = CGGeneric(fill(
michael@0 7349 """
michael@0 7350 if (args.length() == 0) {
michael@0 7351 return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${name} setter");
michael@0 7352 }
michael@0 7353 """,
michael@0 7354 name=self.attr.identifier.name))
michael@0 7355 call = CGSetterCall(self.attr.type, nativeName, self.descriptor,
michael@0 7356 self.attr)
michael@0 7357 return CGIndenter(CGList([checkForArg, call]))
michael@0 7358
michael@0 7359
michael@0 7360 class CGSpecializedForwardingSetter(CGSpecializedSetter):
michael@0 7361 """
michael@0 7362 A class for generating the code for a specialized attribute setter with
michael@0 7363 PutForwards that the JIT can call with lower overhead.
michael@0 7364 """
michael@0 7365 def __init__(self, descriptor, attr):
michael@0 7366 CGSpecializedSetter.__init__(self, descriptor, attr)
michael@0 7367
michael@0 7368 def definition_body(self):
michael@0 7369 attrName = self.attr.identifier.name
michael@0 7370 forwardToAttrName = self.attr.getExtendedAttribute("PutForwards")[0]
michael@0 7371 # JS_GetProperty and JS_SetProperty can only deal with ASCII
michael@0 7372 assert all(ord(c) < 128 for c in attrName)
michael@0 7373 assert all(ord(c) < 128 for c in forwardToAttrName)
michael@0 7374 return indent(fill(
michael@0 7375 """
michael@0 7376 JS::Rooted<JS::Value> v(cx);
michael@0 7377 if (!JS_GetProperty(cx, obj, "${attr}", &v)) {
michael@0 7378 return false;
michael@0 7379 }
michael@0 7380
michael@0 7381 if (!v.isObject()) {
michael@0 7382 return ThrowErrorMessage(cx, MSG_NOT_OBJECT, "${interface}.${attr}");
michael@0 7383 }
michael@0 7384
michael@0 7385 JS::Rooted<JSObject*> targetObj(cx, &v.toObject());
michael@0 7386 return JS_SetProperty(cx, targetObj, "${forwardToAttrName}", args[0]);
michael@0 7387 """,
michael@0 7388 attr=attrName,
michael@0 7389 interface=self.descriptor.interface.identifier.name,
michael@0 7390 forwardToAttrName=forwardToAttrName))
michael@0 7391
michael@0 7392
michael@0 7393 class CGSpecializedReplaceableSetter(CGSpecializedSetter):
michael@0 7394 """
michael@0 7395 A class for generating the code for a specialized attribute setter with
michael@0 7396 Replaceable that the JIT can call with lower overhead.
michael@0 7397 """
michael@0 7398 def __init__(self, descriptor, attr):
michael@0 7399 CGSpecializedSetter.__init__(self, descriptor, attr)
michael@0 7400
michael@0 7401 def definition_body(self):
michael@0 7402 attrName = self.attr.identifier.name
michael@0 7403 # JS_DefineProperty can only deal with ASCII
michael@0 7404 assert all(ord(c) < 128 for c in attrName)
michael@0 7405 return indent('return JS_DefineProperty(cx, obj, "%s", args[0], JSPROP_ENUMERATE);\n' %
michael@0 7406 attrName)
michael@0 7407
michael@0 7408
michael@0 7409 def memberReturnsNewObject(member):
michael@0 7410 return member.getExtendedAttribute("NewObject") is not None
michael@0 7411
michael@0 7412
michael@0 7413 class CGMemberJITInfo(CGThing):
michael@0 7414 """
michael@0 7415 A class for generating the JITInfo for a property that points to
michael@0 7416 our specialized getter and setter.
michael@0 7417 """
michael@0 7418 def __init__(self, descriptor, member):
michael@0 7419 self.member = member
michael@0 7420 self.descriptor = descriptor
michael@0 7421
michael@0 7422 def declare(self):
michael@0 7423 return ""
michael@0 7424
michael@0 7425 def defineJitInfo(self, infoName, opName, opType, infallible, movable,
michael@0 7426 aliasSet, hasSlot, slotIndex, returnTypes, args):
michael@0 7427 """
michael@0 7428 aliasSet is a JSJitInfo::AliasSet value, without the "JSJitInfo::" bit.
michael@0 7429
michael@0 7430 args is None if we don't want to output argTypes for some
michael@0 7431 reason (e.g. we have overloads or we're not a method) and
michael@0 7432 otherwise an iterable of the arguments for this method.
michael@0 7433 """
michael@0 7434 assert(not movable or aliasSet != "AliasEverything") # Can't move write-aliasing things
michael@0 7435 assert(not hasSlot or movable) # Things with slots had better be movable
michael@0 7436
michael@0 7437 def jitInfoInitializer(isTypedMethod):
michael@0 7438 initializer = fill(
michael@0 7439 """
michael@0 7440 {
michael@0 7441 { ${opName} },
michael@0 7442 prototypes::id::${name},
michael@0 7443 PrototypeTraits<prototypes::id::${name}>::Depth,
michael@0 7444 JSJitInfo::${opType},
michael@0 7445 JSJitInfo::${aliasSet}, /* aliasSet. Not relevant for setters. */
michael@0 7446 ${returnType}, /* returnType. Not relevant for setters. */
michael@0 7447 ${isInfallible}, /* isInfallible. False in setters. */
michael@0 7448 ${isMovable}, /* isMovable. Not relevant for setters. */
michael@0 7449 ${isInSlot}, /* isInSlot. Only relevant for getters. */
michael@0 7450 ${isTypedMethod}, /* isTypedMethod. Only relevant for methods. */
michael@0 7451 ${slotIndex} /* Reserved slot index, if we're stored in a slot, else 0. */
michael@0 7452 }
michael@0 7453 """,
michael@0 7454 opName=opName,
michael@0 7455 name=self.descriptor.name,
michael@0 7456 opType=opType,
michael@0 7457 aliasSet=aliasSet,
michael@0 7458 returnType=reduce(CGMemberJITInfo.getSingleReturnType, returnTypes,
michael@0 7459 ""),
michael@0 7460 isInfallible=toStringBool(infallible),
michael@0 7461 isMovable=toStringBool(movable),
michael@0 7462 isInSlot=toStringBool(hasSlot),
michael@0 7463 isTypedMethod=toStringBool(isTypedMethod),
michael@0 7464 slotIndex=slotIndex)
michael@0 7465 return initializer.rstrip()
michael@0 7466
michael@0 7467 if args is not None:
michael@0 7468 argTypes = "%s_argTypes" % infoName
michael@0 7469 args = [CGMemberJITInfo.getJSArgType(arg.type) for arg in args]
michael@0 7470 args.append("JSJitInfo::ArgTypeListEnd")
michael@0 7471 argTypesDecl = (
michael@0 7472 "static const JSJitInfo::ArgType %s[] = { %s };\n" %
michael@0 7473 (argTypes, ", ".join(args)))
michael@0 7474 return fill(
michael@0 7475 """
michael@0 7476
michael@0 7477 $*{argTypesDecl}
michael@0 7478 static const JSTypedMethodJitInfo ${infoName} = {
michael@0 7479 ${jitInfo},
michael@0 7480 ${argTypes}
michael@0 7481 };
michael@0 7482 """,
michael@0 7483 argTypesDecl=argTypesDecl,
michael@0 7484 infoName=infoName,
michael@0 7485 jitInfo=jitInfoInitializer(True),
michael@0 7486 argTypes=argTypes)
michael@0 7487
michael@0 7488 return ("\n"
michael@0 7489 "static const JSJitInfo %s = %s;\n"
michael@0 7490 % (infoName, jitInfoInitializer(False)))
michael@0 7491
michael@0 7492 def define(self):
michael@0 7493 if self.member.isAttr():
michael@0 7494 getterinfo = ("%s_getterinfo" % self.member.identifier.name)
michael@0 7495 # We need the cast here because JSJitGetterOp has a "void* self"
michael@0 7496 # while we have the right type.
michael@0 7497 getter = ("(JSJitGetterOp)get_%s" % self.member.identifier.name)
michael@0 7498 getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True)
michael@0 7499 getterconst = (self.member.getExtendedAttribute("SameObject") or
michael@0 7500 self.member.getExtendedAttribute("Constant"))
michael@0 7501 getterpure = getterconst or self.member.getExtendedAttribute("Pure")
michael@0 7502 if getterconst:
michael@0 7503 aliasSet = "AliasNone"
michael@0 7504 elif getterpure:
michael@0 7505 aliasSet = "AliasDOMSets"
michael@0 7506 else:
michael@0 7507 aliasSet = "AliasEverything"
michael@0 7508 movable = getterpure and getterinfal
michael@0 7509
michael@0 7510 getterinfal = getterinfal and infallibleForMember(self.member, self.member.type, self.descriptor)
michael@0 7511 isInSlot = self.member.getExtendedAttribute("StoreInSlot")
michael@0 7512 if isInSlot:
michael@0 7513 slotIndex = memberReservedSlot(self.member)
michael@0 7514 # We'll statically assert that this is not too big in
michael@0 7515 # CGUpdateMemberSlotsMethod
michael@0 7516 else:
michael@0 7517 slotIndex = "0"
michael@0 7518
michael@0 7519 result = self.defineJitInfo(getterinfo, getter, "Getter",
michael@0 7520 getterinfal, movable, aliasSet,
michael@0 7521 isInSlot, slotIndex,
michael@0 7522 [self.member.type], None)
michael@0 7523 if (not self.member.readonly or
michael@0 7524 self.member.getExtendedAttribute("PutForwards") is not None or
michael@0 7525 self.member.getExtendedAttribute("Replaceable") is not None):
michael@0 7526 setterinfo = ("%s_setterinfo" % self.member.identifier.name)
michael@0 7527 # Actually a JSJitSetterOp, but JSJitGetterOp is first in the
michael@0 7528 # union.
michael@0 7529 setter = ("(JSJitGetterOp)set_%s" % self.member.identifier.name)
michael@0 7530 # Setters are always fallible, since they have to do a typed unwrap.
michael@0 7531 result += self.defineJitInfo(setterinfo, setter, "Setter",
michael@0 7532 False, False, "AliasEverything",
michael@0 7533 False, "0",
michael@0 7534 [BuiltinTypes[IDLBuiltinType.Types.void]],
michael@0 7535 None)
michael@0 7536 return result
michael@0 7537 if self.member.isMethod():
michael@0 7538 methodinfo = ("%s_methodinfo" % self.member.identifier.name)
michael@0 7539 name = CppKeywords.checkMethodName(self.member.identifier.name)
michael@0 7540 if self.member.returnsPromise():
michael@0 7541 name = CGMethodPromiseWrapper.makeName(name)
michael@0 7542 # Actually a JSJitMethodOp, but JSJitGetterOp is first in the union.
michael@0 7543 method = ("(JSJitGetterOp)%s" % name)
michael@0 7544 methodPure = self.member.getExtendedAttribute("Pure")
michael@0 7545
michael@0 7546 # Methods are infallible if they are infallible, have no arguments
michael@0 7547 # to unwrap, and have a return type that's infallible to wrap up for
michael@0 7548 # return.
michael@0 7549 sigs = self.member.signatures()
michael@0 7550 if len(sigs) != 1:
michael@0 7551 # Don't handle overloading. If there's more than one signature,
michael@0 7552 # one of them must take arguments.
michael@0 7553 methodInfal = False
michael@0 7554 args = None
michael@0 7555 movable = False
michael@0 7556 else:
michael@0 7557 sig = sigs[0]
michael@0 7558 # For pure methods, it's OK to set movable to our notion of
michael@0 7559 # infallible on the C++ side, without considering argument
michael@0 7560 # conversions, since argument conversions that can reliably
michael@0 7561 # throw would be effectful anyway and the jit doesn't move
michael@0 7562 # effectful things.
michael@0 7563 hasInfallibleImpl = "infallible" in self.descriptor.getExtendedAttributes(self.member)
michael@0 7564 movable = methodPure and hasInfallibleImpl
michael@0 7565 # XXXbz can we move the smarts about fallibility due to arg
michael@0 7566 # conversions into the JIT, using our new args stuff?
michael@0 7567 if (len(sig[1]) != 0 or
michael@0 7568 not infallibleForMember(self.member, sig[0], self.descriptor)):
michael@0 7569 # We have arguments or our return-value boxing can fail
michael@0 7570 methodInfal = False
michael@0 7571 else:
michael@0 7572 methodInfal = hasInfallibleImpl
michael@0 7573 # For now, only bother to output args if we're pure
michael@0 7574 if methodPure:
michael@0 7575 args = sig[1]
michael@0 7576 else:
michael@0 7577 args = None
michael@0 7578
michael@0 7579 if args is not None:
michael@0 7580 aliasSet = "AliasDOMSets"
michael@0 7581 else:
michael@0 7582 aliasSet = "AliasEverything"
michael@0 7583 result = self.defineJitInfo(methodinfo, method, "Method",
michael@0 7584 methodInfal, movable, aliasSet, False, "0",
michael@0 7585 [s[0] for s in sigs], args)
michael@0 7586 return result
michael@0 7587 raise TypeError("Illegal member type to CGPropertyJITInfo")
michael@0 7588
michael@0 7589 @staticmethod
michael@0 7590 def getJSReturnTypeTag(t):
michael@0 7591 if t.nullable():
michael@0 7592 # Sometimes it might return null, sometimes not
michael@0 7593 return "JSVAL_TYPE_UNKNOWN"
michael@0 7594 if t.isVoid():
michael@0 7595 # No return, every time
michael@0 7596 return "JSVAL_TYPE_UNDEFINED"
michael@0 7597 if t.isArray():
michael@0 7598 # No idea yet
michael@0 7599 assert False
michael@0 7600 if t.isSequence():
michael@0 7601 return "JSVAL_TYPE_OBJECT"
michael@0 7602 if t.isMozMap():
michael@0 7603 return "JSVAL_TYPE_OBJECT"
michael@0 7604 if t.isGeckoInterface():
michael@0 7605 return "JSVAL_TYPE_OBJECT"
michael@0 7606 if t.isString():
michael@0 7607 return "JSVAL_TYPE_STRING"
michael@0 7608 if t.isEnum():
michael@0 7609 return "JSVAL_TYPE_STRING"
michael@0 7610 if t.isCallback():
michael@0 7611 return "JSVAL_TYPE_OBJECT"
michael@0 7612 if t.isAny():
michael@0 7613 # The whole point is to return various stuff
michael@0 7614 return "JSVAL_TYPE_UNKNOWN"
michael@0 7615 if t.isObject():
michael@0 7616 return "JSVAL_TYPE_OBJECT"
michael@0 7617 if t.isSpiderMonkeyInterface():
michael@0 7618 return "JSVAL_TYPE_OBJECT"
michael@0 7619 if t.isUnion():
michael@0 7620 u = t.unroll()
michael@0 7621 if u.hasNullableType:
michael@0 7622 # Might be null or not
michael@0 7623 return "JSVAL_TYPE_UNKNOWN"
michael@0 7624 return reduce(CGMemberJITInfo.getSingleReturnType,
michael@0 7625 u.flatMemberTypes, "")
michael@0 7626 if t.isDictionary():
michael@0 7627 return "JSVAL_TYPE_OBJECT"
michael@0 7628 if t.isDate():
michael@0 7629 return "JSVAL_TYPE_OBJECT"
michael@0 7630 if not t.isPrimitive():
michael@0 7631 raise TypeError("No idea what type " + str(t) + " is.")
michael@0 7632 tag = t.tag()
michael@0 7633 if tag == IDLType.Tags.bool:
michael@0 7634 return "JSVAL_TYPE_BOOLEAN"
michael@0 7635 if tag in [IDLType.Tags.int8, IDLType.Tags.uint8,
michael@0 7636 IDLType.Tags.int16, IDLType.Tags.uint16,
michael@0 7637 IDLType.Tags.int32]:
michael@0 7638 return "JSVAL_TYPE_INT32"
michael@0 7639 if tag in [IDLType.Tags.int64, IDLType.Tags.uint64,
michael@0 7640 IDLType.Tags.unrestricted_float, IDLType.Tags.float,
michael@0 7641 IDLType.Tags.unrestricted_double, IDLType.Tags.double]:
michael@0 7642 # These all use JS_NumberValue, which can return int or double.
michael@0 7643 # But TI treats "double" as meaning "int or double", so we're
michael@0 7644 # good to return JSVAL_TYPE_DOUBLE here.
michael@0 7645 return "JSVAL_TYPE_DOUBLE"
michael@0 7646 if tag != IDLType.Tags.uint32:
michael@0 7647 raise TypeError("No idea what type " + str(t) + " is.")
michael@0 7648 # uint32 is sometimes int and sometimes double.
michael@0 7649 return "JSVAL_TYPE_DOUBLE"
michael@0 7650
michael@0 7651 @staticmethod
michael@0 7652 def getSingleReturnType(existingType, t):
michael@0 7653 type = CGMemberJITInfo.getJSReturnTypeTag(t)
michael@0 7654 if existingType == "":
michael@0 7655 # First element of the list; just return its type
michael@0 7656 return type
michael@0 7657
michael@0 7658 if type == existingType:
michael@0 7659 return existingType
michael@0 7660 if ((type == "JSVAL_TYPE_DOUBLE" and
michael@0 7661 existingType == "JSVAL_TYPE_INT32") or
michael@0 7662 (existingType == "JSVAL_TYPE_DOUBLE" and
michael@0 7663 type == "JSVAL_TYPE_INT32")):
michael@0 7664 # Promote INT32 to DOUBLE as needed
michael@0 7665 return "JSVAL_TYPE_DOUBLE"
michael@0 7666 # Different types
michael@0 7667 return "JSVAL_TYPE_UNKNOWN"
michael@0 7668
michael@0 7669 @staticmethod
michael@0 7670 def getJSArgType(t):
michael@0 7671 assert not t.isVoid()
michael@0 7672 if t.nullable():
michael@0 7673 # Sometimes it might return null, sometimes not
michael@0 7674 return "JSJitInfo::ArgType(JSJitInfo::Null | %s)" % CGMemberJITInfo.getJSArgType(t.inner)
michael@0 7675 if t.isArray():
michael@0 7676 # No idea yet
michael@0 7677 assert False
michael@0 7678 if t.isSequence():
michael@0 7679 return "JSJitInfo::Object"
michael@0 7680 if t.isGeckoInterface():
michael@0 7681 return "JSJitInfo::Object"
michael@0 7682 if t.isString():
michael@0 7683 return "JSJitInfo::String"
michael@0 7684 if t.isEnum():
michael@0 7685 return "JSJitInfo::String"
michael@0 7686 if t.isCallback():
michael@0 7687 return "JSJitInfo::Object"
michael@0 7688 if t.isAny():
michael@0 7689 # The whole point is to return various stuff
michael@0 7690 return "JSJitInfo::Any"
michael@0 7691 if t.isObject():
michael@0 7692 return "JSJitInfo::Object"
michael@0 7693 if t.isSpiderMonkeyInterface():
michael@0 7694 return "JSJitInfo::Object"
michael@0 7695 if t.isUnion():
michael@0 7696 u = t.unroll()
michael@0 7697 type = "JSJitInfo::Null" if u.hasNullableType else ""
michael@0 7698 return ("JSJitInfo::ArgType(%s)" %
michael@0 7699 reduce(CGMemberJITInfo.getSingleArgType,
michael@0 7700 u.flatMemberTypes, type))
michael@0 7701 if t.isDictionary():
michael@0 7702 return "JSJitInfo::Object"
michael@0 7703 if t.isDate():
michael@0 7704 return "JSJitInfo::Object"
michael@0 7705 if not t.isPrimitive():
michael@0 7706 raise TypeError("No idea what type " + str(t) + " is.")
michael@0 7707 tag = t.tag()
michael@0 7708 if tag == IDLType.Tags.bool:
michael@0 7709 return "JSJitInfo::Boolean"
michael@0 7710 if tag in [IDLType.Tags.int8, IDLType.Tags.uint8,
michael@0 7711 IDLType.Tags.int16, IDLType.Tags.uint16,
michael@0 7712 IDLType.Tags.int32]:
michael@0 7713 return "JSJitInfo::Integer"
michael@0 7714 if tag in [IDLType.Tags.int64, IDLType.Tags.uint64,
michael@0 7715 IDLType.Tags.unrestricted_float, IDLType.Tags.float,
michael@0 7716 IDLType.Tags.unrestricted_double, IDLType.Tags.double]:
michael@0 7717 # These all use JS_NumberValue, which can return int or double.
michael@0 7718 # But TI treats "double" as meaning "int or double", so we're
michael@0 7719 # good to return JSVAL_TYPE_DOUBLE here.
michael@0 7720 return "JSJitInfo::Double"
michael@0 7721 if tag != IDLType.Tags.uint32:
michael@0 7722 raise TypeError("No idea what type " + str(t) + " is.")
michael@0 7723 # uint32 is sometimes int and sometimes double.
michael@0 7724 return "JSJitInfo::Double"
michael@0 7725
michael@0 7726 @staticmethod
michael@0 7727 def getSingleArgType(existingType, t):
michael@0 7728 type = CGMemberJITInfo.getJSArgType(t)
michael@0 7729 if existingType == "":
michael@0 7730 # First element of the list; just return its type
michael@0 7731 return type
michael@0 7732
michael@0 7733 if type == existingType:
michael@0 7734 return existingType
michael@0 7735 return "%s | %s" % (existingType, type)
michael@0 7736
michael@0 7737
michael@0 7738 class CGStaticMethodJitinfo(CGGeneric):
michael@0 7739 """
michael@0 7740 A class for generating the JITInfo for a promise-returning static method.
michael@0 7741 """
michael@0 7742 def __init__(self, method):
michael@0 7743 CGGeneric.__init__(
michael@0 7744 self,
michael@0 7745 "\n"
michael@0 7746 "static const JSJitInfo %s_methodinfo = {\n"
michael@0 7747 " { (JSJitGetterOp)%s },\n"
michael@0 7748 " prototypes::id::_ID_Count, 0, JSJitInfo::StaticMethod,\n"
michael@0 7749 " JSJitInfo::AliasEverything, JSVAL_TYPE_MISSING, false, false,\n"
michael@0 7750 " false, false, 0\n"
michael@0 7751 "};\n" %
michael@0 7752 (method.identifier.name, method.identifier.name))
michael@0 7753
michael@0 7754
michael@0 7755 def getEnumValueName(value):
michael@0 7756 # Some enum values can be empty strings. Others might have weird
michael@0 7757 # characters in them. Deal with the former by returning "_empty",
michael@0 7758 # deal with possible name collisions from that by throwing if the
michael@0 7759 # enum value is actually "_empty", and throw on any value
michael@0 7760 # containing non-ASCII chars for now. Replace all chars other than
michael@0 7761 # [0-9A-Za-z_] with '_'.
michael@0 7762 if re.match("[^\x20-\x7E]", value):
michael@0 7763 raise SyntaxError('Enum value "' + value + '" contains non-ASCII characters')
michael@0 7764 if re.match("^[0-9]", value):
michael@0 7765 return '_' + value
michael@0 7766 value = re.sub(r'[^0-9A-Za-z_]', '_', value)
michael@0 7767 if re.match("^_[A-Z]|__", value):
michael@0 7768 raise SyntaxError('Enum value "' + value + '" is reserved by the C++ spec')
michael@0 7769 if value == "_empty":
michael@0 7770 raise SyntaxError('"_empty" is not an IDL enum value we support yet')
michael@0 7771 if value == "":
michael@0 7772 return "_empty"
michael@0 7773 return MakeNativeName(value)
michael@0 7774
michael@0 7775
michael@0 7776 class CGEnum(CGThing):
michael@0 7777 def __init__(self, enum):
michael@0 7778 CGThing.__init__(self)
michael@0 7779 self.enum = enum
michael@0 7780
michael@0 7781 def stringsNamespace(self):
michael@0 7782 return self.enum.identifier.name + "Values"
michael@0 7783
michael@0 7784 def nEnumStrings(self):
michael@0 7785 return len(self.enum.values()) + 1
michael@0 7786
michael@0 7787 def declare(self):
michael@0 7788 decl = fill( # BOGUS extra newline at top
michael@0 7789 """
michael@0 7790
michael@0 7791 MOZ_BEGIN_ENUM_CLASS(${name}, uint32_t)
michael@0 7792 $*{enums}
michael@0 7793 MOZ_END_ENUM_CLASS(${name})
michael@0 7794 """,
michael@0 7795 name=self.enum.identifier.name,
michael@0 7796 enums=",\n".join(map(getEnumValueName, self.enum.values())) + "\n")
michael@0 7797 strings = CGNamespace(self.stringsNamespace(),
michael@0 7798 CGGeneric(declare="extern const EnumEntry %s[%d];\n"
michael@0 7799 % (ENUM_ENTRY_VARIABLE_NAME, self.nEnumStrings())))
michael@0 7800 return decl + "\n" + strings.declare()
michael@0 7801
michael@0 7802 def define(self):
michael@0 7803 strings = fill( # BOGUS extra newline at top
michael@0 7804 """
michael@0 7805
michael@0 7806 extern const EnumEntry ${name}[${count}] = {
michael@0 7807 $*{entries}
michael@0 7808 { nullptr, 0 }
michael@0 7809 };
michael@0 7810 """,
michael@0 7811 name=ENUM_ENTRY_VARIABLE_NAME,
michael@0 7812 count=self.nEnumStrings(),
michael@0 7813 entries=''.join('{"%s", %d},\n' % (val, len(val))
michael@0 7814 for val in self.enum.values()))
michael@0 7815 # BOGUS - CGNamespace automatically indents; the extra indent() below causes
michael@0 7816 # the output to be indented 4 spaces.
michael@0 7817 return CGNamespace(self.stringsNamespace(),
michael@0 7818 CGGeneric(define=indent(strings))).define()
michael@0 7819
michael@0 7820 def deps(self):
michael@0 7821 return self.enum.getDeps()
michael@0 7822
michael@0 7823
michael@0 7824 def getUnionAccessorSignatureType(type, descriptorProvider):
michael@0 7825 """
michael@0 7826 Returns the types that are used in the getter and setter signatures for
michael@0 7827 union types
michael@0 7828 """
michael@0 7829 if type.isArray():
michael@0 7830 raise TypeError("Can't handle array arguments yet")
michael@0 7831
michael@0 7832 if type.isSequence():
michael@0 7833 nullable = type.nullable()
michael@0 7834 if nullable:
michael@0 7835 type = type.inner.inner
michael@0 7836 else:
michael@0 7837 type = type.inner
michael@0 7838 # We don't use the returned template here, so it's OK to just pass no
michael@0 7839 # sourceDescription.
michael@0 7840 elementInfo = getJSToNativeConversionInfo(type, descriptorProvider,
michael@0 7841 isMember="Sequence")
michael@0 7842 typeName = CGTemplatedType("Sequence", elementInfo.declType,
michael@0 7843 isReference=True)
michael@0 7844 if nullable:
michael@0 7845 typeName = CGTemplatedType("Nullable", typeName, isReference=True)
michael@0 7846
michael@0 7847 return typeName
michael@0 7848
michael@0 7849 if type.isUnion():
michael@0 7850 typeName = CGGeneric(type.name)
michael@0 7851 if type.nullable():
michael@0 7852 typeName = CGTemplatedType("Nullable", typeName, isReference=True)
michael@0 7853
michael@0 7854 return typeName
michael@0 7855
michael@0 7856 if type.isGeckoInterface():
michael@0 7857 descriptor = descriptorProvider.getDescriptor(
michael@0 7858 type.unroll().inner.identifier.name)
michael@0 7859 typeName = CGGeneric(descriptor.nativeType)
michael@0 7860 # Allow null pointers for nullable types and old-binding classes
michael@0 7861 if type.nullable() or type.unroll().inner.isExternal():
michael@0 7862 typeName = CGWrapper(typeName, post="*")
michael@0 7863 else:
michael@0 7864 typeName = CGWrapper(typeName, post="&")
michael@0 7865 return typeName
michael@0 7866
michael@0 7867 if type.isSpiderMonkeyInterface():
michael@0 7868 typeName = CGGeneric(type.name)
michael@0 7869 if type.nullable():
michael@0 7870 typeName = CGTemplatedType("Nullable", typeName)
michael@0 7871 return CGWrapper(typeName, post="&")
michael@0 7872
michael@0 7873 if type.isDOMString():
michael@0 7874 return CGGeneric("const nsAString&")
michael@0 7875
michael@0 7876 if type.isByteString():
michael@0 7877 return CGGeneric("const nsCString&")
michael@0 7878
michael@0 7879 if type.isEnum():
michael@0 7880 if type.nullable():
michael@0 7881 raise TypeError("We don't support nullable enumerated arguments or "
michael@0 7882 "union members yet")
michael@0 7883 return CGGeneric(type.inner.identifier.name)
michael@0 7884
michael@0 7885 if type.isCallback():
michael@0 7886 if type.nullable():
michael@0 7887 typeName = "%s*"
michael@0 7888 else:
michael@0 7889 typeName = "%s&"
michael@0 7890 return CGGeneric(typeName % type.unroll().identifier.name)
michael@0 7891
michael@0 7892 if type.isAny():
michael@0 7893 return CGGeneric("JS::Value")
michael@0 7894
michael@0 7895 if type.isObject():
michael@0 7896 return CGGeneric("JSObject*")
michael@0 7897
michael@0 7898 if type.isDictionary():
michael@0 7899 return CGGeneric("const %s&" % type.inner.identifier.name)
michael@0 7900
michael@0 7901 if not type.isPrimitive():
michael@0 7902 raise TypeError("Need native type for argument type '%s'" % str(type))
michael@0 7903
michael@0 7904 typeName = CGGeneric(builtinNames[type.tag()])
michael@0 7905 if type.nullable():
michael@0 7906 typeName = CGTemplatedType("Nullable", typeName, isReference=True)
michael@0 7907 return typeName
michael@0 7908
michael@0 7909
michael@0 7910 def getUnionTypeTemplateVars(unionType, type, descriptorProvider,
michael@0 7911 ownsMembers=False):
michael@0 7912 # For dictionaries and sequences we need to pass None as the failureCode
michael@0 7913 # for getJSToNativeConversionInfo.
michael@0 7914 # Also, for dictionaries we would need to handle conversion of
michael@0 7915 # null/undefined to the dictionary correctly.
michael@0 7916 if type.isSequence():
michael@0 7917 raise TypeError("Can't handle sequences in unions")
michael@0 7918
michael@0 7919 name = getUnionMemberName(type)
michael@0 7920
michael@0 7921 # By the time tryNextCode is invoked, we're guaranteed the union has been
michael@0 7922 # constructed as some type, since we've been trying to convert into the
michael@0 7923 # corresponding member.
michael@0 7924 prefix = "" if ownsMembers else "mUnion."
michael@0 7925 tryNextCode = ("%sDestroy%s();\n"
michael@0 7926 "tryNext = true;\n"
michael@0 7927 "return true;\n" % (prefix, name))
michael@0 7928 conversionInfo = getJSToNativeConversionInfo(
michael@0 7929 type, descriptorProvider, failureCode=tryNextCode,
michael@0 7930 isDefinitelyObject=not type.isDictionary(),
michael@0 7931 isMember=("OwningUnion" if ownsMembers else None),
michael@0 7932 sourceDescription="member of %s" % unionType)
michael@0 7933
michael@0 7934 ctorNeedsCx = conversionInfo.declArgs == "cx"
michael@0 7935 ctorArgs = "cx" if ctorNeedsCx else ""
michael@0 7936
michael@0 7937 # This is ugly, but UnionMember needs to call a constructor with no
michael@0 7938 # arguments so the type can't be const.
michael@0 7939 structType = conversionInfo.declType.define()
michael@0 7940 if structType.startswith("const "):
michael@0 7941 structType = structType[6:]
michael@0 7942 externalType = getUnionAccessorSignatureType(type, descriptorProvider).define()
michael@0 7943
michael@0 7944 if type.isObject():
michael@0 7945 if ownsMembers:
michael@0 7946 body = dedent("""
michael@0 7947 MOZ_ASSERT(mType == eUninitialized);
michael@0 7948 mValue.mObject.SetValue(obj);
michael@0 7949 mType = eObject;
michael@0 7950 """)
michael@0 7951 else:
michael@0 7952 body = dedent("""
michael@0 7953 MOZ_ASSERT(mUnion.mType == mUnion.eUninitialized);
michael@0 7954 mUnion.mValue.mObject.SetValue(cx, obj);
michael@0 7955 mUnion.mType = mUnion.eObject;
michael@0 7956 """)
michael@0 7957 setter = ClassMethod("SetToObject", "void",
michael@0 7958 [Argument("JSContext*", "cx"),
michael@0 7959 Argument("JSObject*", "obj")],
michael@0 7960 inline=True, bodyInHeader=True,
michael@0 7961 body=body)
michael@0 7962
michael@0 7963 else:
michael@0 7964 # Important: we need to not have our declName involve
michael@0 7965 # maybe-GCing operations.
michael@0 7966 jsConversion = string.Template(conversionInfo.template).substitute({
michael@0 7967 "val": "value",
michael@0 7968 "mutableVal": "pvalue",
michael@0 7969 "declName": "memberSlot",
michael@0 7970 "holderName": "m" + name + "Holder",
michael@0 7971 })
michael@0 7972 jsConversion = fill(
michael@0 7973 """
michael@0 7974 tryNext = false;
michael@0 7975 { // scope for memberSlot
michael@0 7976 ${structType}& memberSlot = RawSetAs${name}(${ctorArgs});
michael@0 7977 $*{jsConversion}
michael@0 7978 }
michael@0 7979 return true;
michael@0 7980 """,
michael@0 7981 structType=structType,
michael@0 7982 name=name,
michael@0 7983 ctorArgs=ctorArgs,
michael@0 7984 jsConversion=jsConversion)
michael@0 7985
michael@0 7986 setter = ClassMethod("TrySetTo" + name, "bool",
michael@0 7987 [Argument("JSContext*", "cx"),
michael@0 7988 Argument("JS::Handle<JS::Value>", "value"),
michael@0 7989 Argument("JS::MutableHandle<JS::Value>", "pvalue"),
michael@0 7990 Argument("bool&", "tryNext")],
michael@0 7991 inline=not ownsMembers,
michael@0 7992 bodyInHeader=not ownsMembers,
michael@0 7993 body=jsConversion)
michael@0 7994
michael@0 7995 return {
michael@0 7996 "name": name,
michael@0 7997 "structType": structType,
michael@0 7998 "externalType": externalType,
michael@0 7999 "setter": setter,
michael@0 8000 "holderType": conversionInfo.holderType.define() if conversionInfo.holderType else None,
michael@0 8001 "ctorArgs": ctorArgs,
michael@0 8002 "ctorArgList": [Argument("JSContext*", "cx")] if ctorNeedsCx else []
michael@0 8003 }
michael@0 8004
michael@0 8005
michael@0 8006 class CGUnionStruct(CGThing):
michael@0 8007 def __init__(self, type, descriptorProvider, ownsMembers=False):
michael@0 8008 CGThing.__init__(self)
michael@0 8009 self.type = type.unroll()
michael@0 8010 self.descriptorProvider = descriptorProvider
michael@0 8011 self.ownsMembers = ownsMembers
michael@0 8012 self.struct = self.getStruct()
michael@0 8013
michael@0 8014 def declare(self):
michael@0 8015 return self.struct.declare()
michael@0 8016
michael@0 8017 def define(self):
michael@0 8018 return self.struct.define()
michael@0 8019
michael@0 8020 def getStruct(self):
michael@0 8021
michael@0 8022 members = [ClassMember("mType", "Type", body="eUninitialized"),
michael@0 8023 ClassMember("mValue", "Value")]
michael@0 8024 ctor = ClassConstructor([], bodyInHeader=True, visibility="public",
michael@0 8025 explicit=True)
michael@0 8026
michael@0 8027 methods = []
michael@0 8028 enumValues = ["eUninitialized"]
michael@0 8029 toJSValCases = [CGCase("eUninitialized", CGGeneric("return false;\n"))]
michael@0 8030 destructorCases = [CGCase("eUninitialized", None)]
michael@0 8031 assignmentCases = [
michael@0 8032 CGCase("eUninitialized",
michael@0 8033 CGGeneric('MOZ_ASSERT(mType == eUninitialized,\n'
michael@0 8034 ' "We need to destroy ourselves?");\n'))]
michael@0 8035 traceCases = []
michael@0 8036 unionValues = []
michael@0 8037 if self.type.hasNullableType:
michael@0 8038 enumValues.append("eNull")
michael@0 8039 methods.append(ClassMethod("IsNull", "bool", [], const=True, inline=True,
michael@0 8040 body="return mType == eNull;\n",
michael@0 8041 bodyInHeader=True))
michael@0 8042 methods.append(ClassMethod("SetNull", "void", [], inline=True,
michael@0 8043 body=("Uninit();\n"
michael@0 8044 "mType = eNull;\n"),
michael@0 8045 bodyInHeader=True))
michael@0 8046 destructorCases.append(CGCase("eNull", None))
michael@0 8047 assignmentCases.append(CGCase("eNull",
michael@0 8048 CGGeneric("MOZ_ASSERT(mType == eUninitialized);\n"
michael@0 8049 "mType = eNull;\n")))
michael@0 8050 toJSValCases.append(CGCase("eNull", CGGeneric("rval.setNull();\n"
michael@0 8051 "return true;\n")))
michael@0 8052
michael@0 8053 hasObjectType = any(t.isObject() for t in self.type.flatMemberTypes)
michael@0 8054 for t in self.type.flatMemberTypes:
michael@0 8055 vars = getUnionTypeTemplateVars(self.type,
michael@0 8056 t, self.descriptorProvider,
michael@0 8057 ownsMembers=self.ownsMembers)
michael@0 8058 if vars["name"] != "Object" or self.ownsMembers:
michael@0 8059 body = fill(
michael@0 8060 """
michael@0 8061 if (mType == e${name}) {
michael@0 8062 return mValue.m${name}.Value();
michael@0 8063 }
michael@0 8064 %s
michael@0 8065 mType = e${name};
michael@0 8066 return mValue.m${name}.SetValue(${ctorArgs});
michael@0 8067 """,
michael@0 8068 **vars)
michael@0 8069
michael@0 8070 # bodyInHeader must be false for return values because they own
michael@0 8071 # their union members and we don't want include headers in
michael@0 8072 # UnionTypes.h just to call Addref/Release
michael@0 8073 methods.append(ClassMethod(
michael@0 8074 "RawSetAs" + vars["name"],
michael@0 8075 vars["structType"] + "&",
michael@0 8076 vars["ctorArgList"],
michael@0 8077 bodyInHeader=not self.ownsMembers,
michael@0 8078 body=body % "MOZ_ASSERT(mType == eUninitialized);"))
michael@0 8079 uninit = "Uninit();"
michael@0 8080 if hasObjectType and not self.ownsMembers:
michael@0 8081 uninit = 'MOZ_ASSERT(mType != eObject, "This will not play well with Rooted");\n' + uninit
michael@0 8082 methods.append(ClassMethod(
michael@0 8083 "SetAs" + vars["name"],
michael@0 8084 vars["structType"] + "&",
michael@0 8085 vars["ctorArgList"],
michael@0 8086 bodyInHeader=not self.ownsMembers,
michael@0 8087 body=body % uninit))
michael@0 8088 if self.ownsMembers:
michael@0 8089 methods.append(vars["setter"])
michael@0 8090 if t.isString():
michael@0 8091 methods.append(
michael@0 8092 ClassMethod("SetStringData", "void",
michael@0 8093 [Argument("const nsString::char_type*", "aData"),
michael@0 8094 Argument("nsString::size_type", "aLength")],
michael@0 8095 inline=True, bodyInHeader=True,
michael@0 8096 body="RawSetAsString().Assign(aData, aLength);\n"))
michael@0 8097
michael@0 8098 body = fill(
michael@0 8099 """
michael@0 8100 MOZ_ASSERT(Is${name}(), "Wrong type!");
michael@0 8101 mValue.m${name}.Destroy();
michael@0 8102 mType = eUninitialized;
michael@0 8103 """,
michael@0 8104 **vars)
michael@0 8105 methods.append(ClassMethod("Destroy" + vars["name"],
michael@0 8106 "void",
michael@0 8107 [],
michael@0 8108 visibility="private",
michael@0 8109 bodyInHeader=not self.ownsMembers,
michael@0 8110 body=body))
michael@0 8111
michael@0 8112 body = fill("return mType == e${name};\n", **vars)
michael@0 8113 methods.append(ClassMethod("Is" + vars["name"],
michael@0 8114 "bool",
michael@0 8115 [],
michael@0 8116 const=True,
michael@0 8117 bodyInHeader=True,
michael@0 8118 body=body))
michael@0 8119
michael@0 8120 body = fill(
michael@0 8121 """
michael@0 8122 MOZ_ASSERT(Is${name}(), "Wrong type!");
michael@0 8123 return const_cast<${structType}&>(mValue.m${name}.Value());
michael@0 8124 """,
michael@0 8125 **vars)
michael@0 8126 if self.ownsMembers:
michael@0 8127 getterReturnType = "%s&" % vars["structType"]
michael@0 8128 else:
michael@0 8129 getterReturnType = vars["externalType"]
michael@0 8130 methods.append(ClassMethod("GetAs" + vars["name"],
michael@0 8131 getterReturnType,
michael@0 8132 [],
michael@0 8133 const=True,
michael@0 8134 bodyInHeader=True,
michael@0 8135 body=body))
michael@0 8136
michael@0 8137 unionValues.append(
michael@0 8138 fill("UnionMember<${structType} > m${name}", **vars))
michael@0 8139 enumValues.append("e" + vars["name"])
michael@0 8140
michael@0 8141 toJSValCases.append(
michael@0 8142 CGCase("e" + vars["name"],
michael@0 8143 self.getConversionToJS(vars, t)))
michael@0 8144 destructorCases.append(
michael@0 8145 CGCase("e" + vars["name"],
michael@0 8146 CGGeneric("Destroy%s();\n" % vars["name"])))
michael@0 8147 assignmentCases.append(
michael@0 8148 CGCase("e" + vars["name"],
michael@0 8149 CGGeneric("SetAs%s() = aOther.GetAs%s();\n" %
michael@0 8150 (vars["name"], vars["name"]))))
michael@0 8151 if self.ownsMembers and typeNeedsRooting(t):
michael@0 8152 if t.isObject():
michael@0 8153 traceCases.append(
michael@0 8154 CGCase("e" + vars["name"],
michael@0 8155 CGGeneric('JS_CallObjectTracer(trc, %s, "%s");\n' %
michael@0 8156 ("&mValue.m" + vars["name"] + ".Value()",
michael@0 8157 "mValue.m" + vars["name"]))))
michael@0 8158 elif t.isDictionary():
michael@0 8159 traceCases.append(
michael@0 8160 CGCase("e" + vars["name"],
michael@0 8161 CGGeneric("mValue.m%s.Value().TraceDictionary(trc);\n" %
michael@0 8162 vars["name"])))
michael@0 8163 else:
michael@0 8164 assert t.isSpiderMonkeyInterface()
michael@0 8165 traceCases.append(
michael@0 8166 CGCase("e" + vars["name"],
michael@0 8167 CGGeneric("mValue.m%s.Value().TraceSelf(trc);\n" %
michael@0 8168 vars["name"])))
michael@0 8169
michael@0 8170 dtor = CGSwitch("mType", destructorCases).define()
michael@0 8171
michael@0 8172 methods.append(ClassMethod("Uninit", "void", [],
michael@0 8173 visibility="private", body=dtor,
michael@0 8174 bodyInHeader=not self.ownsMembers,
michael@0 8175 inline=not self.ownsMembers))
michael@0 8176
michael@0 8177 methods.append(
michael@0 8178 ClassMethod(
michael@0 8179 "ToJSVal",
michael@0 8180 "bool",
michael@0 8181 [
michael@0 8182 Argument("JSContext*", "cx"),
michael@0 8183 Argument("JS::Handle<JSObject*>", "scopeObj"),
michael@0 8184 Argument("JS::MutableHandle<JS::Value>", "rval")
michael@0 8185 ],
michael@0 8186 body=CGSwitch("mType", toJSValCases,
michael@0 8187 default=CGGeneric("return false;\n")).define(),
michael@0 8188 const=True))
michael@0 8189
michael@0 8190 constructors = [ctor]
michael@0 8191 selfName = CGUnionStruct.unionTypeName(self.type, self.ownsMembers)
michael@0 8192 if self.ownsMembers:
michael@0 8193 if traceCases:
michael@0 8194 # BOGUS blank line in default case
michael@0 8195 traceBody = CGSwitch("mType", traceCases,
michael@0 8196 default=CGGeneric("\n")).define()
michael@0 8197 else:
michael@0 8198 # BOGUS blank line in method
michael@0 8199 traceBody = "\n"
michael@0 8200 methods.append(ClassMethod("TraceUnion", "void",
michael@0 8201 [Argument("JSTracer*", "trc")],
michael@0 8202 body=traceBody))
michael@0 8203 if CGUnionStruct.isUnionCopyConstructible(self.type):
michael@0 8204 constructors.append(
michael@0 8205 ClassConstructor(
michael@0 8206 [Argument("const %s&" % selfName, "aOther")],
michael@0 8207 bodyInHeader=True,
michael@0 8208 visibility="public",
michael@0 8209 explicit=True,
michael@0 8210 body="*this = aOther;\n"))
michael@0 8211 methods.append(ClassMethod(
michael@0 8212 "operator=", "void",
michael@0 8213 [Argument("const %s&" % selfName, "aOther")],
michael@0 8214 body=CGSwitch("aOther.mType", assignmentCases).define()))
michael@0 8215 disallowCopyConstruction = False
michael@0 8216 else:
michael@0 8217 disallowCopyConstruction = True
michael@0 8218 else:
michael@0 8219 disallowCopyConstruction = True
michael@0 8220
michael@0 8221 friend = " friend class %sArgument;\n" % str(self.type) if not self.ownsMembers else ""
michael@0 8222 bases = [ClassBase("AllOwningUnionBase")] if self.ownsMembers else []
michael@0 8223 return CGClass(selfName,
michael@0 8224 bases=bases,
michael@0 8225 members=members,
michael@0 8226 constructors=constructors,
michael@0 8227 methods=methods,
michael@0 8228 disallowCopyConstruction=disallowCopyConstruction,
michael@0 8229 extradeclarations=friend,
michael@0 8230 destructor=ClassDestructor(visibility="public",
michael@0 8231 body="Uninit();",
michael@0 8232 bodyInHeader=True),
michael@0 8233 enums=[ClassEnum("Type", enumValues, visibility="private")],
michael@0 8234 unions=[ClassUnion("Value", unionValues, visibility="private")])
michael@0 8235
michael@0 8236 def getConversionToJS(self, templateVars, type):
michael@0 8237 assert not type.nullable() # flatMemberTypes never has nullable types
michael@0 8238 val = "mValue.m%(name)s.Value()" % templateVars
michael@0 8239 wrapCode = wrapForType(
michael@0 8240 type, self.descriptorProvider,
michael@0 8241 {
michael@0 8242 "jsvalRef": "rval",
michael@0 8243 "jsvalHandle": "rval",
michael@0 8244 "obj": "scopeObj",
michael@0 8245 "result": val,
michael@0 8246 "typedArraysAreStructs": True
michael@0 8247 })
michael@0 8248 return CGGeneric(wrapCode)
michael@0 8249
michael@0 8250 @staticmethod
michael@0 8251 def isUnionCopyConstructible(type):
michael@0 8252 return all(isTypeCopyConstructible(t) for t in type.flatMemberTypes)
michael@0 8253
michael@0 8254 @staticmethod
michael@0 8255 def unionTypeName(type, ownsMembers):
michael@0 8256 """
michael@0 8257 Returns a string name for this known union type.
michael@0 8258 """
michael@0 8259 assert type.isUnion() and not type.nullable()
michael@0 8260 return ("Owning" if ownsMembers else "") + type.name
michael@0 8261
michael@0 8262 @staticmethod
michael@0 8263 def unionTypeDecl(type, ownsMembers):
michael@0 8264 """
michael@0 8265 Returns a string for declaring this possibly-nullable union type.
michael@0 8266 """
michael@0 8267 assert type.isUnion()
michael@0 8268 nullable = type.nullable()
michael@0 8269 if nullable:
michael@0 8270 type = type.inner
michael@0 8271 decl = CGGeneric(CGUnionStruct.unionTypeName(type, ownsMembers))
michael@0 8272 if nullable:
michael@0 8273 decl = CGTemplatedType("Nullable", decl)
michael@0 8274 return decl.define()
michael@0 8275
michael@0 8276
michael@0 8277 class CGUnionConversionStruct(CGThing):
michael@0 8278 def __init__(self, type, descriptorProvider):
michael@0 8279 CGThing.__init__(self)
michael@0 8280 self.type = type.unroll()
michael@0 8281 self.descriptorProvider = descriptorProvider
michael@0 8282
michael@0 8283 def declare(self):
michael@0 8284
michael@0 8285 structName = str(self.type)
michael@0 8286 members = [ClassMember("mUnion", structName + "&",
michael@0 8287 body="const_cast<%s&>(aUnion)" % structName)]
michael@0 8288 # Argument needs to be a const ref because that's all Maybe<> allows
michael@0 8289 ctor = ClassConstructor([Argument("const %s&" % structName, "aUnion")],
michael@0 8290 bodyInHeader=True,
michael@0 8291 visibility="public",
michael@0 8292 explicit=True)
michael@0 8293 methods = []
michael@0 8294
michael@0 8295 if self.type.hasNullableType:
michael@0 8296 methods.append(ClassMethod("SetNull", "bool", [],
michael@0 8297 body=("MOZ_ASSERT(mUnion.mType == mUnion.eUninitialized);\n"
michael@0 8298 "mUnion.mType = mUnion.eNull;\n"
michael@0 8299 "return true;\n"),
michael@0 8300 inline=True, bodyInHeader=True))
michael@0 8301
michael@0 8302 for t in self.type.flatMemberTypes:
michael@0 8303 vars = getUnionTypeTemplateVars(self.type,
michael@0 8304 t, self.descriptorProvider)
michael@0 8305 methods.append(vars["setter"])
michael@0 8306 if vars["name"] != "Object":
michael@0 8307 body = fill(
michael@0 8308 """
michael@0 8309 MOZ_ASSERT(mUnion.mType == mUnion.eUninitialized);
michael@0 8310 mUnion.mType = mUnion.e${name};
michael@0 8311 return mUnion.mValue.m${name}.SetValue(${ctorArgs});
michael@0 8312 """,
michael@0 8313 **vars)
michael@0 8314 methods.append(ClassMethod("RawSetAs" + vars["name"],
michael@0 8315 vars["structType"] + "&",
michael@0 8316 vars["ctorArgList"],
michael@0 8317 bodyInHeader=True,
michael@0 8318 body=body,
michael@0 8319 visibility="private"))
michael@0 8320 if t.isString():
michael@0 8321 methods.append(
michael@0 8322 ClassMethod("SetStringData", "void",
michael@0 8323 [Argument("const nsDependentString::char_type*", "aData"),
michael@0 8324 Argument("nsDependentString::size_type", "aLength")],
michael@0 8325 inline=True, bodyInHeader=True,
michael@0 8326 body="RawSetAsString().SetData(aData, aLength);\n"))
michael@0 8327
michael@0 8328 if vars["holderType"] is not None:
michael@0 8329 members.append(ClassMember("m%sHolder" % vars["name"],
michael@0 8330 vars["holderType"]))
michael@0 8331
michael@0 8332 return CGClass(structName + "Argument",
michael@0 8333 members=members,
michael@0 8334 constructors=[ctor],
michael@0 8335 methods=methods,
michael@0 8336 disallowCopyConstruction=True).declare()
michael@0 8337
michael@0 8338 def define(self):
michael@0 8339 return "\n"
michael@0 8340
michael@0 8341
michael@0 8342 class ClassItem:
michael@0 8343 """ Use with CGClass """
michael@0 8344 def __init__(self, name, visibility):
michael@0 8345 self.name = name
michael@0 8346 self.visibility = visibility
michael@0 8347
michael@0 8348 def declare(self, cgClass):
michael@0 8349 assert False
michael@0 8350
michael@0 8351 def define(self, cgClass):
michael@0 8352 assert False
michael@0 8353
michael@0 8354
michael@0 8355 class ClassBase(ClassItem):
michael@0 8356 def __init__(self, name, visibility='public'):
michael@0 8357 ClassItem.__init__(self, name, visibility)
michael@0 8358
michael@0 8359 def declare(self, cgClass):
michael@0 8360 return '%s %s' % (self.visibility, self.name)
michael@0 8361
michael@0 8362 def define(self, cgClass):
michael@0 8363 # Only in the header
michael@0 8364 return ''
michael@0 8365
michael@0 8366
michael@0 8367 class ClassMethod(ClassItem):
michael@0 8368 def __init__(self, name, returnType, args, inline=False, static=False,
michael@0 8369 virtual=False, const=False, bodyInHeader=False,
michael@0 8370 templateArgs=None, visibility='public', body=None,
michael@0 8371 breakAfterReturnDecl="\n",
michael@0 8372 breakAfterSelf="\n", override=False):
michael@0 8373 """
michael@0 8374 override indicates whether to flag the method as MOZ_OVERRIDE
michael@0 8375 """
michael@0 8376 assert not override or virtual
michael@0 8377 assert not (override and static)
michael@0 8378 self.returnType = returnType
michael@0 8379 self.args = args
michael@0 8380 self.inline = inline or bodyInHeader
michael@0 8381 self.static = static
michael@0 8382 self.virtual = virtual
michael@0 8383 self.const = const
michael@0 8384 self.bodyInHeader = bodyInHeader
michael@0 8385 self.templateArgs = templateArgs
michael@0 8386 self.body = body
michael@0 8387 self.breakAfterReturnDecl = breakAfterReturnDecl
michael@0 8388 self.breakAfterSelf = breakAfterSelf
michael@0 8389 self.override = override
michael@0 8390 ClassItem.__init__(self, name, visibility)
michael@0 8391
michael@0 8392 def getDecorators(self, declaring):
michael@0 8393 decorators = []
michael@0 8394 if self.inline:
michael@0 8395 decorators.append('inline')
michael@0 8396 if declaring:
michael@0 8397 if self.static:
michael@0 8398 decorators.append('static')
michael@0 8399 if self.virtual:
michael@0 8400 decorators.append('virtual')
michael@0 8401 if decorators:
michael@0 8402 return ' '.join(decorators) + ' '
michael@0 8403 return ''
michael@0 8404
michael@0 8405 def getBody(self):
michael@0 8406 # Override me or pass a string to constructor
michael@0 8407 assert self.body is not None
michael@0 8408 return self.body
michael@0 8409
michael@0 8410 def declare(self, cgClass):
michael@0 8411 templateClause = ('template <%s>\n' % ', '.join(self.templateArgs)
michael@0 8412 if self.bodyInHeader and self.templateArgs else '')
michael@0 8413 args = ', '.join([a.declare() for a in self.args])
michael@0 8414 if self.bodyInHeader:
michael@0 8415 body = indent(self.getBody())
michael@0 8416 body = '\n{\n' + body + '}\n'
michael@0 8417 else:
michael@0 8418 body = ';\n'
michael@0 8419
michael@0 8420 return fill(
michael@0 8421 "${templateClause}${decorators}${returnType}${breakAfterReturnDecl}"
michael@0 8422 "${name}(${args})${const}${override}${body}"
michael@0 8423 "${breakAfterSelf}",
michael@0 8424 templateClause=templateClause,
michael@0 8425 decorators=self.getDecorators(True),
michael@0 8426 returnType=self.returnType,
michael@0 8427 breakAfterReturnDecl=self.breakAfterReturnDecl,
michael@0 8428 name=self.name,
michael@0 8429 args=args,
michael@0 8430 const=' const' if self.const else '',
michael@0 8431 override=' MOZ_OVERRIDE' if self.override else '',
michael@0 8432 body=body,
michael@0 8433 breakAfterSelf=self.breakAfterSelf)
michael@0 8434
michael@0 8435 def define(self, cgClass):
michael@0 8436 if self.bodyInHeader:
michael@0 8437 return ''
michael@0 8438
michael@0 8439 templateArgs = cgClass.templateArgs
michael@0 8440 if templateArgs:
michael@0 8441 if cgClass.templateSpecialization:
michael@0 8442 templateArgs = \
michael@0 8443 templateArgs[len(cgClass.templateSpecialization):]
michael@0 8444
michael@0 8445 if templateArgs:
michael@0 8446 templateClause = \
michael@0 8447 'template <%s>\n' % ', '.join([str(a) for a in templateArgs])
michael@0 8448 else:
michael@0 8449 templateClause = ''
michael@0 8450
michael@0 8451 return fill(
michael@0 8452 """
michael@0 8453 ${templateClause}${decorators}${returnType}
michael@0 8454 ${className}::${name}(${args})${const}
michael@0 8455 {
michael@0 8456 $*{body}
michael@0 8457 }
michael@0 8458 """,
michael@0 8459 templateClause=templateClause,
michael@0 8460 decorators=self.getDecorators(False),
michael@0 8461 returnType=self.returnType,
michael@0 8462 className=cgClass.getNameString(),
michael@0 8463 name=self.name,
michael@0 8464 args=', '.join([a.define() for a in self.args]),
michael@0 8465 const=' const' if self.const else '',
michael@0 8466 body=self.getBody())
michael@0 8467
michael@0 8468
michael@0 8469 class ClassUsingDeclaration(ClassItem):
michael@0 8470 """
michael@0 8471 Used for importing a name from a base class into a CGClass
michael@0 8472
michael@0 8473 baseClass is the name of the base class to import the name from
michael@0 8474
michael@0 8475 name is the name to import
michael@0 8476
michael@0 8477 visibility determines the visibility of the name (public,
michael@0 8478 protected, private), defaults to public.
michael@0 8479 """
michael@0 8480 def __init__(self, baseClass, name, visibility='public'):
michael@0 8481 self.baseClass = baseClass
michael@0 8482 ClassItem.__init__(self, name, visibility)
michael@0 8483
michael@0 8484 def declare(self, cgClass):
michael@0 8485 return "using %s::%s;\n\n" % (self.baseClass, self.name)
michael@0 8486
michael@0 8487 def define(self, cgClass):
michael@0 8488 return ''
michael@0 8489
michael@0 8490
michael@0 8491 class ClassConstructor(ClassItem):
michael@0 8492 """
michael@0 8493 Used for adding a constructor to a CGClass.
michael@0 8494
michael@0 8495 args is a list of Argument objects that are the arguments taken by the
michael@0 8496 constructor.
michael@0 8497
michael@0 8498 inline should be True if the constructor should be marked inline.
michael@0 8499
michael@0 8500 bodyInHeader should be True if the body should be placed in the class
michael@0 8501 declaration in the header.
michael@0 8502
michael@0 8503 visibility determines the visibility of the constructor (public,
michael@0 8504 protected, private), defaults to private.
michael@0 8505
michael@0 8506 explicit should be True if the constructor should be marked explicit.
michael@0 8507
michael@0 8508 baseConstructors is a list of strings containing calls to base constructors,
michael@0 8509 defaults to None.
michael@0 8510
michael@0 8511 body contains a string with the code for the constructor, defaults to empty.
michael@0 8512 """
michael@0 8513 def __init__(self, args, inline=False, bodyInHeader=False,
michael@0 8514 visibility="private", explicit=False, baseConstructors=None,
michael@0 8515 body=""):
michael@0 8516 self.args = args
michael@0 8517 self.inline = inline or bodyInHeader
michael@0 8518 self.bodyInHeader = bodyInHeader
michael@0 8519 self.explicit = explicit
michael@0 8520 self.baseConstructors = baseConstructors or []
michael@0 8521 self.body = body
michael@0 8522 ClassItem.__init__(self, None, visibility)
michael@0 8523
michael@0 8524 def getDecorators(self, declaring):
michael@0 8525 decorators = []
michael@0 8526 if self.explicit:
michael@0 8527 decorators.append('explicit')
michael@0 8528 if self.inline and declaring:
michael@0 8529 decorators.append('inline')
michael@0 8530 if decorators:
michael@0 8531 return ' '.join(decorators) + ' '
michael@0 8532 return ''
michael@0 8533
michael@0 8534 def getInitializationList(self, cgClass):
michael@0 8535 items = [str(c) for c in self.baseConstructors]
michael@0 8536 for m in cgClass.members:
michael@0 8537 if not m.static:
michael@0 8538 initialize = m.body
michael@0 8539 if initialize:
michael@0 8540 items.append(m.name + "(" + initialize + ")")
michael@0 8541
michael@0 8542 if len(items) > 0:
michael@0 8543 return '\n : ' + ',\n '.join(items)
michael@0 8544 return ''
michael@0 8545
michael@0 8546 def getBody(self):
michael@0 8547 return self.body
michael@0 8548
michael@0 8549 def declare(self, cgClass):
michael@0 8550 args = ', '.join([a.declare() for a in self.args])
michael@0 8551 if self.bodyInHeader:
michael@0 8552 body = self.getInitializationList(cgClass) + '\n{\n' + indent(self.getBody()) + '}\n'
michael@0 8553 else:
michael@0 8554 body = ';\n'
michael@0 8555
michael@0 8556 return fill(
michael@0 8557 "${decorators}${className}(${args})${body}\n",
michael@0 8558 decorators=self.getDecorators(True),
michael@0 8559 className=cgClass.getNameString(),
michael@0 8560 args=args,
michael@0 8561 body=body)
michael@0 8562
michael@0 8563 def define(self, cgClass):
michael@0 8564 if self.bodyInHeader:
michael@0 8565 return ''
michael@0 8566
michael@0 8567 return fill(
michael@0 8568 """
michael@0 8569 ${decorators}
michael@0 8570 ${className}::${className}(${args})${initializationList}
michael@0 8571 {
michael@0 8572 $*{body}
michael@0 8573 }
michael@0 8574 """,
michael@0 8575 decorators=self.getDecorators(False),
michael@0 8576 className=cgClass.getNameString(),
michael@0 8577 args=', '.join([a.define() for a in self.args]),
michael@0 8578 initializationList=self.getInitializationList(cgClass),
michael@0 8579 body=self.getBody())
michael@0 8580
michael@0 8581
michael@0 8582 class ClassDestructor(ClassItem):
michael@0 8583 """
michael@0 8584 Used for adding a destructor to a CGClass.
michael@0 8585
michael@0 8586 inline should be True if the destructor should be marked inline.
michael@0 8587
michael@0 8588 bodyInHeader should be True if the body should be placed in the class
michael@0 8589 declaration in the header.
michael@0 8590
michael@0 8591 visibility determines the visibility of the destructor (public,
michael@0 8592 protected, private), defaults to private.
michael@0 8593
michael@0 8594 body contains a string with the code for the destructor, defaults to empty.
michael@0 8595
michael@0 8596 virtual determines whether the destructor is virtual, defaults to False.
michael@0 8597 """
michael@0 8598 def __init__(self, inline=False, bodyInHeader=False,
michael@0 8599 visibility="private", body='', virtual=False):
michael@0 8600 self.inline = inline or bodyInHeader
michael@0 8601 self.bodyInHeader = bodyInHeader
michael@0 8602 self.body = body
michael@0 8603 self.virtual = virtual
michael@0 8604 ClassItem.__init__(self, None, visibility)
michael@0 8605
michael@0 8606 def getDecorators(self, declaring):
michael@0 8607 decorators = []
michael@0 8608 if self.virtual and declaring:
michael@0 8609 decorators.append('virtual')
michael@0 8610 if self.inline and declaring:
michael@0 8611 decorators.append('inline')
michael@0 8612 if decorators:
michael@0 8613 return ' '.join(decorators) + ' '
michael@0 8614 return ''
michael@0 8615
michael@0 8616 def getBody(self):
michael@0 8617 return self.body
michael@0 8618
michael@0 8619 def declare(self, cgClass):
michael@0 8620 if self.bodyInHeader:
michael@0 8621 body = '\n{\n' + indent(self.getBody()) + '}\n'
michael@0 8622 else:
michael@0 8623 body = ';\n'
michael@0 8624
michael@0 8625 return fill(
michael@0 8626 "${decorators}~${className}()${body}\n",
michael@0 8627 decorators=self.getDecorators(True),
michael@0 8628 className=cgClass.getNameString(),
michael@0 8629 body=body)
michael@0 8630
michael@0 8631 def define(self, cgClass):
michael@0 8632 if self.bodyInHeader:
michael@0 8633 return ''
michael@0 8634 return fill(
michael@0 8635 """
michael@0 8636 ${decorators}
michael@0 8637 ${className}::~${className}()
michael@0 8638 {
michael@0 8639 $*{body}
michael@0 8640 }
michael@0 8641 """,
michael@0 8642 decorators=self.getDecorators(False),
michael@0 8643 className=cgClass.getNameString(),
michael@0 8644 body=self.getBody() or "\n") # BOGUS extra blank line if empty
michael@0 8645
michael@0 8646
michael@0 8647 class ClassMember(ClassItem):
michael@0 8648 def __init__(self, name, type, visibility="private", static=False,
michael@0 8649 body=None):
michael@0 8650 self.type = type
michael@0 8651 self.static = static
michael@0 8652 self.body = body
michael@0 8653 ClassItem.__init__(self, name, visibility)
michael@0 8654
michael@0 8655 def declare(self, cgClass):
michael@0 8656 return '%s%s %s;\n' % ('static ' if self.static else '', self.type,
michael@0 8657 self.name)
michael@0 8658
michael@0 8659 def define(self, cgClass):
michael@0 8660 if not self.static:
michael@0 8661 return ''
michael@0 8662 if self.body:
michael@0 8663 body = " = " + self.body
michael@0 8664 else:
michael@0 8665 body = ""
michael@0 8666 return '%s %s::%s%s;\n' % (self.type, cgClass.getNameString(),
michael@0 8667 self.name, body)
michael@0 8668
michael@0 8669
michael@0 8670 class ClassTypedef(ClassItem):
michael@0 8671 def __init__(self, name, type, visibility="public"):
michael@0 8672 self.type = type
michael@0 8673 ClassItem.__init__(self, name, visibility)
michael@0 8674
michael@0 8675 def declare(self, cgClass):
michael@0 8676 return 'typedef %s %s;\n' % (self.type, self.name)
michael@0 8677
michael@0 8678 def define(self, cgClass):
michael@0 8679 # Only goes in the header
michael@0 8680 return ''
michael@0 8681
michael@0 8682
michael@0 8683 class ClassEnum(ClassItem):
michael@0 8684 def __init__(self, name, entries, values=None, visibility="public"):
michael@0 8685 self.entries = entries
michael@0 8686 self.values = values
michael@0 8687 ClassItem.__init__(self, name, visibility)
michael@0 8688
michael@0 8689 def declare(self, cgClass):
michael@0 8690 entries = []
michael@0 8691 for i in range(0, len(self.entries)):
michael@0 8692 if not self.values or i >= len(self.values):
michael@0 8693 entry = '%s' % self.entries[i]
michael@0 8694 else:
michael@0 8695 entry = '%s = %s' % (self.entries[i], self.values[i])
michael@0 8696 entries.append(entry)
michael@0 8697 name = '' if not self.name else ' ' + self.name
michael@0 8698 return 'enum%s\n{\n%s\n};\n' % (name, indent(',\n'.join(entries)))
michael@0 8699
michael@0 8700 def define(self, cgClass):
michael@0 8701 # Only goes in the header
michael@0 8702 return ''
michael@0 8703
michael@0 8704
michael@0 8705 class ClassUnion(ClassItem):
michael@0 8706 def __init__(self, name, entries, visibility="public"):
michael@0 8707 self.entries = [entry + ";\n" for entry in entries]
michael@0 8708 ClassItem.__init__(self, name, visibility)
michael@0 8709
michael@0 8710 def declare(self, cgClass):
michael@0 8711 return "union %s\n{\n%s\n};\n" % (self.name, indent(''.join(self.entries)))
michael@0 8712
michael@0 8713 def define(self, cgClass):
michael@0 8714 # Only goes in the header
michael@0 8715 return ''
michael@0 8716
michael@0 8717
michael@0 8718 class CGClass(CGThing):
michael@0 8719 def __init__(self, name, bases=[], members=[], constructors=[],
michael@0 8720 destructor=None, methods=[],
michael@0 8721 typedefs=[], enums=[], unions=[], templateArgs=[],
michael@0 8722 templateSpecialization=[], isStruct=False,
michael@0 8723 disallowCopyConstruction=False, indent='',
michael@0 8724 decorators='',
michael@0 8725 extradeclarations='',
michael@0 8726 extradefinitions=''):
michael@0 8727 CGThing.__init__(self)
michael@0 8728 self.name = name
michael@0 8729 self.bases = bases
michael@0 8730 self.members = members
michael@0 8731 self.constructors = constructors
michael@0 8732 # We store our single destructor in a list, since all of our
michael@0 8733 # code wants lists of members.
michael@0 8734 self.destructors = [destructor] if destructor else []
michael@0 8735 self.methods = methods
michael@0 8736 self.typedefs = typedefs
michael@0 8737 self.enums = enums
michael@0 8738 self.unions = unions
michael@0 8739 self.templateArgs = templateArgs
michael@0 8740 self.templateSpecialization = templateSpecialization
michael@0 8741 self.isStruct = isStruct
michael@0 8742 self.disallowCopyConstruction = disallowCopyConstruction
michael@0 8743 self.indent = indent
michael@0 8744 self.defaultVisibility = 'public' if isStruct else 'private'
michael@0 8745 self.decorators = decorators
michael@0 8746 self.extradeclarations = extradeclarations
michael@0 8747 self.extradefinitions = extradefinitions
michael@0 8748
michael@0 8749 def getNameString(self):
michael@0 8750 className = self.name
michael@0 8751 if self.templateSpecialization:
michael@0 8752 className += '<%s>' % ', '.join([str(a)
michael@0 8753 for a in self.templateSpecialization])
michael@0 8754 return className
michael@0 8755
michael@0 8756 def declare(self):
michael@0 8757 result = ''
michael@0 8758 if self.templateArgs:
michael@0 8759 templateArgs = [a.declare() for a in self.templateArgs]
michael@0 8760 templateArgs = templateArgs[len(self.templateSpecialization):]
michael@0 8761 result += ('template <%s>\n' %
michael@0 8762 ','.join([str(a) for a in templateArgs]))
michael@0 8763
michael@0 8764 type = 'struct' if self.isStruct else 'class'
michael@0 8765
michael@0 8766 if self.templateSpecialization:
michael@0 8767 specialization = \
michael@0 8768 '<%s>' % ', '.join([str(a) for a in self.templateSpecialization])
michael@0 8769 else:
michael@0 8770 specialization = ''
michael@0 8771
michael@0 8772 myself = '%s %s%s' % (type, self.name, specialization)
michael@0 8773 if self.decorators != '':
michael@0 8774 myself += " " + self.decorators
michael@0 8775 result += myself
michael@0 8776
michael@0 8777 if self.bases:
michael@0 8778 inherit = ' : '
michael@0 8779 result += inherit
michael@0 8780 # Grab our first base
michael@0 8781 baseItems = [CGGeneric(b.declare(self)) for b in self.bases]
michael@0 8782 bases = baseItems[:1]
michael@0 8783 # Indent the rest
michael@0 8784 bases.extend(CGIndenter(b, len(myself) + len(inherit))
michael@0 8785 for b in baseItems[1:])
michael@0 8786 result += ",\n".join(b.define() for b in bases)
michael@0 8787
michael@0 8788 result += '\n{\n'
michael@0 8789
michael@0 8790 result += self.extradeclarations
michael@0 8791
michael@0 8792 def declareMembers(cgClass, memberList, defaultVisibility):
michael@0 8793 members = {'private': [], 'protected': [], 'public': []}
michael@0 8794
michael@0 8795 for member in memberList:
michael@0 8796 members[member.visibility].append(member)
michael@0 8797
michael@0 8798 if defaultVisibility == 'public':
michael@0 8799 order = ['public', 'protected', 'private']
michael@0 8800 else:
michael@0 8801 order = ['private', 'protected', 'public']
michael@0 8802
michael@0 8803 result = ''
michael@0 8804
michael@0 8805 lastVisibility = defaultVisibility
michael@0 8806 for visibility in order:
michael@0 8807 list = members[visibility]
michael@0 8808 if list:
michael@0 8809 if visibility != lastVisibility:
michael@0 8810 result += visibility + ':\n'
michael@0 8811 for member in list:
michael@0 8812 result += indent(member.declare(cgClass))
michael@0 8813 lastVisibility = visibility
michael@0 8814 return (result, lastVisibility)
michael@0 8815
michael@0 8816 if self.disallowCopyConstruction:
michael@0 8817 class DisallowedCopyConstructor(object):
michael@0 8818 def __init__(self):
michael@0 8819 self.visibility = "private"
michael@0 8820
michael@0 8821 def declare(self, cgClass):
michael@0 8822 name = cgClass.getNameString()
michael@0 8823 return ("%s(const %s&) MOZ_DELETE;\n"
michael@0 8824 "void operator=(const %s) MOZ_DELETE;\n" % (name, name, name))
michael@0 8825
michael@0 8826 disallowedCopyConstructors = [DisallowedCopyConstructor()]
michael@0 8827 else:
michael@0 8828 disallowedCopyConstructors = []
michael@0 8829
michael@0 8830 order = [self.enums, self.unions,
michael@0 8831 self.typedefs, self.members,
michael@0 8832 self.constructors + disallowedCopyConstructors,
michael@0 8833 self.destructors, self.methods]
michael@0 8834
michael@0 8835 lastVisibility = self.defaultVisibility
michael@0 8836 pieces = []
michael@0 8837 for memberList in order:
michael@0 8838 code, lastVisibility = declareMembers(self, memberList, lastVisibility)
michael@0 8839
michael@0 8840 if code:
michael@0 8841 code = code.rstrip() + "\n" # remove extra blank lines at the end
michael@0 8842 pieces.append(code)
michael@0 8843
michael@0 8844 result += '\n'.join(pieces)
michael@0 8845 result += '};\n'
michael@0 8846 result = indent(result, len(self.indent))
michael@0 8847 return result
michael@0 8848
michael@0 8849 def define(self):
michael@0 8850 def defineMembers(cgClass, memberList, itemCount, separator=''):
michael@0 8851 result = ''
michael@0 8852 for member in memberList:
michael@0 8853 if itemCount != 0:
michael@0 8854 result = result + separator
michael@0 8855 definition = member.define(cgClass)
michael@0 8856 if definition:
michael@0 8857 # Member variables would only produce empty lines here.
michael@0 8858 result += definition
michael@0 8859 itemCount += 1
michael@0 8860 return (result, itemCount)
michael@0 8861
michael@0 8862 order = [(self.members, ''), (self.constructors, '\n'),
michael@0 8863 (self.destructors, '\n'), (self.methods, '\n')]
michael@0 8864
michael@0 8865 result = self.extradefinitions
michael@0 8866 itemCount = 0
michael@0 8867 for memberList, separator in order:
michael@0 8868 memberString, itemCount = defineMembers(self, memberList,
michael@0 8869 itemCount, separator)
michael@0 8870 result = result + memberString
michael@0 8871 return result
michael@0 8872
michael@0 8873
michael@0 8874 class CGResolveOwnProperty(CGAbstractStaticMethod):
michael@0 8875 def __init__(self, descriptor):
michael@0 8876 args = [Argument('JSContext*', 'cx'),
michael@0 8877 Argument('JS::Handle<JSObject*>', 'wrapper'),
michael@0 8878 Argument('JS::Handle<JSObject*>', 'obj'),
michael@0 8879 Argument('JS::Handle<jsid>', 'id'),
michael@0 8880 Argument('JS::MutableHandle<JSPropertyDescriptor>', 'desc'),
michael@0 8881 ]
michael@0 8882 CGAbstractStaticMethod.__init__(self, descriptor, "ResolveOwnProperty",
michael@0 8883 "bool", args)
michael@0 8884
michael@0 8885 def definition_body(self):
michael@0 8886 # BOGUS extra blank line at end of function
michael@0 8887 return " return js::GetProxyHandler(obj)->getOwnPropertyDescriptor(cx, wrapper, id, desc);\n\n"
michael@0 8888
michael@0 8889
michael@0 8890 class CGResolveOwnPropertyViaNewresolve(CGAbstractBindingMethod):
michael@0 8891 """
michael@0 8892 An implementation of Xray ResolveOwnProperty stuff for things that have a
michael@0 8893 newresolve hook.
michael@0 8894 """
michael@0 8895 def __init__(self, descriptor):
michael@0 8896 args = [Argument('JSContext*', 'cx'),
michael@0 8897 Argument('JS::Handle<JSObject*>', 'wrapper'),
michael@0 8898 Argument('JS::Handle<JSObject*>', 'obj'),
michael@0 8899 Argument('JS::Handle<jsid>', 'id'),
michael@0 8900 Argument('JS::MutableHandle<JSPropertyDescriptor>', 'desc')]
michael@0 8901 CGAbstractBindingMethod.__init__(self, descriptor,
michael@0 8902 "ResolveOwnPropertyViaNewresolve",
michael@0 8903 args, getThisObj="",
michael@0 8904 callArgs="")
michael@0 8905
michael@0 8906 def generate_code(self):
michael@0 8907 return CGGeneric(indent(dedent("""
michael@0 8908 {
michael@0 8909 // Since we're dealing with an Xray, do the resolve on the
michael@0 8910 // underlying object first. That gives it a chance to
michael@0 8911 // define properties on the actual object as needed, and
michael@0 8912 // then use the fact that it created the objects as a flag
michael@0 8913 // to avoid re-resolving the properties if someone deletes
michael@0 8914 // them.
michael@0 8915 JSAutoCompartment ac(cx, obj);
michael@0 8916 JS::Rooted<JSPropertyDescriptor> objDesc(cx);
michael@0 8917 if (!self->DoNewResolve(cx, obj, id, &objDesc)) {
michael@0 8918 return false;
michael@0 8919 }
michael@0 8920 // If desc.value() is undefined, then the DoNewResolve call
michael@0 8921 // has already defined the property on the object. Don't
michael@0 8922 // try to also define it.
michael@0 8923 if (objDesc.object() &&
michael@0 8924 !objDesc.value().isUndefined() &&
michael@0 8925 !JS_DefinePropertyById(cx, obj, id, objDesc.value(),
michael@0 8926 objDesc.getter(), objDesc.setter(),
michael@0 8927 objDesc.attributes())) {
michael@0 8928 return false;
michael@0 8929 }
michael@0 8930 }
michael@0 8931 return self->DoNewResolve(cx, wrapper, id, desc);
michael@0 8932 """)))
michael@0 8933
michael@0 8934
michael@0 8935 class CGEnumerateOwnProperties(CGAbstractStaticMethod):
michael@0 8936 def __init__(self, descriptor):
michael@0 8937 args = [Argument('JSContext*', 'cx'),
michael@0 8938 Argument('JS::Handle<JSObject*>', 'wrapper'),
michael@0 8939 Argument('JS::Handle<JSObject*>', 'obj'),
michael@0 8940 Argument('JS::AutoIdVector&', 'props')]
michael@0 8941 CGAbstractStaticMethod.__init__(self, descriptor,
michael@0 8942 "EnumerateOwnProperties", "bool", args)
michael@0 8943
michael@0 8944 def definition_body(self):
michael@0 8945 # BOGUS extra newline
michael@0 8946 return " return js::GetProxyHandler(obj)->getOwnPropertyNames(cx, wrapper, props);\n\n"
michael@0 8947
michael@0 8948
michael@0 8949 class CGEnumerateOwnPropertiesViaGetOwnPropertyNames(CGAbstractBindingMethod):
michael@0 8950 """
michael@0 8951 An implementation of Xray EnumerateOwnProperties stuff for things
michael@0 8952 that have a newresolve hook.
michael@0 8953 """
michael@0 8954 def __init__(self, descriptor):
michael@0 8955 args = [Argument('JSContext*', 'cx'),
michael@0 8956 Argument('JS::Handle<JSObject*>', 'wrapper'),
michael@0 8957 Argument('JS::Handle<JSObject*>', 'obj'),
michael@0 8958 Argument('JS::AutoIdVector&', 'props')]
michael@0 8959 CGAbstractBindingMethod.__init__(self, descriptor,
michael@0 8960 "EnumerateOwnPropertiesViaGetOwnPropertyNames",
michael@0 8961 args, getThisObj="",
michael@0 8962 callArgs="")
michael@0 8963
michael@0 8964 def generate_code(self):
michael@0 8965 return CGIndenter(CGGeneric(dedent("""
michael@0 8966 nsAutoTArray<nsString, 8> names;
michael@0 8967 ErrorResult rv;
michael@0 8968 self->GetOwnPropertyNames(cx, names, rv);
michael@0 8969 rv.WouldReportJSException();
michael@0 8970 if (rv.Failed()) {
michael@0 8971 return ThrowMethodFailedWithDetails(cx, rv, "%s", "enumerate");
michael@0 8972 }
michael@0 8973 // OK to pass null as "proxy" because it's ignored if
michael@0 8974 // shadowPrototypeProperties is true
michael@0 8975 return AppendNamedPropertyIds(cx, JS::NullPtr(), names, true, props);
michael@0 8976 """)))
michael@0 8977
michael@0 8978
michael@0 8979 class CGPrototypeTraitsClass(CGClass):
michael@0 8980 def __init__(self, descriptor, indent=''):
michael@0 8981 templateArgs = [Argument('prototypes::ID', 'PrototypeID')]
michael@0 8982 templateSpecialization = ['prototypes::id::' + descriptor.name]
michael@0 8983 enums = [ClassEnum('', ['Depth'],
michael@0 8984 [descriptor.interface.inheritanceDepth()])]
michael@0 8985 CGClass.__init__(self, 'PrototypeTraits', indent=indent,
michael@0 8986 templateArgs=templateArgs,
michael@0 8987 templateSpecialization=templateSpecialization,
michael@0 8988 enums=enums, isStruct=True)
michael@0 8989
michael@0 8990 def deps(self):
michael@0 8991 return set()
michael@0 8992
michael@0 8993
michael@0 8994 class CGClassForwardDeclare(CGThing):
michael@0 8995 def __init__(self, name, isStruct=False):
michael@0 8996 CGThing.__init__(self)
michael@0 8997 self.name = name
michael@0 8998 self.isStruct = isStruct
michael@0 8999
michael@0 9000 def declare(self):
michael@0 9001 type = 'struct' if self.isStruct else 'class'
michael@0 9002 return '%s %s;\n' % (type, self.name)
michael@0 9003
michael@0 9004 def define(self):
michael@0 9005 # Header only
michael@0 9006 return ''
michael@0 9007
michael@0 9008 def deps(self):
michael@0 9009 return set()
michael@0 9010
michael@0 9011
michael@0 9012 class CGProxySpecialOperation(CGPerSignatureCall):
michael@0 9013 """
michael@0 9014 Base class for classes for calling an indexed or named special operation
michael@0 9015 (don't use this directly, use the derived classes below).
michael@0 9016
michael@0 9017 If checkFound is False, will just assert that the prop is found instead of
michael@0 9018 checking that it is before wrapping the value.
michael@0 9019 """
michael@0 9020 def __init__(self, descriptor, operation, checkFound=True, argumentMutableValue=None):
michael@0 9021 self.checkFound = checkFound
michael@0 9022
michael@0 9023 nativeName = MakeNativeName(descriptor.binaryNames.get(operation, operation))
michael@0 9024 operation = descriptor.operations[operation]
michael@0 9025 assert len(operation.signatures()) == 1
michael@0 9026 signature = operation.signatures()[0]
michael@0 9027
michael@0 9028 returnType, arguments = signature
michael@0 9029
michael@0 9030 # We pass len(arguments) as the final argument so that the
michael@0 9031 # CGPerSignatureCall won't do any argument conversion of its own.
michael@0 9032 CGPerSignatureCall.__init__(self, returnType, arguments, nativeName,
michael@0 9033 False, descriptor, operation,
michael@0 9034 len(arguments))
michael@0 9035
michael@0 9036 if operation.isSetter() or operation.isCreator():
michael@0 9037 # arguments[0] is the index or name of the item that we're setting.
michael@0 9038 argument = arguments[1]
michael@0 9039 info = getJSToNativeConversionInfo(
michael@0 9040 argument.type, descriptor,
michael@0 9041 treatNullAs=argument.treatNullAs,
michael@0 9042 sourceDescription=("value being assigned to %s setter" %
michael@0 9043 descriptor.interface.identifier.name))
michael@0 9044 if argumentMutableValue is None:
michael@0 9045 argumentMutableValue = "desc.value()"
michael@0 9046 templateValues = {
michael@0 9047 "declName": argument.identifier.name,
michael@0 9048 "holderName": argument.identifier.name + "_holder",
michael@0 9049 "val": argumentMutableValue,
michael@0 9050 "mutableVal": argumentMutableValue,
michael@0 9051 "obj": "obj"
michael@0 9052 }
michael@0 9053 self.cgRoot.prepend(instantiateJSToNativeConversion(info, templateValues))
michael@0 9054 elif operation.isGetter() or operation.isDeleter():
michael@0 9055 self.cgRoot.prepend(CGGeneric("bool found;\n"))
michael@0 9056
michael@0 9057 def getArguments(self):
michael@0 9058 args = [(a, a.identifier.name) for a in self.arguments]
michael@0 9059 if self.idlNode.isGetter() or self.idlNode.isDeleter():
michael@0 9060 args.append((FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean],
michael@0 9061 self.idlNode),
michael@0 9062 "found"))
michael@0 9063 return args
michael@0 9064
michael@0 9065 def wrap_return_value(self):
michael@0 9066 if not self.idlNode.isGetter() or self.templateValues is None:
michael@0 9067 return ""
michael@0 9068
michael@0 9069 wrap = CGGeneric(wrapForType(self.returnType, self.descriptor, self.templateValues))
michael@0 9070 if self.checkFound:
michael@0 9071 wrap = CGIfWrapper(wrap, "found")
michael@0 9072 else:
michael@0 9073 wrap = CGList([CGGeneric("MOZ_ASSERT(found);\n"), wrap])
michael@0 9074 return "\n" + wrap.define()
michael@0 9075
michael@0 9076
michael@0 9077 class CGProxyIndexedOperation(CGProxySpecialOperation):
michael@0 9078 """
michael@0 9079 Class to generate a call to an indexed operation.
michael@0 9080
michael@0 9081 If doUnwrap is False, the caller is responsible for making sure a variable
michael@0 9082 named 'self' holds the C++ object somewhere where the code we generate
michael@0 9083 will see it.
michael@0 9084
michael@0 9085 If checkFound is False, will just assert that the prop is found instead of
michael@0 9086 checking that it is before wrapping the value.
michael@0 9087 """
michael@0 9088 def __init__(self, descriptor, name, doUnwrap=True, checkFound=True,
michael@0 9089 argumentMutableValue=None):
michael@0 9090 self.doUnwrap = doUnwrap
michael@0 9091 CGProxySpecialOperation.__init__(self, descriptor, name, checkFound,
michael@0 9092 argumentMutableValue=argumentMutableValue)
michael@0 9093
michael@0 9094 def define(self):
michael@0 9095 # Our first argument is the id we're getting.
michael@0 9096 argName = self.arguments[0].identifier.name
michael@0 9097 if argName == "index":
michael@0 9098 # We already have our index in a variable with that name
michael@0 9099 setIndex = ""
michael@0 9100 else:
michael@0 9101 setIndex = "uint32_t %s = index;\n" % argName
michael@0 9102 if self.doUnwrap:
michael@0 9103 unwrap = "%s* self = UnwrapProxy(proxy);\n" % self.descriptor.nativeType
michael@0 9104 else:
michael@0 9105 unwrap = ""
michael@0 9106 return (setIndex + unwrap +
michael@0 9107 CGProxySpecialOperation.define(self))
michael@0 9108
michael@0 9109
michael@0 9110 class CGProxyIndexedGetter(CGProxyIndexedOperation):
michael@0 9111 """
michael@0 9112 Class to generate a call to an indexed getter. If templateValues is not None
michael@0 9113 the returned value will be wrapped with wrapForType using templateValues.
michael@0 9114
michael@0 9115 If doUnwrap is False, the caller is responsible for making sure a variable
michael@0 9116 named 'self' holds the C++ object somewhere where the code we generate
michael@0 9117 will see it.
michael@0 9118
michael@0 9119 If checkFound is False, will just assert that the prop is found instead of
michael@0 9120 checking that it is before wrapping the value.
michael@0 9121 """
michael@0 9122 def __init__(self, descriptor, templateValues=None, doUnwrap=True,
michael@0 9123 checkFound=True):
michael@0 9124 self.templateValues = templateValues
michael@0 9125 CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedGetter',
michael@0 9126 doUnwrap, checkFound)
michael@0 9127
michael@0 9128
michael@0 9129 class CGProxyIndexedPresenceChecker(CGProxyIndexedGetter):
michael@0 9130 """
michael@0 9131 Class to generate a call that checks whether an indexed property exists.
michael@0 9132
michael@0 9133 For now, we just delegate to CGProxyIndexedGetter
michael@0 9134 """
michael@0 9135 def __init__(self, descriptor):
michael@0 9136 CGProxyIndexedGetter.__init__(self, descriptor)
michael@0 9137 self.cgRoot.append(CGGeneric("(void)result;\n"))
michael@0 9138
michael@0 9139
michael@0 9140 class CGProxyIndexedSetter(CGProxyIndexedOperation):
michael@0 9141 """
michael@0 9142 Class to generate a call to an indexed setter.
michael@0 9143 """
michael@0 9144 def __init__(self, descriptor, argumentMutableValue=None):
michael@0 9145 CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedSetter',
michael@0 9146 argumentMutableValue=argumentMutableValue)
michael@0 9147
michael@0 9148
michael@0 9149 class CGProxyIndexedDeleter(CGProxyIndexedOperation):
michael@0 9150 """
michael@0 9151 Class to generate a call to an indexed deleter.
michael@0 9152 """
michael@0 9153 def __init__(self, descriptor):
michael@0 9154 CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedDeleter')
michael@0 9155
michael@0 9156
michael@0 9157 class CGProxyNamedOperation(CGProxySpecialOperation):
michael@0 9158 """
michael@0 9159 Class to generate a call to a named operation.
michael@0 9160
michael@0 9161 'value' is the jsval to use for the name; None indicates that it should be
michael@0 9162 gotten from the property id.
michael@0 9163 """
michael@0 9164 def __init__(self, descriptor, name, value=None, argumentMutableValue=None):
michael@0 9165 CGProxySpecialOperation.__init__(self, descriptor, name,
michael@0 9166 argumentMutableValue=argumentMutableValue)
michael@0 9167 self.value = value
michael@0 9168
michael@0 9169 def define(self):
michael@0 9170 # Our first argument is the id we're getting.
michael@0 9171 argName = self.arguments[0].identifier.name
michael@0 9172 if argName == "id":
michael@0 9173 # deal with the name collision
michael@0 9174 idDecl = "JS::Rooted<jsid> id_(cx, id);\n"
michael@0 9175 idName = "id_"
michael@0 9176 else:
michael@0 9177 idDecl = ""
michael@0 9178 idName = "id"
michael@0 9179 unwrapString = fill(
michael@0 9180 """
michael@0 9181 if (!ConvertJSValueToString(cx, nameVal, &nameVal,
michael@0 9182 eStringify, eStringify, ${argName})) {
michael@0 9183 return false;
michael@0 9184 }
michael@0 9185 """,
michael@0 9186 argName=argName)
michael@0 9187 if self.value is None:
michael@0 9188 # We're just using 'id', and if it's an atom we can take a
michael@0 9189 # fast path here.
michael@0 9190 unwrapString = fill(
michael@0 9191 """
michael@0 9192 if (MOZ_LIKELY(JSID_IS_ATOM(${idName}))) {
michael@0 9193 ${argName}.SetData(js::GetAtomChars(JSID_TO_ATOM(${idName})), js::GetAtomLength(JSID_TO_ATOM(${idName})));
michael@0 9194 } else {
michael@0 9195 nameVal = js::IdToValue(${idName});
michael@0 9196 $*{unwrapString}
michael@0 9197 }
michael@0 9198 """,
michael@0 9199 idName=idName,
michael@0 9200 argName=argName,
michael@0 9201 unwrapString=unwrapString)
michael@0 9202 else:
michael@0 9203 unwrapString = ("nameVal = %s;\n" % self.value) + unwrapString
michael@0 9204
michael@0 9205 # Sadly, we have to set up nameVal even if we have an atom id,
michael@0 9206 # because we don't know for sure, and we can end up needing it
michael@0 9207 # so it needs to be higher up the stack. Using a Maybe here
michael@0 9208 # seems like probable overkill.
michael@0 9209 return fill(
michael@0 9210 """
michael@0 9211 JS::Rooted<JS::Value> nameVal(cx);
michael@0 9212 $*{idDecl}
michael@0 9213 binding_detail::FakeDependentString ${argName};
michael@0 9214 $*{unwrapString}
michael@0 9215
michael@0 9216 ${nativeType}* self = UnwrapProxy(proxy);
michael@0 9217 $*{op}
michael@0 9218 """,
michael@0 9219 idDecl=idDecl,
michael@0 9220 argName=argName,
michael@0 9221 unwrapString=unwrapString,
michael@0 9222 nativeType=self.descriptor.nativeType,
michael@0 9223 op=CGProxySpecialOperation.define(self))
michael@0 9224
michael@0 9225
michael@0 9226 class CGProxyNamedGetter(CGProxyNamedOperation):
michael@0 9227 """
michael@0 9228 Class to generate a call to an named getter. If templateValues is not None
michael@0 9229 the returned value will be wrapped with wrapForType using templateValues.
michael@0 9230 'value' is the jsval to use for the name; None indicates that it should be
michael@0 9231 gotten from the property id.
michael@0 9232 """
michael@0 9233 def __init__(self, descriptor, templateValues=None, value=None):
michael@0 9234 self.templateValues = templateValues
michael@0 9235 CGProxyNamedOperation.__init__(self, descriptor, 'NamedGetter', value)
michael@0 9236
michael@0 9237
michael@0 9238 class CGProxyNamedPresenceChecker(CGProxyNamedGetter):
michael@0 9239 """
michael@0 9240 Class to generate a call that checks whether a named property exists.
michael@0 9241
michael@0 9242 For now, we just delegate to CGProxyNamedGetter
michael@0 9243 """
michael@0 9244 def __init__(self, descriptor):
michael@0 9245 CGProxyNamedGetter.__init__(self, descriptor)
michael@0 9246 self.cgRoot.append(CGGeneric("(void)result;\n"))
michael@0 9247
michael@0 9248
michael@0 9249 class CGProxyNamedSetter(CGProxyNamedOperation):
michael@0 9250 """
michael@0 9251 Class to generate a call to a named setter.
michael@0 9252 """
michael@0 9253 def __init__(self, descriptor, argumentMutableValue=None):
michael@0 9254 CGProxyNamedOperation.__init__(self, descriptor, 'NamedSetter',
michael@0 9255 argumentMutableValue=argumentMutableValue)
michael@0 9256
michael@0 9257
michael@0 9258 class CGProxyNamedDeleter(CGProxyNamedOperation):
michael@0 9259 """
michael@0 9260 Class to generate a call to a named deleter.
michael@0 9261 """
michael@0 9262 def __init__(self, descriptor):
michael@0 9263 CGProxyNamedOperation.__init__(self, descriptor, 'NamedDeleter')
michael@0 9264
michael@0 9265
michael@0 9266 class CGProxyIsProxy(CGAbstractMethod):
michael@0 9267 def __init__(self, descriptor):
michael@0 9268 args = [Argument('JSObject*', 'obj')]
michael@0 9269 CGAbstractMethod.__init__(self, descriptor, "IsProxy", "bool", args, alwaysInline=True)
michael@0 9270
michael@0 9271 def declare(self):
michael@0 9272 return ""
michael@0 9273
michael@0 9274 def definition_body(self):
michael@0 9275 return " return js::IsProxy(obj) && js::GetProxyHandler(obj) == DOMProxyHandler::getInstance();\n"
michael@0 9276
michael@0 9277
michael@0 9278 class CGProxyUnwrap(CGAbstractMethod):
michael@0 9279 def __init__(self, descriptor):
michael@0 9280 args = [Argument('JSObject*', 'obj')]
michael@0 9281 CGAbstractMethod.__init__(self, descriptor, "UnwrapProxy", descriptor.nativeType + '*', args, alwaysInline=True)
michael@0 9282
michael@0 9283 def declare(self):
michael@0 9284 return ""
michael@0 9285
michael@0 9286 def definition_body(self):
michael@0 9287 return indent(fill(
michael@0 9288 """
michael@0 9289 MOZ_ASSERT(js::IsProxy(obj));
michael@0 9290 if (js::GetProxyHandler(obj) != DOMProxyHandler::getInstance()) {
michael@0 9291 MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(obj));
michael@0 9292 obj = js::UncheckedUnwrap(obj);
michael@0 9293 }
michael@0 9294 MOZ_ASSERT(IsProxy(obj));
michael@0 9295 return static_cast<${type}*>(js::GetProxyPrivate(obj).toPrivate());
michael@0 9296 """,
michael@0 9297 type=self.descriptor.nativeType))
michael@0 9298
michael@0 9299
michael@0 9300 class CGDOMJSProxyHandler_getOwnPropDescriptor(ClassMethod):
michael@0 9301 def __init__(self, descriptor):
michael@0 9302 args = [Argument('JSContext*', 'cx'),
michael@0 9303 Argument('JS::Handle<JSObject*>', 'proxy'),
michael@0 9304 Argument('JS::Handle<jsid>', 'id'),
michael@0 9305 Argument('bool', 'ignoreNamedProps'),
michael@0 9306 Argument('JS::MutableHandle<JSPropertyDescriptor>', 'desc')]
michael@0 9307 ClassMethod.__init__(self, "getOwnPropDescriptor", "bool", args,
michael@0 9308 virtual=True, override=True)
michael@0 9309 self.descriptor = descriptor
michael@0 9310
michael@0 9311 def getBody(self):
michael@0 9312 indexedGetter = self.descriptor.operations['IndexedGetter']
michael@0 9313 indexedSetter = self.descriptor.operations['IndexedSetter']
michael@0 9314
michael@0 9315 if self.descriptor.supportsIndexedProperties():
michael@0 9316 readonly = toStringBool(indexedSetter is None)
michael@0 9317 fillDescriptor = "FillPropertyDescriptor(desc, proxy, %s);\nreturn true;\n" % readonly
michael@0 9318 templateValues = {
michael@0 9319 'jsvalRef': 'desc.value()',
michael@0 9320 'jsvalHandle': 'desc.value()',
michael@0 9321 'obj': 'proxy',
michael@0 9322 'successCode': fillDescriptor
michael@0 9323 }
michael@0 9324 getIndexed = fill(
michael@0 9325 """
michael@0 9326 int32_t index = GetArrayIndexFromId(cx, id);
michael@0 9327 if (IsArrayIndex(index)) {
michael@0 9328 $*{callGetter}
michael@0 9329 }
michael@0 9330
michael@0 9331 """,
michael@0 9332 callGetter=CGProxyIndexedGetter(self.descriptor, templateValues).define())
michael@0 9333 else:
michael@0 9334 getIndexed = ""
michael@0 9335
michael@0 9336 if UseHolderForUnforgeable(self.descriptor):
michael@0 9337 tryHolder = dedent("""
michael@0 9338 if (!JS_GetPropertyDescriptorById(cx, ${holder}, id, desc)) {
michael@0 9339 return false;
michael@0 9340 }
michael@0 9341 MOZ_ASSERT_IF(desc.object(), desc.object() == ${holder});
michael@0 9342 """)
michael@0 9343
michael@0 9344 # We don't want to look at the unforgeable holder at all
michael@0 9345 # in the xray case; that part got handled already.
michael@0 9346 getUnforgeable = fill(
michael@0 9347 """
michael@0 9348 if (!isXray) {
michael@0 9349 $*{callOnUnforgeable}
michael@0 9350 if (desc.object()) {
michael@0 9351 desc.object().set(proxy);
michael@0 9352 return true;
michael@0 9353 }
michael@0 9354 }
michael@0 9355
michael@0 9356 """,
michael@0 9357 callOnUnforgeable=CallOnUnforgeableHolder(self.descriptor, tryHolder))
michael@0 9358 else:
michael@0 9359 getUnforgeable = ""
michael@0 9360
michael@0 9361 if self.descriptor.supportsNamedProperties():
michael@0 9362 operations = self.descriptor.operations
michael@0 9363 readonly = toStringBool(operations['NamedSetter'] is None)
michael@0 9364 enumerable = (
michael@0 9365 "self->NameIsEnumerable(Constify(%s))" %
michael@0 9366 # First [0] means first (and only) signature, [1] means
michael@0 9367 # "arguments" as opposed to return type, [0] means first (and
michael@0 9368 # only) argument.
michael@0 9369 operations['NamedGetter'].signatures()[0][1][0].identifier.name)
michael@0 9370 fillDescriptor = (
michael@0 9371 "FillPropertyDescriptor(desc, proxy, %s, %s);\n"
michael@0 9372 "return true;\n" % (readonly, enumerable))
michael@0 9373 templateValues = {'jsvalRef': 'desc.value()', 'jsvalHandle': 'desc.value()',
michael@0 9374 'obj': 'proxy', 'successCode': fillDescriptor}
michael@0 9375 condition = "!HasPropertyOnPrototype(cx, proxy, id)"
michael@0 9376 if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
michael@0 9377 condition = "(!isXray || %s)" % condition
michael@0 9378 condition = "!ignoreNamedProps && " + condition
michael@0 9379 if self.descriptor.supportsIndexedProperties():
michael@0 9380 condition = "!IsArrayIndex(index) && " + condition
michael@0 9381 namedGet = (CGIfWrapper(CGProxyNamedGetter(self.descriptor, templateValues),
michael@0 9382 condition).define() +
michael@0 9383 "\n")
michael@0 9384 else:
michael@0 9385 namedGet = ""
michael@0 9386
michael@0 9387 return fill(
michael@0 9388 """
michael@0 9389 bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy);
michael@0 9390 $*{getIndexed}
michael@0 9391 $*{getUnforgeable}
michael@0 9392 JS::Rooted<JSObject*> expando(cx);
michael@0 9393 if (!isXray && (expando = GetExpandoObject(proxy))) {
michael@0 9394 if (!JS_GetPropertyDescriptorById(cx, expando, id, desc)) {
michael@0 9395 return false;
michael@0 9396 }
michael@0 9397 if (desc.object()) {
michael@0 9398 // Pretend the property lives on the wrapper.
michael@0 9399 desc.object().set(proxy);
michael@0 9400 return true;
michael@0 9401 }
michael@0 9402 }
michael@0 9403
michael@0 9404 $*{namedGet}
michael@0 9405 desc.object().set(nullptr);
michael@0 9406 return true;
michael@0 9407 """,
michael@0 9408 getIndexed=getIndexed,
michael@0 9409 getUnforgeable=getUnforgeable,
michael@0 9410 namedGet=namedGet)
michael@0 9411
michael@0 9412
michael@0 9413 class CGDOMJSProxyHandler_defineProperty(ClassMethod):
michael@0 9414 def __init__(self, descriptor):
michael@0 9415 args = [Argument('JSContext*', 'cx'),
michael@0 9416 Argument('JS::Handle<JSObject*>', 'proxy'),
michael@0 9417 Argument('JS::Handle<jsid>', 'id'),
michael@0 9418 Argument('JS::MutableHandle<JSPropertyDescriptor>', 'desc'),
michael@0 9419 Argument('bool*', 'defined')]
michael@0 9420 ClassMethod.__init__(self, "defineProperty", "bool", args, virtual=True, override=True)
michael@0 9421 self.descriptor = descriptor
michael@0 9422
michael@0 9423 def getBody(self):
michael@0 9424 set = ""
michael@0 9425
michael@0 9426 indexedSetter = self.descriptor.operations['IndexedSetter']
michael@0 9427 if indexedSetter:
michael@0 9428 if self.descriptor.operations['IndexedCreator'] is not indexedSetter:
michael@0 9429 raise TypeError("Can't handle creator that's different from the setter")
michael@0 9430 set += fill(
michael@0 9431 """
michael@0 9432 int32_t index = GetArrayIndexFromId(cx, id);
michael@0 9433 if (IsArrayIndex(index)) {
michael@0 9434 *defined = true;
michael@0 9435 $*{callSetter}
michael@0 9436 return true;
michael@0 9437 }
michael@0 9438 """,
michael@0 9439 callSetter=CGProxyIndexedSetter(self.descriptor).define())
michael@0 9440 elif self.descriptor.supportsIndexedProperties():
michael@0 9441 set += fill(
michael@0 9442 """
michael@0 9443 if (IsArrayIndex(GetArrayIndexFromId(cx, id))) {
michael@0 9444 return js::IsInNonStrictPropertySet(cx) || ThrowErrorMessage(cx, MSG_NO_INDEXED_SETTER, "${name}");
michael@0 9445 }
michael@0 9446 """,
michael@0 9447 name=self.descriptor.name)
michael@0 9448
michael@0 9449 if UseHolderForUnforgeable(self.descriptor):
michael@0 9450 defineOnUnforgeable = ("bool hasUnforgeable;\n"
michael@0 9451 "if (!JS_HasPropertyById(cx, ${holder}, id, &hasUnforgeable)) {\n"
michael@0 9452 " return false;\n"
michael@0 9453 "}\n"
michael@0 9454 "if (hasUnforgeable) {\n"
michael@0 9455 " *defined = true;" # SUPER BOGUS missing newline
michael@0 9456 " bool unused;\n"
michael@0 9457 " return js_DefineOwnProperty(cx, ${holder}, id, desc, &unused);\n"
michael@0 9458 "}\n"
michael@0 9459 "\n") # BOGUS extra blank line at end of block or method
michael@0 9460 set += CallOnUnforgeableHolder(self.descriptor,
michael@0 9461 defineOnUnforgeable,
michael@0 9462 "xpc::WrapperFactory::IsXrayWrapper(proxy)")
michael@0 9463
michael@0 9464 namedSetter = self.descriptor.operations['NamedSetter']
michael@0 9465 if namedSetter:
michael@0 9466 if self.descriptor.operations['NamedCreator'] is not namedSetter:
michael@0 9467 raise TypeError("Can't handle creator that's different from the setter")
michael@0 9468 # If we support indexed properties, we won't get down here for
michael@0 9469 # indices, so we can just do our setter unconditionally here.
michael@0 9470 set += fill(
michael@0 9471 """
michael@0 9472 *defined = true;
michael@0 9473 $*{callSetter}
michael@0 9474
michael@0 9475 return true;
michael@0 9476
michael@0 9477 """, # BOGUS extra blank line at end of method
michael@0 9478 callSetter=CGProxyNamedSetter(self.descriptor).define())
michael@0 9479 else:
michael@0 9480 if self.descriptor.supportsNamedProperties():
michael@0 9481 set += fill(
michael@0 9482 """
michael@0 9483 $*{presenceChecker}
michael@0 9484
michael@0 9485 if (found) {
michael@0 9486 return js::IsInNonStrictPropertySet(cx) || ThrowErrorMessage(cx, MSG_NO_NAMED_SETTER, "${name}");
michael@0 9487 }
michael@0 9488 """,
michael@0 9489 presenceChecker=CGProxyNamedPresenceChecker(self.descriptor).define(),
michael@0 9490 name=self.descriptor.name)
michael@0 9491 set += ("return mozilla::dom::DOMProxyHandler::defineProperty(%s);\n" %
michael@0 9492 ", ".join(a.name for a in self.args))
michael@0 9493 return set
michael@0 9494
michael@0 9495
michael@0 9496 class CGDOMJSProxyHandler_delete(ClassMethod):
michael@0 9497 def __init__(self, descriptor):
michael@0 9498 args = [Argument('JSContext*', 'cx'),
michael@0 9499 Argument('JS::Handle<JSObject*>', 'proxy'),
michael@0 9500 Argument('JS::Handle<jsid>', 'id'),
michael@0 9501 Argument('bool*', 'bp')]
michael@0 9502 ClassMethod.__init__(self, "delete_", "bool", args,
michael@0 9503 virtual=True, override=True)
michael@0 9504 self.descriptor = descriptor
michael@0 9505
michael@0 9506 def getBody(self):
michael@0 9507 def getDeleterBody(type):
michael@0 9508 """
michael@0 9509 type should be "Named" or "Indexed"
michael@0 9510 """
michael@0 9511 assert type in ("Named", "Indexed")
michael@0 9512 deleter = self.descriptor.operations[type + 'Deleter']
michael@0 9513 if deleter:
michael@0 9514 if (not deleter.signatures()[0][0].isPrimitive() or
michael@0 9515 deleter.signatures()[0][0].nullable() or
michael@0 9516 deleter.signatures()[0][0].tag() != IDLType.Tags.bool):
michael@0 9517 setBp = "*bp = true;\n"
michael@0 9518 else:
michael@0 9519 setBp = dedent("""
michael@0 9520 if (found) {
michael@0 9521 *bp = result;
michael@0 9522 } else {
michael@0 9523 *bp = true;
michael@0 9524 }
michael@0 9525 """)
michael@0 9526 body = (eval("CGProxy%sDeleter" % type)(self.descriptor).define() +
michael@0 9527 setBp)
michael@0 9528 elif eval("self.descriptor.supports%sProperties()" % type):
michael@0 9529 body = (eval("CGProxy%sPresenceChecker" % type)(self.descriptor).define() +
michael@0 9530 dedent("""
michael@0 9531 if (found) {
michael@0 9532 *bp = false;
michael@0 9533 } else {
michael@0 9534 *bp = true;
michael@0 9535 }
michael@0 9536 """))
michael@0 9537 else:
michael@0 9538 body = None
michael@0 9539 return body
michael@0 9540
michael@0 9541 delete = dedent("""
michael@0 9542 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
michael@0 9543 "Should not have a XrayWrapper here");
michael@0 9544
michael@0 9545 """)
michael@0 9546
michael@0 9547 indexedBody = getDeleterBody("Indexed")
michael@0 9548 if indexedBody is not None:
michael@0 9549 delete += fill(
michael@0 9550 """
michael@0 9551 int32_t index = GetArrayIndexFromId(cx, id);
michael@0 9552 if (IsArrayIndex(index)) {
michael@0 9553 $*{indexedBody}
michael@0 9554 // We always return here, even if the property was not found
michael@0 9555 return true;
michael@0 9556 }
michael@0 9557 """,
michael@0 9558 indexedBody=indexedBody)
michael@0 9559
michael@0 9560 if UseHolderForUnforgeable(self.descriptor):
michael@0 9561 unforgeable = dedent("""
michael@0 9562 bool hasUnforgeable;
michael@0 9563 if (!JS_HasPropertyById(cx, ${holder}, id, &hasUnforgeable)) {
michael@0 9564 return false;
michael@0 9565 }
michael@0 9566 if (hasUnforgeable) {
michael@0 9567 *bp = false;
michael@0 9568 return true;
michael@0 9569 }
michael@0 9570 """)
michael@0 9571 delete += CallOnUnforgeableHolder(self.descriptor, unforgeable)
michael@0 9572 delete += "\n"
michael@0 9573
michael@0 9574 namedBody = getDeleterBody("Named")
michael@0 9575 if namedBody is not None:
michael@0 9576 # We always return above for an index id in the case when we support
michael@0 9577 # indexed properties, so we can just treat the id as a name
michael@0 9578 # unconditionally here.
michael@0 9579 delete += (namedBody +
michael@0 9580 "if (found) {\n"
michael@0 9581 " return true;\n"
michael@0 9582 "}\n\n") # BOGUS extra blank line
michael@0 9583 if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
michael@0 9584 delete = CGIfWrapper(CGGeneric(delete),
michael@0 9585 "!HasPropertyOnPrototype(cx, proxy, id)").define()
michael@0 9586 else:
michael@0 9587 delete += "\n" # BOGUS extra blank line
michael@0 9588
michael@0 9589 delete += dedent("""
michael@0 9590
michael@0 9591 return dom::DOMProxyHandler::delete_(cx, proxy, id, bp);
michael@0 9592 """)
michael@0 9593
michael@0 9594 return delete
michael@0 9595
michael@0 9596
michael@0 9597 class CGDOMJSProxyHandler_ownPropNames(ClassMethod):
michael@0 9598 def __init__(self, descriptor, ):
michael@0 9599 args = [Argument('JSContext*', 'cx'),
michael@0 9600 Argument('JS::Handle<JSObject*>', 'proxy'),
michael@0 9601 Argument('unsigned', 'flags'),
michael@0 9602 Argument('JS::AutoIdVector&', 'props')]
michael@0 9603 ClassMethod.__init__(self, "ownPropNames", "bool", args,
michael@0 9604 virtual=True, override=True)
michael@0 9605 self.descriptor = descriptor
michael@0 9606
michael@0 9607 def getBody(self):
michael@0 9608 # Per spec, we do indices, then named props, then everything else
michael@0 9609 if self.descriptor.supportsIndexedProperties():
michael@0 9610 addIndices = dedent("""
michael@0 9611
michael@0 9612 uint32_t length = UnwrapProxy(proxy)->Length();
michael@0 9613 MOZ_ASSERT(int32_t(length) >= 0);
michael@0 9614 for (int32_t i = 0; i < int32_t(length); ++i) {
michael@0 9615 if (!props.append(INT_TO_JSID(i))) {
michael@0 9616 return false;
michael@0 9617 }
michael@0 9618 }
michael@0 9619 """)
michael@0 9620 else:
michael@0 9621 addIndices = ""
michael@0 9622
michael@0 9623 if UseHolderForUnforgeable(self.descriptor):
michael@0 9624 addUnforgeable = dedent("""
michael@0 9625 if (!js::GetPropertyNames(cx, ${holder}, flags, &props)) {
michael@0 9626 return false;
michael@0 9627 }
michael@0 9628 """)
michael@0 9629 addUnforgeable = CallOnUnforgeableHolder(self.descriptor,
michael@0 9630 addUnforgeable,
michael@0 9631 "isXray")
michael@0 9632 else:
michael@0 9633 addUnforgeable = ""
michael@0 9634
michael@0 9635 if self.descriptor.supportsNamedProperties():
michael@0 9636 if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
michael@0 9637 shadow = "!isXray"
michael@0 9638 else:
michael@0 9639 shadow = "false"
michael@0 9640 addNames = fill(
michael@0 9641 """
michael@0 9642
michael@0 9643 nsTArray<nsString> names;
michael@0 9644 UnwrapProxy(proxy)->GetSupportedNames(flags, names);
michael@0 9645 if (!AppendNamedPropertyIds(cx, proxy, names, ${shadow}, props)) {
michael@0 9646 return false;
michael@0 9647 }
michael@0 9648 """,
michael@0 9649 shadow=shadow)
michael@0 9650 else:
michael@0 9651 addNames = ""
michael@0 9652
michael@0 9653 return fill(
michael@0 9654 """
michael@0 9655 bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy);
michael@0 9656 $*{addIndices}
michael@0 9657 $*{addUnforgeable}
michael@0 9658 $*{addNames}
michael@0 9659
michael@0 9660 JS::Rooted<JSObject*> expando(cx);
michael@0 9661 if (!isXray && (expando = DOMProxyHandler::GetExpandoObject(proxy)) &&
michael@0 9662 !js::GetPropertyNames(cx, expando, flags, &props)) {
michael@0 9663 return false;
michael@0 9664 }
michael@0 9665
michael@0 9666 return true;
michael@0 9667 """,
michael@0 9668 addIndices=addIndices,
michael@0 9669 addUnforgeable=addUnforgeable,
michael@0 9670 addNames=addNames)
michael@0 9671
michael@0 9672
michael@0 9673 class CGDOMJSProxyHandler_hasOwn(ClassMethod):
michael@0 9674 def __init__(self, descriptor):
michael@0 9675 args = [Argument('JSContext*', 'cx'),
michael@0 9676 Argument('JS::Handle<JSObject*>', 'proxy'),
michael@0 9677 Argument('JS::Handle<jsid>', 'id'),
michael@0 9678 Argument('bool*', 'bp')]
michael@0 9679 ClassMethod.__init__(self, "hasOwn", "bool", args,
michael@0 9680 virtual=True, override=True)
michael@0 9681 self.descriptor = descriptor
michael@0 9682
michael@0 9683 def getBody(self):
michael@0 9684 if self.descriptor.supportsIndexedProperties():
michael@0 9685 indexed = fill(
michael@0 9686 """
michael@0 9687 int32_t index = GetArrayIndexFromId(cx, id);
michael@0 9688 if (IsArrayIndex(index)) {
michael@0 9689 $*{presenceChecker}
michael@0 9690
michael@0 9691 *bp = found;
michael@0 9692 return true;
michael@0 9693 }
michael@0 9694
michael@0 9695 """,
michael@0 9696 presenceChecker=CGProxyIndexedPresenceChecker(self.descriptor).define())
michael@0 9697 else:
michael@0 9698 indexed = ""
michael@0 9699
michael@0 9700 if UseHolderForUnforgeable(self.descriptor):
michael@0 9701 unforgeable = dedent("""
michael@0 9702 bool b = true;
michael@0 9703 bool ok = JS_AlreadyHasOwnPropertyById(cx, ${holder}, id, &b);
michael@0 9704 *bp = !!b;
michael@0 9705 if (!ok || *bp) {
michael@0 9706 return ok;
michael@0 9707 }
michael@0 9708 """)
michael@0 9709 unforgeable = CallOnUnforgeableHolder(self.descriptor, unforgeable)
michael@0 9710 else:
michael@0 9711 unforgeable = ""
michael@0 9712
michael@0 9713 if self.descriptor.supportsNamedProperties():
michael@0 9714 # If we support indexed properties we always return above for index
michael@0 9715 # property names, so no need to check for those here.
michael@0 9716 named = (CGProxyNamedPresenceChecker(self.descriptor).define() +
michael@0 9717 "\n" +
michael@0 9718 "*bp = found;\n")
michael@0 9719 if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
michael@0 9720 # BOGUS extra blank line at end of block
michael@0 9721 named = CGIfWrapper(CGGeneric(named + "return true;\n\n"),
michael@0 9722 "!HasPropertyOnPrototype(cx, proxy, id)").define()
michael@0 9723 named += "*bp = false;\n"
michael@0 9724 else:
michael@0 9725 named += "\n"
michael@0 9726 else:
michael@0 9727 named = "*bp = false;\n"
michael@0 9728
michael@0 9729 return fill(
michael@0 9730 """
michael@0 9731 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
michael@0 9732 "Should not have a XrayWrapper here");
michael@0 9733
michael@0 9734 $*{indexed}
michael@0 9735 $*{unforgeable}
michael@0 9736
michael@0 9737 JS::Rooted<JSObject*> expando(cx, GetExpandoObject(proxy));
michael@0 9738 if (expando) {
michael@0 9739 bool b = true;
michael@0 9740 bool ok = JS_HasPropertyById(cx, expando, id, &b);
michael@0 9741 *bp = !!b;
michael@0 9742 if (!ok || *bp) {
michael@0 9743 return ok;
michael@0 9744 }
michael@0 9745 }
michael@0 9746
michael@0 9747 $*{named}
michael@0 9748 return true;
michael@0 9749 """,
michael@0 9750 indexed=indexed,
michael@0 9751 unforgeable=unforgeable,
michael@0 9752 named=named)
michael@0 9753
michael@0 9754
michael@0 9755 class CGDOMJSProxyHandler_get(ClassMethod):
michael@0 9756 def __init__(self, descriptor):
michael@0 9757 args = [Argument('JSContext*', 'cx'),
michael@0 9758 Argument('JS::Handle<JSObject*>', 'proxy'),
michael@0 9759 Argument('JS::Handle<JSObject*>', 'receiver'),
michael@0 9760 Argument('JS::Handle<jsid>', 'id'),
michael@0 9761 Argument('JS::MutableHandle<JS::Value>', 'vp')]
michael@0 9762 ClassMethod.__init__(self, "get", "bool", args,
michael@0 9763 virtual=True, override=True)
michael@0 9764 self.descriptor = descriptor
michael@0 9765
michael@0 9766 def getBody(self):
michael@0 9767 getUnforgeableOrExpando = "JS::Rooted<JSObject*> sharedRoot(cx);\n"
michael@0 9768 if UseHolderForUnforgeable(self.descriptor):
michael@0 9769 hasUnforgeable = dedent("""
michael@0 9770 bool hasUnforgeable;
michael@0 9771 if (!JS_AlreadyHasOwnPropertyById(cx, ${holder}, id, &hasUnforgeable)) {
michael@0 9772 return false;
michael@0 9773 }
michael@0 9774 if (hasUnforgeable) {
michael@0 9775 return JS_ForwardGetPropertyTo(cx, ${holder}, id, proxy, vp);
michael@0 9776 }
michael@0 9777 """)
michael@0 9778 getUnforgeableOrExpando += CallOnUnforgeableHolder(self.descriptor,
michael@0 9779 hasUnforgeable,
michael@0 9780 useSharedRoot=True)
michael@0 9781 getUnforgeableOrExpando += dedent("""
michael@0 9782 { // Scope for expando
michael@0 9783 JS::Rooted<JSObject*>& expando(sharedRoot);
michael@0 9784 expando = DOMProxyHandler::GetExpandoObject(proxy);
michael@0 9785 if (expando) {
michael@0 9786 bool hasProp;
michael@0 9787 if (!JS_HasPropertyById(cx, expando, id, &hasProp)) {
michael@0 9788 return false;
michael@0 9789 }
michael@0 9790
michael@0 9791 if (hasProp) {
michael@0 9792 // Forward the get to the expando object, but our receiver is whatever our
michael@0 9793 // receiver is.
michael@0 9794 return JS_ForwardGetPropertyTo(cx, expando, id, receiver, vp);
michael@0 9795 }
michael@0 9796 }
michael@0 9797 }
michael@0 9798 """)
michael@0 9799
michael@0 9800 templateValues = {'jsvalRef': 'vp', 'jsvalHandle': 'vp', 'obj': 'proxy'}
michael@0 9801
michael@0 9802 if self.descriptor.supportsIndexedProperties():
michael@0 9803 getIndexedOrExpando = fill(
michael@0 9804 """
michael@0 9805 int32_t index = GetArrayIndexFromId(cx, id);
michael@0 9806 if (IsArrayIndex(index)) {
michael@0 9807 $*{callGetter}
michael@0 9808 // Even if we don't have this index, we don't forward the
michael@0 9809 // get on to our expando object.
michael@0 9810 } else {
michael@0 9811 $*{getUnforgeableOrExpando}
michael@0 9812 }
michael@0 9813 """,
michael@0 9814 callGetter=CGProxyIndexedGetter(self.descriptor, templateValues).define(),
michael@0 9815 getUnforgeableOrExpando=getUnforgeableOrExpando)
michael@0 9816 else:
michael@0 9817 getIndexedOrExpando = getUnforgeableOrExpando
michael@0 9818
michael@0 9819 if self.descriptor.supportsNamedProperties():
michael@0 9820 getNamed = CGProxyNamedGetter(self.descriptor, templateValues)
michael@0 9821 if self.descriptor.supportsIndexedProperties():
michael@0 9822 getNamed = CGIfWrapper(getNamed, "!IsArrayIndex(index)")
michael@0 9823 getNamed = getNamed.define() + "\n"
michael@0 9824 else:
michael@0 9825 getNamed = ""
michael@0 9826
michael@0 9827 getOnPrototype = dedent("""
michael@0 9828 bool foundOnPrototype;
michael@0 9829 if (!GetPropertyOnPrototype(cx, proxy, id, &foundOnPrototype, vp.address())) {
michael@0 9830 return false;
michael@0 9831 }
michael@0 9832
michael@0 9833 if (foundOnPrototype) {
michael@0 9834 return true;
michael@0 9835 }
michael@0 9836
michael@0 9837 """)
michael@0 9838 if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'):
michael@0 9839 getNamed = getNamed + getOnPrototype
michael@0 9840 else:
michael@0 9841 getNamed = getOnPrototype + getNamed
michael@0 9842
michael@0 9843 return fill(
michael@0 9844 """
michael@0 9845 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
michael@0 9846 "Should not have a XrayWrapper here");
michael@0 9847
michael@0 9848 $*{indexedOrExpando}
michael@0 9849
michael@0 9850 $*{named}
michael@0 9851 vp.setUndefined();
michael@0 9852 return true;
michael@0 9853 """,
michael@0 9854 indexedOrExpando=getIndexedOrExpando,
michael@0 9855 named=getNamed)
michael@0 9856
michael@0 9857
michael@0 9858 class CGDOMJSProxyHandler_setCustom(ClassMethod):
michael@0 9859 def __init__(self, descriptor):
michael@0 9860 args = [Argument('JSContext*', 'cx'),
michael@0 9861 Argument('JS::Handle<JSObject*>', 'proxy'),
michael@0 9862 Argument('JS::Handle<jsid>', 'id'),
michael@0 9863 Argument('JS::MutableHandle<JS::Value>', 'vp'),
michael@0 9864 Argument('bool*', 'done')]
michael@0 9865 ClassMethod.__init__(self, "setCustom", "bool", args, virtual=True, override=True)
michael@0 9866 self.descriptor = descriptor
michael@0 9867
michael@0 9868 def getBody(self):
michael@0 9869 assertion = ("MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),\n"
michael@0 9870 ' "Should not have a XrayWrapper here");\n')
michael@0 9871
michael@0 9872 # Correctness first. If we have a NamedSetter and [OverrideBuiltins],
michael@0 9873 # always call the NamedSetter and never do anything else.
michael@0 9874 namedSetter = self.descriptor.operations['NamedSetter']
michael@0 9875 if (namedSetter is not None and
michael@0 9876 self.descriptor.interface.getExtendedAttribute('OverrideBuiltins')):
michael@0 9877 # Check assumptions.
michael@0 9878 if self.descriptor.supportsIndexedProperties():
michael@0 9879 raise ValueError("In interface " + self.descriptor.name + ": " +
michael@0 9880 "Can't cope with [OverrideBuiltins] and an indexed getter")
michael@0 9881 if self.descriptor.operations['NamedCreator'] is not namedSetter:
michael@0 9882 raise ValueError("In interface " + self.descriptor.name + ": " +
michael@0 9883 "Can't cope with named setter that is not also a named creator")
michael@0 9884 if UseHolderForUnforgeable(self.descriptor):
michael@0 9885 raise ValueError("In interface " + self.descriptor.name + ": " +
michael@0 9886 "Can't cope with [OverrideBuiltins] and unforgeable members")
michael@0 9887
michael@0 9888 callSetter = CGProxyNamedSetter(self.descriptor, argumentMutableValue="vp")
michael@0 9889 return (assertion +
michael@0 9890 callSetter.define() +
michael@0 9891 "*done = true;\n"
michael@0 9892 "return true;\n")
michael@0 9893
michael@0 9894 # As an optimization, if we are going to call an IndexedSetter, go
michael@0 9895 # ahead and call it and have done.
michael@0 9896 indexedSetter = self.descriptor.operations['IndexedSetter']
michael@0 9897 if indexedSetter is not None:
michael@0 9898 if self.descriptor.operations['IndexedCreator'] is not indexedSetter:
michael@0 9899 raise ValueError("In interface " + self.descriptor.name + ": " +
michael@0 9900 "Can't cope with indexed setter that is not " +
michael@0 9901 "also an indexed creator")
michael@0 9902 setIndexed = fill(
michael@0 9903 """
michael@0 9904 int32_t index = GetArrayIndexFromId(cx, id);
michael@0 9905 if (IsArrayIndex(index)) {
michael@0 9906 $*{callSetter}
michael@0 9907 *done = true;
michael@0 9908 return true;
michael@0 9909 }
michael@0 9910
michael@0 9911 """,
michael@0 9912 callSetter=CGProxyIndexedSetter(self.descriptor,
michael@0 9913 argumentMutableValue="vp").define())
michael@0 9914 else:
michael@0 9915 setIndexed = ""
michael@0 9916
michael@0 9917 return (assertion +
michael@0 9918 setIndexed +
michael@0 9919 "*done = false;\n"
michael@0 9920 "return true;\n")
michael@0 9921
michael@0 9922
michael@0 9923 class CGDOMJSProxyHandler_className(ClassMethod):
michael@0 9924 def __init__(self, descriptor):
michael@0 9925 args = [Argument('JSContext*', 'cx'),
michael@0 9926 Argument('JS::Handle<JSObject*>', 'proxy')]
michael@0 9927 ClassMethod.__init__(self, "className", "const char*", args,
michael@0 9928 virtual=True, override=True)
michael@0 9929 self.descriptor = descriptor
michael@0 9930
michael@0 9931 def getBody(self):
michael@0 9932 return 'return "%s";\n' % self.descriptor.name
michael@0 9933
michael@0 9934
michael@0 9935 class CGDOMJSProxyHandler_finalizeInBackground(ClassMethod):
michael@0 9936 def __init__(self, descriptor):
michael@0 9937 args = [Argument('JS::Value', 'priv')]
michael@0 9938 ClassMethod.__init__(self, "finalizeInBackground", "bool", args,
michael@0 9939 virtual=True, override=True)
michael@0 9940 self.descriptor = descriptor
michael@0 9941
michael@0 9942 def getBody(self):
michael@0 9943 return "return false;\n"
michael@0 9944
michael@0 9945
michael@0 9946 class CGDOMJSProxyHandler_finalize(ClassMethod):
michael@0 9947 def __init__(self, descriptor):
michael@0 9948 args = [Argument('JSFreeOp*', 'fop'), Argument('JSObject*', 'proxy')]
michael@0 9949 ClassMethod.__init__(self, "finalize", "void", args,
michael@0 9950 virtual=True, override=True)
michael@0 9951 self.descriptor = descriptor
michael@0 9952
michael@0 9953 def getBody(self):
michael@0 9954 return ("%s* self = UnwrapProxy(proxy);\n\n" % self.descriptor.nativeType +
michael@0 9955 finalizeHook(self.descriptor, FINALIZE_HOOK_NAME, self.args[0].name).define())
michael@0 9956
michael@0 9957
michael@0 9958 class CGDOMJSProxyHandler_slice(ClassMethod):
michael@0 9959 def __init__(self, descriptor):
michael@0 9960 assert descriptor.supportsIndexedProperties()
michael@0 9961
michael@0 9962 args = [Argument('JSContext*', 'cx'),
michael@0 9963 Argument('JS::Handle<JSObject*>', 'proxy'),
michael@0 9964 Argument('uint32_t', 'begin'),
michael@0 9965 Argument('uint32_t', 'end'),
michael@0 9966 Argument('JS::Handle<JSObject*>', 'array')]
michael@0 9967 ClassMethod.__init__(self, "slice", "bool", args, virtual=True, override=True)
michael@0 9968 self.descriptor = descriptor
michael@0 9969
michael@0 9970 def getBody(self):
michael@0 9971 # Just like getOwnPropertyNames we'll assume that we have no holes, so
michael@0 9972 # we have all properties from 0 to length. If that ever changes
michael@0 9973 # (unlikely), we'll need to do something a bit more clever with how we
michael@0 9974 # forward on to our ancestor.
michael@0 9975
michael@0 9976 templateValues = {
michael@0 9977 'jsvalRef': 'temp',
michael@0 9978 'jsvalHandle': '&temp',
michael@0 9979 'obj': 'proxy',
michael@0 9980 'successCode': ("js::UnsafeDefineElement(cx, array, index - begin, temp);\n"
michael@0 9981 "continue;\n")
michael@0 9982 }
michael@0 9983 get = CGProxyIndexedGetter(self.descriptor, templateValues, False, False).define()
michael@0 9984
michael@0 9985 return fill(
michael@0 9986 """
michael@0 9987 JS::Rooted<JS::Value> temp(cx);
michael@0 9988 MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
michael@0 9989 "Should not have a XrayWrapper here");
michael@0 9990
michael@0 9991 ${nativeType}* self = UnwrapProxy(proxy);
michael@0 9992 uint32_t length = self->Length();
michael@0 9993 // Compute the end of the indices we'll get ourselves
michael@0 9994 uint32_t ourEnd = std::max(begin, std::min(end, length));
michael@0 9995
michael@0 9996 for (uint32_t index = begin; index < ourEnd; ++index) {
michael@0 9997 $*{get}
michael@0 9998 }
michael@0 9999
michael@0 10000 if (end > ourEnd) {
michael@0 10001 JS::Rooted<JSObject*> proto(cx);
michael@0 10002 if (!js::GetObjectProto(cx, proxy, &proto)) {
michael@0 10003 return false;
michael@0 10004 }
michael@0 10005 return js::SliceSlowly(cx, proto, proxy, ourEnd, end, array);
michael@0 10006 }
michael@0 10007
michael@0 10008 return true;
michael@0 10009 """,
michael@0 10010 nativeType=self.descriptor.nativeType,
michael@0 10011 get=get)
michael@0 10012
michael@0 10013
michael@0 10014 class CGDOMJSProxyHandler_getInstance(ClassMethod):
michael@0 10015 def __init__(self):
michael@0 10016 ClassMethod.__init__(self, "getInstance", "DOMProxyHandler*", [], static=True)
michael@0 10017
michael@0 10018 def getBody(self):
michael@0 10019 return dedent("""
michael@0 10020 static DOMProxyHandler instance;
michael@0 10021 return &instance;
michael@0 10022 """)
michael@0 10023
michael@0 10024
michael@0 10025 class CGDOMJSProxyHandler(CGClass):
michael@0 10026 def __init__(self, descriptor):
michael@0 10027 assert (descriptor.supportsIndexedProperties() or
michael@0 10028 descriptor.supportsNamedProperties())
michael@0 10029 methods = [CGDOMJSProxyHandler_getOwnPropDescriptor(descriptor),
michael@0 10030 CGDOMJSProxyHandler_defineProperty(descriptor),
michael@0 10031 ClassUsingDeclaration("mozilla::dom::DOMProxyHandler",
michael@0 10032 "defineProperty"),
michael@0 10033 CGDOMJSProxyHandler_ownPropNames(descriptor),
michael@0 10034 CGDOMJSProxyHandler_hasOwn(descriptor),
michael@0 10035 CGDOMJSProxyHandler_get(descriptor),
michael@0 10036 CGDOMJSProxyHandler_className(descriptor),
michael@0 10037 CGDOMJSProxyHandler_finalizeInBackground(descriptor),
michael@0 10038 CGDOMJSProxyHandler_finalize(descriptor),
michael@0 10039 CGDOMJSProxyHandler_getInstance(),
michael@0 10040 CGDOMJSProxyHandler_delete(descriptor)]
michael@0 10041 if descriptor.supportsIndexedProperties():
michael@0 10042 methods.append(CGDOMJSProxyHandler_slice(descriptor))
michael@0 10043 if (descriptor.operations['IndexedSetter'] is not None or
michael@0 10044 (descriptor.operations['NamedSetter'] is not None and
michael@0 10045 descriptor.interface.getExtendedAttribute('OverrideBuiltins'))):
michael@0 10046 methods.append(CGDOMJSProxyHandler_setCustom(descriptor))
michael@0 10047
michael@0 10048 CGClass.__init__(self, 'DOMProxyHandler',
michael@0 10049 bases=[ClassBase('mozilla::dom::DOMProxyHandler')],
michael@0 10050 methods=methods)
michael@0 10051
michael@0 10052
michael@0 10053 class CGDOMJSProxyHandlerDeclarer(CGThing):
michael@0 10054 """
michael@0 10055 A class for declaring a DOMProxyHandler.
michael@0 10056 """
michael@0 10057 def __init__(self, handlerThing):
michael@0 10058 self.handlerThing = handlerThing
michael@0 10059
michael@0 10060 def declare(self):
michael@0 10061 # Our class declaration should happen when we're defining
michael@0 10062 return ""
michael@0 10063
michael@0 10064 def define(self):
michael@0 10065 return self.handlerThing.declare()
michael@0 10066
michael@0 10067
michael@0 10068 class CGDOMJSProxyHandlerDefiner(CGThing):
michael@0 10069 """
michael@0 10070 A class for defining a DOMProxyHandler.
michael@0 10071 """
michael@0 10072 def __init__(self, handlerThing):
michael@0 10073 self.handlerThing = handlerThing
michael@0 10074
michael@0 10075 def declare(self):
michael@0 10076 return ""
michael@0 10077
michael@0 10078 def define(self):
michael@0 10079 return self.handlerThing.define()
michael@0 10080
michael@0 10081
michael@0 10082 def stripTrailingWhitespace(text):
michael@0 10083 tail = '\n' if text.endswith('\n') else ''
michael@0 10084 lines = text.splitlines()
michael@0 10085 return '\n'.join(line.rstrip() for line in lines) + tail
michael@0 10086
michael@0 10087
michael@0 10088 class CGDescriptor(CGThing):
michael@0 10089 def __init__(self, descriptor):
michael@0 10090 CGThing.__init__(self)
michael@0 10091
michael@0 10092 assert not descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject()
michael@0 10093
michael@0 10094 if descriptor.nativeOwnership == 'owned' and (
michael@0 10095 descriptor.interface.hasChildInterfaces() or
michael@0 10096 descriptor.interface.parent):
michael@0 10097 raise TypeError("Owned interface cannot have a parent or children")
michael@0 10098
michael@0 10099 self._deps = descriptor.interface.getDeps()
michael@0 10100
michael@0 10101 cgThings = []
michael@0 10102 cgThings.append(CGGeneric(declare="typedef %s NativeType;\n" %
michael@0 10103 descriptor.nativeType))
michael@0 10104 # These are set to true if at least one non-static
michael@0 10105 # method/getter/setter or jsonifier exist on the interface.
michael@0 10106 (hasMethod, hasGetter, hasLenientGetter, hasSetter, hasJsonifier,
michael@0 10107 hasLenientSetter) = False, False, False, False, False, False
michael@0 10108 crossOriginMethods, crossOriginGetters, crossOriginSetters = set(), set(), set()
michael@0 10109 for n in descriptor.interface.namedConstructors:
michael@0 10110 cgThings.append(CGClassConstructor(descriptor, n,
michael@0 10111 NamedConstructorName(n)))
michael@0 10112 for m in descriptor.interface.members:
michael@0 10113 if m.isMethod() and m.identifier.name == 'queryInterface':
michael@0 10114 continue
michael@0 10115 if m.isMethod() and m == descriptor.operations['Jsonifier']:
michael@0 10116 hasJsonifier = True
michael@0 10117 hasMethod = descriptor.needsSpecialGenericOps()
michael@0 10118 jsonifierMethod = m
michael@0 10119 elif (m.isMethod() and
michael@0 10120 (not m.isIdentifierLess() or m == descriptor.operations['Stringifier'])):
michael@0 10121 if m.isStatic():
michael@0 10122 assert descriptor.interface.hasInterfaceObject
michael@0 10123 cgThings.append(CGStaticMethod(descriptor, m))
michael@0 10124 if m.returnsPromise():
michael@0 10125 cgThings.append(CGStaticMethodJitinfo(m))
michael@0 10126 elif descriptor.interface.hasInterfacePrototypeObject():
michael@0 10127 specializedMethod = CGSpecializedMethod(descriptor, m)
michael@0 10128 cgThings.append(specializedMethod)
michael@0 10129 if m.returnsPromise():
michael@0 10130 cgThings.append(CGMethodPromiseWrapper(descriptor, specializedMethod))
michael@0 10131 cgThings.append(CGMemberJITInfo(descriptor, m))
michael@0 10132 if m.getExtendedAttribute("CrossOriginCallable"):
michael@0 10133 crossOriginMethods.add(m.identifier.name)
michael@0 10134 elif descriptor.needsSpecialGenericOps():
michael@0 10135 hasMethod = True
michael@0 10136 elif m.isAttr():
michael@0 10137 if m.stringifier:
michael@0 10138 raise TypeError("Stringifier attributes not supported yet. "
michael@0 10139 "See bug 824857.\n"
michael@0 10140 "%s" % m.location)
michael@0 10141 if m.isStatic():
michael@0 10142 assert descriptor.interface.hasInterfaceObject
michael@0 10143 cgThings.append(CGStaticGetter(descriptor, m))
michael@0 10144 elif descriptor.interface.hasInterfacePrototypeObject():
michael@0 10145 cgThings.append(CGSpecializedGetter(descriptor, m))
michael@0 10146 if m.hasLenientThis():
michael@0 10147 hasLenientGetter = True
michael@0 10148 elif m.getExtendedAttribute("CrossOriginReadable"):
michael@0 10149 crossOriginGetters.add(m.identifier.name)
michael@0 10150 elif descriptor.needsSpecialGenericOps():
michael@0 10151 hasGetter = True
michael@0 10152 if not m.readonly:
michael@0 10153 for extAttr in ["PutForwards", "Replaceable"]:
michael@0 10154 if m.getExtendedAttribute(extAttr):
michael@0 10155 raise TypeError("Writable attributes should not "
michael@0 10156 "have %s specified.\n"
michael@0 10157 "%s" %
michael@0 10158 (extAttr, m.location))
michael@0 10159 if m.isStatic():
michael@0 10160 assert descriptor.interface.hasInterfaceObject
michael@0 10161 cgThings.append(CGStaticSetter(descriptor, m))
michael@0 10162 elif descriptor.interface.hasInterfacePrototypeObject():
michael@0 10163 cgThings.append(CGSpecializedSetter(descriptor, m))
michael@0 10164 if m.hasLenientThis():
michael@0 10165 hasLenientSetter = True
michael@0 10166 elif m.getExtendedAttribute("CrossOriginWritable"):
michael@0 10167 crossOriginSetters.add(m.identifier.name)
michael@0 10168 elif descriptor.needsSpecialGenericOps():
michael@0 10169 hasSetter = True
michael@0 10170 elif m.getExtendedAttribute("PutForwards"):
michael@0 10171 cgThings.append(CGSpecializedForwardingSetter(descriptor, m))
michael@0 10172 if m.getExtendedAttribute("CrossOriginWritable"):
michael@0 10173 crossOriginSetters.add(m.identifier.name)
michael@0 10174 elif descriptor.needsSpecialGenericOps():
michael@0 10175 hasSetter = True
michael@0 10176 elif m.getExtendedAttribute("Replaceable"):
michael@0 10177 cgThings.append(CGSpecializedReplaceableSetter(descriptor, m))
michael@0 10178 if descriptor.needsSpecialGenericOps():
michael@0 10179 hasSetter = True
michael@0 10180 if (not m.isStatic() and
michael@0 10181 descriptor.interface.hasInterfacePrototypeObject()):
michael@0 10182 cgThings.append(CGMemberJITInfo(descriptor, m))
michael@0 10183 if hasJsonifier:
michael@0 10184 cgThings.append(CGJsonifierMethod(descriptor, jsonifierMethod))
michael@0 10185 cgThings.append(CGMemberJITInfo(descriptor, jsonifierMethod))
michael@0 10186 if hasMethod:
michael@0 10187 cgThings.append(CGGenericMethod(descriptor))
michael@0 10188 if len(crossOriginMethods):
michael@0 10189 cgThings.append(CGGenericMethod(descriptor,
michael@0 10190 allowCrossOriginThis=True))
michael@0 10191 if hasGetter:
michael@0 10192 cgThings.append(CGGenericGetter(descriptor))
michael@0 10193 if hasLenientGetter:
michael@0 10194 cgThings.append(CGGenericGetter(descriptor, lenientThis=True))
michael@0 10195 if len(crossOriginGetters):
michael@0 10196 cgThings.append(CGGenericGetter(descriptor,
michael@0 10197 allowCrossOriginThis=True))
michael@0 10198 if hasSetter:
michael@0 10199 cgThings.append(CGGenericSetter(descriptor))
michael@0 10200 if hasLenientSetter:
michael@0 10201 cgThings.append(CGGenericSetter(descriptor, lenientThis=True))
michael@0 10202 if len(crossOriginSetters):
michael@0 10203 cgThings.append(CGGenericSetter(descriptor,
michael@0 10204 allowCrossOriginThis=True))
michael@0 10205
michael@0 10206 if descriptor.interface.getNavigatorProperty():
michael@0 10207 cgThings.append(CGConstructNavigatorObjectHelper(descriptor))
michael@0 10208 cgThings.append(CGConstructNavigatorObject(descriptor))
michael@0 10209
michael@0 10210 if descriptor.concrete and not descriptor.proxy:
michael@0 10211 if wantsAddProperty(descriptor):
michael@0 10212 cgThings.append(CGAddPropertyHook(descriptor))
michael@0 10213
michael@0 10214 # Always have a finalize hook, regardless of whether the class
michael@0 10215 # wants a custom hook.
michael@0 10216 cgThings.append(CGClassFinalizeHook(descriptor))
michael@0 10217
michael@0 10218 properties = PropertyArrays(descriptor)
michael@0 10219 cgThings.append(CGGeneric(define=str(properties)))
michael@0 10220 cgThings.append(CGNativeProperties(descriptor, properties))
michael@0 10221
michael@0 10222 # Set up our Xray callbacks as needed. Note that we don't need to do
michael@0 10223 # it in workers.
michael@0 10224 if not descriptor.workers and descriptor.concrete and descriptor.proxy:
michael@0 10225 cgThings.append(CGResolveOwnProperty(descriptor))
michael@0 10226 cgThings.append(CGEnumerateOwnProperties(descriptor))
michael@0 10227 elif descriptor.needsXrayResolveHooks():
michael@0 10228 cgThings.append(CGResolveOwnPropertyViaNewresolve(descriptor))
michael@0 10229 cgThings.append(CGEnumerateOwnPropertiesViaGetOwnPropertyNames(descriptor))
michael@0 10230
michael@0 10231 # Now that we have our ResolveOwnProperty/EnumerateOwnProperties stuff
michael@0 10232 # done, set up our NativePropertyHooks.
michael@0 10233 cgThings.append(CGNativePropertyHooks(descriptor, properties))
michael@0 10234
michael@0 10235 if descriptor.interface.hasInterfaceObject():
michael@0 10236 cgThings.append(CGClassConstructor(descriptor,
michael@0 10237 descriptor.interface.ctor()))
michael@0 10238 cgThings.append(CGClassHasInstanceHook(descriptor))
michael@0 10239 cgThings.append(CGInterfaceObjectJSClass(descriptor, properties))
michael@0 10240 if descriptor.needsConstructHookHolder():
michael@0 10241 cgThings.append(CGClassConstructHookHolder(descriptor))
michael@0 10242 cgThings.append(CGNamedConstructors(descriptor))
michael@0 10243
michael@0 10244 cgThings.append(CGLegacyCallHook(descriptor))
michael@0 10245 if descriptor.interface.getExtendedAttribute("NeedNewResolve"):
michael@0 10246 cgThings.append(CGNewResolveHook(descriptor))
michael@0 10247 cgThings.append(CGEnumerateHook(descriptor))
michael@0 10248
michael@0 10249 if descriptor.interface.hasInterfacePrototypeObject():
michael@0 10250 cgThings.append(CGPrototypeJSClass(descriptor, properties))
michael@0 10251
michael@0 10252 if descriptor.interface.hasInterfaceObject():
michael@0 10253 cgThings.append(CGDefineDOMInterfaceMethod(descriptor))
michael@0 10254
michael@0 10255 if ((descriptor.interface.hasInterfaceObject() or descriptor.interface.getNavigatorProperty()) and
michael@0 10256 not descriptor.interface.isExternal() and
michael@0 10257 descriptor.isExposedConditionally() and
michael@0 10258 # Workers stuff is never conditional
michael@0 10259 not descriptor.workers):
michael@0 10260 cgThings.append(CGConstructorEnabled(descriptor))
michael@0 10261
michael@0 10262 if descriptor.concrete:
michael@0 10263 if descriptor.proxy:
michael@0 10264 if descriptor.interface.totalMembersInSlots != 0:
michael@0 10265 raise TypeError("We can't have extra reserved slots for "
michael@0 10266 "proxy interface %s" %
michael@0 10267 descriptor.interface.identifier.name)
michael@0 10268 cgThings.append(CGGeneric(fill(
michael@0 10269 """
michael@0 10270 static_assert(IsBaseOf<nsISupports, ${nativeType} >::value,
michael@0 10271 "We don't support non-nsISupports native classes for "
michael@0 10272 "proxy-based bindings yet");
michael@0 10273
michael@0 10274 """,
michael@0 10275 nativeType=descriptor.nativeType)))
michael@0 10276 if not descriptor.wrapperCache:
michael@0 10277 raise TypeError("We need a wrappercache to support expandos for proxy-based "
michael@0 10278 "bindings (" + descriptor.name + ")")
michael@0 10279 handlerThing = CGDOMJSProxyHandler(descriptor)
michael@0 10280 cgThings.append(CGDOMJSProxyHandlerDeclarer(handlerThing))
michael@0 10281 cgThings.append(CGProxyIsProxy(descriptor))
michael@0 10282 cgThings.append(CGProxyUnwrap(descriptor))
michael@0 10283 cgThings.append(CGDOMJSProxyHandlerDefiner(handlerThing))
michael@0 10284 cgThings.append(CGDOMProxyJSClass(descriptor))
michael@0 10285 else:
michael@0 10286 cgThings.append(CGDOMJSClass(descriptor))
michael@0 10287 cgThings.append(CGGetJSClassMethod(descriptor))
michael@0 10288 if descriptor.interface.hasMembersInSlots():
michael@0 10289 if descriptor.interface.hasChildInterfaces():
michael@0 10290 raise TypeError("We don't support members in slots on "
michael@0 10291 "non-leaf interfaces like %s" %
michael@0 10292 descriptor.interface.identifier.name)
michael@0 10293 cgThings.append(CGUpdateMemberSlotsMethod(descriptor))
michael@0 10294
michael@0 10295 if descriptor.interface.getExtendedAttribute("Global"):
michael@0 10296 assert descriptor.wrapperCache
michael@0 10297 cgThings.append(CGWrapGlobalMethod(descriptor, properties))
michael@0 10298 elif descriptor.wrapperCache:
michael@0 10299 cgThings.append(CGWrapWithCacheMethod(descriptor, properties))
michael@0 10300 cgThings.append(CGWrapMethod(descriptor))
michael@0 10301 else:
michael@0 10302 cgThings.append(CGWrapNonWrapperCacheMethod(descriptor,
michael@0 10303 properties))
michael@0 10304
michael@0 10305 # If we're not wrappercached, we don't know how to clear our
michael@0 10306 # cached values, since we can't get at the JSObject.
michael@0 10307 if descriptor.wrapperCache:
michael@0 10308 cgThings.extend(CGClearCachedValueMethod(descriptor, m) for
michael@0 10309 m in descriptor.interface.members if
michael@0 10310 m.isAttr() and
michael@0 10311 # Constants should never need clearing!
michael@0 10312 not m.getExtendedAttribute("Constant") and
michael@0 10313 not m.getExtendedAttribute("SameObject") and
michael@0 10314 m.slotIndex is not None)
michael@0 10315
michael@0 10316 # CGCreateInterfaceObjectsMethod needs to come after our
michael@0 10317 # CGDOMJSClass, if any.
michael@0 10318 cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties))
michael@0 10319
michael@0 10320 # CGGetProtoObjectMethod and CGGetConstructorObjectMethod need
michael@0 10321 # to come after CGCreateInterfaceObjectsMethod.
michael@0 10322 if descriptor.interface.hasInterfacePrototypeObject():
michael@0 10323 cgThings.append(CGGetProtoObjectMethod(descriptor))
michael@0 10324 if descriptor.interface.hasInterfaceObject():
michael@0 10325 cgThings.append(CGGetConstructorObjectMethod(descriptor))
michael@0 10326
michael@0 10327 # See whether we need we need to generate an IsPermitted method
michael@0 10328 if crossOriginGetters or crossOriginSetters or crossOriginMethods:
michael@0 10329 cgThings.append(CGIsPermittedMethod(descriptor,
michael@0 10330 crossOriginGetters,
michael@0 10331 crossOriginSetters,
michael@0 10332 crossOriginMethods))
michael@0 10333
michael@0 10334 cgThings = CGList((CGIndenter(t, declareOnly=True) for t in cgThings), "\n")
michael@0 10335 cgThings = CGWrapper(cgThings, pre='\n', post='\n')
michael@0 10336 self.cgRoot = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name),
michael@0 10337 cgThings),
michael@0 10338 post='\n')
michael@0 10339
michael@0 10340 def declare(self):
michael@0 10341 return self.cgRoot.declare()
michael@0 10342
michael@0 10343 def define(self):
michael@0 10344 return self.cgRoot.define()
michael@0 10345
michael@0 10346 def deps(self):
michael@0 10347 return self._deps
michael@0 10348
michael@0 10349
michael@0 10350 class CGNamespacedEnum(CGThing):
michael@0 10351 def __init__(self, namespace, enumName, names, values, comment=""):
michael@0 10352
michael@0 10353 if not values:
michael@0 10354 values = []
michael@0 10355
michael@0 10356 # Account for explicit enum values.
michael@0 10357 entries = []
michael@0 10358 for i in range(0, len(names)):
michael@0 10359 if len(values) > i and values[i] is not None:
michael@0 10360 entry = "%s = %s" % (names[i], values[i])
michael@0 10361 else:
michael@0 10362 entry = names[i]
michael@0 10363 entries.append(entry)
michael@0 10364
michael@0 10365 # Append a Count.
michael@0 10366 entries.append('_' + enumName + '_Count')
michael@0 10367
michael@0 10368 # Indent.
michael@0 10369 entries = [' ' + e for e in entries]
michael@0 10370
michael@0 10371 # Build the enum body.
michael@0 10372 enumstr = comment + 'enum %s\n{\n%s\n};\n' % (enumName, ',\n'.join(entries))
michael@0 10373 curr = CGGeneric(declare=enumstr)
michael@0 10374
michael@0 10375 # Add some whitespace padding.
michael@0 10376 curr = CGWrapper(curr, pre='\n', post='\n')
michael@0 10377
michael@0 10378 # Add the namespace.
michael@0 10379 curr = CGNamespace(namespace, curr)
michael@0 10380
michael@0 10381 # Add the typedef
michael@0 10382 typedef = '\ntypedef %s::%s %s;\n\n' % (namespace, enumName, enumName)
michael@0 10383 curr = CGList([curr, CGGeneric(declare=typedef)])
michael@0 10384
michael@0 10385 # Save the result.
michael@0 10386 self.node = curr
michael@0 10387
michael@0 10388 def declare(self):
michael@0 10389 return self.node.declare()
michael@0 10390
michael@0 10391 def define(self):
michael@0 10392 return ""
michael@0 10393
michael@0 10394
michael@0 10395 class CGDictionary(CGThing):
michael@0 10396 def __init__(self, dictionary, descriptorProvider):
michael@0 10397 self.dictionary = dictionary
michael@0 10398 self.descriptorProvider = descriptorProvider
michael@0 10399 self.needToInitIds = len(dictionary.members) > 0
michael@0 10400 self.memberInfo = [
michael@0 10401 (member,
michael@0 10402 getJSToNativeConversionInfo(
michael@0 10403 member.type,
michael@0 10404 descriptorProvider,
michael@0 10405 isEnforceRange=member.enforceRange,
michael@0 10406 isClamp=member.clamp,
michael@0 10407 isMember="Dictionary",
michael@0 10408 isOptional=(not member.defaultValue),
michael@0 10409 defaultValue=member.defaultValue,
michael@0 10410 sourceDescription=("'%s' member of %s" %
michael@0 10411 (member.identifier.name, dictionary.identifier.name))))
michael@0 10412 for member in dictionary.members]
michael@0 10413
michael@0 10414 # If we have a union member containing something in the same
michael@0 10415 # file as us, bail: the C++ includes won't work out.
michael@0 10416 for member in dictionary.members:
michael@0 10417 type = member.type.unroll()
michael@0 10418 if type.isUnion():
michael@0 10419 for t in type.flatMemberTypes:
michael@0 10420 if (t.isDictionary() and
michael@0 10421 CGHeaders.getDeclarationFilename(t.inner) ==
michael@0 10422 CGHeaders.getDeclarationFilename(dictionary)):
michael@0 10423 raise TypeError(
michael@0 10424 "Dictionary contains a union that contains a "
michael@0 10425 "dictionary in the same WebIDL file. This won't "
michael@0 10426 "compile. Move the inner dictionary to a "
michael@0 10427 "different file.\n%s\n%s" %
michael@0 10428 (t.location, t.inner.location))
michael@0 10429 self.structs = self.getStructs()
michael@0 10430
michael@0 10431 def declare(self):
michael@0 10432 return self.structs.declare()
michael@0 10433
michael@0 10434 def define(self):
michael@0 10435 return self.structs.define()
michael@0 10436
michael@0 10437 def base(self):
michael@0 10438 if self.dictionary.parent:
michael@0 10439 return self.makeClassName(self.dictionary.parent)
michael@0 10440 return "DictionaryBase"
michael@0 10441
michael@0 10442 def initMethod(self):
michael@0 10443 body = dedent("""
michael@0 10444 // Passing a null JSContext is OK only if we're initing from null,
michael@0 10445 // Since in that case we will not have to do any property gets
michael@0 10446 MOZ_ASSERT_IF(!cx, val.isNull());
michael@0 10447 """)
michael@0 10448
michael@0 10449 if self.needToInitIds:
michael@0 10450 body += fill(
michael@0 10451 """
michael@0 10452 ${dictName}Atoms* atomsCache = nullptr;
michael@0 10453 if (cx) {
michael@0 10454 atomsCache = GetAtomCache<${dictName}Atoms>(cx);
michael@0 10455 if (!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) {
michael@0 10456 return false;
michael@0 10457 }
michael@0 10458 }
michael@0 10459
michael@0 10460 """,
michael@0 10461 dictName=self.makeClassName(self.dictionary))
michael@0 10462
michael@0 10463 if self.dictionary.parent:
michael@0 10464 body += fill(
michael@0 10465 """
michael@0 10466 // Per spec, we init the parent's members first
michael@0 10467 if (!${dictName}::Init(cx, val)) {
michael@0 10468 return false;
michael@0 10469 }
michael@0 10470 MOZ_ASSERT(IsConvertibleToDictionary(cx, val));
michael@0 10471
michael@0 10472 """,
michael@0 10473 dictName=self.makeClassName(self.dictionary.parent))
michael@0 10474 else:
michael@0 10475 body += fill(
michael@0 10476 """
michael@0 10477 if (!IsConvertibleToDictionary(cx, val)) {
michael@0 10478 return ThrowErrorMessage(cx, MSG_NOT_DICTIONARY, sourceDescription);
michael@0 10479 }
michael@0 10480
michael@0 10481 """)
michael@0 10482
michael@0 10483 memberInits = [self.getMemberConversion(m).define()
michael@0 10484 for m in self.memberInfo]
michael@0 10485 if memberInits:
michael@0 10486 body += fill(
michael@0 10487 """
michael@0 10488 bool isNull = val.isNullOrUndefined();
michael@0 10489 // We only need these if !isNull, in which case we have |cx|.
michael@0 10490 Maybe<JS::Rooted<JSObject *> > object;
michael@0 10491 Maybe<JS::Rooted<JS::Value> > temp;
michael@0 10492 if (!isNull) {
michael@0 10493 object.construct(cx, &val.toObject());
michael@0 10494 temp.construct(cx);
michael@0 10495 }
michael@0 10496 $*{memberInits}
michael@0 10497 """,
michael@0 10498 memberInits="\n".join(memberInits))
michael@0 10499
michael@0 10500 body += "return true;\n"
michael@0 10501
michael@0 10502 return ClassMethod("Init", "bool", [
michael@0 10503 Argument('JSContext*', 'cx'),
michael@0 10504 Argument('JS::Handle<JS::Value>', 'val'),
michael@0 10505 Argument('const char*', 'sourceDescription', default='"Value"')
michael@0 10506 ], body=body)
michael@0 10507
michael@0 10508 def initFromJSONMethod(self):
michael@0 10509 return ClassMethod(
michael@0 10510 "Init", "bool",
michael@0 10511 [Argument('const nsAString&', 'aJSON')],
michael@0 10512 body=dedent("""
michael@0 10513 MOZ_ASSERT(NS_IsMainThread());
michael@0 10514 AutoSafeJSContext cx;
michael@0 10515 JS::Rooted<JS::Value> json(cx);
michael@0 10516 bool ok = ParseJSON(cx, aJSON, &json);
michael@0 10517 NS_ENSURE_TRUE(ok, false);
michael@0 10518 return Init(cx, json);
michael@0 10519 """))
michael@0 10520
michael@0 10521 def toObjectMethod(self):
michael@0 10522 body = ""
michael@0 10523 if self.needToInitIds:
michael@0 10524 body += fill(
michael@0 10525 """
michael@0 10526 ${dictName}Atoms* atomsCache = GetAtomCache<${dictName}Atoms>(cx);
michael@0 10527 if (!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) {
michael@0 10528 return false;
michael@0 10529 }
michael@0 10530
michael@0 10531 """,
michael@0 10532 dictName=self.makeClassName(self.dictionary))
michael@0 10533
michael@0 10534 if self.dictionary.parent:
michael@0 10535 body += fill(
michael@0 10536 """
michael@0 10537 // Per spec, we define the parent's members first
michael@0 10538 if (!${dictName}::ToObject(cx, rval)) {
michael@0 10539 return false;
michael@0 10540 }
michael@0 10541 JS::Rooted<JSObject*> obj(cx, &rval.toObject());
michael@0 10542
michael@0 10543 """,
michael@0 10544 dictName=self.makeClassName(self.dictionary.parent))
michael@0 10545 else:
michael@0 10546 body += fill(
michael@0 10547 """
michael@0 10548 JS::Rooted<JSObject*> obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
michael@0 10549 if (!obj) {
michael@0 10550 return false;
michael@0 10551 }
michael@0 10552 rval.set(JS::ObjectValue(*obj));
michael@0 10553
michael@0 10554 """)
michael@0 10555
michael@0 10556 if self.memberInfo:
michael@0 10557 body += "\n".join(self.getMemberDefinition(m).define()
michael@0 10558 for m in self.memberInfo)
michael@0 10559 else:
michael@0 10560 body += "\n" # BOGUS extra blank line
michael@0 10561 body += "\nreturn true;\n"
michael@0 10562
michael@0 10563 return ClassMethod("ToObject", "bool", [
michael@0 10564 Argument('JSContext*', 'cx'),
michael@0 10565 Argument('JS::MutableHandle<JS::Value>', 'rval'),
michael@0 10566 ], const=True, body=body)
michael@0 10567
michael@0 10568 def initIdsMethod(self):
michael@0 10569 assert self.needToInitIds
michael@0 10570 idinit = ['!atomsCache->%s.init(cx, "%s")' %
michael@0 10571 (CGDictionary.makeIdName(m.identifier.name),
michael@0 10572 m.identifier.name)
michael@0 10573 for m in self.dictionary.members]
michael@0 10574 idinit.reverse()
michael@0 10575 body = fill(
michael@0 10576 """
michael@0 10577 MOZ_ASSERT(!*reinterpret_cast<jsid**>(atomsCache));
michael@0 10578
michael@0 10579 // Initialize these in reverse order so that any failure leaves the first one
michael@0 10580 // uninitialized.
michael@0 10581 if (${idinit}) {
michael@0 10582 return false;
michael@0 10583 }
michael@0 10584 return true;
michael@0 10585 """,
michael@0 10586 idinit=" ||\n ".join(idinit))
michael@0 10587
michael@0 10588 return ClassMethod("InitIds", "bool", [
michael@0 10589 Argument("JSContext*", "cx"),
michael@0 10590 Argument("%sAtoms*" % self.makeClassName(self.dictionary),
michael@0 10591 "atomsCache"),
michael@0 10592 ], static=True, body=body, visibility="private")
michael@0 10593
michael@0 10594 def traceDictionaryMethod(self):
michael@0 10595 body = ""
michael@0 10596 if self.dictionary.parent:
michael@0 10597 cls = self.makeClassName(self.dictionary.parent)
michael@0 10598 body += "%s::TraceDictionary(trc);\n" % cls
michael@0 10599
michael@0 10600 memberTraces = [self.getMemberTrace(m)
michael@0 10601 for m in self.dictionary.members
michael@0 10602 if typeNeedsRooting(m.type)]
michael@0 10603
michael@0 10604 if memberTraces:
michael@0 10605 body += "\n".join(memberTraces)
michael@0 10606 else:
michael@0 10607 body += "\n" # BOGUS extra newline
michael@0 10608
michael@0 10609 return ClassMethod("TraceDictionary", "void", [
michael@0 10610 Argument("JSTracer*", "trc"),
michael@0 10611 ], body=body)
michael@0 10612
michael@0 10613 def assignmentOperator(self):
michael@0 10614 body = CGList([])
michael@0 10615 if self.dictionary.parent:
michael@0 10616 body.append(CGGeneric(
michael@0 10617 "%s::operator=(aOther);\n" %
michael@0 10618 self.makeClassName(self.dictionary.parent)))
michael@0 10619 for m, _ in self.memberInfo:
michael@0 10620 memberName = self.makeMemberName(m.identifier.name)
michael@0 10621 if not m.defaultValue:
michael@0 10622 memberAssign = CGGeneric(fill(
michael@0 10623 """
michael@0 10624 if (aOther.${name}.WasPassed()) {
michael@0 10625 ${name}.Construct();
michael@0 10626 ${name}.Value() = aOther.${name}.Value();
michael@0 10627 } else {
michael@0 10628 ${name}.Reset();
michael@0 10629 }
michael@0 10630 """,
michael@0 10631 name=memberName))
michael@0 10632 else:
michael@0 10633 memberAssign = CGGeneric(
michael@0 10634 "%s = aOther.%s;\n" % (memberName, memberName))
michael@0 10635 body.append(memberAssign)
michael@0 10636 return ClassMethod(
michael@0 10637 "operator=", "void",
michael@0 10638 [Argument("const %s&" % self.makeClassName(self.dictionary),
michael@0 10639 "aOther")],
michael@0 10640 body=body.define() or "\n") # BOGUS blank line when empty
michael@0 10641
michael@0 10642 def getStructs(self):
michael@0 10643 d = self.dictionary
michael@0 10644 selfName = self.makeClassName(d)
michael@0 10645 members = [ClassMember(self.makeMemberName(m[0].identifier.name),
michael@0 10646 self.getMemberType(m),
michael@0 10647 visibility="public",
michael@0 10648 body=self.getMemberInitializer(m))
michael@0 10649 for m in self.memberInfo]
michael@0 10650 ctors = [
michael@0 10651 ClassConstructor(
michael@0 10652 [],
michael@0 10653 visibility="public",
michael@0 10654 body=(
michael@0 10655 "// Safe to pass a null context if we pass a null value\n"
michael@0 10656 "Init(nullptr, JS::NullHandleValue);\n")),
michael@0 10657 ClassConstructor(
michael@0 10658 [Argument("int", "")],
michael@0 10659 visibility="protected",
michael@0 10660 explicit=True,
michael@0 10661 bodyInHeader=True,
michael@0 10662 body='// Do nothing here; this is used by our "Fast" subclass\n')
michael@0 10663 ]
michael@0 10664 methods = []
michael@0 10665
michael@0 10666 if self.needToInitIds:
michael@0 10667 methods.append(self.initIdsMethod())
michael@0 10668
michael@0 10669 methods.append(self.initMethod())
michael@0 10670 methods.append(self.initFromJSONMethod())
michael@0 10671 try:
michael@0 10672 methods.append(self.toObjectMethod())
michael@0 10673 except MethodNotNewObjectError:
michael@0 10674 # If we can't have a ToObject() because one of our members can only
michael@0 10675 # be returned from [NewObject] methods, then just skip generating
michael@0 10676 # ToObject().
michael@0 10677 pass
michael@0 10678 methods.append(self.traceDictionaryMethod())
michael@0 10679
michael@0 10680 if CGDictionary.isDictionaryCopyConstructible(d):
michael@0 10681 disallowCopyConstruction = False
michael@0 10682 # Note: no base constructors because our operator= will
michael@0 10683 # deal with that.
michael@0 10684 ctors.append(ClassConstructor([Argument("const %s&" % selfName,
michael@0 10685 "aOther")],
michael@0 10686 bodyInHeader=True,
michael@0 10687 visibility="public",
michael@0 10688 explicit=True,
michael@0 10689 body="*this = aOther;\n"))
michael@0 10690 methods.append(self.assignmentOperator())
michael@0 10691 else:
michael@0 10692 disallowCopyConstruction = True
michael@0 10693
michael@0 10694 struct = CGClass(selfName,
michael@0 10695 bases=[ClassBase(self.base())],
michael@0 10696 members=members,
michael@0 10697 constructors=ctors,
michael@0 10698 methods=methods,
michael@0 10699 isStruct=True,
michael@0 10700 disallowCopyConstruction=disallowCopyConstruction)
michael@0 10701
michael@0 10702 fastDictionaryCtor = ClassConstructor(
michael@0 10703 [],
michael@0 10704 visibility="public",
michael@0 10705 bodyInHeader=True,
michael@0 10706 baseConstructors=["%s(42)" % selfName],
michael@0 10707 body="// Doesn't matter what int we pass to the parent constructor\n")
michael@0 10708
michael@0 10709 fastStruct = CGClass("Fast" + selfName,
michael@0 10710 bases=[ClassBase(selfName)],
michael@0 10711 constructors=[fastDictionaryCtor],
michael@0 10712 isStruct=True)
michael@0 10713
michael@0 10714 return CGList([struct,
michael@0 10715 CGNamespace('binding_detail', fastStruct)],
michael@0 10716 "\n")
michael@0 10717
michael@0 10718 def deps(self):
michael@0 10719 return self.dictionary.getDeps()
michael@0 10720
michael@0 10721 @staticmethod
michael@0 10722 def makeDictionaryName(dictionary):
michael@0 10723 return dictionary.identifier.name
michael@0 10724
michael@0 10725 def makeClassName(self, dictionary):
michael@0 10726 return self.makeDictionaryName(dictionary)
michael@0 10727
michael@0 10728 @staticmethod
michael@0 10729 def makeMemberName(name):
michael@0 10730 return "m" + name[0].upper() + name[1:]
michael@0 10731
michael@0 10732 def getMemberType(self, memberInfo):
michael@0 10733 _, conversionInfo = memberInfo
michael@0 10734 # We can't handle having a holderType here
michael@0 10735 assert conversionInfo.holderType is None
michael@0 10736 declType = conversionInfo.declType
michael@0 10737 if conversionInfo.dealWithOptional:
michael@0 10738 declType = CGTemplatedType("Optional", declType)
michael@0 10739 return declType.define()
michael@0 10740
michael@0 10741 def getMemberConversion(self, memberInfo):
michael@0 10742 member, conversionInfo = memberInfo
michael@0 10743 replacements = {
michael@0 10744 "val": "temp.ref()",
michael@0 10745 "mutableVal": "&temp.ref()",
michael@0 10746 "declName": self.makeMemberName(member.identifier.name),
michael@0 10747 # We need a holder name for external interfaces, but
michael@0 10748 # it's scoped down to the conversion so we can just use
michael@0 10749 # anything we want.
michael@0 10750 "holderName": "holder"
michael@0 10751 }
michael@0 10752 # We can't handle having a holderType here
michael@0 10753 assert conversionInfo.holderType is None
michael@0 10754 if conversionInfo.dealWithOptional:
michael@0 10755 replacements["declName"] = "(" + replacements["declName"] + ".Value())"
michael@0 10756 if member.defaultValue:
michael@0 10757 replacements["haveValue"] = "!isNull && !temp.ref().isUndefined()"
michael@0 10758
michael@0 10759 propId = self.makeIdName(member.identifier.name)
michael@0 10760 propGet = ("JS_GetPropertyById(cx, object.ref(), atomsCache->%s, &temp.ref())" %
michael@0 10761 propId)
michael@0 10762
michael@0 10763 conversionReplacements = {
michael@0 10764 "prop": self.makeMemberName(member.identifier.name),
michael@0 10765 "convert": string.Template(conversionInfo.template).substitute(replacements),
michael@0 10766 "propGet": propGet
michael@0 10767 }
michael@0 10768 conversion = ("if (!isNull && !${propGet}) {\n"
michael@0 10769 " return false;\n"
michael@0 10770 "}\n")
michael@0 10771 if member.defaultValue:
michael@0 10772 conversion += "${convert}"
michael@0 10773 else:
michael@0 10774 conversion += (
michael@0 10775 "if (!isNull && !temp.ref().isUndefined()) {\n"
michael@0 10776 " ${prop}.Construct();\n"
michael@0 10777 "${convert}"
michael@0 10778 "}\n")
michael@0 10779 conversionReplacements["convert"] = indent(conversionReplacements["convert"])
michael@0 10780
michael@0 10781 return CGGeneric(
michael@0 10782 string.Template(conversion).substitute(conversionReplacements))
michael@0 10783
michael@0 10784 def getMemberDefinition(self, memberInfo):
michael@0 10785 member = memberInfo[0]
michael@0 10786 declType = memberInfo[1].declType
michael@0 10787 memberLoc = self.makeMemberName(member.identifier.name)
michael@0 10788 if member.defaultValue:
michael@0 10789 memberData = memberLoc
michael@0 10790 else:
michael@0 10791 # The data is inside the Optional<>
michael@0 10792 memberData = "%s.InternalValue()" % memberLoc
michael@0 10793
michael@0 10794 # If you have to change this list (which you shouldn't!), make sure it
michael@0 10795 # continues to match the list in test_Object.prototype_props.html
michael@0 10796 if (member.identifier.name in
michael@0 10797 ["constructor", "toSource", "toString", "toLocaleString", "valueOf",
michael@0 10798 "watch", "unwatch", "hasOwnProperty", "isPrototypeOf",
michael@0 10799 "propertyIsEnumerable", "__defineGetter__", "__defineSetter__",
michael@0 10800 "__lookupGetter__", "__lookupSetter__", "__proto__"]):
michael@0 10801 raise TypeError("'%s' member of %s dictionary shadows "
michael@0 10802 "a property of Object.prototype, and Xrays to "
michael@0 10803 "Object can't handle that.\n"
michael@0 10804 "%s" %
michael@0 10805 (member.identifier.name,
michael@0 10806 self.dictionary.identifier.name,
michael@0 10807 member.location))
michael@0 10808
michael@0 10809 propDef = (
michael@0 10810 'JS_DefinePropertyById(cx, obj, atomsCache->%s, temp, nullptr, nullptr, JSPROP_ENUMERATE)' %
michael@0 10811 self.makeIdName(member.identifier.name))
michael@0 10812
michael@0 10813 innerTemplate = wrapForType(
michael@0 10814 member.type, self.descriptorProvider,
michael@0 10815 {
michael@0 10816 'result': "currentValue",
michael@0 10817 'successCode': ("if (!%s) {\n"
michael@0 10818 " return false;\n"
michael@0 10819 "}\n"
michael@0 10820 "break;\n" % propDef),
michael@0 10821 'jsvalRef': "temp",
michael@0 10822 'jsvalHandle': "&temp",
michael@0 10823 'returnsNewObject': False,
michael@0 10824 # 'obj' can just be allowed to be the string "obj", since that
michael@0 10825 # will be our dictionary object, which is presumably itself in
michael@0 10826 # the right scope.
michael@0 10827 'typedArraysAreStructs': True
michael@0 10828 })
michael@0 10829 conversion = CGGeneric(innerTemplate)
michael@0 10830 conversion = CGWrapper(conversion,
michael@0 10831 pre=("JS::Rooted<JS::Value> temp(cx);\n"
michael@0 10832 "%s const & currentValue = %s;\n" %
michael@0 10833 (declType.define(), memberData)
michael@0 10834 ))
michael@0 10835
michael@0 10836 # Now make sure that our successCode can actually break out of the
michael@0 10837 # conversion. This incidentally gives us a scope for 'temp' and
michael@0 10838 # 'currentValue'.
michael@0 10839 conversion = CGWrapper(
michael@0 10840 CGIndenter(conversion),
michael@0 10841 pre=("do {\n"
michael@0 10842 " // block for our 'break' successCode and scope for 'temp' and 'currentValue'\n"),
michael@0 10843 post="} while(0);\n")
michael@0 10844 if not member.defaultValue:
michael@0 10845 # Only do the conversion if we have a value
michael@0 10846 conversion = CGIfWrapper(conversion, "%s.WasPassed()" % memberLoc)
michael@0 10847 return conversion
michael@0 10848
michael@0 10849 def getMemberTrace(self, member):
michael@0 10850 type = member.type
michael@0 10851 assert typeNeedsRooting(type)
michael@0 10852 memberLoc = self.makeMemberName(member.identifier.name)
michael@0 10853 if member.defaultValue:
michael@0 10854 memberData = memberLoc
michael@0 10855 else:
michael@0 10856 # The data is inside the Optional<>
michael@0 10857 memberData = "%s.Value()" % memberLoc
michael@0 10858
michael@0 10859 memberName = "%s.%s" % (self.makeClassName(self.dictionary),
michael@0 10860 memberLoc)
michael@0 10861
michael@0 10862 if type.isObject():
michael@0 10863 trace = CGGeneric('JS_CallObjectTracer(trc, %s, "%s");\n' %
michael@0 10864 ("&"+memberData, memberName))
michael@0 10865 if type.nullable():
michael@0 10866 trace = CGIfWrapper(trace, memberData)
michael@0 10867 elif type.isAny():
michael@0 10868 trace = CGGeneric('JS_CallValueTracer(trc, %s, "%s");\n' %
michael@0 10869 ("&"+memberData, memberName))
michael@0 10870 elif (type.isSequence() or type.isDictionary() or
michael@0 10871 type.isSpiderMonkeyInterface() or type.isUnion()):
michael@0 10872 if type.nullable():
michael@0 10873 memberNullable = memberData
michael@0 10874 memberData = "%s.Value()" % memberData
michael@0 10875 if type.isSequence():
michael@0 10876 trace = CGGeneric('DoTraceSequence(trc, %s);\n' % memberData)
michael@0 10877 elif type.isDictionary():
michael@0 10878 trace = CGGeneric('%s.TraceDictionary(trc);\n' % memberData)
michael@0 10879 elif type.isUnion():
michael@0 10880 trace = CGGeneric('%s.TraceUnion(trc);\n' % memberData)
michael@0 10881 else:
michael@0 10882 assert type.isSpiderMonkeyInterface()
michael@0 10883 trace = CGGeneric('%s.TraceSelf(trc);\n' % memberData)
michael@0 10884 if type.nullable():
michael@0 10885 trace = CGIfWrapper(trace, "!%s.IsNull()" % memberNullable)
michael@0 10886 else:
michael@0 10887 assert False # unknown type
michael@0 10888
michael@0 10889 if not member.defaultValue:
michael@0 10890 trace = CGIfWrapper(trace, "%s.WasPassed()" % memberLoc)
michael@0 10891
michael@0 10892 return trace.define()
michael@0 10893
michael@0 10894 def getMemberInitializer(self, memberInfo):
michael@0 10895 """
michael@0 10896 Get the right initializer for the member. Most members don't need one,
michael@0 10897 but we need to pre-initialize 'any' and 'object' that have a default
michael@0 10898 value, so they're safe to trace at all times.
michael@0 10899 """
michael@0 10900 member, _ = memberInfo
michael@0 10901 if not member.defaultValue:
michael@0 10902 # No default value means no need to set it up front, since it's
michael@0 10903 # inside an Optional and won't get traced until it's actually set
michael@0 10904 # up.
michael@0 10905 return None
michael@0 10906 type = member.type
michael@0 10907 if type.isAny():
michael@0 10908 return "JS::UndefinedValue()"
michael@0 10909 if type.isObject():
michael@0 10910 return "nullptr"
michael@0 10911 return None
michael@0 10912
michael@0 10913 @staticmethod
michael@0 10914 def makeIdName(name):
michael@0 10915 return name + "_id"
michael@0 10916
michael@0 10917 @staticmethod
michael@0 10918 def getDictionaryDependenciesFromType(type):
michael@0 10919 if type.isDictionary():
michael@0 10920 return set([type.unroll().inner])
michael@0 10921 if type.isSequence() or type.isArray():
michael@0 10922 return CGDictionary.getDictionaryDependenciesFromType(type.unroll())
michael@0 10923 return set()
michael@0 10924
michael@0 10925 @staticmethod
michael@0 10926 def getDictionaryDependencies(dictionary):
michael@0 10927 deps = set()
michael@0 10928 if dictionary.parent:
michael@0 10929 deps.add(dictionary.parent)
michael@0 10930 for member in dictionary.members:
michael@0 10931 deps |= CGDictionary.getDictionaryDependenciesFromType(member.type)
michael@0 10932 return deps
michael@0 10933
michael@0 10934 @staticmethod
michael@0 10935 def isDictionaryCopyConstructible(dictionary):
michael@0 10936 if (dictionary.parent and
michael@0 10937 not CGDictionary.isDictionaryCopyConstructible(dictionary.parent)):
michael@0 10938 return False
michael@0 10939 return all(isTypeCopyConstructible(m.type) for m in dictionary.members)
michael@0 10940
michael@0 10941
michael@0 10942 class CGRegisterProtos(CGAbstractMethod):
michael@0 10943 def __init__(self, config):
michael@0 10944 CGAbstractMethod.__init__(self, None, 'Register', 'void',
michael@0 10945 [Argument('nsScriptNameSpaceManager*', 'aNameSpaceManager')])
michael@0 10946 self.config = config
michael@0 10947
michael@0 10948 def _defineMacro(self):
michael@0 10949 return dedent("""
michael@0 10950 #define REGISTER_PROTO(_dom_class, _ctor_check) \\
michael@0 10951 aNameSpaceManager->RegisterDefineDOMInterface(MOZ_UTF16(#_dom_class), _dom_class##Binding::DefineDOMInterface, _ctor_check);
michael@0 10952 #define REGISTER_CONSTRUCTOR(_dom_constructor, _dom_class, _ctor_check) \\
michael@0 10953 aNameSpaceManager->RegisterDefineDOMInterface(MOZ_UTF16(#_dom_constructor), _dom_class##Binding::DefineDOMInterface, _ctor_check);
michael@0 10954 #define REGISTER_NAVIGATOR_CONSTRUCTOR(_prop, _dom_class, _ctor_check) \\
michael@0 10955 aNameSpaceManager->RegisterNavigatorDOMConstructor(MOZ_UTF16(_prop), _dom_class##Binding::ConstructNavigatorObject, _ctor_check);
michael@0 10956 """)
michael@0 10957
michael@0 10958 def _undefineMacro(self):
michael@0 10959 return dedent("""
michael@0 10960 #undef REGISTER_CONSTRUCTOR
michael@0 10961 #undef REGISTER_PROTO
michael@0 10962 #undef REGISTER_NAVIGATOR_CONSTRUCTOR
michael@0 10963 """)
michael@0 10964
michael@0 10965 def _registerProtos(self):
michael@0 10966 def getCheck(desc):
michael@0 10967 if not desc.isExposedConditionally():
michael@0 10968 return "nullptr"
michael@0 10969 return "%sBinding::ConstructorEnabled" % desc.name
michael@0 10970 lines = []
michael@0 10971 for desc in self.config.getDescriptors(hasInterfaceObject=True,
michael@0 10972 isExternal=False,
michael@0 10973 workers=False,
michael@0 10974 register=True):
michael@0 10975 lines.append("REGISTER_PROTO(%s, %s);\n" % (desc.name, getCheck(desc)))
michael@0 10976 lines.extend("REGISTER_CONSTRUCTOR(%s, %s, %s);\n" % (n.identifier.name, desc.name, getCheck(desc))
michael@0 10977 for n in desc.interface.namedConstructors)
michael@0 10978 for desc in self.config.getDescriptors(isNavigatorProperty=True, register=True):
michael@0 10979 propName = desc.interface.getNavigatorProperty()
michael@0 10980 assert propName
michael@0 10981 lines.append('REGISTER_NAVIGATOR_CONSTRUCTOR("%s", %s, %s);\n' % (propName, desc.name, getCheck(desc)))
michael@0 10982 return ''.join(lines)
michael@0 10983
michael@0 10984 def definition_body(self):
michael@0 10985 return "\n" + self._defineMacro() + "\n" + self._registerProtos() + "\n" + self._undefineMacro()
michael@0 10986
michael@0 10987
michael@0 10988 def dependencySortObjects(objects, dependencyGetter, nameGetter):
michael@0 10989 """
michael@0 10990 Sort IDL objects with dependencies on each other such that if A
michael@0 10991 depends on B then B will come before A. This is needed for
michael@0 10992 declaring C++ classes in the right order, for example. Objects
michael@0 10993 that have no dependencies are just sorted by name.
michael@0 10994
michael@0 10995 objects should be something that can produce a set of objects
michael@0 10996 (e.g. a set, iterator, list, etc).
michael@0 10997
michael@0 10998 dependencyGetter is something that, given an object, should return
michael@0 10999 the set of objects it depends on.
michael@0 11000 """
michael@0 11001 # XXXbz this will fail if we have two webidl files F1 and F2 such that F1
michael@0 11002 # declares an object which depends on an object in F2, and F2 declares an
michael@0 11003 # object (possibly a different one!) that depends on an object in F1. The
michael@0 11004 # good news is that I expect this to never happen.
michael@0 11005 sortedObjects = []
michael@0 11006 objects = set(objects)
michael@0 11007 while len(objects) != 0:
michael@0 11008 # Find the dictionaries that don't depend on anything else
michael@0 11009 # anymore and move them over.
michael@0 11010 toMove = [o for o in objects if
michael@0 11011 len(dependencyGetter(o) & objects) == 0]
michael@0 11012 if len(toMove) == 0:
michael@0 11013 raise TypeError("Loop in dependency graph\n" +
michael@0 11014 "\n".join(o.location for o in objects))
michael@0 11015 objects = objects - set(toMove)
michael@0 11016 sortedObjects.extend(sorted(toMove, key=nameGetter))
michael@0 11017 return sortedObjects
michael@0 11018
michael@0 11019
michael@0 11020 class ForwardDeclarationBuilder:
michael@0 11021 """
michael@0 11022 Create a canonical representation of a set of namespaced forward
michael@0 11023 declarations.
michael@0 11024 """
michael@0 11025 def __init__(self):
michael@0 11026 """
michael@0 11027 The set of declarations is represented as a tree of nested namespaces.
michael@0 11028 Each tree node has a set of declarations |decls| and a dict |children|.
michael@0 11029 Each declaration is a pair consisting of the class name and a boolean
michael@0 11030 that is true iff the class is really a struct. |children| maps the
michael@0 11031 names of inner namespaces to the declarations in that namespace.
michael@0 11032 """
michael@0 11033 self.decls = set()
michael@0 11034 self.children = {}
michael@0 11035
michael@0 11036 def _listAdd(self, namespaces, name, isStruct=False):
michael@0 11037 """
michael@0 11038 Add a forward declaration, where |namespaces| is a list of namespaces.
michael@0 11039 |name| should not contain any other namespaces.
michael@0 11040 """
michael@0 11041 if namespaces:
michael@0 11042 child = self.children.setdefault(namespaces[0], ForwardDeclarationBuilder())
michael@0 11043 child._listAdd(namespaces[1:], name, isStruct)
michael@0 11044 else:
michael@0 11045 assert '::' not in name
michael@0 11046 self.decls.add((name, isStruct))
michael@0 11047
michael@0 11048 def addInMozillaDom(self, name, isStruct=False):
michael@0 11049 """
michael@0 11050 Add a forward declaration to the mozilla::dom:: namespace. |name| should not
michael@0 11051 contain any other namespaces.
michael@0 11052 """
michael@0 11053 self._listAdd(["mozilla", "dom"], name, isStruct)
michael@0 11054
michael@0 11055 def add(self, nativeType, isStruct=False):
michael@0 11056 """
michael@0 11057 Add a forward declaration, where |nativeType| is a string containing
michael@0 11058 the type and its namespaces, in the usual C++ way.
michael@0 11059 """
michael@0 11060 components = nativeType.split('::')
michael@0 11061 self._listAdd(components[:-1], components[-1], isStruct)
michael@0 11062
michael@0 11063 def _build(self, atTopLevel):
michael@0 11064 """
michael@0 11065 Return a codegenerator for the forward declarations.
michael@0 11066 """
michael@0 11067 decls = []
michael@0 11068 if self.decls:
michael@0 11069 decls.append(CGList([CGClassForwardDeclare(cname, isStruct)
michael@0 11070 for cname, isStruct in sorted(self.decls)]))
michael@0 11071 for namespace, child in sorted(self.children.iteritems()):
michael@0 11072 decls.append(CGNamespace(namespace, child._build(atTopLevel=False), declareOnly=True))
michael@0 11073
michael@0 11074 cg = CGList(decls, "\n")
michael@0 11075 if not atTopLevel and len(decls) + len(self.decls) > 1:
michael@0 11076 cg = CGWrapper(cg, pre='\n', post='\n')
michael@0 11077 return cg
michael@0 11078
michael@0 11079 def build(self):
michael@0 11080 return self._build(atTopLevel=True)
michael@0 11081
michael@0 11082
michael@0 11083 class CGForwardDeclarations(CGWrapper):
michael@0 11084 """
michael@0 11085 Code generate the forward declarations for a header file.
michael@0 11086 """
michael@0 11087 def __init__(self, config, descriptors, mainCallbacks, workerCallbacks,
michael@0 11088 dictionaries, callbackInterfaces):
michael@0 11089 builder = ForwardDeclarationBuilder()
michael@0 11090
michael@0 11091 def forwardDeclareForType(t, workerness='both'):
michael@0 11092 t = t.unroll()
michael@0 11093 if t.isGeckoInterface():
michael@0 11094 name = t.inner.identifier.name
michael@0 11095 # Find and add the non-worker implementation, if any.
michael@0 11096 if workerness != 'workeronly':
michael@0 11097 try:
michael@0 11098 desc = config.getDescriptor(name, False)
michael@0 11099 builder.add(desc.nativeType)
michael@0 11100 except NoSuchDescriptorError:
michael@0 11101 pass
michael@0 11102 # Find and add the worker implementation, if any.
michael@0 11103 if workerness != 'mainthreadonly':
michael@0 11104 try:
michael@0 11105 desc = config.getDescriptor(name, True)
michael@0 11106 builder.add(desc.nativeType)
michael@0 11107 except NoSuchDescriptorError:
michael@0 11108 pass
michael@0 11109 elif t.isCallback():
michael@0 11110 builder.addInMozillaDom(str(t))
michael@0 11111 elif t.isDictionary():
michael@0 11112 builder.addInMozillaDom(t.inner.identifier.name, isStruct=True)
michael@0 11113 elif t.isCallbackInterface():
michael@0 11114 builder.addInMozillaDom(t.inner.identifier.name)
michael@0 11115 elif t.isUnion():
michael@0 11116 # Forward declare both the owning and non-owning version,
michael@0 11117 # since we don't know which one we might want
michael@0 11118 builder.addInMozillaDom(CGUnionStruct.unionTypeName(t, False))
michael@0 11119 builder.addInMozillaDom(CGUnionStruct.unionTypeName(t, True))
michael@0 11120 elif t.isMozMap():
michael@0 11121 forwardDeclareForType(t.inner, workerness)
michael@0 11122 # Don't need to do anything for void, primitive, string, any or object.
michael@0 11123 # There may be some other cases we are missing.
michael@0 11124
michael@0 11125 # Needed for at least Wrap.
michael@0 11126 for d in descriptors:
michael@0 11127 builder.add(d.nativeType)
michael@0 11128
michael@0 11129 # We just about always need NativePropertyHooks
michael@0 11130 builder.addInMozillaDom("NativePropertyHooks")
michael@0 11131 builder.addInMozillaDom("ProtoAndIfaceCache")
michael@0 11132
michael@0 11133 for callback in mainCallbacks:
michael@0 11134 forwardDeclareForType(callback)
michael@0 11135 for t in getTypesFromCallback(callback):
michael@0 11136 forwardDeclareForType(t, workerness='mainthreadonly')
michael@0 11137
michael@0 11138 for callback in workerCallbacks:
michael@0 11139 forwardDeclareForType(callback)
michael@0 11140 for t in getTypesFromCallback(callback):
michael@0 11141 forwardDeclareForType(t, workerness='workeronly')
michael@0 11142
michael@0 11143 for d in callbackInterfaces:
michael@0 11144 builder.add(d.nativeType)
michael@0 11145 for t in getTypesFromDescriptor(d):
michael@0 11146 forwardDeclareForType(t)
michael@0 11147
michael@0 11148 for d in dictionaries:
michael@0 11149 if len(d.members) > 0:
michael@0 11150 builder.addInMozillaDom(d.identifier.name + "Atoms", isStruct=True)
michael@0 11151 for t in getTypesFromDictionary(d):
michael@0 11152 forwardDeclareForType(t)
michael@0 11153
michael@0 11154 CGWrapper.__init__(self, builder.build())
michael@0 11155
michael@0 11156
michael@0 11157 class CGBindingRoot(CGThing):
michael@0 11158 """
michael@0 11159 Root codegen class for binding generation. Instantiate the class, and call
michael@0 11160 declare or define to generate header or cpp code (respectively).
michael@0 11161 """
michael@0 11162 def __init__(self, config, prefix, webIDLFile):
michael@0 11163 bindingHeaders = {}
michael@0 11164 descriptors = config.getDescriptors(webIDLFile=webIDLFile,
michael@0 11165 hasInterfaceOrInterfacePrototypeObject=True,
michael@0 11166 skipGen=False)
michael@0 11167
michael@0 11168 def descriptorRequiresPreferences(desc):
michael@0 11169 iface = desc.interface
michael@0 11170 return any(m.getExtendedAttribute("Pref") for m in iface.members + [iface])
michael@0 11171
michael@0 11172 bindingHeaders["mozilla/Preferences.h"] = any(
michael@0 11173 descriptorRequiresPreferences(d) for d in descriptors)
michael@0 11174 bindingHeaders["mozilla/dom/NonRefcountedDOMObject.h"] = any(
michael@0 11175 d.nativeOwnership == 'owned' for d in descriptors)
michael@0 11176 bindingHeaders["mozilla/dom/DOMJSProxyHandler.h"] = any(
michael@0 11177 d.concrete and d.proxy for d in descriptors)
michael@0 11178
michael@0 11179 def descriptorHasChromeOnly(desc):
michael@0 11180 return (any(isChromeOnly(a) for a in desc.interface.members) or
michael@0 11181 desc.interface.getExtendedAttribute("ChromeOnly") is not None or
michael@0 11182 # JS-implemented interfaces with an interface object get a
michael@0 11183 # chromeonly _create method.
michael@0 11184 (desc.interface.isJSImplemented() and
michael@0 11185 desc.interface.hasInterfaceObject()))
michael@0 11186
michael@0 11187 bindingHeaders["nsContentUtils.h"] = any(
michael@0 11188 descriptorHasChromeOnly(d) for d in descriptors)
michael@0 11189 # XXXkhuey ugly hack but this is going away soon.
michael@0 11190 bindingHeaders['xpcprivate.h'] = webIDLFile.endswith("EventTarget.webidl")
michael@0 11191 hasWorkerStuff = len(config.getDescriptors(webIDLFile=webIDLFile,
michael@0 11192 workers=True)) != 0
michael@0 11193 bindingHeaders["WorkerPrivate.h"] = hasWorkerStuff
michael@0 11194 bindingHeaders["nsThreadUtils.h"] = hasWorkerStuff
michael@0 11195
michael@0 11196 dictionaries = config.getDictionaries(webIDLFile=webIDLFile)
michael@0 11197 bindingHeaders["nsCxPusher.h"] = dictionaries
michael@0 11198 bindingHeaders["AtomList.h"] = any(
michael@0 11199 len(dict.members) > 0 for dict in dictionaries)
michael@0 11200 mainCallbacks = config.getCallbacks(webIDLFile=webIDLFile,
michael@0 11201 workers=False)
michael@0 11202 workerCallbacks = config.getCallbacks(webIDLFile=webIDLFile,
michael@0 11203 workers=True)
michael@0 11204 callbackDescriptors = config.getDescriptors(webIDLFile=webIDLFile,
michael@0 11205 isCallback=True)
michael@0 11206 jsImplemented = config.getDescriptors(webIDLFile=webIDLFile,
michael@0 11207 isJSImplemented=True)
michael@0 11208 bindingHeaders["nsPIDOMWindow.h"] = jsImplemented
michael@0 11209
michael@0 11210 def addHeaderBasedOnTypes(header, typeChecker):
michael@0 11211 bindingHeaders[header] = (
michael@0 11212 bindingHeaders.get(header, False) or
michael@0 11213 any(map(typeChecker,
michael@0 11214 getAllTypes(descriptors + callbackDescriptors,
michael@0 11215 dictionaries,
michael@0 11216 mainCallbacks + workerCallbacks))))
michael@0 11217
michael@0 11218 bindingHeaders["nsDOMQS.h"] = any(d.hasXPConnectImpls for d in descriptors)
michael@0 11219 # Only mainthread things can have hasXPConnectImpls
michael@0 11220 provider = config.getDescriptorProvider(False)
michael@0 11221
michael@0 11222 def checkForXPConnectImpls(typeInfo):
michael@0 11223 type, _, _ = typeInfo
michael@0 11224 type = type.unroll()
michael@0 11225 while type.isMozMap():
michael@0 11226 type = type.inner.unroll()
michael@0 11227 if not type.isInterface() or not type.isGeckoInterface():
michael@0 11228 return False
michael@0 11229 try:
michael@0 11230 typeDesc = provider.getDescriptor(type.inner.identifier.name)
michael@0 11231 except NoSuchDescriptorError:
michael@0 11232 return False
michael@0 11233 return typeDesc.hasXPConnectImpls
michael@0 11234 addHeaderBasedOnTypes("nsDOMQS.h", checkForXPConnectImpls)
michael@0 11235
michael@0 11236 def descriptorClearsPropsInSlots(descriptor):
michael@0 11237 if not descriptor.wrapperCache:
michael@0 11238 return False
michael@0 11239 return any(m.isAttr() and m.getExtendedAttribute("StoreInSlot")
michael@0 11240 for m in descriptor.interface.members)
michael@0 11241 bindingHeaders["nsJSUtils.h"] = any(descriptorClearsPropsInSlots(d) for d in descriptors)
michael@0 11242
michael@0 11243 # Do codegen for all the enums
michael@0 11244 enums = config.getEnums(webIDLFile)
michael@0 11245 cgthings = [CGEnum(e) for e in enums]
michael@0 11246
michael@0 11247 hasCode = (descriptors or callbackDescriptors or dictionaries or
michael@0 11248 mainCallbacks or workerCallbacks)
michael@0 11249 bindingHeaders["mozilla/dom/BindingUtils.h"] = hasCode
michael@0 11250 bindingHeaders["mozilla/dom/OwningNonNull.h"] = hasCode
michael@0 11251 bindingHeaders["mozilla/dom/BindingDeclarations.h"] = (
michael@0 11252 not hasCode and enums)
michael@0 11253
michael@0 11254 bindingHeaders["WrapperFactory.h"] = descriptors
michael@0 11255 bindingHeaders["mozilla/dom/DOMJSClass.h"] = descriptors
michael@0 11256
michael@0 11257 # Do codegen for all the dictionaries. We have to be a bit careful
michael@0 11258 # here, because we have to generate these in order from least derived
michael@0 11259 # to most derived so that class inheritance works out. We also have to
michael@0 11260 # generate members before the dictionary that contains them.
michael@0 11261 cgthings.extend([CGDictionary(d, config.getDescriptorProvider(False))
michael@0 11262 for d in
michael@0 11263 dependencySortObjects(dictionaries,
michael@0 11264 CGDictionary.getDictionaryDependencies,
michael@0 11265 lambda d: d.identifier.name)])
michael@0 11266
michael@0 11267 # Do codegen for all the callbacks.
michael@0 11268 cgthings.extend(CGCallbackFunction(c, config.getDescriptorProvider(False))
michael@0 11269 for c in mainCallbacks)
michael@0 11270
michael@0 11271 cgthings.extend(CGCallbackFunction(c, config.getDescriptorProvider(True))
michael@0 11272 for c in workerCallbacks if c not in mainCallbacks)
michael@0 11273
michael@0 11274 # Do codegen for all the descriptors
michael@0 11275 cgthings.extend([CGDescriptor(x) for x in descriptors])
michael@0 11276
michael@0 11277 # Do codegen for all the callback interfaces. Skip worker callbacks.
michael@0 11278 cgthings.extend([CGCallbackInterface(x) for x in callbackDescriptors if
michael@0 11279 not x.workers])
michael@0 11280
michael@0 11281 # Do codegen for JS implemented classes
michael@0 11282 def getParentDescriptor(desc):
michael@0 11283 if not desc.interface.parent:
michael@0 11284 return set()
michael@0 11285 return {desc.getDescriptor(desc.interface.parent.identifier.name)}
michael@0 11286 for x in dependencySortObjects(jsImplemented, getParentDescriptor,
michael@0 11287 lambda d: d.interface.identifier.name):
michael@0 11288 cgthings.append(CGCallbackInterface(x))
michael@0 11289 cgthings.append(CGJSImplClass(x))
michael@0 11290
michael@0 11291 # And make sure we have the right number of newlines at the end
michael@0 11292 curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n")
michael@0 11293
michael@0 11294 # Wrap all of that in our namespaces.
michael@0 11295 curr = CGNamespace.build(['mozilla', 'dom'],
michael@0 11296 CGWrapper(curr, pre="\n"))
michael@0 11297
michael@0 11298 curr = CGList([CGForwardDeclarations(config, descriptors,
michael@0 11299 mainCallbacks, workerCallbacks,
michael@0 11300 dictionaries,
michael@0 11301 callbackDescriptors + jsImplemented),
michael@0 11302 curr],
michael@0 11303 "\n")
michael@0 11304
michael@0 11305 # Add header includes.
michael@0 11306 bindingHeaders = [header
michael@0 11307 for header, include in bindingHeaders.iteritems()
michael@0 11308 if include]
michael@0 11309 declareIncludes = [
michael@0 11310 'mozilla/dom/BindingDeclarations.h',
michael@0 11311 'mozilla/dom/Nullable.h',
michael@0 11312 'mozilla/ErrorResult.h',
michael@0 11313 'jspubtd.h',
michael@0 11314 'js/RootingAPI.h',
michael@0 11315 ]
michael@0 11316 if jsImplemented:
michael@0 11317 declareIncludes.append('nsWeakReference.h')
michael@0 11318
michael@0 11319 curr = CGHeaders(descriptors,
michael@0 11320 dictionaries,
michael@0 11321 mainCallbacks + workerCallbacks,
michael@0 11322 callbackDescriptors,
michael@0 11323 declareIncludes,
michael@0 11324 bindingHeaders,
michael@0 11325 prefix,
michael@0 11326 curr,
michael@0 11327 config,
michael@0 11328 jsImplemented)
michael@0 11329
michael@0 11330 # Add include guards.
michael@0 11331 curr = CGIncludeGuard(prefix, curr)
michael@0 11332
michael@0 11333 # Add the auto-generated comment.
michael@0 11334 curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
michael@0 11335
michael@0 11336 # Store the final result.
michael@0 11337 self.root = curr
michael@0 11338
michael@0 11339 def declare(self):
michael@0 11340 return stripTrailingWhitespace(self.root.declare())
michael@0 11341
michael@0 11342 def define(self):
michael@0 11343 return stripTrailingWhitespace(self.root.define())
michael@0 11344
michael@0 11345 def deps(self):
michael@0 11346 return self.root.deps()
michael@0 11347
michael@0 11348
michael@0 11349 class CGNativeMember(ClassMethod):
michael@0 11350 def __init__(self, descriptorProvider, member, name, signature, extendedAttrs,
michael@0 11351 breakAfter=True, passJSBitsAsNeeded=True, visibility="public",
michael@0 11352 jsObjectsArePtr=False, variadicIsSequence=False):
michael@0 11353 """
michael@0 11354 If jsObjectsArePtr is true, typed arrays and "object" will be
michael@0 11355 passed as JSObject*.
michael@0 11356
michael@0 11357 If passJSBitsAsNeeded is false, we don't automatically pass in a
michael@0 11358 JSContext* or a JSObject* based on the return and argument types. We
michael@0 11359 can still pass it based on 'implicitJSContext' annotations.
michael@0 11360 """
michael@0 11361 self.descriptorProvider = descriptorProvider
michael@0 11362 self.member = member
michael@0 11363 self.extendedAttrs = extendedAttrs
michael@0 11364 self.resultAlreadyAddRefed = isResultAlreadyAddRefed(self.extendedAttrs)
michael@0 11365 self.passJSBitsAsNeeded = passJSBitsAsNeeded
michael@0 11366 self.jsObjectsArePtr = jsObjectsArePtr
michael@0 11367 self.variadicIsSequence = variadicIsSequence
michael@0 11368 breakAfterSelf = "\n" if breakAfter else ""
michael@0 11369 ClassMethod.__init__(self, name,
michael@0 11370 self.getReturnType(signature[0], False),
michael@0 11371 self.getArgs(signature[0], signature[1]),
michael@0 11372 static=member.isStatic(),
michael@0 11373 # Mark our getters, which are attrs that
michael@0 11374 # have a non-void return type, as const.
michael@0 11375 const=(not member.isStatic() and member.isAttr() and
michael@0 11376 not signature[0].isVoid()),
michael@0 11377 breakAfterReturnDecl=" ",
michael@0 11378 breakAfterSelf=breakAfterSelf,
michael@0 11379 visibility=visibility)
michael@0 11380
michael@0 11381 def getReturnType(self, type, isMember):
michael@0 11382 return self.getRetvalInfo(type, isMember)[0]
michael@0 11383
michael@0 11384 def getRetvalInfo(self, type, isMember):
michael@0 11385 """
michael@0 11386 Returns a tuple:
michael@0 11387
michael@0 11388 The first element is the type declaration for the retval
michael@0 11389
michael@0 11390 The second element is a default value that can be used on error returns.
michael@0 11391 For cases whose behavior depends on isMember, the second element will be
michael@0 11392 None if isMember is true.
michael@0 11393
michael@0 11394 The third element is a template for actually returning a value stored in
michael@0 11395 "${declName}" and "${holderName}". This means actually returning it if
michael@0 11396 we're not outparam, else assigning to the "retval" outparam. If
michael@0 11397 isMember is true, this can be None, since in that case the caller will
michael@0 11398 never examine this value.
michael@0 11399 """
michael@0 11400 if type.isVoid():
michael@0 11401 return "void", "", ""
michael@0 11402 if type.isPrimitive() and type.tag() in builtinNames:
michael@0 11403 result = CGGeneric(builtinNames[type.tag()])
michael@0 11404 defaultReturnArg = "0"
michael@0 11405 if type.nullable():
michael@0 11406 result = CGTemplatedType("Nullable", result)
michael@0 11407 defaultReturnArg = ""
michael@0 11408 return (result.define(),
michael@0 11409 "%s(%s)" % (result.define(), defaultReturnArg),
michael@0 11410 "return ${declName};\n")
michael@0 11411 if type.isDOMString():
michael@0 11412 if isMember:
michael@0 11413 # No need for a third element in the isMember case
michael@0 11414 return "nsString", None, None
michael@0 11415 # Outparam
michael@0 11416 return "void", "", "aRetVal = ${declName};\n"
michael@0 11417 if type.isByteString():
michael@0 11418 if isMember:
michael@0 11419 # No need for a third element in the isMember case
michael@0 11420 return "nsCString", None, None
michael@0 11421 # Outparam
michael@0 11422 return "void", "", "aRetVal = ${declName};\n"
michael@0 11423 if type.isEnum():
michael@0 11424 enumName = type.unroll().inner.identifier.name
michael@0 11425 if type.nullable():
michael@0 11426 enumName = CGTemplatedType("Nullable",
michael@0 11427 CGGeneric(enumName)).define()
michael@0 11428 defaultValue = "%s()" % enumName
michael@0 11429 else:
michael@0 11430 defaultValue = "%s(0)" % enumName
michael@0 11431 return enumName, defaultValue, "return ${declName};\n"
michael@0 11432 if type.isGeckoInterface():
michael@0 11433 iface = type.unroll().inner
michael@0 11434 result = CGGeneric(self.descriptorProvider.getDescriptor(
michael@0 11435 iface.identifier.name).prettyNativeType)
michael@0 11436 if self.resultAlreadyAddRefed:
michael@0 11437 if isMember:
michael@0 11438 holder = "nsRefPtr"
michael@0 11439 else:
michael@0 11440 holder = "already_AddRefed"
michael@0 11441 if memberReturnsNewObject(self.member):
michael@0 11442 warning = ""
michael@0 11443 else:
michael@0 11444 warning = "// Mark this as resultNotAddRefed to return raw pointers\n"
michael@0 11445 result = CGWrapper(result,
michael@0 11446 pre=("%s%s<" % (warning, holder)),
michael@0 11447 post=">")
michael@0 11448 else:
michael@0 11449 result = CGWrapper(result, post="*")
michael@0 11450 # Since we always force an owning type for callback return values,
michael@0 11451 # our ${declName} is an OwningNonNull or nsRefPtr. So we can just
michael@0 11452 # .forget() to get our already_AddRefed.
michael@0 11453 return result.define(), "nullptr", "return ${declName}.forget();\n"
michael@0 11454 if type.isCallback():
michael@0 11455 return ("already_AddRefed<%s>" % type.unroll().identifier.name,
michael@0 11456 "nullptr", "return ${declName}.forget();\n")
michael@0 11457 if type.isAny():
michael@0 11458 if isMember:
michael@0 11459 # No need for a third element in the isMember case
michael@0 11460 return "JS::Value", None, None
michael@0 11461 # Outparam
michael@0 11462 return "void", "", "aRetVal.set(${declName});\n"
michael@0 11463
michael@0 11464 if type.isObject():
michael@0 11465 if isMember:
michael@0 11466 # No need for a third element in the isMember case
michael@0 11467 return "JSObject*", None, None
michael@0 11468 return "void", "", "aRetVal.set(${declName});\n"
michael@0 11469 if type.isSpiderMonkeyInterface():
michael@0 11470 if isMember:
michael@0 11471 # No need for a third element in the isMember case
michael@0 11472 return "JSObject*", None, None
michael@0 11473 if type.nullable():
michael@0 11474 returnCode = "${declName}.IsNull() ? nullptr : ${declName}.Value().Obj();\n"
michael@0 11475 else:
michael@0 11476 returnCode = "${declName}.Obj();\n"
michael@0 11477 return "void", "", "aRetVal.set(%s);\n" % returnCode
michael@0 11478 if type.isSequence():
michael@0 11479 # If we want to handle sequence-of-sequences return values, we're
michael@0 11480 # going to need to fix example codegen to not produce nsTArray<void>
michael@0 11481 # for the relevant argument...
michael@0 11482 assert not isMember
michael@0 11483 # Outparam.
michael@0 11484 if type.nullable():
michael@0 11485 returnCode = dedent("""
michael@0 11486 if (${declName}.IsNull()) {
michael@0 11487 aRetVal.SetNull();
michael@0 11488 } else {
michael@0 11489 aRetVal.SetValue().SwapElements(${declName}.Value());
michael@0 11490 }
michael@0 11491 """)
michael@0 11492 else:
michael@0 11493 returnCode = "aRetVal.SwapElements(${declName});\n"
michael@0 11494 return "void", "", returnCode
michael@0 11495 if type.isMozMap():
michael@0 11496 # If we want to handle MozMap-of-MozMap return values, we're
michael@0 11497 # going to need to fix example codegen to not produce MozMap<void>
michael@0 11498 # for the relevant argument...
michael@0 11499 assert not isMember
michael@0 11500 # In this case we convert directly into our outparam to start with
michael@0 11501 return "void", "", ""
michael@0 11502 if type.isDate():
michael@0 11503 result = CGGeneric("Date")
michael@0 11504 if type.nullable():
michael@0 11505 result = CGTemplatedType("Nullable", result)
michael@0 11506 return (result.define(), "%s()" % result.define(),
michael@0 11507 "return ${declName};\n")
michael@0 11508 if type.isDictionary():
michael@0 11509 if isMember:
michael@0 11510 # Only the first member of the tuple matters here, but return
michael@0 11511 # bogus values for the others in case someone decides to use
michael@0 11512 # them.
michael@0 11513 return CGDictionary.makeDictionaryName(type.inner), None, None
michael@0 11514 # In this case we convert directly into our outparam to start with
michael@0 11515 return "void", "", ""
michael@0 11516 if type.isUnion():
michael@0 11517 if isMember:
michael@0 11518 # Only the first member of the tuple matters here, but return
michael@0 11519 # bogus values for the others in case someone decides to use
michael@0 11520 # them.
michael@0 11521 return CGUnionStruct.unionTypeDecl(type, True), None, None
michael@0 11522 # In this case we convert directly into our outparam to start with
michael@0 11523 return "void", "", ""
michael@0 11524
michael@0 11525 raise TypeError("Don't know how to declare return value for %s" %
michael@0 11526 type)
michael@0 11527
michael@0 11528 def getArgs(self, returnType, argList):
michael@0 11529 args = [self.getArg(arg) for arg in argList]
michael@0 11530 # Now the outparams
michael@0 11531 if returnType.isDOMString():
michael@0 11532 args.append(Argument("nsString&", "aRetVal"))
michael@0 11533 elif returnType.isByteString():
michael@0 11534 args.append(Argument("nsCString&", "aRetVal"))
michael@0 11535 elif returnType.isSequence():
michael@0 11536 nullable = returnType.nullable()
michael@0 11537 if nullable:
michael@0 11538 returnType = returnType.inner
michael@0 11539 # And now the actual underlying type
michael@0 11540 elementDecl = self.getReturnType(returnType.inner, True)
michael@0 11541 type = CGTemplatedType("nsTArray", CGGeneric(elementDecl))
michael@0 11542 if nullable:
michael@0 11543 type = CGTemplatedType("Nullable", type)
michael@0 11544 args.append(Argument("%s&" % type.define(), "aRetVal"))
michael@0 11545 elif returnType.isMozMap():
michael@0 11546 nullable = returnType.nullable()
michael@0 11547 if nullable:
michael@0 11548 returnType = returnType.inner
michael@0 11549 # And now the actual underlying type
michael@0 11550 elementDecl = self.getReturnType(returnType.inner, True)
michael@0 11551 type = CGTemplatedType("MozMap", CGGeneric(elementDecl))
michael@0 11552 if nullable:
michael@0 11553 type = CGTemplatedType("Nullable", type)
michael@0 11554 args.append(Argument("%s&" % type.define(), "aRetVal"))
michael@0 11555 elif returnType.isDictionary():
michael@0 11556 nullable = returnType.nullable()
michael@0 11557 if nullable:
michael@0 11558 returnType = returnType.inner
michael@0 11559 dictType = CGGeneric(CGDictionary.makeDictionaryName(returnType.inner))
michael@0 11560 if nullable:
michael@0 11561 dictType = CGTemplatedType("Nullable", dictType)
michael@0 11562 args.append(Argument("%s&" % dictType.define(), "aRetVal"))
michael@0 11563 elif returnType.isUnion():
michael@0 11564 args.append(Argument("%s&" %
michael@0 11565 CGUnionStruct.unionTypeDecl(returnType, True),
michael@0 11566 "aRetVal"))
michael@0 11567 elif returnType.isAny():
michael@0 11568 args.append(Argument("JS::MutableHandle<JS::Value>", "aRetVal"))
michael@0 11569 elif returnType.isObject() or returnType.isSpiderMonkeyInterface():
michael@0 11570 args.append(Argument("JS::MutableHandle<JSObject*>", "aRetVal"))
michael@0 11571
michael@0 11572 # And the ErrorResult
michael@0 11573 if 'infallible' not in self.extendedAttrs:
michael@0 11574 # Use aRv so it won't conflict with local vars named "rv"
michael@0 11575 args.append(Argument("ErrorResult&", "aRv"))
michael@0 11576 # The legacycaller thisval
michael@0 11577 if self.member.isMethod() and self.member.isLegacycaller():
michael@0 11578 # If it has an identifier, we can't deal with it yet
michael@0 11579 assert self.member.isIdentifierLess()
michael@0 11580 args.insert(0, Argument("JS::Value", "aThisVal"))
michael@0 11581 # And jscontext bits.
michael@0 11582 if needCx(returnType, argList, self.extendedAttrs,
michael@0 11583 self.passJSBitsAsNeeded):
michael@0 11584 args.insert(0, Argument("JSContext*", "cx"))
michael@0 11585 if needScopeObject(returnType, argList, self.extendedAttrs,
michael@0 11586 self.descriptorProvider.wrapperCache,
michael@0 11587 self.passJSBitsAsNeeded,
michael@0 11588 self.member.getExtendedAttribute("StoreInSlot")):
michael@0 11589 args.insert(1, Argument("JS::Handle<JSObject*>", "obj"))
michael@0 11590 # And if we're static, a global
michael@0 11591 if self.member.isStatic():
michael@0 11592 args.insert(0, Argument("const GlobalObject&", "global"))
michael@0 11593 return args
michael@0 11594
michael@0 11595 def doGetArgType(self, type, optional, isMember):
michael@0 11596 """
michael@0 11597 The main work of getArgType. Returns a string type decl, whether this
michael@0 11598 is a const ref, as well as whether the type should be wrapped in
michael@0 11599 Nullable as needed.
michael@0 11600
michael@0 11601 isMember can be false or one of the strings "Sequence", "Variadic",
michael@0 11602 "MozMap"
michael@0 11603 """
michael@0 11604 if type.isArray():
michael@0 11605 raise TypeError("Can't handle array arguments yet")
michael@0 11606
michael@0 11607 if type.isSequence():
michael@0 11608 nullable = type.nullable()
michael@0 11609 if nullable:
michael@0 11610 type = type.inner
michael@0 11611 elementType = type.inner
michael@0 11612 argType = self.getArgType(elementType, False, "Sequence")[0]
michael@0 11613 decl = CGTemplatedType("Sequence", argType)
michael@0 11614 return decl.define(), True, True
michael@0 11615
michael@0 11616 if type.isMozMap():
michael@0 11617 nullable = type.nullable()
michael@0 11618 if nullable:
michael@0 11619 type = type.inner
michael@0 11620 elementType = type.inner
michael@0 11621 argType = self.getArgType(elementType, False, "MozMap")[0]
michael@0 11622 decl = CGTemplatedType("MozMap", argType)
michael@0 11623 return decl.define(), True, True
michael@0 11624
michael@0 11625 if type.isUnion():
michael@0 11626 # unionTypeDecl will handle nullable types, so return False for
michael@0 11627 # auto-wrapping in Nullable
michael@0 11628 return CGUnionStruct.unionTypeDecl(type, isMember), True, False
michael@0 11629
michael@0 11630 if type.isGeckoInterface() and not type.isCallbackInterface():
michael@0 11631 iface = type.unroll().inner
michael@0 11632 argIsPointer = type.nullable() or iface.isExternal()
michael@0 11633 forceOwningType = iface.isCallback() or isMember
michael@0 11634 if argIsPointer:
michael@0 11635 if (optional or isMember) and forceOwningType:
michael@0 11636 typeDecl = "nsRefPtr<%s>"
michael@0 11637 else:
michael@0 11638 typeDecl = "%s*"
michael@0 11639 else:
michael@0 11640 if optional or isMember:
michael@0 11641 if forceOwningType:
michael@0 11642 typeDecl = "OwningNonNull<%s>"
michael@0 11643 else:
michael@0 11644 typeDecl = "NonNull<%s>"
michael@0 11645 else:
michael@0 11646 typeDecl = "%s&"
michael@0 11647 return ((typeDecl %
michael@0 11648 self.descriptorProvider.getDescriptor(iface.identifier.name).prettyNativeType),
michael@0 11649 False, False)
michael@0 11650
michael@0 11651 if type.isSpiderMonkeyInterface():
michael@0 11652 if self.jsObjectsArePtr:
michael@0 11653 return "JSObject*", False, False
michael@0 11654
michael@0 11655 return type.name, True, True
michael@0 11656
michael@0 11657 if type.isDOMString():
michael@0 11658 if isMember:
michael@0 11659 declType = "nsString"
michael@0 11660 else:
michael@0 11661 declType = "nsAString"
michael@0 11662 return declType, True, False
michael@0 11663
michael@0 11664 if type.isByteString():
michael@0 11665 declType = "nsCString"
michael@0 11666 return declType, True, False
michael@0 11667
michael@0 11668 if type.isEnum():
michael@0 11669 return type.unroll().inner.identifier.name, False, True
michael@0 11670
michael@0 11671 if type.isCallback() or type.isCallbackInterface():
michael@0 11672 forceOwningType = optional or isMember
michael@0 11673 if type.nullable():
michael@0 11674 if forceOwningType:
michael@0 11675 declType = "nsRefPtr<%s>"
michael@0 11676 else:
michael@0 11677 declType = "%s*"
michael@0 11678 else:
michael@0 11679 if forceOwningType:
michael@0 11680 declType = "OwningNonNull<%s>"
michael@0 11681 else:
michael@0 11682 declType = "%s&"
michael@0 11683 if type.isCallback():
michael@0 11684 name = type.unroll().identifier.name
michael@0 11685 else:
michael@0 11686 name = type.unroll().inner.identifier.name
michael@0 11687 return declType % name, False, False
michael@0 11688
michael@0 11689 if type.isAny():
michael@0 11690 # Don't do the rooting stuff for variadics for now
michael@0 11691 if isMember:
michael@0 11692 declType = "JS::Value"
michael@0 11693 else:
michael@0 11694 declType = "JS::Handle<JS::Value>"
michael@0 11695 return declType, False, False
michael@0 11696
michael@0 11697 if type.isObject():
michael@0 11698 if isMember:
michael@0 11699 declType = "JSObject*"
michael@0 11700 else:
michael@0 11701 declType = "JS::Handle<JSObject*>"
michael@0 11702 return declType, False, False
michael@0 11703
michael@0 11704 if type.isDictionary():
michael@0 11705 typeName = CGDictionary.makeDictionaryName(type.inner)
michael@0 11706 return typeName, True, True
michael@0 11707
michael@0 11708 if type.isDate():
michael@0 11709 return "Date", False, True
michael@0 11710
michael@0 11711 assert type.isPrimitive()
michael@0 11712
michael@0 11713 return builtinNames[type.tag()], False, True
michael@0 11714
michael@0 11715 def getArgType(self, type, optional, isMember):
michael@0 11716 """
michael@0 11717 Get the type of an argument declaration. Returns the type CGThing, and
michael@0 11718 whether this should be a const ref.
michael@0 11719
michael@0 11720 isMember can be False, "Sequence", or "Variadic"
michael@0 11721 """
michael@0 11722 decl, ref, handleNullable = self.doGetArgType(type, optional, isMember)
michael@0 11723 decl = CGGeneric(decl)
michael@0 11724 if handleNullable and type.nullable():
michael@0 11725 decl = CGTemplatedType("Nullable", decl)
michael@0 11726 ref = True
michael@0 11727 if isMember == "Variadic":
michael@0 11728 arrayType = "Sequence" if self.variadicIsSequence else "nsTArray"
michael@0 11729 decl = CGTemplatedType(arrayType, decl)
michael@0 11730 ref = True
michael@0 11731 elif optional:
michael@0 11732 # Note: All variadic args claim to be optional, but we can just use
michael@0 11733 # empty arrays to represent them not being present.
michael@0 11734 decl = CGTemplatedType("Optional", decl)
michael@0 11735 ref = True
michael@0 11736 return (decl, ref)
michael@0 11737
michael@0 11738 def getArg(self, arg):
michael@0 11739 """
michael@0 11740 Get the full argument declaration for an argument
michael@0 11741 """
michael@0 11742 decl, ref = self.getArgType(arg.type,
michael@0 11743 arg.optional and not arg.defaultValue,
michael@0 11744 "Variadic" if arg.variadic else False)
michael@0 11745 if ref:
michael@0 11746 decl = CGWrapper(decl, pre="const ", post="&")
michael@0 11747
michael@0 11748 return Argument(decl.define(), arg.identifier.name)
michael@0 11749
michael@0 11750
michael@0 11751 class CGExampleMethod(CGNativeMember):
michael@0 11752 def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
michael@0 11753 CGNativeMember.__init__(self, descriptor, method,
michael@0 11754 CGSpecializedMethod.makeNativeName(descriptor,
michael@0 11755 method),
michael@0 11756 signature,
michael@0 11757 descriptor.getExtendedAttributes(method),
michael@0 11758 breakAfter=breakAfter,
michael@0 11759 variadicIsSequence=True)
michael@0 11760
michael@0 11761 def define(self, cgClass):
michael@0 11762 return ''
michael@0 11763
michael@0 11764
michael@0 11765 class CGExampleGetter(CGNativeMember):
michael@0 11766 def __init__(self, descriptor, attr):
michael@0 11767 CGNativeMember.__init__(self, descriptor, attr,
michael@0 11768 CGSpecializedGetter.makeNativeName(descriptor,
michael@0 11769 attr),
michael@0 11770 (attr.type, []),
michael@0 11771 descriptor.getExtendedAttributes(attr,
michael@0 11772 getter=True))
michael@0 11773
michael@0 11774 def define(self, cgClass):
michael@0 11775 return ''
michael@0 11776
michael@0 11777
michael@0 11778 class CGExampleSetter(CGNativeMember):
michael@0 11779 def __init__(self, descriptor, attr):
michael@0 11780 CGNativeMember.__init__(self, descriptor, attr,
michael@0 11781 CGSpecializedSetter.makeNativeName(descriptor,
michael@0 11782 attr),
michael@0 11783 (BuiltinTypes[IDLBuiltinType.Types.void],
michael@0 11784 [FakeArgument(attr.type, attr)]),
michael@0 11785 descriptor.getExtendedAttributes(attr,
michael@0 11786 setter=True))
michael@0 11787
michael@0 11788 def define(self, cgClass):
michael@0 11789 return ''
michael@0 11790
michael@0 11791
michael@0 11792 class CGBindingImplClass(CGClass):
michael@0 11793 """
michael@0 11794 Common codegen for generating a C++ implementation of a WebIDL interface
michael@0 11795 """
michael@0 11796 def __init__(self, descriptor, cgMethod, cgGetter, cgSetter, wantGetParent=True):
michael@0 11797 """
michael@0 11798 cgMethod, cgGetter and cgSetter are classes used to codegen methods,
michael@0 11799 getters and setters.
michael@0 11800 """
michael@0 11801 self.descriptor = descriptor
michael@0 11802 self._deps = descriptor.interface.getDeps()
michael@0 11803
michael@0 11804 iface = descriptor.interface
michael@0 11805
michael@0 11806 self.methodDecls = []
michael@0 11807
michael@0 11808 def appendMethod(m, isConstructor=False):
michael@0 11809 sigs = m.signatures()
michael@0 11810 for s in sigs[:-1]:
michael@0 11811 # Don't put a blank line after overloads, until we
michael@0 11812 # get to the last one.
michael@0 11813 self.methodDecls.append(cgMethod(descriptor, m, s,
michael@0 11814 isConstructor,
michael@0 11815 breakAfter=False))
michael@0 11816 self.methodDecls.append(cgMethod(descriptor, m, sigs[-1],
michael@0 11817 isConstructor))
michael@0 11818
michael@0 11819 if iface.ctor():
michael@0 11820 appendMethod(iface.ctor(), isConstructor=True)
michael@0 11821 for n in iface.namedConstructors:
michael@0 11822 appendMethod(n, isConstructor=True)
michael@0 11823 for m in iface.members:
michael@0 11824 if m.isMethod():
michael@0 11825 if m.isIdentifierLess():
michael@0 11826 continue
michael@0 11827 appendMethod(m)
michael@0 11828 elif m.isAttr():
michael@0 11829 self.methodDecls.append(cgGetter(descriptor, m))
michael@0 11830 if not m.readonly:
michael@0 11831 self.methodDecls.append(cgSetter(descriptor, m))
michael@0 11832
michael@0 11833 # Now do the special operations
michael@0 11834 def appendSpecialOperation(name, op):
michael@0 11835 if op is None:
michael@0 11836 return
michael@0 11837 if name == "IndexedCreator" or name == "NamedCreator":
michael@0 11838 # These are identical to the setters
michael@0 11839 return
michael@0 11840 assert len(op.signatures()) == 1
michael@0 11841 returnType, args = op.signatures()[0]
michael@0 11842 # Make a copy of the args, since we plan to modify them.
michael@0 11843 args = list(args)
michael@0 11844 if op.isGetter() or op.isDeleter():
michael@0 11845 # This is a total hack. The '&' belongs with the
michael@0 11846 # type, not the name! But it works, and is simpler
michael@0 11847 # than trying to somehow make this pretty.
michael@0 11848 args.append(FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean],
michael@0 11849 op, name="&found"))
michael@0 11850 if name == "Stringifier":
michael@0 11851 if op.isIdentifierLess():
michael@0 11852 # XXXbz I wish we were consistent about our renaming here.
michael@0 11853 name = "Stringify"
michael@0 11854 else:
michael@0 11855 # We already added this method
michael@0 11856 return
michael@0 11857 if name == "LegacyCaller":
michael@0 11858 if op.isIdentifierLess():
michael@0 11859 # XXXbz I wish we were consistent about our renaming here.
michael@0 11860 name = "LegacyCall"
michael@0 11861 else:
michael@0 11862 # We already added this method
michael@0 11863 return
michael@0 11864 if name == "Jsonifier":
michael@0 11865 # We already added this method
michael@0 11866 return
michael@0 11867 self.methodDecls.append(
michael@0 11868 CGNativeMember(descriptor, op,
michael@0 11869 name,
michael@0 11870 (returnType, args),
michael@0 11871 descriptor.getExtendedAttributes(op)))
michael@0 11872 # Sort things by name so we get stable ordering in the output.
michael@0 11873 ops = descriptor.operations.items()
michael@0 11874 ops.sort(key=lambda x: x[0])
michael@0 11875 for name, op in ops:
michael@0 11876 appendSpecialOperation(name, op)
michael@0 11877 # If we support indexed properties, then we need a Length()
michael@0 11878 # method so we know which indices are supported.
michael@0 11879 if descriptor.supportsIndexedProperties():
michael@0 11880 # But we don't need it if we already have an infallible
michael@0 11881 # "length" attribute, which we often do.
michael@0 11882 haveLengthAttr = any(
michael@0 11883 m for m in iface.members if m.isAttr() and
michael@0 11884 CGSpecializedGetter.makeNativeName(descriptor, m) == "Length")
michael@0 11885 if not haveLengthAttr:
michael@0 11886 self.methodDecls.append(
michael@0 11887 CGNativeMember(descriptor, FakeMember(),
michael@0 11888 "Length",
michael@0 11889 (BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
michael@0 11890 []),
michael@0 11891 {"infallible": True}))
michael@0 11892 # And if we support named properties we need to be able to
michael@0 11893 # enumerate the supported names and test whether they're enumerable.
michael@0 11894 if descriptor.supportsNamedProperties():
michael@0 11895 self.methodDecls.append(
michael@0 11896 CGNativeMember(
michael@0 11897 descriptor, FakeMember(),
michael@0 11898 "GetSupportedNames",
michael@0 11899 (IDLSequenceType(None,
michael@0 11900 BuiltinTypes[IDLBuiltinType.Types.domstring]),
michael@0 11901 # Let's use unsigned long for the type here, though really
michael@0 11902 # it's just a C++ "unsigned"...
michael@0 11903 [FakeArgument(BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
michael@0 11904 FakeMember(),
michael@0 11905 name="aFlags")]),
michael@0 11906 {"infallible": True}))
michael@0 11907 self.methodDecls.append(
michael@0 11908 CGNativeMember(
michael@0 11909 descriptor, FakeMember(),
michael@0 11910 "NameIsEnumerable",
michael@0 11911 (BuiltinTypes[IDLBuiltinType.Types.boolean],
michael@0 11912 [FakeArgument(BuiltinTypes[IDLBuiltinType.Types.domstring],
michael@0 11913 FakeMember(),
michael@0 11914 name="aName")]),
michael@0 11915 { "infallible": True }))
michael@0 11916
michael@0 11917 wrapArgs = [Argument('JSContext*', 'aCx')]
michael@0 11918 self.methodDecls.insert(0,
michael@0 11919 ClassMethod("WrapObject", "JSObject*",
michael@0 11920 wrapArgs, virtual=descriptor.wrapperCache,
michael@0 11921 breakAfterReturnDecl=" ",
michael@0 11922 override=descriptor.wrapperCache,
michael@0 11923 body=self.getWrapObjectBody()))
michael@0 11924 if wantGetParent:
michael@0 11925 self.methodDecls.insert(0,
michael@0 11926 ClassMethod("GetParentObject",
michael@0 11927 self.getGetParentObjectReturnType(),
michael@0 11928 [], const=True,
michael@0 11929 breakAfterReturnDecl=" ",
michael@0 11930 body=self.getGetParentObjectBody()))
michael@0 11931
michael@0 11932 # Invoke CGClass.__init__ in any subclasses afterwards to do the actual codegen.
michael@0 11933
michael@0 11934 def getWrapObjectBody(self):
michael@0 11935 return None
michael@0 11936
michael@0 11937 def getGetParentObjectReturnType(self):
michael@0 11938 return ("// TODO: return something sensible here, and change the return type\n"
michael@0 11939 "%s*" % self.descriptor.nativeType.split('::')[-1])
michael@0 11940
michael@0 11941 def getGetParentObjectBody(self):
michael@0 11942 return None
michael@0 11943
michael@0 11944 def deps(self):
michael@0 11945 return self._deps
michael@0 11946
michael@0 11947
michael@0 11948 class CGExampleClass(CGBindingImplClass):
michael@0 11949 """
michael@0 11950 Codegen for the actual example class implementation for this descriptor
michael@0 11951 """
michael@0 11952 def __init__(self, descriptor):
michael@0 11953 CGBindingImplClass.__init__(self, descriptor,
michael@0 11954 CGExampleMethod, CGExampleGetter, CGExampleSetter,
michael@0 11955 wantGetParent=descriptor.wrapperCache)
michael@0 11956
michael@0 11957 self.refcounted = descriptor.nativeOwnership == "refcounted"
michael@0 11958
michael@0 11959 self.parentIface = descriptor.interface.parent
michael@0 11960 if self.parentIface:
michael@0 11961 self.parentDesc = descriptor.getDescriptor(
michael@0 11962 self.parentIface.identifier.name)
michael@0 11963 bases = [ClassBase(self.nativeLeafName(self.parentDesc))]
michael@0 11964 else:
michael@0 11965 bases = []
michael@0 11966 if self.refcounted:
michael@0 11967 bases.append(ClassBase("nsISupports /* Change nativeOwnership in the binding configuration if you don't want this */"))
michael@0 11968 if descriptor.wrapperCache:
michael@0 11969 bases.append(ClassBase("nsWrapperCache /* Change wrapperCache in the binding configuration if you don't want this */"))
michael@0 11970 else:
michael@0 11971 bases.append(ClassBase("NonRefcountedDOMObject"))
michael@0 11972
michael@0 11973 if self.refcounted:
michael@0 11974 if self.parentIface:
michael@0 11975 extradeclarations = (
michael@0 11976 "public:\n"
michael@0 11977 " NS_DECL_ISUPPORTS_INHERITED\n"
michael@0 11978 " NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(%s, %s)\n"
michael@0 11979 "\n" % (self.nativeLeafName(descriptor),
michael@0 11980 self.nativeLeafName(self.parentDesc)))
michael@0 11981 else:
michael@0 11982 extradeclarations = (
michael@0 11983 "public:\n"
michael@0 11984 " NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n"
michael@0 11985 " NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(%s)\n"
michael@0 11986 "\n" % self.nativeLeafName(descriptor))
michael@0 11987 else:
michael@0 11988 extradeclarations = ""
michael@0 11989
michael@0 11990 if descriptor.interface.hasChildInterfaces():
michael@0 11991 decorators = ""
michael@0 11992 else:
michael@0 11993 decorators = "MOZ_FINAL"
michael@0 11994
michael@0 11995 CGClass.__init__(self, self.nativeLeafName(descriptor),
michael@0 11996 bases=bases,
michael@0 11997 constructors=[ClassConstructor([],
michael@0 11998 visibility="public")],
michael@0 11999 destructor=ClassDestructor(visibility="public"),
michael@0 12000 methods=self.methodDecls,
michael@0 12001 decorators=decorators,
michael@0 12002 extradeclarations=extradeclarations)
michael@0 12003
michael@0 12004 def define(self):
michael@0 12005 # Just override CGClass and do our own thing
michael@0 12006 if self.descriptor.wrapperCache:
michael@0 12007 setDOMBinding = " SetIsDOMBinding();\n"
michael@0 12008 else:
michael@0 12009 setDOMBinding = ""
michael@0 12010 if self.refcounted:
michael@0 12011 ctordtor = dedent("""
michael@0 12012 ${nativeType}::${nativeType}()
michael@0 12013 {
michael@0 12014 %s}
michael@0 12015
michael@0 12016 ${nativeType}::~${nativeType}()
michael@0 12017 {
michael@0 12018 }
michael@0 12019 """) % setDOMBinding
michael@0 12020 else:
michael@0 12021 ctordtor = dedent("""
michael@0 12022 ${nativeType}::${nativeType}()
michael@0 12023 {
michael@0 12024 MOZ_COUNT_CTOR(${nativeType});
michael@0 12025 }
michael@0 12026
michael@0 12027 ${nativeType}::~${nativeType}()
michael@0 12028 {
michael@0 12029 MOZ_COUNT_DTOR(${nativeType});
michael@0 12030 }
michael@0 12031 """)
michael@0 12032
michael@0 12033 if self.refcounted:
michael@0 12034 if self.parentIface:
michael@0 12035 ccImpl = dedent("""
michael@0 12036
michael@0 12037 NS_IMPL_CYCLE_COLLECTION_INHERITED_0(${nativeType}, ${parentType})
michael@0 12038 NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType})
michael@0 12039 NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType})
michael@0 12040 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})
michael@0 12041 NS_INTERFACE_MAP_END_INHERITING(${parentType})
michael@0 12042
michael@0 12043 """)
michael@0 12044 else:
michael@0 12045 ccImpl = dedent("""
michael@0 12046
michael@0 12047 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(${nativeType})
michael@0 12048 NS_IMPL_CYCLE_COLLECTING_ADDREF(${nativeType})
michael@0 12049 NS_IMPL_CYCLE_COLLECTING_RELEASE(${nativeType})
michael@0 12050 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})
michael@0 12051 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
michael@0 12052 NS_INTERFACE_MAP_ENTRY(nsISupports)
michael@0 12053 NS_INTERFACE_MAP_END
michael@0 12054
michael@0 12055 """)
michael@0 12056 else:
michael@0 12057 ccImpl = ""
michael@0 12058
michael@0 12059 classImpl = ccImpl + ctordtor + "\n" + dedent("""
michael@0 12060 JSObject*
michael@0 12061 ${nativeType}::WrapObject(JSContext* aCx)
michael@0 12062 {
michael@0 12063 return ${ifaceName}Binding::Wrap(aCx, this);
michael@0 12064 }
michael@0 12065
michael@0 12066 """)
michael@0 12067 return string.Template(classImpl).substitute(
michael@0 12068 ifaceName=self.descriptor.name,
michael@0 12069 nativeType=self.nativeLeafName(self.descriptor),
michael@0 12070 parentType=self.nativeLeafName(self.parentDesc) if self.parentIface else "")
michael@0 12071
michael@0 12072 @staticmethod
michael@0 12073 def nativeLeafName(descriptor):
michael@0 12074 return descriptor.nativeType.split('::')[-1]
michael@0 12075
michael@0 12076
michael@0 12077 class CGExampleRoot(CGThing):
michael@0 12078 """
michael@0 12079 Root codegen class for example implementation generation. Instantiate the
michael@0 12080 class and call declare or define to generate header or cpp code,
michael@0 12081 respectively.
michael@0 12082 """
michael@0 12083 def __init__(self, config, interfaceName):
michael@0 12084 # Let's assume we're not doing workers stuff
michael@0 12085 descriptor = config.getDescriptor(interfaceName, False)
michael@0 12086
michael@0 12087 self.root = CGWrapper(CGExampleClass(descriptor),
michael@0 12088 pre="\n", post="\n")
michael@0 12089
michael@0 12090 self.root = CGNamespace.build(["mozilla", "dom"], self.root)
michael@0 12091
michael@0 12092 self.root = CGList([CGClassForwardDeclare("JSContext", isStruct=True),
michael@0 12093 self.root], "\n")
michael@0 12094
michael@0 12095 # Throw in our #includes
michael@0 12096 self.root = CGHeaders([], [], [], [],
michael@0 12097 ["nsWrapperCache.h",
michael@0 12098 "nsCycleCollectionParticipant.h",
michael@0 12099 "mozilla/Attributes.h",
michael@0 12100 "mozilla/ErrorResult.h"],
michael@0 12101 ["mozilla/dom/%s.h" % interfaceName,
michael@0 12102 ("mozilla/dom/%s" %
michael@0 12103 CGHeaders.getDeclarationFilename(descriptor.interface))], "", self.root)
michael@0 12104
michael@0 12105 # And now some include guards
michael@0 12106 self.root = CGIncludeGuard(interfaceName, self.root)
michael@0 12107
michael@0 12108 # And our license block comes before everything else
michael@0 12109 self.root = CGWrapper(self.root, pre=dedent("""
michael@0 12110 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 12111 /* vim:set ts=2 sw=2 sts=2 et cindent: */
michael@0 12112 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 12113 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 12114 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 12115
michael@0 12116 """))
michael@0 12117
michael@0 12118 def declare(self):
michael@0 12119 return self.root.declare()
michael@0 12120
michael@0 12121 def define(self):
michael@0 12122 return self.root.define()
michael@0 12123
michael@0 12124
michael@0 12125 def jsImplName(name):
michael@0 12126 return name + "JSImpl"
michael@0 12127
michael@0 12128
michael@0 12129 class CGJSImplMember(CGNativeMember):
michael@0 12130 """
michael@0 12131 Base class for generating code for the members of the implementation class
michael@0 12132 for a JS-implemented WebIDL interface.
michael@0 12133 """
michael@0 12134 def __init__(self, descriptorProvider, member, name, signature,
michael@0 12135 extendedAttrs, breakAfter=True, passJSBitsAsNeeded=True,
michael@0 12136 visibility="public", jsObjectsArePtr=False,
michael@0 12137 variadicIsSequence=False):
michael@0 12138 CGNativeMember.__init__(self, descriptorProvider, member, name,
michael@0 12139 signature, extendedAttrs, breakAfter=breakAfter,
michael@0 12140 passJSBitsAsNeeded=passJSBitsAsNeeded,
michael@0 12141 visibility=visibility,
michael@0 12142 jsObjectsArePtr=jsObjectsArePtr,
michael@0 12143 variadicIsSequence=variadicIsSequence)
michael@0 12144 self.body = self.getImpl()
michael@0 12145
michael@0 12146 def getArgs(self, returnType, argList):
michael@0 12147 args = CGNativeMember.getArgs(self, returnType, argList)
michael@0 12148 args.append(Argument("JSCompartment*", "aCompartment", "nullptr"))
michael@0 12149 return args
michael@0 12150
michael@0 12151
michael@0 12152 class CGJSImplMethod(CGJSImplMember):
michael@0 12153 """
michael@0 12154 Class for generating code for the methods for a JS-implemented WebIDL
michael@0 12155 interface.
michael@0 12156 """
michael@0 12157 def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
michael@0 12158 self.signature = signature
michael@0 12159 self.descriptor = descriptor
michael@0 12160 self.isConstructor = isConstructor
michael@0 12161 CGJSImplMember.__init__(self, descriptor, method,
michael@0 12162 CGSpecializedMethod.makeNativeName(descriptor,
michael@0 12163 method),
michael@0 12164 signature,
michael@0 12165 descriptor.getExtendedAttributes(method),
michael@0 12166 breakAfter=breakAfter,
michael@0 12167 variadicIsSequence=True,
michael@0 12168 passJSBitsAsNeeded=False)
michael@0 12169
michael@0 12170 def getArgs(self, returnType, argList):
michael@0 12171 if self.isConstructor:
michael@0 12172 # Skip the JSCompartment bits for constructors; it's handled
michael@0 12173 # manually in getImpl.
michael@0 12174 return CGNativeMember.getArgs(self, returnType, argList)
michael@0 12175 return CGJSImplMember.getArgs(self, returnType, argList)
michael@0 12176
michael@0 12177 def getImpl(self):
michael@0 12178 args = self.getArgs(self.signature[0], self.signature[1])
michael@0 12179 if not self.isConstructor:
michael@0 12180 return 'return mImpl->%s(%s);\n' % (self.name, ", ".join(arg.name for arg in args))
michael@0 12181
michael@0 12182 assert self.descriptor.interface.isJSImplemented()
michael@0 12183 if self.name != 'Constructor':
michael@0 12184 raise TypeError("Named constructors are not supported for JS implemented WebIDL. See bug 851287.")
michael@0 12185 if len(self.signature[1]) != 0:
michael@0 12186 # The first two arguments to the constructor implementation are not
michael@0 12187 # arguments to the WebIDL constructor, so don't pass them to __Init()
michael@0 12188 assert args[0].argType == 'const GlobalObject&'
michael@0 12189 assert args[1].argType == 'JSContext*'
michael@0 12190 constructorArgs = [arg.name for arg in args[2:]]
michael@0 12191 constructorArgs.append("js::GetObjectCompartment(scopeObj)")
michael@0 12192 initCall = fill(
michael@0 12193 """
michael@0 12194 // Wrap the object before calling __Init so that __DOM_IMPL__ is available.
michael@0 12195 nsCOMPtr<nsIGlobalObject> globalHolder = do_QueryInterface(window);
michael@0 12196 JS::Rooted<JSObject*> scopeObj(cx, globalHolder->GetGlobalJSObject());
michael@0 12197 MOZ_ASSERT(js::IsObjectInContextCompartment(scopeObj, cx));
michael@0 12198 JS::Rooted<JS::Value> wrappedVal(cx);
michael@0 12199 if (!WrapNewBindingObject(cx, impl, &wrappedVal)) {
michael@0 12200 //XXX Assertion disabled for now, see bug 991271.
michael@0 12201 MOZ_ASSERT(true || JS_IsExceptionPending(cx));
michael@0 12202 aRv.Throw(NS_ERROR_UNEXPECTED);
michael@0 12203 return nullptr;
michael@0 12204 }
michael@0 12205 // Initialize the object with the constructor arguments.
michael@0 12206 impl->mImpl->__Init(${args});
michael@0 12207 if (aRv.Failed()) {
michael@0 12208 return nullptr;
michael@0 12209 }
michael@0 12210 """,
michael@0 12211 args=", ".join(constructorArgs))
michael@0 12212 else:
michael@0 12213 initCall = ""
michael@0 12214 return genConstructorBody(self.descriptor, initCall)
michael@0 12215
michael@0 12216
michael@0 12217 def genConstructorBody(descriptor, initCall=""):
michael@0 12218 return fill(
michael@0 12219 """
michael@0 12220 JS::Rooted<JSObject*> jsImplObj(cx);
michael@0 12221 nsCOMPtr<nsPIDOMWindow> window =
michael@0 12222 ConstructJSImplementation(cx, "${contractId}", global, &jsImplObj, aRv);
michael@0 12223 if (aRv.Failed()) {
michael@0 12224 return nullptr;
michael@0 12225 }
michael@0 12226 // Build the C++ implementation.
michael@0 12227 nsRefPtr<${implClass}> impl = new ${implClass}(jsImplObj, window);
michael@0 12228 $*{initCall}
michael@0 12229 return impl.forget();
michael@0 12230 """,
michael@0 12231 contractId=descriptor.interface.getJSImplementation(),
michael@0 12232 implClass=descriptor.name,
michael@0 12233 initCall=initCall)
michael@0 12234
michael@0 12235
michael@0 12236 # We're always fallible
michael@0 12237 def callbackGetterName(attr, descriptor):
michael@0 12238 return "Get" + MakeNativeName(
michael@0 12239 descriptor.binaryNames.get(attr.identifier.name, attr.identifier.name))
michael@0 12240
michael@0 12241
michael@0 12242 def callbackSetterName(attr, descriptor):
michael@0 12243 return "Set" + MakeNativeName(
michael@0 12244 descriptor.binaryNames.get(attr.identifier.name, attr.identifier.name))
michael@0 12245
michael@0 12246
michael@0 12247 class CGJSImplGetter(CGJSImplMember):
michael@0 12248 """
michael@0 12249 Class for generating code for the getters of attributes for a JS-implemented
michael@0 12250 WebIDL interface.
michael@0 12251 """
michael@0 12252 def __init__(self, descriptor, attr):
michael@0 12253 CGJSImplMember.__init__(self, descriptor, attr,
michael@0 12254 CGSpecializedGetter.makeNativeName(descriptor,
michael@0 12255 attr),
michael@0 12256 (attr.type, []),
michael@0 12257 descriptor.getExtendedAttributes(attr,
michael@0 12258 getter=True),
michael@0 12259 passJSBitsAsNeeded=False)
michael@0 12260
michael@0 12261 def getImpl(self):
michael@0 12262 callbackArgs = [arg.name for arg in self.getArgs(self.member.type, [])]
michael@0 12263 return 'return mImpl->%s(%s);\n' % (
michael@0 12264 callbackGetterName(self.member, self.descriptorProvider),
michael@0 12265 ", ".join(callbackArgs))
michael@0 12266
michael@0 12267
michael@0 12268 class CGJSImplSetter(CGJSImplMember):
michael@0 12269 """
michael@0 12270 Class for generating code for the setters of attributes for a JS-implemented
michael@0 12271 WebIDL interface.
michael@0 12272 """
michael@0 12273 def __init__(self, descriptor, attr):
michael@0 12274 CGJSImplMember.__init__(self, descriptor, attr,
michael@0 12275 CGSpecializedSetter.makeNativeName(descriptor,
michael@0 12276 attr),
michael@0 12277 (BuiltinTypes[IDLBuiltinType.Types.void],
michael@0 12278 [FakeArgument(attr.type, attr)]),
michael@0 12279 descriptor.getExtendedAttributes(attr,
michael@0 12280 setter=True),
michael@0 12281 passJSBitsAsNeeded=False)
michael@0 12282
michael@0 12283 def getImpl(self):
michael@0 12284 callbackArgs = [arg.name for arg in self.getArgs(BuiltinTypes[IDLBuiltinType.Types.void],
michael@0 12285 [FakeArgument(self.member.type, self.member)])]
michael@0 12286 return 'mImpl->%s(%s);\n' % (
michael@0 12287 callbackSetterName(self.member, self.descriptorProvider),
michael@0 12288 ", ".join(callbackArgs))
michael@0 12289
michael@0 12290
michael@0 12291 class CGJSImplClass(CGBindingImplClass):
michael@0 12292 def __init__(self, descriptor):
michael@0 12293 CGBindingImplClass.__init__(self, descriptor, CGJSImplMethod, CGJSImplGetter, CGJSImplSetter)
michael@0 12294
michael@0 12295 if descriptor.interface.parent:
michael@0 12296 parentClass = descriptor.getDescriptor(
michael@0 12297 descriptor.interface.parent.identifier.name).jsImplParent
michael@0 12298 baseClasses = [ClassBase(parentClass)]
michael@0 12299 isupportsDecl = "NS_DECL_ISUPPORTS_INHERITED\n"
michael@0 12300 ccDecl = ("NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(%s, %s)\n" %
michael@0 12301 (descriptor.name, parentClass))
michael@0 12302 constructorBody = dedent("""
michael@0 12303 // Make sure we're an nsWrapperCache already
michael@0 12304 MOZ_ASSERT(static_cast<nsWrapperCache*>(this));
michael@0 12305 // And that our ancestor has called SetIsDOMBinding()
michael@0 12306 MOZ_ASSERT(IsDOMBinding());
michael@0 12307 """)
michael@0 12308 extradefinitions = fill(
michael@0 12309 """
michael@0 12310 NS_IMPL_CYCLE_COLLECTION_INHERITED(${ifaceName}, ${parentClass}, mImpl, mParent)
michael@0 12311 NS_IMPL_ADDREF_INHERITED(${ifaceName}, ${parentClass})
michael@0 12312 NS_IMPL_RELEASE_INHERITED(${ifaceName}, ${parentClass})
michael@0 12313 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(${ifaceName})
michael@0 12314 NS_INTERFACE_MAP_END_INHERITING(${parentClass})
michael@0 12315 """,
michael@0 12316 ifaceName=self.descriptor.name,
michael@0 12317 parentClass=parentClass)
michael@0 12318 else:
michael@0 12319 baseClasses = [ClassBase("nsSupportsWeakReference"),
michael@0 12320 ClassBase("nsWrapperCache")]
michael@0 12321 isupportsDecl = "NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n"
michael@0 12322 ccDecl = ("NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(%s)\n" %
michael@0 12323 descriptor.name)
michael@0 12324 constructorBody = "SetIsDOMBinding();\n"
michael@0 12325 extradefinitions = fill(
michael@0 12326 """
michael@0 12327 NS_IMPL_CYCLE_COLLECTION_CLASS(${ifaceName})
michael@0 12328 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(${ifaceName})
michael@0 12329 NS_IMPL_CYCLE_COLLECTION_UNLINK(mImpl)
michael@0 12330 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
michael@0 12331 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
michael@0 12332 tmp->ClearWeakReferences();
michael@0 12333 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
michael@0 12334 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(${ifaceName})
michael@0 12335 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImpl)
michael@0 12336 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
michael@0 12337 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
michael@0 12338 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
michael@0 12339 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(${ifaceName})
michael@0 12340 NS_IMPL_CYCLE_COLLECTING_ADDREF(${ifaceName})
michael@0 12341 NS_IMPL_CYCLE_COLLECTING_RELEASE(${ifaceName})
michael@0 12342 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${ifaceName})
michael@0 12343 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
michael@0 12344 NS_INTERFACE_MAP_ENTRY(nsISupports)
michael@0 12345 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
michael@0 12346 NS_INTERFACE_MAP_END
michael@0 12347 """,
michael@0 12348 ifaceName=self.descriptor.name)
michael@0 12349
michael@0 12350 extradeclarations = fill(
michael@0 12351 """
michael@0 12352 public:
michael@0 12353 $*{isupportsDecl}
michael@0 12354 $*{ccDecl}
michael@0 12355
michael@0 12356 private:
michael@0 12357 nsRefPtr<${jsImplName}> mImpl;
michael@0 12358 nsCOMPtr<nsISupports> mParent;
michael@0 12359
michael@0 12360 """,
michael@0 12361 isupportsDecl=isupportsDecl,
michael@0 12362 ccDecl=ccDecl,
michael@0 12363 jsImplName=jsImplName(descriptor.name))
michael@0 12364
michael@0 12365 if descriptor.interface.hasChildInterfaces():
michael@0 12366 decorators = ""
michael@0 12367 # We need a public virtual destructor our subclasses can use
michael@0 12368 destructor = ClassDestructor(virtual=True, visibility="public")
michael@0 12369 else:
michael@0 12370 decorators = "MOZ_FINAL"
michael@0 12371 destructor = None
michael@0 12372
michael@0 12373 baseConstructors = [
michael@0 12374 ("mImpl(new %s(aJSImplObject, /* aIncumbentGlobal = */ nullptr))" %
michael@0 12375 jsImplName(descriptor.name)),
michael@0 12376 "mParent(aParent)"]
michael@0 12377 parentInterface = descriptor.interface.parent
michael@0 12378 while parentInterface:
michael@0 12379 if parentInterface.isJSImplemented():
michael@0 12380 baseConstructors.insert(
michael@0 12381 0, "%s(aJSImplObject, aParent)" % parentClass)
michael@0 12382 break
michael@0 12383 parentInterface = parentInterface.parent
michael@0 12384 if not parentInterface and descriptor.interface.parent:
michael@0 12385 # We only have C++ ancestors, so only pass along the window
michael@0 12386 baseConstructors.insert(0,
michael@0 12387 "%s(aParent)" % parentClass)
michael@0 12388
michael@0 12389 constructor = ClassConstructor(
michael@0 12390 [Argument("JS::Handle<JSObject*>", "aJSImplObject"),
michael@0 12391 Argument("nsPIDOMWindow*", "aParent")],
michael@0 12392 visibility="public",
michael@0 12393 baseConstructors=baseConstructors,
michael@0 12394 body=constructorBody)
michael@0 12395
michael@0 12396 self.methodDecls.append(
michael@0 12397 ClassMethod("_Create",
michael@0 12398 "bool",
michael@0 12399 [Argument("JSContext*", "cx"),
michael@0 12400 Argument("unsigned", "argc"),
michael@0 12401 Argument("JS::Value*", "vp")],
michael@0 12402 static=True,
michael@0 12403 body=self.getCreateFromExistingBody()))
michael@0 12404
michael@0 12405 CGClass.__init__(self, descriptor.name,
michael@0 12406 bases=baseClasses,
michael@0 12407 constructors=[constructor],
michael@0 12408 destructor=destructor,
michael@0 12409 methods=self.methodDecls,
michael@0 12410 decorators=decorators,
michael@0 12411 extradeclarations=extradeclarations,
michael@0 12412 extradefinitions=extradefinitions)
michael@0 12413
michael@0 12414 def getWrapObjectBody(self):
michael@0 12415 return fill(
michael@0 12416 """
michael@0 12417 JS::Rooted<JSObject*> obj(aCx, ${name}Binding::Wrap(aCx, this));
michael@0 12418 if (!obj) {
michael@0 12419 return nullptr;
michael@0 12420 }
michael@0 12421
michael@0 12422 // Now define it on our chrome object
michael@0 12423 JSAutoCompartment ac(aCx, mImpl->Callback());
michael@0 12424 if (!JS_WrapObject(aCx, &obj)) {
michael@0 12425 return nullptr;
michael@0 12426 }
michael@0 12427 if (!JS_DefineProperty(aCx, mImpl->Callback(), "__DOM_IMPL__", obj, 0)) {
michael@0 12428 return nullptr;
michael@0 12429 }
michael@0 12430 return obj;
michael@0 12431 """,
michael@0 12432 name=self.descriptor.name)
michael@0 12433
michael@0 12434 def getGetParentObjectReturnType(self):
michael@0 12435 return "nsISupports*"
michael@0 12436
michael@0 12437 def getGetParentObjectBody(self):
michael@0 12438 return "return mParent;\n"
michael@0 12439
michael@0 12440 def getCreateFromExistingBody(self):
michael@0 12441 # XXXbz we could try to get parts of this (e.g. the argument
michael@0 12442 # conversions) auto-generated by somehow creating an IDLMethod and
michael@0 12443 # adding it to our interface, but we'd still need to special-case the
michael@0 12444 # implementation slightly to have it not try to forward to the JS
michael@0 12445 # object...
michael@0 12446 return fill(
michael@0 12447 """
michael@0 12448 JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
michael@0 12449 if (args.length() < 2) {
michael@0 12450 return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${ifaceName}._create");
michael@0 12451 }
michael@0 12452 if (!args[0].isObject()) {
michael@0 12453 return ThrowErrorMessage(cx, MSG_NOT_OBJECT, "Argument 1 of ${ifaceName}._create");
michael@0 12454 }
michael@0 12455 if (!args[1].isObject()) {
michael@0 12456 return ThrowErrorMessage(cx, MSG_NOT_OBJECT, "Argument 2 of ${ifaceName}._create");
michael@0 12457 }
michael@0 12458
michael@0 12459 // GlobalObject will go through wrappers as needed for us, and
michael@0 12460 // is simpler than the right UnwrapArg incantation.
michael@0 12461 GlobalObject global(cx, &args[0].toObject());
michael@0 12462 if (global.Failed()) {
michael@0 12463 return false;
michael@0 12464 }
michael@0 12465 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(global.GetAsSupports());
michael@0 12466 if (!window) {
michael@0 12467 return ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "Argument 1 of ${ifaceName}._create", "Window");
michael@0 12468 }
michael@0 12469 JS::Rooted<JSObject*> arg(cx, &args[1].toObject());
michael@0 12470 nsRefPtr<${implName}> impl = new ${implName}(arg, window);
michael@0 12471 MOZ_ASSERT(js::IsObjectInContextCompartment(arg, cx));
michael@0 12472 return WrapNewBindingObject(cx, impl, args.rval());
michael@0 12473 """,
michael@0 12474 ifaceName=self.descriptor.interface.identifier.name,
michael@0 12475 implName=self.descriptor.name)
michael@0 12476
michael@0 12477
michael@0 12478 def isJSImplementedDescriptor(descriptorProvider):
michael@0 12479 return (isinstance(descriptorProvider, Descriptor) and
michael@0 12480 descriptorProvider.interface.isJSImplemented())
michael@0 12481
michael@0 12482
michael@0 12483 class CGCallback(CGClass):
michael@0 12484 def __init__(self, idlObject, descriptorProvider, baseName, methods,
michael@0 12485 getters=[], setters=[]):
michael@0 12486 self.baseName = baseName
michael@0 12487 self._deps = idlObject.getDeps()
michael@0 12488 self.idlObject = idlObject
michael@0 12489 name = idlObject.identifier.name
michael@0 12490 if isJSImplementedDescriptor(descriptorProvider):
michael@0 12491 name = jsImplName(name)
michael@0 12492 # For our public methods that needThisHandling we want most of the
michael@0 12493 # same args and the same return type as what CallbackMember
michael@0 12494 # generates. So we want to take advantage of all its
michael@0 12495 # CGNativeMember infrastructure, but that infrastructure can't deal
michael@0 12496 # with templates and most especially template arguments. So just
michael@0 12497 # cheat and have CallbackMember compute all those things for us.
michael@0 12498 realMethods = []
michael@0 12499 for method in methods:
michael@0 12500 if not method.needThisHandling:
michael@0 12501 realMethods.append(method)
michael@0 12502 else:
michael@0 12503 realMethods.extend(self.getMethodImpls(method))
michael@0 12504 realMethods.append(
michael@0 12505 ClassMethod("operator==", "bool",
michael@0 12506 [Argument("const %s&" % name, "aOther")],
michael@0 12507 inline=True, bodyInHeader=True,
michael@0 12508 const=True,
michael@0 12509 body=("return %s::operator==(aOther);\n" % baseName)))
michael@0 12510 CGClass.__init__(self, name,
michael@0 12511 bases=[ClassBase(baseName)],
michael@0 12512 constructors=self.getConstructors(),
michael@0 12513 methods=realMethods+getters+setters)
michael@0 12514
michael@0 12515 def getConstructors(self):
michael@0 12516 if (not self.idlObject.isInterface() and
michael@0 12517 not self.idlObject._treatNonObjectAsNull):
michael@0 12518 body = "MOZ_ASSERT(JS_ObjectIsCallable(nullptr, mCallback));\n"
michael@0 12519 else:
michael@0 12520 # Not much we can assert about it, other than not being null, and
michael@0 12521 # CallbackObject does that already.
michael@0 12522 body = ""
michael@0 12523 return [ClassConstructor(
michael@0 12524 [Argument("JS::Handle<JSObject*>", "aCallback"),
michael@0 12525 Argument("nsIGlobalObject*", "aIncumbentGlobal")],
michael@0 12526 bodyInHeader=True,
michael@0 12527 visibility="public",
michael@0 12528 explicit=True,
michael@0 12529 baseConstructors=[
michael@0 12530 "%s(aCallback, aIncumbentGlobal)" % self.baseName,
michael@0 12531 ],
michael@0 12532 body=body)]
michael@0 12533
michael@0 12534 def getMethodImpls(self, method):
michael@0 12535 assert method.needThisHandling
michael@0 12536 args = list(method.args)
michael@0 12537 # Strip out the JSContext*/JSObject* args
michael@0 12538 # that got added.
michael@0 12539 assert args[0].name == "cx" and args[0].argType == "JSContext*"
michael@0 12540 assert args[1].name == "aThisVal" and args[1].argType == "JS::Handle<JS::Value>"
michael@0 12541 args = args[2:]
michael@0 12542 # Record the names of all the arguments, so we can use them when we call
michael@0 12543 # the private method.
michael@0 12544 argnames = [arg.name for arg in args]
michael@0 12545 argnamesWithThis = ["s.GetContext()", "thisValJS"] + argnames
michael@0 12546 argnamesWithoutThis = ["s.GetContext()", "JS::UndefinedHandleValue"] + argnames
michael@0 12547 # Now that we've recorded the argnames for our call to our private
michael@0 12548 # method, insert our optional argument for deciding whether the
michael@0 12549 # CallSetup should re-throw exceptions on aRv.
michael@0 12550 args.append(Argument("ExceptionHandling", "aExceptionHandling",
michael@0 12551 "eReportExceptions"))
michael@0 12552 # And now insert our template argument.
michael@0 12553 argsWithoutThis = list(args)
michael@0 12554 args.insert(0, Argument("const T&", "thisObjPtr"))
michael@0 12555 errorReturn = method.getDefaultRetval()
michael@0 12556
michael@0 12557 setupCall = fill(
michael@0 12558 """
michael@0 12559 CallSetup s(this, aRv, aExceptionHandling);
michael@0 12560 if (!s.GetContext()) {
michael@0 12561 aRv.Throw(NS_ERROR_UNEXPECTED);
michael@0 12562 return${errorReturn};
michael@0 12563 }
michael@0 12564 """,
michael@0 12565 errorReturn=errorReturn)
michael@0 12566
michael@0 12567 bodyWithThis = fill(
michael@0 12568 """
michael@0 12569 $*{setupCall}
michael@0 12570 JS::Rooted<JSObject*> thisObjJS(s.GetContext(),
michael@0 12571 WrapCallThisObject(s.GetContext(), thisObjPtr));
michael@0 12572 if (!thisObjJS) {
michael@0 12573 aRv.Throw(NS_ERROR_FAILURE);
michael@0 12574 return${errorReturn};
michael@0 12575 }
michael@0 12576 JS::Rooted<JS::Value> thisValJS(s.GetContext(),
michael@0 12577 JS::ObjectValue(*thisObjJS));
michael@0 12578 return ${methodName}(${callArgs});
michael@0 12579 """,
michael@0 12580 setupCall=setupCall,
michael@0 12581 errorReturn=errorReturn,
michael@0 12582 methodName=method.name,
michael@0 12583 callArgs=", ".join(argnamesWithThis))
michael@0 12584 bodyWithoutThis = fill(
michael@0 12585 """
michael@0 12586 $*{setupCall}
michael@0 12587 return ${methodName}(${callArgs});
michael@0 12588 """,
michael@0 12589 setupCall=setupCall,
michael@0 12590 errorReturn=errorReturn,
michael@0 12591 methodName=method.name,
michael@0 12592 callArgs=", ".join(argnamesWithoutThis))
michael@0 12593
michael@0 12594 return [ClassMethod(method.name, method.returnType, args,
michael@0 12595 bodyInHeader=True,
michael@0 12596 templateArgs=["typename T"],
michael@0 12597 body=bodyWithThis),
michael@0 12598 ClassMethod(method.name, method.returnType, argsWithoutThis,
michael@0 12599 bodyInHeader=True,
michael@0 12600 body=bodyWithoutThis),
michael@0 12601 method]
michael@0 12602
michael@0 12603 def deps(self):
michael@0 12604 return self._deps
michael@0 12605
michael@0 12606
michael@0 12607 class CGCallbackFunction(CGCallback):
michael@0 12608 def __init__(self, callback, descriptorProvider):
michael@0 12609 self.callback = callback
michael@0 12610 CGCallback.__init__(self, callback, descriptorProvider,
michael@0 12611 "CallbackFunction",
michael@0 12612 methods=[CallCallback(callback, descriptorProvider)])
michael@0 12613
michael@0 12614 def getConstructors(self):
michael@0 12615 return CGCallback.getConstructors(self) + [
michael@0 12616 ClassConstructor(
michael@0 12617 [Argument("CallbackFunction*", "aOther")],
michael@0 12618 bodyInHeader=True,
michael@0 12619 visibility="public",
michael@0 12620 explicit=True,
michael@0 12621 baseConstructors=["CallbackFunction(aOther)"])]
michael@0 12622
michael@0 12623
michael@0 12624 class CGCallbackInterface(CGCallback):
michael@0 12625 def __init__(self, descriptor):
michael@0 12626 iface = descriptor.interface
michael@0 12627 attrs = [m for m in iface.members if m.isAttr() and not m.isStatic()]
michael@0 12628 getters = [CallbackGetter(a, descriptor) for a in attrs]
michael@0 12629 setters = [CallbackSetter(a, descriptor) for a in attrs
michael@0 12630 if not a.readonly]
michael@0 12631 methods = [m for m in iface.members
michael@0 12632 if m.isMethod() and not m.isStatic() and not m.isIdentifierLess()]
michael@0 12633 methods = [CallbackOperation(m, sig, descriptor) for m in methods
michael@0 12634 for sig in m.signatures()]
michael@0 12635 if iface.isJSImplemented() and iface.ctor():
michael@0 12636 sigs = descriptor.interface.ctor().signatures()
michael@0 12637 if len(sigs) != 1:
michael@0 12638 raise TypeError("We only handle one constructor. See bug 869268.")
michael@0 12639 methods.append(CGJSImplInitOperation(sigs[0], descriptor))
michael@0 12640 CGCallback.__init__(self, iface, descriptor, "CallbackInterface",
michael@0 12641 methods, getters=getters, setters=setters)
michael@0 12642
michael@0 12643
michael@0 12644 class FakeMember():
michael@0 12645 def __init__(self):
michael@0 12646 self.treatNullAs = "Default"
michael@0 12647
michael@0 12648 def isStatic(self):
michael@0 12649 return False
michael@0 12650
michael@0 12651 def isAttr(self):
michael@0 12652 return False
michael@0 12653
michael@0 12654 def isMethod(self):
michael@0 12655 return False
michael@0 12656
michael@0 12657 def getExtendedAttribute(self, name):
michael@0 12658 # Claim to be a [NewObject] so we can avoid the "mark this
michael@0 12659 # resultNotAddRefed" comments CGNativeMember codegen would
michael@0 12660 # otherwise stick in.
michael@0 12661 if name == "NewObject":
michael@0 12662 return True
michael@0 12663 return None
michael@0 12664
michael@0 12665
michael@0 12666 class CallbackMember(CGNativeMember):
michael@0 12667 def __init__(self, sig, name, descriptorProvider, needThisHandling, rethrowContentException=False):
michael@0 12668 """
michael@0 12669 needThisHandling is True if we need to be able to accept a specified
michael@0 12670 thisObj, False otherwise.
michael@0 12671 """
michael@0 12672 assert not rethrowContentException or not needThisHandling
michael@0 12673
michael@0 12674 self.retvalType = sig[0]
michael@0 12675 self.originalSig = sig
michael@0 12676 args = sig[1]
michael@0 12677 self.argCount = len(args)
michael@0 12678 if self.argCount > 0:
michael@0 12679 # Check for variadic arguments
michael@0 12680 lastArg = args[self.argCount-1]
michael@0 12681 if lastArg.variadic:
michael@0 12682 self.argCountStr = ("(%d - 1) + %s.Length()" %
michael@0 12683 (self.argCount, lastArg.identifier.name))
michael@0 12684 else:
michael@0 12685 self.argCountStr = "%d" % self.argCount
michael@0 12686 self.needThisHandling = needThisHandling
michael@0 12687 # If needThisHandling, we generate ourselves as private and the caller
michael@0 12688 # will handle generating public versions that handle the "this" stuff.
michael@0 12689 visibility = "private" if needThisHandling else "public"
michael@0 12690 self.rethrowContentException = rethrowContentException
michael@0 12691 # We don't care, for callback codegen, whether our original member was
michael@0 12692 # a method or attribute or whatnot. Just always pass FakeMember()
michael@0 12693 # here.
michael@0 12694 CGNativeMember.__init__(self, descriptorProvider, FakeMember(),
michael@0 12695 name, (self.retvalType, args),
michael@0 12696 extendedAttrs={},
michael@0 12697 passJSBitsAsNeeded=False,
michael@0 12698 visibility=visibility,
michael@0 12699 jsObjectsArePtr=True)
michael@0 12700 # We have to do all the generation of our body now, because
michael@0 12701 # the caller relies on us throwing if we can't manage it.
michael@0 12702 self.exceptionCode = ("aRv.Throw(NS_ERROR_UNEXPECTED);\n"
michael@0 12703 "return%s;\n" % self.getDefaultRetval())
michael@0 12704 self.body = self.getImpl()
michael@0 12705
michael@0 12706 def getImpl(self):
michael@0 12707 setupCall = self.getCallSetup()
michael@0 12708 declRval = self.getRvalDecl()
michael@0 12709 if self.argCount > 0:
michael@0 12710 argvDecl = fill(
michael@0 12711 """
michael@0 12712 JS::AutoValueVector argv(cx);
michael@0 12713 if (!argv.resize(${argCount})) {
michael@0 12714 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
michael@0 12715 return${errorReturn};
michael@0 12716 }
michael@0 12717 """,
michael@0 12718 argCount=self.argCountStr,
michael@0 12719 errorReturn=self.getDefaultRetval())
michael@0 12720 else:
michael@0 12721 # Avoid weird 0-sized arrays
michael@0 12722 argvDecl = ""
michael@0 12723 convertArgs = self.getArgConversions()
michael@0 12724 doCall = self.getCall()
michael@0 12725 returnResult = self.getResultConversion()
michael@0 12726
michael@0 12727 return setupCall + declRval + argvDecl + convertArgs + doCall + returnResult
michael@0 12728
michael@0 12729 def getResultConversion(self):
michael@0 12730 replacements = {
michael@0 12731 "val": "rval",
michael@0 12732 "mutableVal": "&rval",
michael@0 12733 "holderName": "rvalHolder",
michael@0 12734 "declName": "rvalDecl",
michael@0 12735 # We actually want to pass in a null scope object here, because
michael@0 12736 # wrapping things into our current compartment (that of mCallback)
michael@0 12737 # is what we want.
michael@0 12738 "obj": "nullptr"
michael@0 12739 }
michael@0 12740
michael@0 12741 if isJSImplementedDescriptor(self.descriptorProvider):
michael@0 12742 isCallbackReturnValue = "JSImpl"
michael@0 12743 else:
michael@0 12744 isCallbackReturnValue = "Callback"
michael@0 12745 sourceDescription = "return value of %s" % self.getPrettyName()
michael@0 12746 convertType = instantiateJSToNativeConversion(
michael@0 12747 getJSToNativeConversionInfo(self.retvalType,
michael@0 12748 self.descriptorProvider,
michael@0 12749 exceptionCode=self.exceptionCode,
michael@0 12750 isCallbackReturnValue=isCallbackReturnValue,
michael@0 12751 sourceDescription=sourceDescription),
michael@0 12752 replacements)
michael@0 12753 assignRetval = string.Template(
michael@0 12754 self.getRetvalInfo(self.retvalType,
michael@0 12755 False)[2]).substitute(replacements)
michael@0 12756 type = convertType.define()
michael@0 12757 if type == "":
michael@0 12758 type = "\n" # BOGUS extra blank line
michael@0 12759 if assignRetval == "":
michael@0 12760 assignRetval = "\n" # BOGUS extra blank line
michael@0 12761 return type + assignRetval
michael@0 12762
michael@0 12763 def getArgConversions(self):
michael@0 12764 # Just reget the arglist from self.originalSig, because our superclasses
michael@0 12765 # just have way to many members they like to clobber, so I can't find a
michael@0 12766 # safe member name to store it in.
michael@0 12767 argConversions = [self.getArgConversion(i, arg)
michael@0 12768 for i, arg in enumerate(self.originalSig[1])]
michael@0 12769 if not argConversions:
michael@0 12770 return "\n\n" # BOGUS extra blank line
michael@0 12771
michael@0 12772 # Do them back to front, so our argc modifications will work
michael@0 12773 # correctly, because we examine trailing arguments first.
michael@0 12774 argConversions.reverse()
michael@0 12775 # Wrap each one in a scope so that any locals it has don't leak out, and
michael@0 12776 # also so that we can just "break;" for our successCode.
michael@0 12777 argConversions = [CGWrapper(CGIndenter(CGGeneric(c)),
michael@0 12778 pre="do {\n",
michael@0 12779 post="} while (0);\n")
michael@0 12780 for c in argConversions]
michael@0 12781 if self.argCount > 0:
michael@0 12782 argConversions.insert(0, self.getArgcDecl())
michael@0 12783 # And slap them together.
michael@0 12784 return CGList(argConversions, "\n").define() + "\n"
michael@0 12785
michael@0 12786 def getArgConversion(self, i, arg):
michael@0 12787 argval = arg.identifier.name
michael@0 12788
michael@0 12789 if arg.variadic:
michael@0 12790 argval = argval + "[idx]"
michael@0 12791 jsvalIndex = "%d + idx" % i
michael@0 12792 else:
michael@0 12793 jsvalIndex = "%d" % i
michael@0 12794 if arg.optional and not arg.defaultValue:
michael@0 12795 argval += ".Value()"
michael@0 12796 if arg.type.isDOMString():
michael@0 12797 # XPConnect string-to-JS conversion wants to mutate the string. So
michael@0 12798 # let's give it a string it can mutate
michael@0 12799 # XXXbz if we try to do a sequence of strings, this will kinda fail.
michael@0 12800 result = "mutableStr"
michael@0 12801 prepend = "nsString mutableStr(%s);\n" % argval
michael@0 12802 else:
michael@0 12803 result = argval
michael@0 12804 prepend = ""
michael@0 12805
michael@0 12806 try:
michael@0 12807 conversion = prepend + wrapForType(
michael@0 12808 arg.type, self.descriptorProvider,
michael@0 12809 {
michael@0 12810 'result': result,
michael@0 12811 'successCode': "continue;\n" if arg.variadic else "break;\n",
michael@0 12812 'jsvalRef': "argv.handleAt(%s)" % jsvalIndex,
michael@0 12813 'jsvalHandle': "argv.handleAt(%s)" % jsvalIndex,
michael@0 12814 # XXXbz we don't have anything better to use for 'obj',
michael@0 12815 # really... It's OK to use CallbackPreserveColor because
michael@0 12816 # CallSetup already handled the unmark-gray bits for us.
michael@0 12817 'obj': 'CallbackPreserveColor()',
michael@0 12818 'returnsNewObject': False,
michael@0 12819 'exceptionCode': self.exceptionCode
michael@0 12820 })
michael@0 12821 except MethodNotNewObjectError as err:
michael@0 12822 raise TypeError("%s being passed as an argument to %s but is not "
michael@0 12823 "wrapper cached, so can't be reliably converted to "
michael@0 12824 "a JS object." %
michael@0 12825 (err.typename, self.getPrettyName()))
michael@0 12826 if arg.variadic:
michael@0 12827 conversion = fill(
michael@0 12828 """
michael@0 12829 for (uint32_t idx = 0; idx < ${arg}.Length(); ++idx) {
michael@0 12830 $*{conversion}
michael@0 12831 }
michael@0 12832 break;
michael@0 12833 """,
michael@0 12834 arg=arg.identifier.name,
michael@0 12835 conversion=conversion)
michael@0 12836 elif arg.optional and not arg.defaultValue:
michael@0 12837 conversion = fill(
michael@0 12838 """
michael@0 12839 if (${argName}.WasPassed()) {
michael@0 12840 $*{conversion}
michael@0 12841 } else if (argc == ${iPlus1}) {
michael@0 12842 // This is our current trailing argument; reduce argc
michael@0 12843 --argc;
michael@0 12844 } else {
michael@0 12845 argv[${i}] = JS::UndefinedValue();
michael@0 12846 }
michael@0 12847 """,
michael@0 12848 argName=arg.identifier.name,
michael@0 12849 conversion=conversion,
michael@0 12850 iPlus1=i + 1,
michael@0 12851 i=i)
michael@0 12852 return conversion
michael@0 12853
michael@0 12854 def getDefaultRetval(self):
michael@0 12855 default = self.getRetvalInfo(self.retvalType, False)[1]
michael@0 12856 if len(default) != 0:
michael@0 12857 default = " " + default
michael@0 12858 return default
michael@0 12859
michael@0 12860 def getArgs(self, returnType, argList):
michael@0 12861 args = CGNativeMember.getArgs(self, returnType, argList)
michael@0 12862 if not self.needThisHandling:
michael@0 12863 # Since we don't need this handling, we're the actual method that
michael@0 12864 # will be called, so we need an aRethrowExceptions argument.
michael@0 12865 if self.rethrowContentException:
michael@0 12866 args.append(Argument("JSCompartment*", "aCompartment", "nullptr"))
michael@0 12867 else:
michael@0 12868 args.append(Argument("ExceptionHandling", "aExceptionHandling",
michael@0 12869 "eReportExceptions"))
michael@0 12870 return args
michael@0 12871 # We want to allow the caller to pass in a "this" value, as
michael@0 12872 # well as a JSContext.
michael@0 12873 return [Argument("JSContext*", "cx"),
michael@0 12874 Argument("JS::Handle<JS::Value>", "aThisVal")] + args
michael@0 12875
michael@0 12876 def getCallSetup(self):
michael@0 12877 if self.needThisHandling:
michael@0 12878 # It's been done for us already
michael@0 12879 return ""
michael@0 12880 callSetup = "CallSetup s(this, aRv"
michael@0 12881 if self.rethrowContentException:
michael@0 12882 # getArgs doesn't add the aExceptionHandling argument but does add
michael@0 12883 # aCompartment for us.
michael@0 12884 callSetup += ", eRethrowContentExceptions, aCompartment, /* aIsJSImplementedWebIDL = */ "
michael@0 12885 callSetup += toStringBool(isJSImplementedDescriptor(self.descriptorProvider))
michael@0 12886 else:
michael@0 12887 callSetup += ", aExceptionHandling"
michael@0 12888 callSetup += ");\n"
michael@0 12889 return fill(
michael@0 12890 """
michael@0 12891 $*{callSetup}
michael@0 12892 JSContext* cx = s.GetContext();
michael@0 12893 if (!cx) {
michael@0 12894 aRv.Throw(NS_ERROR_UNEXPECTED);
michael@0 12895 return${errorReturn};
michael@0 12896 }
michael@0 12897 """,
michael@0 12898 callSetup=callSetup,
michael@0 12899 errorReturn=self.getDefaultRetval())
michael@0 12900
michael@0 12901 def getArgcDecl(self):
michael@0 12902 return CGGeneric("unsigned argc = %s;\n" % self.argCountStr)
michael@0 12903
michael@0 12904 @staticmethod
michael@0 12905 def ensureASCIIName(idlObject):
michael@0 12906 type = "attribute" if idlObject.isAttr() else "operation"
michael@0 12907 if re.match("[^\x20-\x7E]", idlObject.identifier.name):
michael@0 12908 raise SyntaxError('Callback %s name "%s" contains non-ASCII '
michael@0 12909 "characters. We can't handle that. %s" %
michael@0 12910 (type, idlObject.identifier.name,
michael@0 12911 idlObject.location))
michael@0 12912 if re.match('"', idlObject.identifier.name):
michael@0 12913 raise SyntaxError("Callback %s name '%s' contains "
michael@0 12914 "double-quote character. We can't handle "
michael@0 12915 "that. %s" %
michael@0 12916 (type, idlObject.identifier.name,
michael@0 12917 idlObject.location))
michael@0 12918
michael@0 12919
michael@0 12920 class CallbackMethod(CallbackMember):
michael@0 12921 def __init__(self, sig, name, descriptorProvider, needThisHandling,
michael@0 12922 rethrowContentException=False):
michael@0 12923 CallbackMember.__init__(self, sig, name, descriptorProvider,
michael@0 12924 needThisHandling, rethrowContentException)
michael@0 12925
michael@0 12926 def getRvalDecl(self):
michael@0 12927 return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
michael@0 12928
michael@0 12929 def getCall(self):
michael@0 12930 if self.argCount > 0:
michael@0 12931 args = "JS::HandleValueArray::subarray(argv, 0, argc)"
michael@0 12932 else:
michael@0 12933 args = "JS::HandleValueArray::empty()"
michael@0 12934
michael@0 12935 return fill(
michael@0 12936 """
michael@0 12937 $*{declCallable}
michael@0 12938 $*{declThis}
michael@0 12939 if (${callGuard}!JS::Call(cx, ${thisVal}, callable,
michael@0 12940 ${args}, &rval)) {
michael@0 12941 aRv.Throw(NS_ERROR_UNEXPECTED);
michael@0 12942 return${errorReturn};
michael@0 12943 }
michael@0 12944 """,
michael@0 12945 declCallable=self.getCallableDecl(),
michael@0 12946 declThis=self.getThisDecl(),
michael@0 12947 callGuard=self.getCallGuard(),
michael@0 12948 thisVal=self.getThisVal(),
michael@0 12949 args=args,
michael@0 12950 errorReturn=self.getDefaultRetval())
michael@0 12951
michael@0 12952
michael@0 12953 class CallCallback(CallbackMethod):
michael@0 12954 def __init__(self, callback, descriptorProvider):
michael@0 12955 self.callback = callback
michael@0 12956 CallbackMethod.__init__(self, callback.signatures()[0], "Call",
michael@0 12957 descriptorProvider, needThisHandling=True)
michael@0 12958
michael@0 12959 def getThisDecl(self):
michael@0 12960 return ""
michael@0 12961
michael@0 12962 def getThisVal(self):
michael@0 12963 return "aThisVal"
michael@0 12964
michael@0 12965 def getCallableDecl(self):
michael@0 12966 return "JS::Rooted<JS::Value> callable(cx, JS::ObjectValue(*mCallback));\n"
michael@0 12967
michael@0 12968 def getPrettyName(self):
michael@0 12969 return self.callback.identifier.name
michael@0 12970
michael@0 12971 def getCallGuard(self):
michael@0 12972 if self.callback._treatNonObjectAsNull:
michael@0 12973 return "JS_ObjectIsCallable(cx, mCallback) && "
michael@0 12974 return ""
michael@0 12975
michael@0 12976
michael@0 12977 class CallbackOperationBase(CallbackMethod):
michael@0 12978 """
michael@0 12979 Common class for implementing various callback operations.
michael@0 12980 """
michael@0 12981 def __init__(self, signature, jsName, nativeName, descriptor, singleOperation, rethrowContentException=False):
michael@0 12982 self.singleOperation = singleOperation
michael@0 12983 self.methodName = descriptor.binaryNames.get(jsName, jsName)
michael@0 12984 CallbackMethod.__init__(self, signature, nativeName, descriptor, singleOperation, rethrowContentException)
michael@0 12985
michael@0 12986 def getThisDecl(self):
michael@0 12987 if not self.singleOperation:
michael@0 12988 return "JS::Rooted<JS::Value> thisValue(cx, JS::ObjectValue(*mCallback));\n"
michael@0 12989 # This relies on getCallableDecl declaring a boolean
michael@0 12990 # isCallable in the case when we're a single-operation
michael@0 12991 # interface.
michael@0 12992 return dedent("""
michael@0 12993 JS::Rooted<JS::Value> thisValue(cx, isCallable ? aThisVal.get()
michael@0 12994 : JS::ObjectValue(*mCallback));
michael@0 12995 """)
michael@0 12996
michael@0 12997 def getThisVal(self):
michael@0 12998 return "thisValue"
michael@0 12999
michael@0 13000 def getCallableDecl(self):
michael@0 13001 getCallableFromProp = fill(
michael@0 13002 """
michael@0 13003 if (!GetCallableProperty(cx, "${methodName}", &callable)) {
michael@0 13004 aRv.Throw(NS_ERROR_UNEXPECTED);
michael@0 13005 return${errorReturn};
michael@0 13006 }
michael@0 13007 """,
michael@0 13008 methodName=self.methodName,
michael@0 13009 errorReturn=self.getDefaultRetval())
michael@0 13010 if not self.singleOperation:
michael@0 13011 return 'JS::Rooted<JS::Value> callable(cx);\n' + getCallableFromProp
michael@0 13012 return fill(
michael@0 13013 """
michael@0 13014 bool isCallable = JS_ObjectIsCallable(cx, mCallback);
michael@0 13015 JS::Rooted<JS::Value> callable(cx);
michael@0 13016 if (isCallable) {
michael@0 13017 callable = JS::ObjectValue(*mCallback);
michael@0 13018 } else {
michael@0 13019 $*{getCallableFromProp}
michael@0 13020 }
michael@0 13021 """,
michael@0 13022 getCallableFromProp=getCallableFromProp)
michael@0 13023
michael@0 13024 def getCallGuard(self):
michael@0 13025 return ""
michael@0 13026
michael@0 13027
michael@0 13028 class CallbackOperation(CallbackOperationBase):
michael@0 13029 """
michael@0 13030 Codegen actual WebIDL operations on callback interfaces.
michael@0 13031 """
michael@0 13032 def __init__(self, method, signature, descriptor):
michael@0 13033 self.ensureASCIIName(method)
michael@0 13034 self.method = method
michael@0 13035 jsName = method.identifier.name
michael@0 13036 CallbackOperationBase.__init__(self, signature,
michael@0 13037 jsName,
michael@0 13038 MakeNativeName(descriptor.binaryNames.get(jsName, jsName)),
michael@0 13039 descriptor, descriptor.interface.isSingleOperationInterface(),
michael@0 13040 rethrowContentException=descriptor.interface.isJSImplemented())
michael@0 13041
michael@0 13042 def getPrettyName(self):
michael@0 13043 return "%s.%s" % (self.descriptorProvider.interface.identifier.name,
michael@0 13044 self.method.identifier.name)
michael@0 13045
michael@0 13046
michael@0 13047 class CallbackAccessor(CallbackMember):
michael@0 13048 """
michael@0 13049 Shared superclass for CallbackGetter and CallbackSetter.
michael@0 13050 """
michael@0 13051 def __init__(self, attr, sig, name, descriptor):
michael@0 13052 self.ensureASCIIName(attr)
michael@0 13053 self.attrName = attr.identifier.name
michael@0 13054 CallbackMember.__init__(self, sig, name, descriptor,
michael@0 13055 needThisHandling=False,
michael@0 13056 rethrowContentException=descriptor.interface.isJSImplemented())
michael@0 13057
michael@0 13058 def getPrettyName(self):
michael@0 13059 return "%s.%s" % (self.descriptorProvider.interface.identifier.name,
michael@0 13060 self.attrName)
michael@0 13061
michael@0 13062
michael@0 13063 class CallbackGetter(CallbackAccessor):
michael@0 13064 def __init__(self, attr, descriptor):
michael@0 13065 CallbackAccessor.__init__(self, attr,
michael@0 13066 (attr.type, []),
michael@0 13067 callbackGetterName(attr, descriptor),
michael@0 13068 descriptor)
michael@0 13069
michael@0 13070 def getRvalDecl(self):
michael@0 13071 return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
michael@0 13072
michael@0 13073 def getCall(self):
michael@0 13074 return fill(
michael@0 13075 """
michael@0 13076 JS::Rooted<JSObject *> callback(cx, mCallback);
michael@0 13077 if (!JS_GetProperty(cx, callback, "${attrName}", &rval)) {
michael@0 13078 aRv.Throw(NS_ERROR_UNEXPECTED);
michael@0 13079 return${errorReturn};
michael@0 13080 }
michael@0 13081 """,
michael@0 13082 attrName=self.descriptorProvider.binaryNames.get(self.attrName,
michael@0 13083 self.attrName),
michael@0 13084 errorReturn=self.getDefaultRetval())
michael@0 13085
michael@0 13086
michael@0 13087 class CallbackSetter(CallbackAccessor):
michael@0 13088 def __init__(self, attr, descriptor):
michael@0 13089 CallbackAccessor.__init__(self, attr,
michael@0 13090 (BuiltinTypes[IDLBuiltinType.Types.void],
michael@0 13091 [FakeArgument(attr.type, attr)]),
michael@0 13092 callbackSetterName(attr, descriptor),
michael@0 13093 descriptor)
michael@0 13094
michael@0 13095 def getRvalDecl(self):
michael@0 13096 # We don't need an rval
michael@0 13097 return ""
michael@0 13098
michael@0 13099 def getCall(self):
michael@0 13100 return fill(
michael@0 13101 """
michael@0 13102 MOZ_ASSERT(argv.length() == 1);
michael@0 13103 if (!JS_SetProperty(cx, CallbackPreserveColor(), "${attrName}", argv.handleAt(0))) {
michael@0 13104 aRv.Throw(NS_ERROR_UNEXPECTED);
michael@0 13105 return${errorReturn};
michael@0 13106 }
michael@0 13107 """,
michael@0 13108 attrName=self.descriptorProvider.binaryNames.get(self.attrName,
michael@0 13109 self.attrName),
michael@0 13110 errorReturn=self.getDefaultRetval())
michael@0 13111
michael@0 13112 def getArgcDecl(self):
michael@0 13113 return None
michael@0 13114
michael@0 13115
michael@0 13116 class CGJSImplInitOperation(CallbackOperationBase):
michael@0 13117 """
michael@0 13118 Codegen the __Init() method used to pass along constructor arguments for JS-implemented WebIDL.
michael@0 13119 """
michael@0 13120 def __init__(self, sig, descriptor):
michael@0 13121 assert sig in descriptor.interface.ctor().signatures()
michael@0 13122 CallbackOperationBase.__init__(self, (BuiltinTypes[IDLBuiltinType.Types.void], sig[1]),
michael@0 13123 "__init", "__Init", descriptor, False, True)
michael@0 13124
michael@0 13125 def getPrettyName(self):
michael@0 13126 return "__init"
michael@0 13127
michael@0 13128
michael@0 13129 class GlobalGenRoots():
michael@0 13130 """
michael@0 13131 Roots for global codegen.
michael@0 13132
michael@0 13133 To generate code, call the method associated with the target, and then
michael@0 13134 call the appropriate define/declare method.
michael@0 13135 """
michael@0 13136
michael@0 13137 @staticmethod
michael@0 13138 def GeneratedAtomList(config):
michael@0 13139 # Atom enum
michael@0 13140 dictionaries = config.dictionaries
michael@0 13141
michael@0 13142 structs = []
michael@0 13143
michael@0 13144 for dict in dictionaries:
michael@0 13145 dictMembers = dict.members
michael@0 13146 if len(dictMembers) == 0:
michael@0 13147 continue
michael@0 13148
michael@0 13149 classMembers = [ClassMember(CGDictionary.makeIdName(m.identifier.name),
michael@0 13150 "InternedStringId",
michael@0 13151 visibility="public") for m in dictMembers]
michael@0 13152
michael@0 13153 structName = dict.identifier.name + "Atoms"
michael@0 13154 structs.append((structName,
michael@0 13155 CGWrapper(CGClass(structName,
michael@0 13156 bases=None,
michael@0 13157 isStruct=True,
michael@0 13158 members=classMembers), post='\n')))
michael@0 13159
michael@0 13160 structs.sort()
michael@0 13161 generatedStructs = [struct for structName, struct in structs]
michael@0 13162 structNames = [structName for structName, struct in structs]
michael@0 13163
michael@0 13164 mainStruct = CGWrapper(CGClass("PerThreadAtomCache",
michael@0 13165 bases=[ClassBase(structName) for structName in structNames],
michael@0 13166 isStruct=True),
michael@0 13167 post='\n')
michael@0 13168
michael@0 13169 structs = CGList(generatedStructs + [mainStruct])
michael@0 13170
michael@0 13171 # Wrap all of that in our namespaces.
michael@0 13172 curr = CGNamespace.build(['mozilla', 'dom'],
michael@0 13173 CGWrapper(structs, pre='\n'))
michael@0 13174 curr = CGWrapper(curr, post='\n')
michael@0 13175
michael@0 13176 # Add include statement for InternedStringId.
michael@0 13177 declareIncludes = ['mozilla/dom/BindingUtils.h']
michael@0 13178 curr = CGHeaders([], [], [], [], declareIncludes, [], 'GeneratedAtomList',
michael@0 13179 curr)
michael@0 13180
michael@0 13181 # Add include guards.
michael@0 13182 curr = CGIncludeGuard('GeneratedAtomList', curr)
michael@0 13183
michael@0 13184 # Add the auto-generated comment.
michael@0 13185 curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
michael@0 13186
michael@0 13187 # Done.
michael@0 13188 return curr
michael@0 13189
michael@0 13190 @staticmethod
michael@0 13191 def PrototypeList(config):
michael@0 13192
michael@0 13193 # Prototype ID enum.
michael@0 13194 descriptorsWithPrototype = config.getDescriptors(hasInterfacePrototypeObject=True)
michael@0 13195 protos = [d.name for d in descriptorsWithPrototype]
michael@0 13196 idEnum = CGNamespacedEnum('id', 'ID', ['_ID_Start'] + protos,
michael@0 13197 [0, '_ID_Start'])
michael@0 13198 idEnum = CGList([idEnum])
michael@0 13199
michael@0 13200 # This is only used by DOM worker code, once there are no more consumers
michael@0 13201 # of INTERFACE_CHAIN_* this code should be removed.
michael@0 13202 def ifaceChainMacro(ifaceCount):
michael@0 13203 supplied = [CGGeneric(declare="_iface_" + str(i + 1)) for i in range(ifaceCount)]
michael@0 13204 remaining = [CGGeneric(declare="prototypes::id::_ID_Count")] * (config.maxProtoChainLength - ifaceCount)
michael@0 13205 macro = CGWrapper(CGList(supplied, ", "),
michael@0 13206 pre="#define INTERFACE_CHAIN_" + str(ifaceCount) + "(",
michael@0 13207 post=") \\\n",
michael@0 13208 declareOnly=True)
michael@0 13209 macroContent = CGIndenter(CGList(supplied + remaining, ", \\\n"))
michael@0 13210 macroContent = CGIndenter(CGWrapper(macroContent, pre="{ \\\n",
michael@0 13211 post=" \\\n}",
michael@0 13212 declareOnly=True))
michael@0 13213 return CGWrapper(CGList([macro, macroContent]), post="\n\n",
michael@0 13214 declareOnly=True)
michael@0 13215
michael@0 13216 idEnum.append(ifaceChainMacro(1))
michael@0 13217
michael@0 13218 def fieldSizeAssert(amount, jitInfoField, message):
michael@0 13219 maxFieldValue = "(uint64_t(1) << (sizeof(((JSJitInfo*)nullptr)->%s) * 8))" % jitInfoField
michael@0 13220 return CGGeneric(declare="static_assert(%s < %s, \"%s\");\n\n"
michael@0 13221 % (amount, maxFieldValue, message))
michael@0 13222
michael@0 13223 idEnum.append(fieldSizeAssert("id::_ID_Count", "protoID",
michael@0 13224 "Too many prototypes!"))
michael@0 13225
michael@0 13226 # Wrap all of that in our namespaces.
michael@0 13227 idEnum = CGNamespace.build(['mozilla', 'dom', 'prototypes'],
michael@0 13228 CGWrapper(idEnum, pre='\n'))
michael@0 13229 idEnum = CGWrapper(idEnum, post='\n')
michael@0 13230
michael@0 13231 curr = CGList([CGGeneric(define="#include <stdint.h>\n\n"),
michael@0 13232 idEnum])
michael@0 13233
michael@0 13234 # Let things know the maximum length of the prototype chain.
michael@0 13235 maxMacroName = "MAX_PROTOTYPE_CHAIN_LENGTH"
michael@0 13236 maxMacro = CGGeneric(declare="#define " + maxMacroName + " " + str(config.maxProtoChainLength))
michael@0 13237 curr.append(CGWrapper(maxMacro, post='\n\n'))
michael@0 13238 curr.append(fieldSizeAssert(maxMacroName, "depth",
michael@0 13239 "Some inheritance chain is too long!"))
michael@0 13240
michael@0 13241 # Constructor ID enum.
michael@0 13242 constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True)]
michael@0 13243 idEnum = CGNamespacedEnum('id', 'ID', ['_ID_Start'] + constructors,
michael@0 13244 ['prototypes::id::_ID_Count', '_ID_Start'])
michael@0 13245
michael@0 13246 # Wrap all of that in our namespaces.
michael@0 13247 idEnum = CGNamespace.build(['mozilla', 'dom', 'constructors'],
michael@0 13248 CGWrapper(idEnum, pre='\n'))
michael@0 13249 idEnum = CGWrapper(idEnum, post='\n')
michael@0 13250
michael@0 13251 curr.append(idEnum)
michael@0 13252
michael@0 13253 traitsDecls = [CGGeneric(declare=dedent("""
michael@0 13254 template <prototypes::ID PrototypeID>
michael@0 13255 struct PrototypeTraits;
michael@0 13256 """))]
michael@0 13257 traitsDecls.extend(CGPrototypeTraitsClass(d) for d in descriptorsWithPrototype)
michael@0 13258
michael@0 13259 ifaceNamesWithProto = [d.interface.identifier.name
michael@0 13260 for d in descriptorsWithPrototype]
michael@0 13261 traitsDecls.append(CGStringTable("NamesOfInterfacesWithProtos",
michael@0 13262 ifaceNamesWithProto))
michael@0 13263
michael@0 13264 traitsDecl = CGNamespace.build(['mozilla', 'dom'],
michael@0 13265 CGList(traitsDecls))
michael@0 13266
michael@0 13267 curr.append(traitsDecl)
michael@0 13268
michael@0 13269 # Add include guards.
michael@0 13270 curr = CGIncludeGuard('PrototypeList', curr)
michael@0 13271
michael@0 13272 # Add the auto-generated comment.
michael@0 13273 curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
michael@0 13274
michael@0 13275 # Done.
michael@0 13276 return curr
michael@0 13277
michael@0 13278 @staticmethod
michael@0 13279 def RegisterBindings(config):
michael@0 13280
michael@0 13281 # TODO - Generate the methods we want
michael@0 13282 curr = CGRegisterProtos(config)
michael@0 13283
michael@0 13284 # Wrap all of that in our namespaces.
michael@0 13285 curr = CGNamespace.build(['mozilla', 'dom'],
michael@0 13286 CGWrapper(curr, post='\n'))
michael@0 13287 curr = CGWrapper(curr, post='\n')
michael@0 13288
michael@0 13289 # Add the includes
michael@0 13290 defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface)
michael@0 13291 for desc in config.getDescriptors(hasInterfaceObject=True,
michael@0 13292 workers=False,
michael@0 13293 register=True)]
michael@0 13294 defineIncludes.append('nsScriptNameSpaceManager.h')
michael@0 13295 defineIncludes.extend([CGHeaders.getDeclarationFilename(desc.interface)
michael@0 13296 for desc in config.getDescriptors(isNavigatorProperty=True,
michael@0 13297 workers=False,
michael@0 13298 register=True)])
michael@0 13299 curr = CGHeaders([], [], [], [], [], defineIncludes, 'RegisterBindings',
michael@0 13300 curr)
michael@0 13301
michael@0 13302 # Add include guards.
michael@0 13303 curr = CGIncludeGuard('RegisterBindings', curr)
michael@0 13304
michael@0 13305 # Done.
michael@0 13306 return curr
michael@0 13307
michael@0 13308 @staticmethod
michael@0 13309 def UnionTypes(config):
michael@0 13310
michael@0 13311 (includes, implincludes,
michael@0 13312 declarations, unions) = UnionTypes(config.getDescriptors(),
michael@0 13313 config.getDictionaries(),
michael@0 13314 config.getCallbacks(),
michael@0 13315 config)
michael@0 13316 includes.add("mozilla/dom/OwningNonNull.h")
michael@0 13317 includes.add("mozilla/dom/UnionMember.h")
michael@0 13318 includes.add("mozilla/dom/BindingDeclarations.h")
michael@0 13319 # Need BindingUtils.h for FakeDependentString
michael@0 13320 includes.add("mozilla/dom/BindingUtils.h")
michael@0 13321 implincludes.add("mozilla/dom/PrimitiveConversions.h")
michael@0 13322
michael@0 13323 # Wrap all of that in our namespaces.
michael@0 13324 curr = CGNamespace.build(['mozilla', 'dom'], unions)
michael@0 13325
michael@0 13326 curr = CGWrapper(curr, post='\n')
michael@0 13327
michael@0 13328 namespaces = []
michael@0 13329 stack = [CGList([])]
michael@0 13330 for clazz, isStruct in sorted(declarations):
michael@0 13331 elements = clazz.split("::")
michael@0 13332 clazz = CGClassForwardDeclare(elements.pop(), isStruct=isStruct)
michael@0 13333 i = 0
michael@0 13334 if len(elements) > 0:
michael@0 13335 common = min(len(namespaces), len(elements))
michael@0 13336 while i < common and namespaces[i] == elements[i]:
michael@0 13337 i += 1
michael@0 13338
michael@0 13339 # pop all the namespaces that should be closed
michael@0 13340 namespaces = namespaces[:i]
michael@0 13341
michael@0 13342 # add all the namespaces that should be opened
michael@0 13343 for j, namespace in enumerate(elements[i:]):
michael@0 13344 namespaces.append(namespace)
michael@0 13345 # every CGNamespace that we add holds a CGList
michael@0 13346 list = CGList([])
michael@0 13347 # add the new namespace to the list on top of the stack
michael@0 13348 stack[i + j].append(CGNamespace(namespace, list))
michael@0 13349 # set the top of the namespace stack to the list of the new
michael@0 13350 # namespace
michael@0 13351 stack[i + j + 1:] = [list]
michael@0 13352
michael@0 13353 stack[len(elements)].append(clazz)
michael@0 13354
michael@0 13355 curr = CGList([stack[0], curr], "\n")
michael@0 13356
michael@0 13357 curr = CGHeaders([], [], [], [], includes, implincludes, 'UnionTypes',
michael@0 13358 curr)
michael@0 13359
michael@0 13360 # Add include guards.
michael@0 13361 curr = CGIncludeGuard('UnionTypes', curr)
michael@0 13362
michael@0 13363 # Done.
michael@0 13364 return curr
michael@0 13365
michael@0 13366 @staticmethod
michael@0 13367 def UnionConversions(config):
michael@0 13368
michael@0 13369 headers, unions = UnionConversions(config.getDescriptors(),
michael@0 13370 config.getDictionaries(),
michael@0 13371 config.getCallbacks(),
michael@0 13372 config)
michael@0 13373
michael@0 13374 # Wrap all of that in our namespaces.
michael@0 13375 curr = CGNamespace.build(['mozilla', 'dom'], unions)
michael@0 13376
michael@0 13377 curr = CGWrapper(curr, post='\n')
michael@0 13378
michael@0 13379 headers.update(["nsDebug.h", "mozilla/dom/UnionTypes.h"])
michael@0 13380 curr = CGHeaders([], [], [], [], headers, [], 'UnionConversions', curr)
michael@0 13381
michael@0 13382 # Add include guards.
michael@0 13383 curr = CGIncludeGuard('UnionConversions', curr)
michael@0 13384
michael@0 13385 # Done.
michael@0 13386 return curr
michael@0 13387
michael@0 13388
michael@0 13389 # Code generator for simple events
michael@0 13390 class CGEventGetter(CGNativeMember):
michael@0 13391 def __init__(self, descriptor, attr):
michael@0 13392 ea = descriptor.getExtendedAttributes(attr, getter=True)
michael@0 13393 ea.append('resultNotAddRefed')
michael@0 13394 CGNativeMember.__init__(self, descriptor, attr,
michael@0 13395 CGSpecializedGetter.makeNativeName(descriptor,
michael@0 13396 attr),
michael@0 13397 (attr.type, []),
michael@0 13398 ea)
michael@0 13399 self.body = self.getMethodBody()
michael@0 13400
michael@0 13401 def getArgs(self, returnType, argList):
michael@0 13402 if 'infallible' not in self.extendedAttrs:
michael@0 13403 raise TypeError("Event code generator does not support [Throws]!")
michael@0 13404 if not self.member.isAttr():
michael@0 13405 raise TypeError("Event code generator does not support methods")
michael@0 13406 if self.member.isStatic():
michael@0 13407 raise TypeError("Event code generators does not support static attributes")
michael@0 13408 return CGNativeMember.getArgs(self, returnType, argList)
michael@0 13409
michael@0 13410 def getMethodBody(self):
michael@0 13411 type = self.member.type
michael@0 13412 memberName = CGDictionary.makeMemberName(self.member.identifier.name)
michael@0 13413 if (type.isPrimitive() and type.tag() in builtinNames) or type.isEnum() or type.isGeckoInterface():
michael@0 13414 return "return " + memberName + ";\n"
michael@0 13415 if type.isDOMString() or type.isByteString():
michael@0 13416 return "aRetVal = " + memberName + ";\n"
michael@0 13417 if type.isSpiderMonkeyInterface() or type.isObject():
michael@0 13418 return fill(
michael@0 13419 """
michael@0 13420 if (${memberName}) {
michael@0 13421 JS::ExposeObjectToActiveJS(${memberName});
michael@0 13422 }
michael@0 13423 aRetVal.set(${memberName});
michael@0 13424 return;
michael@0 13425 """,
michael@0 13426 memberName=memberName)
michael@0 13427 if type.isAny():
michael@0 13428 return fill(
michael@0 13429 """
michael@0 13430 JS::ExposeValueToActiveJS(${memberName});
michael@0 13431 aRetVal.set(${memberName});
michael@0 13432 return;
michael@0 13433 """,
michael@0 13434 memberName=memberName)
michael@0 13435 if type.isUnion():
michael@0 13436 return "aRetVal = " + memberName + ";\n"
michael@0 13437 raise TypeError("Event code generator does not support this type!")
michael@0 13438
michael@0 13439 def declare(self, cgClass):
michael@0 13440 if getattr(self.member, "originatingInterface",
michael@0 13441 cgClass.descriptor.interface) != cgClass.descriptor.interface:
michael@0 13442 return ""
michael@0 13443 return CGNativeMember.declare(self, cgClass)
michael@0 13444
michael@0 13445 def define(self, cgClass):
michael@0 13446 if getattr(self.member, "originatingInterface",
michael@0 13447 cgClass.descriptor.interface) != cgClass.descriptor.interface:
michael@0 13448 return ""
michael@0 13449 return CGNativeMember.define(self, cgClass)
michael@0 13450
michael@0 13451
michael@0 13452 class CGEventSetter(CGNativeMember):
michael@0 13453 def __init__(self):
michael@0 13454 raise TypeError("Event code generator does not support setters!")
michael@0 13455
michael@0 13456
michael@0 13457 class CGEventMethod(CGNativeMember):
michael@0 13458 def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
michael@0 13459 if not isConstructor:
michael@0 13460 raise TypeError("Event code generator does not support methods!")
michael@0 13461 self.wantsConstructorForNativeCaller = True
michael@0 13462 CGNativeMember.__init__(self, descriptor, method,
michael@0 13463 CGSpecializedMethod.makeNativeName(descriptor,
michael@0 13464 method),
michael@0 13465 signature,
michael@0 13466 descriptor.getExtendedAttributes(method),
michael@0 13467 breakAfter=breakAfter,
michael@0 13468 variadicIsSequence=True)
michael@0 13469 self.originalArgs = list(self.args)
michael@0 13470
michael@0 13471 def getArgs(self, returnType, argList):
michael@0 13472 args = [self.getArg(arg) for arg in argList]
michael@0 13473 return args
michael@0 13474
michael@0 13475 def getArg(self, arg):
michael@0 13476 decl, ref = self.getArgType(arg.type,
michael@0 13477 arg.optional and not arg.defaultValue,
michael@0 13478 "Variadic" if arg.variadic else False)
michael@0 13479 if ref:
michael@0 13480 decl = CGWrapper(decl, pre="const ", post="&")
michael@0 13481
michael@0 13482 name = arg.identifier.name
michael@0 13483 name = "a" + name[0].upper() + name[1:]
michael@0 13484 return Argument(decl.define(), name)
michael@0 13485
michael@0 13486 def declare(self, cgClass):
michael@0 13487 self.args = list(self.originalArgs)
michael@0 13488 self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner"))
michael@0 13489 constructorForNativeCaller = CGNativeMember.declare(self, cgClass) + "\n"
michael@0 13490 self.args = list(self.originalArgs)
michael@0 13491 if needCx(None, self.descriptorProvider.interface.members, [], True):
michael@0 13492 self.args.insert(0, Argument("JSContext*", "aCx"))
michael@0 13493 self.args.insert(0, Argument("const GlobalObject&", "aGlobal"))
michael@0 13494 self.args.append(Argument('ErrorResult&', 'aRv'))
michael@0 13495 return constructorForNativeCaller + CGNativeMember.declare(self, cgClass)
michael@0 13496
michael@0 13497 def define(self, cgClass):
michael@0 13498 self.args = list(self.originalArgs)
michael@0 13499 members = ""
michael@0 13500 holdJS = ""
michael@0 13501 iface = self.descriptorProvider.interface
michael@0 13502 while iface.identifier.name != "Event":
michael@0 13503 for m in self.descriptorProvider.getDescriptor(iface.identifier.name).interface.members:
michael@0 13504 if m.isAttr():
michael@0 13505 # We initialize all the other member variables in the
michael@0 13506 # Constructor except those ones coming from the Event.
michael@0 13507 if getattr(m, "originatingInterface",
michael@0 13508 cgClass.descriptor.interface).identifier.name == "Event":
michael@0 13509 continue
michael@0 13510 name = CGDictionary.makeMemberName(m.identifier.name)
michael@0 13511 members += "e->%s = %s.%s;\n" % (name, self.args[1].name, name)
michael@0 13512 if m.type.isAny() or m.type.isObject() or m.type.isSpiderMonkeyInterface():
michael@0 13513 holdJS = "mozilla::HoldJSObjects(e.get());\n"
michael@0 13514 iface = iface.parent
michael@0 13515
michael@0 13516 self.body = fill(
michael@0 13517 """
michael@0 13518 nsRefPtr<${nativeType}> e = new ${nativeType}(aOwner);
michael@0 13519 bool trusted = e->Init(aOwner);
michael@0 13520 e->InitEvent(${eventType}, ${eventInit}.mBubbles, ${eventInit}.mCancelable);
michael@0 13521 $*{members}
michael@0 13522 e->SetTrusted(trusted);
michael@0 13523 $*{holdJS}
michael@0 13524 return e.forget();
michael@0 13525 """,
michael@0 13526 nativeType=self.descriptorProvider.nativeType.split('::')[-1],
michael@0 13527 eventType=self.args[0].name,
michael@0 13528 eventInit=self.args[1].name,
michael@0 13529 members=members,
michael@0 13530 holdJS=holdJS)
michael@0 13531
michael@0 13532 self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner"))
michael@0 13533 constructorForNativeCaller = CGNativeMember.define(self, cgClass) + "\n"
michael@0 13534 self.args = list(self.originalArgs)
michael@0 13535 self.body = fill(
michael@0 13536 """
michael@0 13537 nsCOMPtr<mozilla::dom::EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports());
michael@0 13538 return Constructor(owner, ${arg0}, ${arg1});
michael@0 13539 """,
michael@0 13540 arg0=self.args[0].name,
michael@0 13541 arg1=self.args[1].name)
michael@0 13542 if needCx(None, self.descriptorProvider.interface.members, [], True):
michael@0 13543 self.args.insert(0, Argument("JSContext*", "aCx"))
michael@0 13544 self.args.insert(0, Argument("const GlobalObject&", "aGlobal"))
michael@0 13545 self.args.append(Argument('ErrorResult&', 'aRv'))
michael@0 13546 return constructorForNativeCaller + CGNativeMember.define(self, cgClass)
michael@0 13547
michael@0 13548
michael@0 13549 class CGEventClass(CGBindingImplClass):
michael@0 13550 """
michael@0 13551 Codegen for the actual Event class implementation for this descriptor
michael@0 13552 """
michael@0 13553 def __init__(self, descriptor):
michael@0 13554 CGBindingImplClass.__init__(self, descriptor, CGEventMethod, CGEventGetter, CGEventSetter, False)
michael@0 13555 members = []
michael@0 13556 for m in descriptor.interface.members:
michael@0 13557 if m.isAttr():
michael@0 13558 if getattr(m, "originatingInterface",
michael@0 13559 descriptor.interface) != descriptor.interface:
michael@0 13560 continue
michael@0 13561 if m.type.isPrimitive() and m.type.tag() in builtinNames:
michael@0 13562 nativeType = CGGeneric(builtinNames[m.type.tag()])
michael@0 13563 if m.type.nullable():
michael@0 13564 nativeType = CGTemplatedType("Nullable", nativeType)
michael@0 13565 nativeType = nativeType.define()
michael@0 13566 elif m.type.isEnum():
michael@0 13567 nativeType = m.type.unroll().inner.identifier.name
michael@0 13568 if m.type.nullable():
michael@0 13569 nativeType = CGTemplatedType("Nullable",
michael@0 13570 CGGeneric(nativeType)).define()
michael@0 13571 elif m.type.isDOMString():
michael@0 13572 nativeType = "nsString"
michael@0 13573 elif m.type.isByteString():
michael@0 13574 nativeType = "nsCString"
michael@0 13575 elif m.type.isGeckoInterface():
michael@0 13576 iface = m.type.unroll().inner
michael@0 13577 nativeType = self.descriptor.getDescriptor(
michael@0 13578 iface.identifier.name).nativeType
michael@0 13579 # Now trim off unnecessary namespaces
michael@0 13580 nativeType = nativeType.split("::")
michael@0 13581 if nativeType[0] == "mozilla":
michael@0 13582 nativeType.pop(0)
michael@0 13583 if nativeType[0] == "dom":
michael@0 13584 nativeType.pop(0)
michael@0 13585 nativeType = CGWrapper(CGGeneric("::".join(nativeType)), pre="nsRefPtr<", post=">").define()
michael@0 13586 elif m.type.isAny():
michael@0 13587 nativeType = "JS::Heap<JS::Value>"
michael@0 13588 elif m.type.isObject() or m.type.isSpiderMonkeyInterface():
michael@0 13589 nativeType = "JS::Heap<JSObject*>"
michael@0 13590 elif m.type.isUnion():
michael@0 13591 nativeType = CGUnionStruct.unionTypeDecl(m.type, True)
michael@0 13592 else:
michael@0 13593 raise TypeError("Don't know how to declare member of type %s" %
michael@0 13594 m.type)
michael@0 13595 members.append(ClassMember(CGDictionary.makeMemberName(m.identifier.name),
michael@0 13596 nativeType,
michael@0 13597 visibility="private",
michael@0 13598 body="body"))
michael@0 13599
michael@0 13600 parent = self.descriptor.interface.parent
michael@0 13601 self.parentType = self.descriptor.getDescriptor(parent.identifier.name).nativeType.split('::')[-1]
michael@0 13602 baseDeclarations = fill(
michael@0 13603 """
michael@0 13604 public:
michael@0 13605 NS_DECL_ISUPPORTS_INHERITED
michael@0 13606 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(${nativeType}, ${parentType})
michael@0 13607 virtual ~${nativeType}();
michael@0 13608 protected:
michael@0 13609 ${nativeType}(mozilla::dom::EventTarget* aOwner);
michael@0 13610
michael@0 13611 """,
michael@0 13612 nativeType=self.descriptor.nativeType.split('::')[-1],
michael@0 13613 parentType=self.parentType)
michael@0 13614
michael@0 13615 className = descriptor.nativeType.split('::')[-1]
michael@0 13616 asConcreteTypeMethod = ClassMethod("As%s" % className,
michael@0 13617 "%s*" % className,
michael@0 13618 [],
michael@0 13619 virtual=True,
michael@0 13620 body="return this;\n",
michael@0 13621 breakAfterReturnDecl=" ")
michael@0 13622
michael@0 13623 CGClass.__init__(self, className,
michael@0 13624 bases=[ClassBase(self.parentType)],
michael@0 13625 methods=[asConcreteTypeMethod]+self.methodDecls,
michael@0 13626 members=members,
michael@0 13627 extradeclarations=baseDeclarations)
michael@0 13628
michael@0 13629 def getWrapObjectBody(self):
michael@0 13630 return "return %sBinding::Wrap(aCx, this);\n" % self.descriptor.name
michael@0 13631
michael@0 13632 def implTraverse(self):
michael@0 13633 retVal = ""
michael@0 13634 for m in self.descriptor.interface.members:
michael@0 13635 if m.isAttr() and m.type.isGeckoInterface():
michael@0 13636 retVal += (" NS_IMPL_CYCLE_COLLECTION_TRAVERSE(" +
michael@0 13637 CGDictionary.makeMemberName(m.identifier.name) +
michael@0 13638 ")\n")
michael@0 13639 return retVal
michael@0 13640
michael@0 13641 def implUnlink(self):
michael@0 13642 retVal = ""
michael@0 13643 for m in self.descriptor.interface.members:
michael@0 13644 if m.isAttr():
michael@0 13645 name = CGDictionary.makeMemberName(m.identifier.name)
michael@0 13646 if m.type.isGeckoInterface():
michael@0 13647 retVal += " NS_IMPL_CYCLE_COLLECTION_UNLINK(" + name + ")\n"
michael@0 13648 elif m.type.isAny():
michael@0 13649 retVal += " tmp->" + name + ".setUndefined();\n"
michael@0 13650 elif m.type.isObject() or m.type.isSpiderMonkeyInterface():
michael@0 13651 retVal += " tmp->" + name + " = nullptr;\n"
michael@0 13652 return retVal
michael@0 13653
michael@0 13654 def implTrace(self):
michael@0 13655 retVal = ""
michael@0 13656 for m in self.descriptor.interface.members:
michael@0 13657 if m.isAttr():
michael@0 13658 name = CGDictionary.makeMemberName(m.identifier.name)
michael@0 13659 if m.type.isAny():
michael@0 13660 retVal += " NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(" + name + ")\n"
michael@0 13661 elif m.type.isObject() or m.type.isSpiderMonkeyInterface():
michael@0 13662 retVal += " NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(" + name + ")\n"
michael@0 13663 elif typeNeedsRooting(m.type):
michael@0 13664 raise TypeError("Need to implement tracing for event "
michael@0 13665 "member of type %s" % m.type)
michael@0 13666 return retVal
michael@0 13667
michael@0 13668 def define(self):
michael@0 13669 dropJS = ""
michael@0 13670 for m in self.descriptor.interface.members:
michael@0 13671 if m.isAttr():
michael@0 13672 member = CGDictionary.makeMemberName(m.identifier.name)
michael@0 13673 if m.type.isAny():
michael@0 13674 dropJS += member + " = JS::UndefinedValue();\n"
michael@0 13675 elif m.type.isObject() or m.type.isSpiderMonkeyInterface():
michael@0 13676 dropJS += member + " = nullptr;\n"
michael@0 13677 if dropJS != "":
michael@0 13678 dropJS += "mozilla::DropJSObjects(this);\n"
michael@0 13679 # Just override CGClass and do our own thing
michael@0 13680 nativeType = self.descriptor.nativeType.split('::')[-1]
michael@0 13681 ctorParams = ("aOwner, nullptr, nullptr" if self.parentType == "Event"
michael@0 13682 else "aOwner")
michael@0 13683
michael@0 13684 classImpl = fill(
michael@0 13685 """
michael@0 13686
michael@0 13687 NS_IMPL_CYCLE_COLLECTION_CLASS(${nativeType})
michael@0 13688
michael@0 13689 NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType})
michael@0 13690 NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType})
michael@0 13691
michael@0 13692 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(${nativeType}, ${parentType})
michael@0 13693 $*{traverse}
michael@0 13694 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
michael@0 13695
michael@0 13696 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(${nativeType}, ${parentType})
michael@0 13697 $*{trace}
michael@0 13698 NS_IMPL_CYCLE_COLLECTION_TRACE_END
michael@0 13699
michael@0 13700 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(${nativeType}, ${parentType})
michael@0 13701 $*{unlink}
michael@0 13702 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
michael@0 13703
michael@0 13704 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(${nativeType})
michael@0 13705 NS_INTERFACE_MAP_END_INHERITING(${parentType})
michael@0 13706
michael@0 13707 ${nativeType}::${nativeType}(mozilla::dom::EventTarget* aOwner)
michael@0 13708 : ${parentType}(${ctorParams})
michael@0 13709 {
michael@0 13710 }
michael@0 13711
michael@0 13712 ${nativeType}::~${nativeType}()
michael@0 13713 {
michael@0 13714 $*{dropJS}
michael@0 13715 }
michael@0 13716
michael@0 13717 """,
michael@0 13718 ifaceName=self.descriptor.name,
michael@0 13719 nativeType=nativeType,
michael@0 13720 ctorParams=ctorParams,
michael@0 13721 parentType=self.parentType,
michael@0 13722 traverse=self.implTraverse(),
michael@0 13723 unlink=self.implUnlink(),
michael@0 13724 trace=self.implTrace(),
michael@0 13725 dropJS=dropJS)
michael@0 13726 return classImpl + CGBindingImplClass.define(self)
michael@0 13727
michael@0 13728
michael@0 13729 class CGEventRoot(CGThing):
michael@0 13730 def __init__(self, config, interfaceName):
michael@0 13731 # Let's assume we're not doing workers stuff, for now
michael@0 13732 descriptor = config.getDescriptor(interfaceName, False)
michael@0 13733
michael@0 13734 self.root = CGWrapper(CGEventClass(descriptor),
michael@0 13735 pre="\n", post="\n")
michael@0 13736
michael@0 13737 self.root = CGNamespace.build(["mozilla", "dom"], self.root)
michael@0 13738
michael@0 13739 self.root = CGList([CGClassForwardDeclare("JSContext", isStruct=True),
michael@0 13740 self.root])
michael@0 13741
michael@0 13742 parent = descriptor.interface.parent.identifier.name
michael@0 13743
michael@0 13744 # Throw in our #includes
michael@0 13745 self.root = CGHeaders([descriptor], [], [], [],
michael@0 13746 [
michael@0 13747 config.getDescriptor(parent, False).headerFile,
michael@0 13748 "mozilla/Attributes.h",
michael@0 13749 "mozilla/ErrorResult.h",
michael@0 13750 "mozilla/dom/%sBinding.h" % interfaceName,
michael@0 13751 'mozilla/dom/BindingUtils.h',
michael@0 13752 ],
michael@0 13753 [
michael@0 13754 "%s.h" % interfaceName,
michael@0 13755 "js/GCAPI.h",
michael@0 13756 'mozilla/dom/Nullable.h',
michael@0 13757 'nsDOMQS.h'
michael@0 13758 ],
michael@0 13759 "", self.root)
michael@0 13760
michael@0 13761 # And now some include guards
michael@0 13762 self.root = CGIncludeGuard(interfaceName, self.root)
michael@0 13763
michael@0 13764 self.root = CGWrapper(self.root, pre=AUTOGENERATED_WARNING_COMMENT)
michael@0 13765
michael@0 13766 self.root = CGWrapper(self.root, pre=dedent("""
michael@0 13767 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 13768 /* vim:set ts=2 sw=2 sts=2 et cindent: */
michael@0 13769 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 13770 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 13771 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 13772
michael@0 13773 """))
michael@0 13774
michael@0 13775 def declare(self):
michael@0 13776 return self.root.declare()
michael@0 13777
michael@0 13778 def define(self):
michael@0 13779 return self.root.define()

mercurial