Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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() |