michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: # You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: # Common codegen classes. michael@0: michael@0: import os michael@0: import re michael@0: import string michael@0: import math michael@0: import textwrap michael@0: michael@0: from WebIDL import BuiltinTypes, IDLBuiltinType, IDLNullValue, IDLSequenceType, IDLType, IDLAttribute, IDLUndefinedValue michael@0: from Configuration import NoSuchDescriptorError, getTypesFromDescriptor, getTypesFromDictionary, getTypesFromCallback, Descriptor michael@0: michael@0: AUTOGENERATED_WARNING_COMMENT = \ michael@0: "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n" michael@0: ADDPROPERTY_HOOK_NAME = '_addProperty' michael@0: FINALIZE_HOOK_NAME = '_finalize' michael@0: CONSTRUCT_HOOK_NAME = '_constructor' michael@0: LEGACYCALLER_HOOK_NAME = '_legacycaller' michael@0: HASINSTANCE_HOOK_NAME = '_hasInstance' michael@0: NEWRESOLVE_HOOK_NAME = '_newResolve' michael@0: ENUMERATE_HOOK_NAME = '_enumerate' michael@0: ENUM_ENTRY_VARIABLE_NAME = 'strings' michael@0: INSTANCE_RESERVED_SLOTS = 3 michael@0: michael@0: michael@0: def memberReservedSlot(member): michael@0: return "(DOM_INSTANCE_RESERVED_SLOTS + %d)" % member.slotIndex michael@0: michael@0: michael@0: def toStringBool(arg): michael@0: return str(not not arg).lower() michael@0: michael@0: michael@0: def toBindingNamespace(arg): michael@0: return re.sub("((_workers)?$)", "Binding\\1", arg) michael@0: michael@0: michael@0: def isTypeCopyConstructible(type): michael@0: # Nullable and sequence stuff doesn't affect copy-constructibility michael@0: type = type.unroll() michael@0: return (type.isPrimitive() or type.isString() or type.isEnum() or michael@0: (type.isUnion() and michael@0: CGUnionStruct.isUnionCopyConstructible(type)) or michael@0: (type.isDictionary() and michael@0: CGDictionary.isDictionaryCopyConstructible(type.inner)) or michael@0: # Interface types are only copy-constructible if they're Gecko michael@0: # interfaces. SpiderMonkey interfaces are not copy-constructible michael@0: # because of rooting issues. michael@0: (type.isInterface() and type.isGeckoInterface())) michael@0: michael@0: michael@0: def wantsAddProperty(desc): michael@0: return (desc.concrete and michael@0: desc.wrapperCache and michael@0: not (desc.workers and michael@0: desc.interface.getExtendedAttribute("Global"))) michael@0: michael@0: michael@0: # We'll want to insert the indent at the beginnings of lines, but we michael@0: # don't want to indent empty lines. So only indent lines that have a michael@0: # non-newline character on them. michael@0: lineStartDetector = re.compile("^(?=[^\n#])", re.MULTILINE) michael@0: michael@0: michael@0: def indent(s, indentLevel=2): michael@0: """ michael@0: Indent C++ code. michael@0: michael@0: Weird secret feature: this doesn't indent lines that start with # (such as michael@0: #include lines). michael@0: """ michael@0: if s == "": michael@0: return s michael@0: return re.sub(lineStartDetector, indentLevel * " ", s) michael@0: michael@0: michael@0: def dedent(s): michael@0: """ michael@0: Remove all leading whitespace from s, and remove a blank line michael@0: at the beginning. michael@0: """ michael@0: if s.startswith('\n'): michael@0: s = s[1:] michael@0: return textwrap.dedent(s) michael@0: michael@0: def fill(template, **args): michael@0: """ michael@0: Convenience function for filling in a multiline template. michael@0: michael@0: `fill(template, name1=v1, name2=v2)` is a lot like michael@0: `string.Template(template).substitute({"name1": v1, "name2": v2})`. michael@0: michael@0: However, it's shorter, and has a few nice features: michael@0: michael@0: * If `template` is indented, fill() automatically dedents it! michael@0: This makes code using fill() with Python's multiline strings michael@0: much nicer to look at. michael@0: michael@0: * If `template` starts with a blank line, fill() strips it off. michael@0: (Again, convenient with multiline strings.) michael@0: michael@0: * fill() recognizes a special kind of substitution michael@0: of the form `$*{name}`. michael@0: michael@0: Use this to paste in, and automatically indent, multiple lines. michael@0: (Mnemonic: The `*` is for "multiple lines"). michael@0: michael@0: A `$*` substitution must appear by itself on a line, with optional michael@0: preceding indentation (spaces only). The whole line is replaced by the michael@0: corresponding keyword argument, indented appropriately. If the michael@0: argument is an empty string, no output is generated, not even a blank michael@0: line. michael@0: """ michael@0: michael@0: # This works by transforming the fill()-template to an equivalent michael@0: # string.Template. michael@0: multiline_substitution_re = re.compile(r"( *)\$\*{(\w+)}(\n)?") michael@0: michael@0: def replace(match): michael@0: """ michael@0: Replaces a line like ' $*{xyz}\n' with '${xyz_n}', michael@0: where n is the indent depth, and add a corresponding entry to args. michael@0: """ michael@0: indentation, name, nl = match.groups() michael@0: depth = len(indentation) michael@0: michael@0: # Check that $*{xyz} appears by itself on a line. michael@0: prev = match.string[:match.start()] michael@0: if (prev and not prev.endswith("\n")) or nl is None: michael@0: raise ValueError("Invalid fill() template: $*{%s} must appear by itself on a line" % name) michael@0: michael@0: # Multiline text without a newline at the end is probably a mistake. michael@0: if not (args[name] == "" or args[name].endswith("\n")): michael@0: raise ValueError("Argument %s with value %r is missing a newline" % (name, args[name])) michael@0: michael@0: # Now replace this whole line of template with the indented equivalent. michael@0: modified_name = name + "_" + str(depth) michael@0: indented_value = indent(args[name], depth) michael@0: if modified_name in args: michael@0: assert args[modified_name] == indented_value michael@0: else: michael@0: args[modified_name] = indented_value michael@0: return "${" + modified_name + "}" michael@0: michael@0: t = dedent(template) michael@0: assert t.endswith("\n") or "\n" not in t michael@0: t = re.sub(multiline_substitution_re, replace, t) michael@0: t = string.Template(t) michael@0: return t.substitute(args) michael@0: michael@0: michael@0: class CGThing(): michael@0: """ michael@0: Abstract base class for things that spit out code. michael@0: """ michael@0: def __init__(self): michael@0: pass # Nothing for now michael@0: michael@0: def declare(self): michael@0: """Produce code for a header file.""" michael@0: assert False # Override me! michael@0: michael@0: def define(self): michael@0: """Produce code for a cpp file.""" michael@0: assert False # Override me! michael@0: michael@0: def deps(self): michael@0: """Produce the deps for a pp file""" michael@0: assert False # Override me! michael@0: michael@0: michael@0: class CGStringTable(CGThing): michael@0: """ michael@0: Generate a string table for the given strings with a function accessor: michael@0: michael@0: const char *accessorName(unsigned int index) { michael@0: static const char table[] = "..."; michael@0: static const uint16_t indices = { ... }; michael@0: return &table[indices[index]]; michael@0: } michael@0: michael@0: This is more efficient than the more natural: michael@0: michael@0: const char *table[] = { michael@0: ... michael@0: }; michael@0: michael@0: The uint16_t indices are smaller than the pointer equivalents, and the michael@0: string table requires no runtime relocations. michael@0: """ michael@0: def __init__(self, accessorName, strings): michael@0: CGThing.__init__(self) michael@0: self.accessorName = accessorName michael@0: self.strings = strings michael@0: michael@0: def declare(self): michael@0: return "extern const char *%s(unsigned int aIndex);\n" % self.accessorName michael@0: michael@0: def define(self): michael@0: table = ' "\\0" '.join('"%s"' % s for s in self.strings) michael@0: indices = [] michael@0: currentIndex = 0 michael@0: for s in self.strings: michael@0: indices.append(currentIndex) michael@0: currentIndex += len(s) + 1 # for the null terminator michael@0: return fill( michael@0: """ michael@0: const char *${name}(unsigned int aIndex) michael@0: { michael@0: static const char table[] = ${table}; michael@0: static const uint16_t indices[] = { ${indices} }; michael@0: static_assert(${currentIndex} <= UINT16_MAX, "string table overflow!"); michael@0: return &table[indices[aIndex]]; michael@0: } michael@0: """, michael@0: name=self.accessorName, michael@0: table=table, michael@0: indices=", ".join("%d" % index for index in indices), michael@0: currentIndex=currentIndex) michael@0: michael@0: michael@0: class CGNativePropertyHooks(CGThing): michael@0: """ michael@0: Generate a NativePropertyHooks for a given descriptor michael@0: """ michael@0: def __init__(self, descriptor, properties): michael@0: CGThing.__init__(self) michael@0: self.descriptor = descriptor michael@0: self.properties = properties michael@0: michael@0: def declare(self): michael@0: if self.descriptor.workers: michael@0: return "" michael@0: return dedent(""" michael@0: // We declare this as an array so that retrieving a pointer to this michael@0: // binding's property hooks only requires compile/link-time resolvable michael@0: // address arithmetic. Declaring it as a pointer instead would require michael@0: // doing a run-time load to fetch a pointer to this binding's property michael@0: // hooks. And then structures which embedded a pointer to this structure michael@0: // would require a run-time load for proper initialization, which would michael@0: // then induce static constructors. Lots of static constructors. michael@0: extern const NativePropertyHooks sNativePropertyHooks[]; michael@0: """).rstrip() # BOGUS strip newline from the last line here (!) michael@0: michael@0: def define(self): michael@0: if self.descriptor.workers: michael@0: return "" michael@0: if self.descriptor.concrete and self.descriptor.proxy: michael@0: resolveOwnProperty = "ResolveOwnProperty" michael@0: enumerateOwnProperties = "EnumerateOwnProperties" michael@0: elif self.descriptor.needsXrayResolveHooks(): michael@0: resolveOwnProperty = "ResolveOwnPropertyViaNewresolve" michael@0: enumerateOwnProperties = "EnumerateOwnPropertiesViaGetOwnPropertyNames" michael@0: else: michael@0: resolveOwnProperty = "nullptr" michael@0: enumerateOwnProperties = "nullptr" michael@0: if self.properties.hasNonChromeOnly(): michael@0: regular = "&sNativeProperties" michael@0: else: michael@0: regular = "nullptr" michael@0: if self.properties.hasChromeOnly(): michael@0: chrome = "&sChromeOnlyNativeProperties" michael@0: else: michael@0: chrome = "nullptr" michael@0: constructorID = "constructors::id::" michael@0: if self.descriptor.interface.hasInterfaceObject(): michael@0: constructorID += self.descriptor.name michael@0: else: michael@0: constructorID += "_ID_Count" michael@0: prototypeID = "prototypes::id::" michael@0: if self.descriptor.interface.hasInterfacePrototypeObject(): michael@0: prototypeID += self.descriptor.name michael@0: else: michael@0: prototypeID += "_ID_Count" michael@0: parent = self.descriptor.interface.parent michael@0: parentHooks = (toBindingNamespace(parent.identifier.name) + "::sNativePropertyHooks" michael@0: if parent else 'nullptr') michael@0: michael@0: return fill( michael@0: """ michael@0: const NativePropertyHooks sNativePropertyHooks[] = { { michael@0: ${resolveOwnProperty}, michael@0: ${enumerateOwnProperties}, michael@0: { ${regular}, ${chrome} }, michael@0: ${prototypeID}, michael@0: ${constructorID}, michael@0: ${parentHooks} michael@0: } }; michael@0: """, michael@0: resolveOwnProperty=resolveOwnProperty, michael@0: enumerateOwnProperties=enumerateOwnProperties, michael@0: regular=regular, michael@0: chrome=chrome, michael@0: prototypeID=prototypeID, michael@0: constructorID=constructorID, michael@0: parentHooks=parentHooks) michael@0: michael@0: michael@0: def NativePropertyHooks(descriptor): michael@0: return "&sWorkerNativePropertyHooks" if descriptor.workers else "sNativePropertyHooks" michael@0: michael@0: michael@0: def DOMClass(descriptor): michael@0: def make_name(d): michael@0: return "%s%s" % (d.interface.identifier.name, '_workers' if d.workers else '') michael@0: michael@0: protoList = ['prototypes::id::' + make_name(descriptor.getDescriptor(proto)) for proto in descriptor.prototypeChain] michael@0: # Pad out the list to the right length with _ID_Count so we michael@0: # guarantee that all the lists are the same length. _ID_Count michael@0: # is never the ID of any prototype, so it's safe to use as michael@0: # padding. michael@0: protoList.extend(['prototypes::id::_ID_Count'] * (descriptor.config.maxProtoChainLength - len(protoList))) michael@0: michael@0: return fill( michael@0: """ michael@0: { michael@0: { ${protoChain} }, michael@0: IsBaseOf::value, michael@0: ${hooks}, michael@0: GetParentObject<${nativeType}>::Get, michael@0: GetProtoObject, michael@0: GetCCParticipant<${nativeType}>::Get() michael@0: } michael@0: """, michael@0: protoChain=', '.join(protoList), michael@0: nativeType=descriptor.nativeType, michael@0: hooks=NativePropertyHooks(descriptor)) michael@0: michael@0: michael@0: class CGDOMJSClass(CGThing): michael@0: """ michael@0: Generate a DOMJSClass for a given descriptor michael@0: """ michael@0: def __init__(self, descriptor): michael@0: CGThing.__init__(self) michael@0: self.descriptor = descriptor michael@0: michael@0: def declare(self): michael@0: return "" michael@0: michael@0: def define(self): michael@0: traceHook = 'nullptr' michael@0: callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr' michael@0: slotCount = INSTANCE_RESERVED_SLOTS + self.descriptor.interface.totalMembersInSlots michael@0: classFlags = "JSCLASS_IS_DOMJSCLASS | " michael@0: classExtensionAndObjectOps = """\ michael@0: JS_NULL_CLASS_EXT, michael@0: JS_NULL_OBJECT_OPS michael@0: """ michael@0: if self.descriptor.interface.getExtendedAttribute("Global"): michael@0: classFlags += "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS) | JSCLASS_IMPLEMENTS_BARRIERS" michael@0: traceHook = "JS_GlobalObjectTraceHook" michael@0: if not self.descriptor.workers: michael@0: classExtensionAndObjectOps = """\ michael@0: { michael@0: nsGlobalWindow::OuterObject, /* outerObject */ michael@0: nullptr, /* innerObject */ michael@0: nullptr, /* iteratorObject */ michael@0: false, /* isWrappedNative */ michael@0: nullptr /* weakmapKeyDelegateOp */ michael@0: }, michael@0: { michael@0: nullptr, /* lookupGeneric */ michael@0: nullptr, /* lookupProperty */ michael@0: nullptr, /* lookupElement */ michael@0: nullptr, /* defineGeneric */ michael@0: nullptr, /* defineProperty */ michael@0: nullptr, /* defineElement */ michael@0: nullptr, /* getGeneric */ michael@0: nullptr, /* getProperty */ michael@0: nullptr, /* getElement */ michael@0: nullptr, /* setGeneric */ michael@0: nullptr, /* setProperty */ michael@0: nullptr, /* setElement */ michael@0: nullptr, /* getGenericAttributes */ michael@0: nullptr, /* setGenericAttributes */ michael@0: nullptr, /* deleteProperty */ michael@0: nullptr, /* deleteElement */ michael@0: nullptr, /* watch */ michael@0: nullptr, /* unwatch */ michael@0: nullptr, /* slice */ michael@0: nullptr, /* enumerate */ michael@0: JS_ObjectToOuterObject /* thisObject */ michael@0: } michael@0: """ michael@0: else: michael@0: classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount michael@0: if self.descriptor.interface.getExtendedAttribute("NeedNewResolve"): michael@0: newResolveHook = "(JSResolveOp)" + NEWRESOLVE_HOOK_NAME michael@0: classFlags += " | JSCLASS_NEW_RESOLVE" michael@0: enumerateHook = ENUMERATE_HOOK_NAME michael@0: elif self.descriptor.interface.getExtendedAttribute("Global"): michael@0: newResolveHook = "(JSResolveOp) mozilla::dom::ResolveGlobal" michael@0: classFlags += " | JSCLASS_NEW_RESOLVE" michael@0: enumerateHook = "mozilla::dom::EnumerateGlobal" michael@0: else: michael@0: newResolveHook = "JS_ResolveStub" michael@0: enumerateHook = "JS_EnumerateStub" michael@0: michael@0: return fill( # BOGUS extra blank line at the top michael@0: """ michael@0: michael@0: static const DOMJSClass Class = { michael@0: { "${name}", michael@0: ${flags}, michael@0: ${addProperty}, /* addProperty */ michael@0: JS_DeletePropertyStub, /* delProperty */ michael@0: JS_PropertyStub, /* getProperty */ michael@0: JS_StrictPropertyStub, /* setProperty */ michael@0: ${enumerate}, /* enumerate */ michael@0: ${resolve}, /* resolve */ michael@0: JS_ConvertStub, michael@0: ${finalize}, /* finalize */ michael@0: ${call}, /* call */ michael@0: nullptr, /* hasInstance */ michael@0: nullptr, /* construct */ michael@0: ${trace}, /* trace */ michael@0: JS_NULL_CLASS_SPEC, michael@0: $*{classExtensionAndObjectOps} michael@0: }, michael@0: $*{descriptor} michael@0: }; michael@0: """, michael@0: name=self.descriptor.interface.identifier.name, michael@0: flags=classFlags, michael@0: addProperty=ADDPROPERTY_HOOK_NAME if wantsAddProperty(self.descriptor) else 'JS_PropertyStub', michael@0: enumerate=enumerateHook, michael@0: resolve=newResolveHook, michael@0: finalize=FINALIZE_HOOK_NAME, michael@0: call=callHook, michael@0: trace=traceHook, michael@0: classExtensionAndObjectOps=classExtensionAndObjectOps, michael@0: descriptor=DOMClass(self.descriptor)) michael@0: michael@0: michael@0: class CGDOMProxyJSClass(CGThing): michael@0: """ michael@0: Generate a DOMJSClass for a given proxy descriptor michael@0: """ michael@0: def __init__(self, descriptor): michael@0: CGThing.__init__(self) michael@0: self.descriptor = descriptor michael@0: michael@0: def declare(self): michael@0: return "" michael@0: michael@0: def define(self): michael@0: flags = ["JSCLASS_IS_DOMJSCLASS"] michael@0: # We don't use an IDL annotation for JSCLASS_EMULATES_UNDEFINED because michael@0: # we don't want people ever adding that to any interface other than michael@0: # HTMLAllCollection. So just hardcode it here. michael@0: if self.descriptor.interface.identifier.name == "HTMLAllCollection": michael@0: flags.append("JSCLASS_EMULATES_UNDEFINED") michael@0: callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr' michael@0: return fill( # BOGUS extra blank line at the top michael@0: """ michael@0: michael@0: static const DOMJSClass Class = { michael@0: PROXY_CLASS_DEF("${name}", michael@0: 0, /* extra slots */ michael@0: ${flags}, michael@0: ${call}, /* call */ michael@0: nullptr /* construct */), michael@0: $*{descriptor} michael@0: }; michael@0: """, michael@0: name=self.descriptor.interface.identifier.name, michael@0: flags=" | ".join(flags), michael@0: call=callHook, michael@0: descriptor=DOMClass(self.descriptor)) michael@0: michael@0: michael@0: def PrototypeIDAndDepth(descriptor): michael@0: prototypeID = "prototypes::id::" michael@0: if descriptor.interface.hasInterfacePrototypeObject(): michael@0: prototypeID += descriptor.interface.identifier.name michael@0: if descriptor.workers: michael@0: prototypeID += "_workers" michael@0: depth = "PrototypeTraits<%s>::Depth" % prototypeID michael@0: else: michael@0: prototypeID += "_ID_Count" michael@0: depth = "0" michael@0: return (prototypeID, depth) michael@0: michael@0: michael@0: def UseHolderForUnforgeable(descriptor): michael@0: return (descriptor.concrete and michael@0: descriptor.proxy and michael@0: any(m for m in descriptor.interface.members if m.isAttr() and m.isUnforgeable())) michael@0: michael@0: michael@0: def CallOnUnforgeableHolder(descriptor, code, isXrayCheck=None, michael@0: useSharedRoot=False): michael@0: """ michael@0: Generate the code to execute the code in "code" on an unforgeable holder if michael@0: needed. code should be a string containing the code to execute. If it michael@0: contains a ${holder} string parameter it will be replaced with the michael@0: unforgeable holder object. michael@0: michael@0: If isXrayCheck is not None it should be a string that contains a statement michael@0: returning whether proxy is an Xray. If isXrayCheck is None the generated michael@0: code won't try to unwrap Xrays. michael@0: michael@0: If useSharedRoot is true, we will use an existing michael@0: JS::Rooted sharedRoot for storing our unforgeable holder instead michael@0: of declaring a new Rooted. michael@0: """ michael@0: if isXrayCheck is not None: michael@0: pre = fill( michael@0: """ michael@0: // Scope for 'global', 'ac' and 'unforgeableHolder' michael@0: { michael@0: JS::Rooted global(cx); michael@0: Maybe ac; michael@0: if (${isXrayCheck}) { michael@0: global = js::GetGlobalForObjectCrossCompartment(js::UncheckedUnwrap(proxy)); michael@0: ac.construct(cx, global); michael@0: } else { michael@0: global = js::GetGlobalForObjectCrossCompartment(proxy); michael@0: } michael@0: """, michael@0: isXrayCheck=isXrayCheck) michael@0: else: michael@0: pre = dedent(""" michael@0: // Scope for 'global' and 'unforgeableHolder' michael@0: { michael@0: JSObject* global = js::GetGlobalForObjectCrossCompartment(proxy); michael@0: """) michael@0: michael@0: if useSharedRoot: michael@0: holderDecl = "JS::Rooted& unforgeableHolder(sharedRoot);\n" michael@0: else: michael@0: holderDecl = "JS::Rooted unforgeableHolder(cx);\n" michael@0: michael@0: code = string.Template(code).substitute({"holder": "unforgeableHolder"}) michael@0: return fill( michael@0: """ michael@0: $*{pre} michael@0: $*{holderDecl} michael@0: unforgeableHolder = GetUnforgeableHolder(global, prototypes::id::${name}); michael@0: $*{code} michael@0: } michael@0: """, michael@0: pre=pre, michael@0: holderDecl=holderDecl, michael@0: name=descriptor.name, michael@0: code=code) michael@0: michael@0: michael@0: class CGPrototypeJSClass(CGThing): michael@0: def __init__(self, descriptor, properties): michael@0: CGThing.__init__(self) michael@0: self.descriptor = descriptor michael@0: self.properties = properties michael@0: michael@0: def declare(self): michael@0: # We're purely for internal consumption michael@0: return "" michael@0: michael@0: def define(self): michael@0: prototypeID, depth = PrototypeIDAndDepth(self.descriptor) michael@0: slotCount = "DOM_INTERFACE_PROTO_SLOTS_BASE" michael@0: if UseHolderForUnforgeable(self.descriptor): michael@0: slotCount += " + 1 /* slot for the JSObject holding the unforgeable properties */" michael@0: return fill( michael@0: """ michael@0: static const DOMIfaceAndProtoJSClass PrototypeClass = { michael@0: { michael@0: "${name}Prototype", michael@0: JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}), michael@0: JS_PropertyStub, /* addProperty */ michael@0: JS_DeletePropertyStub, /* delProperty */ michael@0: JS_PropertyStub, /* getProperty */ michael@0: JS_StrictPropertyStub, /* setProperty */ michael@0: JS_EnumerateStub, michael@0: JS_ResolveStub, michael@0: JS_ConvertStub, michael@0: nullptr, /* finalize */ michael@0: nullptr, /* call */ michael@0: nullptr, /* hasInstance */ michael@0: nullptr, /* construct */ michael@0: nullptr, /* trace */ michael@0: JSCLASS_NO_INTERNAL_MEMBERS michael@0: }, michael@0: eInterfacePrototype, michael@0: ${hooks}, michael@0: "[object ${name}Prototype]", michael@0: ${prototypeID}, michael@0: ${depth} michael@0: }; michael@0: """, michael@0: name=self.descriptor.interface.identifier.name, michael@0: slotCount=slotCount, michael@0: hooks=NativePropertyHooks(self.descriptor), michael@0: prototypeID=prototypeID, michael@0: depth=depth) michael@0: michael@0: michael@0: def NeedsGeneratedHasInstance(descriptor): michael@0: return descriptor.hasXPConnectImpls or descriptor.interface.isConsequential() michael@0: michael@0: michael@0: class CGInterfaceObjectJSClass(CGThing): michael@0: def __init__(self, descriptor, properties): michael@0: CGThing.__init__(self) michael@0: self.descriptor = descriptor michael@0: self.properties = properties michael@0: michael@0: def declare(self): michael@0: # We're purely for internal consumption michael@0: return "" michael@0: michael@0: def define(self): michael@0: if self.descriptor.interface.ctor(): michael@0: ctorname = CONSTRUCT_HOOK_NAME michael@0: else: michael@0: ctorname = "ThrowingConstructor" michael@0: if NeedsGeneratedHasInstance(self.descriptor): michael@0: hasinstance = HASINSTANCE_HOOK_NAME michael@0: elif self.descriptor.interface.hasInterfacePrototypeObject(): michael@0: hasinstance = "InterfaceHasInstance" michael@0: else: michael@0: hasinstance = "nullptr" michael@0: prototypeID, depth = PrototypeIDAndDepth(self.descriptor) michael@0: slotCount = "DOM_INTERFACE_SLOTS_BASE" michael@0: if len(self.descriptor.interface.namedConstructors) > 0: michael@0: slotCount += (" + %i /* slots for the named constructors */" % michael@0: len(self.descriptor.interface.namedConstructors)) michael@0: return fill( # BOGUS extra newline at the top michael@0: """ michael@0: michael@0: static const DOMIfaceAndProtoJSClass InterfaceObjectClass = { michael@0: { michael@0: "Function", michael@0: JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(${slotCount}), michael@0: JS_PropertyStub, /* addProperty */ michael@0: JS_DeletePropertyStub, /* delProperty */ michael@0: JS_PropertyStub, /* getProperty */ michael@0: JS_StrictPropertyStub, /* setProperty */ michael@0: JS_EnumerateStub, michael@0: JS_ResolveStub, michael@0: JS_ConvertStub, michael@0: nullptr, /* finalize */ michael@0: ${ctorname}, /* call */ michael@0: ${hasInstance}, /* hasInstance */ michael@0: ${ctorname}, /* construct */ michael@0: nullptr, /* trace */ michael@0: JSCLASS_NO_INTERNAL_MEMBERS michael@0: }, michael@0: eInterface, michael@0: ${hooks}, michael@0: "function ${name}() {\\n [native code]\\n}", michael@0: ${prototypeID}, michael@0: ${depth} michael@0: }; michael@0: """, michael@0: slotCount=slotCount, michael@0: ctorname=ctorname, michael@0: hasInstance=hasinstance, michael@0: hooks=NativePropertyHooks(self.descriptor), michael@0: name=self.descriptor.interface.identifier.name, michael@0: prototypeID=prototypeID, michael@0: depth=depth) michael@0: michael@0: michael@0: class CGList(CGThing): michael@0: """ michael@0: Generate code for a list of GCThings. Just concatenates them together, with michael@0: an optional joiner string. "\n" is a common joiner. michael@0: """ michael@0: def __init__(self, children, joiner=""): michael@0: CGThing.__init__(self) michael@0: # Make a copy of the kids into a list, because if someone passes in a michael@0: # generator we won't be able to both declare and define ourselves, or michael@0: # define ourselves more than once! michael@0: self.children = list(children) michael@0: self.joiner = joiner michael@0: michael@0: def append(self, child): michael@0: self.children.append(child) michael@0: michael@0: def prepend(self, child): michael@0: self.children.insert(0, child) michael@0: michael@0: def extend(self, kids): michael@0: self.children.extend(kids) michael@0: michael@0: def join(self, iterable): michael@0: return self.joiner.join(s for s in iterable if len(s) > 0) michael@0: michael@0: def declare(self): michael@0: return self.join(child.declare() for child in self.children if child is not None) michael@0: michael@0: def define(self): michael@0: return self.join(child.define() for child in self.children if child is not None) michael@0: michael@0: def deps(self): michael@0: deps = set() michael@0: for child in self.children: michael@0: if child is None: michael@0: continue michael@0: deps = deps.union(child.deps()) michael@0: return deps michael@0: michael@0: michael@0: class CGGeneric(CGThing): michael@0: """ michael@0: A class that spits out a fixed string into the codegen. Can spit out a michael@0: separate string for the declaration too. michael@0: """ michael@0: def __init__(self, define="", declare=""): michael@0: self.declareText = declare michael@0: self.defineText = define michael@0: michael@0: def declare(self): michael@0: return self.declareText michael@0: michael@0: def define(self): michael@0: return self.defineText michael@0: michael@0: def deps(self): michael@0: return set() michael@0: michael@0: michael@0: class CGIndenter(CGThing): michael@0: """ michael@0: A class that takes another CGThing and generates code that indents that michael@0: CGThing by some number of spaces. The default indent is two spaces. michael@0: """ michael@0: def __init__(self, child, indentLevel=2, declareOnly=False): michael@0: assert isinstance(child, CGThing) michael@0: CGThing.__init__(self) michael@0: self.child = child michael@0: self.indentLevel = indentLevel michael@0: self.declareOnly = declareOnly michael@0: michael@0: def declare(self): michael@0: return indent(self.child.declare(), self.indentLevel) michael@0: michael@0: def define(self): michael@0: defn = self.child.define() michael@0: if self.declareOnly: michael@0: return defn michael@0: else: michael@0: return indent(defn, self.indentLevel) michael@0: michael@0: michael@0: class CGWrapper(CGThing): michael@0: """ michael@0: Generic CGThing that wraps other CGThings with pre and post text. michael@0: """ michael@0: def __init__(self, child, pre="", post="", declarePre=None, michael@0: declarePost=None, definePre=None, definePost=None, michael@0: declareOnly=False, defineOnly=False, reindent=False): michael@0: CGThing.__init__(self) michael@0: self.child = child michael@0: self.declarePre = declarePre or pre michael@0: self.declarePost = declarePost or post michael@0: self.definePre = definePre or pre michael@0: self.definePost = definePost or post michael@0: self.declareOnly = declareOnly michael@0: self.defineOnly = defineOnly michael@0: self.reindent = reindent michael@0: michael@0: def declare(self): michael@0: if self.defineOnly: michael@0: return '' michael@0: decl = self.child.declare() michael@0: if self.reindent: michael@0: decl = self.reindentString(decl, self.declarePre) michael@0: return self.declarePre + decl + self.declarePost michael@0: michael@0: def define(self): michael@0: if self.declareOnly: michael@0: return '' michael@0: defn = self.child.define() michael@0: if self.reindent: michael@0: defn = self.reindentString(defn, self.definePre) michael@0: return self.definePre + defn + self.definePost michael@0: michael@0: @staticmethod michael@0: def reindentString(stringToIndent, widthString): michael@0: # We don't use lineStartDetector because we don't want to michael@0: # insert whitespace at the beginning of our _first_ line. michael@0: # Use the length of the last line of width string, in case michael@0: # it is a multiline string. michael@0: lastLineWidth = len(widthString.splitlines()[-1]) michael@0: return stripTrailingWhitespace( michael@0: stringToIndent.replace("\n", "\n" + (" " * lastLineWidth))) michael@0: michael@0: def deps(self): michael@0: return self.child.deps() michael@0: michael@0: michael@0: class CGIfWrapper(CGList): michael@0: def __init__(self, child, condition): michael@0: CGList.__init__(self, [ michael@0: CGWrapper(CGGeneric(condition), pre="if (", post=") {\n", reindent=True), michael@0: CGIndenter(child), michael@0: CGGeneric("}\n") michael@0: ]) michael@0: michael@0: michael@0: class CGIfElseWrapper(CGList): michael@0: def __init__(self, condition, ifTrue, ifFalse): michael@0: CGList.__init__(self, [ michael@0: CGWrapper(CGGeneric(condition), pre="if (", post=") {\n", reindent=True), michael@0: CGIndenter(ifTrue), michael@0: CGGeneric("} else {\n"), michael@0: CGIndenter(ifFalse), michael@0: CGGeneric("}\n") michael@0: ]) michael@0: michael@0: michael@0: class CGElseChain(CGThing): michael@0: """ michael@0: Concatenate if statements in an if-else-if-else chain. michael@0: """ michael@0: def __init__(self, children): michael@0: self.children = [c for c in children if c is not None] michael@0: michael@0: def declare(self): michael@0: assert False michael@0: michael@0: def define(self): michael@0: if not self.children: michael@0: return "" michael@0: s = self.children[0].define() michael@0: assert s.endswith("\n") michael@0: for child in self.children[1:]: michael@0: code = child.define() michael@0: assert code.startswith("if") or code.startswith("{") michael@0: assert code.endswith("\n") michael@0: s = s.rstrip() + " else " + code michael@0: return s michael@0: michael@0: michael@0: class CGTemplatedType(CGWrapper): michael@0: def __init__(self, templateName, child, isConst=False, isReference=False): michael@0: const = "const " if isConst else "" michael@0: pre = "%s%s<" % (const, templateName) michael@0: ref = "&" if isReference else "" michael@0: post = ">%s" % ref michael@0: CGWrapper.__init__(self, child, pre=pre, post=post) michael@0: michael@0: michael@0: class CGNamespace(CGWrapper): michael@0: def __init__(self, namespace, child, declareOnly=False): michael@0: pre = "namespace %s {\n" % namespace michael@0: post = "} // namespace %s\n" % namespace michael@0: CGWrapper.__init__(self, child, pre=pre, post=post, michael@0: declareOnly=declareOnly) michael@0: michael@0: @staticmethod michael@0: def build(namespaces, child, declareOnly=False): michael@0: """ michael@0: Static helper method to build multiple wrapped namespaces. michael@0: """ michael@0: if not namespaces: michael@0: return CGWrapper(child, declareOnly=declareOnly) michael@0: inner = CGNamespace.build(namespaces[1:], child, declareOnly=declareOnly) michael@0: return CGNamespace(namespaces[0], inner, declareOnly=declareOnly) michael@0: michael@0: michael@0: class CGIncludeGuard(CGWrapper): michael@0: """ michael@0: Generates include guards for a header. michael@0: """ michael@0: def __init__(self, prefix, child): michael@0: """|prefix| is the filename without the extension.""" michael@0: define = 'mozilla_dom_%s_h' % prefix michael@0: CGWrapper.__init__(self, child, michael@0: declarePre='#ifndef %s\n#define %s\n\n' % (define, define), michael@0: declarePost='\n#endif // %s\n' % define) michael@0: michael@0: michael@0: def getRelevantProviders(descriptor, config): michael@0: if descriptor is not None: michael@0: return [descriptor] michael@0: # Do both the non-worker and worker versions michael@0: return [ michael@0: config.getDescriptorProvider(False), michael@0: config.getDescriptorProvider(True) michael@0: ] michael@0: michael@0: michael@0: def getAllTypes(descriptors, dictionaries, callbacks): michael@0: """ michael@0: Generate all the types we're dealing with. For each type, a tuple michael@0: containing type, descriptor, dictionary is yielded. The michael@0: descriptor and dictionary can be None if the type does not come michael@0: from a descriptor or dictionary; they will never both be non-None. michael@0: """ michael@0: for d in descriptors: michael@0: if d.interface.isExternal(): michael@0: continue michael@0: for t in getTypesFromDescriptor(d): michael@0: yield (t, d, None) michael@0: for dictionary in dictionaries: michael@0: for t in getTypesFromDictionary(dictionary): michael@0: yield (t, None, dictionary) michael@0: for callback in callbacks: michael@0: for t in getTypesFromCallback(callback): michael@0: yield (t, None, None) michael@0: michael@0: michael@0: class CGHeaders(CGWrapper): michael@0: """ michael@0: Generates the appropriate include statements. michael@0: """ michael@0: def __init__(self, descriptors, dictionaries, callbacks, michael@0: callbackDescriptors, michael@0: declareIncludes, defineIncludes, prefix, child, michael@0: config=None, jsImplementedDescriptors=[]): michael@0: """ michael@0: Builds a set of includes to cover |descriptors|. michael@0: michael@0: Also includes the files in |declareIncludes| in the header michael@0: file and the files in |defineIncludes| in the .cpp. michael@0: michael@0: |prefix| contains the basename of the file that we generate include michael@0: statements for. michael@0: """ michael@0: michael@0: # Determine the filenames for which we need headers. michael@0: interfaceDeps = [d.interface for d in descriptors] michael@0: ancestors = [] michael@0: for iface in interfaceDeps: michael@0: if iface.parent: michael@0: # We're going to need our parent's prototype, to use as the michael@0: # prototype of our prototype object. michael@0: ancestors.append(iface.parent) michael@0: # And if we have an interface object, we'll need the nearest michael@0: # ancestor with an interface object too, so we can use its michael@0: # interface object as the proto of our interface object. michael@0: if iface.hasInterfaceObject(): michael@0: parent = iface.parent michael@0: while parent and not parent.hasInterfaceObject(): michael@0: parent = parent.parent michael@0: if parent: michael@0: ancestors.append(parent) michael@0: interfaceDeps.extend(ancestors) michael@0: bindingIncludes = set(self.getDeclarationFilename(d) for d in interfaceDeps) michael@0: michael@0: # Grab all the implementation declaration files we need. michael@0: implementationIncludes = set(d.headerFile for d in descriptors if d.needsHeaderInclude()) michael@0: michael@0: # Grab the includes for checking hasInstance michael@0: interfacesImplementingSelf = set() michael@0: for d in descriptors: michael@0: interfacesImplementingSelf |= d.interface.interfacesImplementingSelf michael@0: implementationIncludes |= set(self.getDeclarationFilename(i) for i in michael@0: interfacesImplementingSelf) michael@0: michael@0: # Grab the includes for the things that involve XPCOM interfaces michael@0: hasInstanceIncludes = set("nsIDOM" + d.interface.identifier.name + ".h" for d michael@0: in descriptors if michael@0: NeedsGeneratedHasInstance(d) and michael@0: d.interface.hasInterfacePrototypeObject()) michael@0: michael@0: # Now find all the things we'll need as arguments because we michael@0: # need to wrap or unwrap them. michael@0: bindingHeaders = set() michael@0: declareIncludes = set(declareIncludes) michael@0: michael@0: def addHeadersForType((t, descriptor, dictionary)): michael@0: """ michael@0: Add the relevant headers for this type. We use descriptor and michael@0: dictionary, if passed, to decide what to do with interface types. michael@0: """ michael@0: assert not descriptor or not dictionary michael@0: # Dictionaries have members that need to be actually michael@0: # declared, not just forward-declared. michael@0: if dictionary: michael@0: headerSet = declareIncludes michael@0: else: michael@0: headerSet = bindingHeaders michael@0: if t.nullable(): michael@0: # Need to make sure that Nullable as a dictionary michael@0: # member works. michael@0: headerSet.add("mozilla/dom/Nullable.h") michael@0: unrolled = t.unroll() michael@0: if unrolled.isUnion(): michael@0: # UnionConversions.h includes UnionTypes.h michael@0: bindingHeaders.add("mozilla/dom/UnionConversions.h") michael@0: if dictionary: michael@0: # Our dictionary definition is in the header and michael@0: # needs the union type. michael@0: declareIncludes.add("mozilla/dom/UnionTypes.h") michael@0: elif unrolled.isDate(): michael@0: if dictionary or jsImplementedDescriptors: michael@0: declareIncludes.add("mozilla/dom/Date.h") michael@0: else: michael@0: bindingHeaders.add("mozilla/dom/Date.h") michael@0: elif unrolled.isInterface(): michael@0: if unrolled.isSpiderMonkeyInterface(): michael@0: bindingHeaders.add("jsfriendapi.h") michael@0: headerSet.add("mozilla/dom/TypedArray.h") michael@0: else: michael@0: providers = getRelevantProviders(descriptor, config) michael@0: for p in providers: michael@0: try: michael@0: typeDesc = p.getDescriptor(unrolled.inner.identifier.name) michael@0: except NoSuchDescriptorError: michael@0: continue michael@0: # Dictionaries with interface members rely on the michael@0: # actual class definition of that interface member michael@0: # being visible in the binding header, because they michael@0: # store them in nsRefPtr and have inline michael@0: # constructors/destructors. michael@0: # michael@0: # XXXbz maybe dictionaries with interface members michael@0: # should just have out-of-line constructors and michael@0: # destructors? michael@0: headerSet.add(typeDesc.headerFile) michael@0: elif unrolled.isDictionary(): michael@0: headerSet.add(self.getDeclarationFilename(unrolled.inner)) michael@0: elif unrolled.isCallback(): michael@0: # Callbacks are both a type and an object michael@0: headerSet.add(self.getDeclarationFilename(unrolled)) michael@0: elif unrolled.isFloat() and not unrolled.isUnrestricted(): michael@0: # Restricted floats are tested for finiteness michael@0: bindingHeaders.add("mozilla/FloatingPoint.h") michael@0: bindingHeaders.add("mozilla/dom/PrimitiveConversions.h") michael@0: elif unrolled.isEnum(): michael@0: filename = self.getDeclarationFilename(unrolled.inner) michael@0: declareIncludes.add(filename) michael@0: elif unrolled.isPrimitive(): michael@0: bindingHeaders.add("mozilla/dom/PrimitiveConversions.h") michael@0: elif unrolled.isMozMap(): michael@0: if dictionary or jsImplementedDescriptors: michael@0: declareIncludes.add("mozilla/dom/MozMap.h") michael@0: else: michael@0: bindingHeaders.add("mozilla/dom/MozMap.h") michael@0: # Also add headers for the type the MozMap is michael@0: # parametrized over, if needed. michael@0: addHeadersForType((t.inner, descriptor, dictionary)) michael@0: michael@0: map(addHeadersForType, michael@0: getAllTypes(descriptors + callbackDescriptors, dictionaries, michael@0: callbacks)) michael@0: michael@0: # Now make sure we're not trying to include the header from inside itself michael@0: declareIncludes.discard(prefix + ".h") michael@0: michael@0: # Now for non-callback descriptors make sure we include any michael@0: # headers needed by Func declarations. michael@0: for desc in descriptors: michael@0: if desc.interface.isExternal(): michael@0: continue michael@0: michael@0: def addHeaderForFunc(func): michael@0: if func is None: michael@0: return michael@0: # Include the right class header, which we can only do michael@0: # if this is a class member function. michael@0: if not desc.headerIsDefault: michael@0: # An explicit header file was provided, assume that we know michael@0: # what we're doing. michael@0: return michael@0: michael@0: if "::" in func: michael@0: # Strip out the function name and convert "::" to "/" michael@0: bindingHeaders.add("/".join(func.split("::")[:-1]) + ".h") michael@0: michael@0: for m in desc.interface.members: michael@0: addHeaderForFunc(PropertyDefiner.getStringAttr(m, "Func")) michael@0: # getExtendedAttribute() returns a list, extract the entry. michael@0: funcList = desc.interface.getExtendedAttribute("Func") michael@0: if funcList is not None: michael@0: addHeaderForFunc(funcList[0]) michael@0: michael@0: for d in dictionaries: michael@0: if d.parent: michael@0: declareIncludes.add(self.getDeclarationFilename(d.parent)) michael@0: bindingHeaders.add(self.getDeclarationFilename(d)) michael@0: michael@0: for c in callbacks: michael@0: bindingHeaders.add(self.getDeclarationFilename(c)) michael@0: michael@0: for c in callbackDescriptors: michael@0: bindingHeaders.add(self.getDeclarationFilename(c.interface)) michael@0: michael@0: if len(callbacks) != 0: michael@0: # We need CallbackFunction to serve as our parent class michael@0: declareIncludes.add("mozilla/dom/CallbackFunction.h") michael@0: # And we need BindingUtils.h so we can wrap "this" objects michael@0: declareIncludes.add("mozilla/dom/BindingUtils.h") michael@0: michael@0: if len(callbackDescriptors) != 0 or len(jsImplementedDescriptors) != 0: michael@0: # We need CallbackInterface to serve as our parent class michael@0: declareIncludes.add("mozilla/dom/CallbackInterface.h") michael@0: # And we need BindingUtils.h so we can wrap "this" objects michael@0: declareIncludes.add("mozilla/dom/BindingUtils.h") michael@0: michael@0: # Also need to include the headers for ancestors of michael@0: # JS-implemented interfaces. michael@0: for jsImplemented in jsImplementedDescriptors: michael@0: jsParent = jsImplemented.interface.parent michael@0: if jsParent: michael@0: parentDesc = jsImplemented.getDescriptor(jsParent.identifier.name) michael@0: declareIncludes.add(parentDesc.jsImplParentHeader) michael@0: michael@0: # Let the machinery do its thing. michael@0: def _includeString(includes): michael@0: return ''.join(['#include "%s"\n' % i for i in includes]) + '\n' michael@0: CGWrapper.__init__(self, child, michael@0: declarePre=_includeString(sorted(declareIncludes)), michael@0: definePre=_includeString(sorted(set(defineIncludes) | michael@0: bindingIncludes | michael@0: bindingHeaders | michael@0: hasInstanceIncludes | michael@0: implementationIncludes))) michael@0: michael@0: @staticmethod michael@0: def getDeclarationFilename(decl): michael@0: # Use our local version of the header, not the exported one, so that michael@0: # test bindings, which don't export, will work correctly. michael@0: basename = os.path.basename(decl.filename()) michael@0: return basename.replace('.webidl', 'Binding.h') michael@0: michael@0: michael@0: def SortedDictValues(d): michael@0: """ michael@0: Returns a list of values from the dict sorted by key. michael@0: """ michael@0: return [v for k, v in sorted(d.items())] michael@0: michael@0: michael@0: def UnionTypes(descriptors, dictionaries, callbacks, config): michael@0: """ michael@0: Returns a tuple containing a set of header filenames to include in michael@0: UnionTypes.h, a set of header filenames to include in UnionTypes.cpp, a set michael@0: of tuples containing a type declaration and a boolean if the type is a michael@0: struct for member types of the unions and a CGList containing CGUnionStructs michael@0: for every union. michael@0: """ michael@0: michael@0: # Now find all the things we'll need as arguments and return values because michael@0: # we need to wrap or unwrap them. michael@0: headers = set() michael@0: implheaders = set(["UnionTypes.h"]) michael@0: declarations = set() michael@0: unionStructs = dict() michael@0: owningUnionStructs = dict() michael@0: michael@0: for t, descriptor, dictionary in getAllTypes(descriptors, dictionaries, callbacks): michael@0: # Add info for the given type. descriptor and dictionary, if present, are michael@0: # used to figure out what to do with interface types. michael@0: assert not descriptor or not dictionary michael@0: michael@0: t = t.unroll() michael@0: while t.isMozMap(): michael@0: t = t.inner.unroll() michael@0: if not t.isUnion(): michael@0: continue michael@0: name = str(t) michael@0: if name not in unionStructs: michael@0: providers = getRelevantProviders(descriptor, config) michael@0: # FIXME: Unions are broken in workers. See bug 809899. michael@0: unionStructs[name] = CGUnionStruct(t, providers[0]) michael@0: owningUnionStructs[name] = CGUnionStruct(t, providers[0], michael@0: ownsMembers=True) michael@0: michael@0: def addHeadersForType(f): michael@0: if f.nullable(): michael@0: headers.add("mozilla/dom/Nullable.h") michael@0: f = f.unroll() michael@0: if f.isInterface(): michael@0: if f.isSpiderMonkeyInterface(): michael@0: headers.add("jsfriendapi.h") michael@0: headers.add("mozilla/dom/TypedArray.h") michael@0: else: michael@0: for p in providers: michael@0: try: michael@0: typeDesc = p.getDescriptor(f.inner.identifier.name) michael@0: except NoSuchDescriptorError: michael@0: continue michael@0: if typeDesc.interface.isCallback(): michael@0: # Callback interfaces always use strong refs, so michael@0: # we need to include the right header to be able michael@0: # to Release() in our inlined code. michael@0: headers.add(typeDesc.headerFile) michael@0: else: michael@0: declarations.add((typeDesc.nativeType, False)) michael@0: implheaders.add(typeDesc.headerFile) michael@0: elif f.isDictionary(): michael@0: # For a dictionary, we need to see its declaration in michael@0: # UnionTypes.h so we have its sizeof and know how big to michael@0: # make our union. michael@0: headers.add(CGHeaders.getDeclarationFilename(f.inner)) michael@0: # And if it needs rooting, we need RootedDictionary too michael@0: if typeNeedsRooting(f): michael@0: headers.add("mozilla/dom/RootedDictionary.h") michael@0: elif f.isEnum(): michael@0: # Need to see the actual definition of the enum, michael@0: # unfortunately. michael@0: headers.add(CGHeaders.getDeclarationFilename(f.inner)) michael@0: elif f.isCallback(): michael@0: # Callbacks always use strong refs, so we need to include michael@0: # the right header to be able to Release() in our inlined michael@0: # code. michael@0: headers.add(CGHeaders.getDeclarationFilename(f)) michael@0: elif f.isMozMap(): michael@0: headers.add("mozilla/dom/MozMap.h") michael@0: # And add headers for the type we're parametrized over michael@0: addHeadersForType(f.inner) michael@0: michael@0: for f in t.flatMemberTypes: michael@0: assert not f.nullable() michael@0: addHeadersForType(f) michael@0: michael@0: return (headers, implheaders, declarations, michael@0: CGList(SortedDictValues(unionStructs) + michael@0: SortedDictValues(owningUnionStructs), michael@0: "\n")) michael@0: michael@0: michael@0: def UnionConversions(descriptors, dictionaries, callbacks, config): michael@0: """ michael@0: Returns a CGThing to declare all union argument conversion helper structs. michael@0: """ michael@0: # Now find all the things we'll need as arguments because we michael@0: # need to unwrap them. michael@0: headers = set() michael@0: unionConversions = dict() michael@0: michael@0: for t, descriptor, dictionary in getAllTypes(descriptors, dictionaries, callbacks): michael@0: # Add info for the given type. descriptor and dictionary, if passed, are michael@0: # used to figure out what to do with interface types. michael@0: assert not descriptor or not dictionary michael@0: michael@0: t = t.unroll() michael@0: if not t.isUnion(): michael@0: continue michael@0: name = str(t) michael@0: if name not in unionConversions: michael@0: providers = getRelevantProviders(descriptor, config) michael@0: unionConversions[name] = CGUnionConversionStruct(t, providers[0]) michael@0: def addHeadersForType(f, providers): michael@0: f = f.unroll() michael@0: if f.isInterface(): michael@0: if f.isSpiderMonkeyInterface(): michael@0: headers.add("jsfriendapi.h") michael@0: headers.add("mozilla/dom/TypedArray.h") michael@0: elif f.inner.isExternal(): michael@0: providers = getRelevantProviders(descriptor, config) michael@0: for p in providers: michael@0: try: michael@0: typeDesc = p.getDescriptor(f.inner.identifier.name) michael@0: except NoSuchDescriptorError: michael@0: continue michael@0: headers.add(typeDesc.headerFile) michael@0: else: michael@0: headers.add(CGHeaders.getDeclarationFilename(f.inner)) michael@0: # Check for whether we have a possibly-XPConnect-implemented michael@0: # interface. If we do, the right descriptor will come from michael@0: # providers[0], because that would be the non-worker michael@0: # descriptor provider, if we have one at all. michael@0: if (f.isGeckoInterface() and michael@0: providers[0].getDescriptor(f.inner.identifier.name).hasXPConnectImpls): michael@0: headers.add("nsDOMQS.h") michael@0: elif f.isDictionary(): michael@0: headers.add(CGHeaders.getDeclarationFilename(f.inner)) michael@0: elif f.isPrimitive(): michael@0: headers.add("mozilla/dom/PrimitiveConversions.h") michael@0: elif f.isMozMap(): michael@0: headers.add("mozilla/dom/MozMap.h") michael@0: # And the internal type of the MozMap michael@0: addHeadersForType(f.inner, providers) michael@0: michael@0: for f in t.flatMemberTypes: michael@0: addHeadersForType(f, providers) michael@0: michael@0: return (headers, michael@0: CGWrapper(CGList(SortedDictValues(unionConversions), "\n"), michael@0: post="\n\n")) michael@0: michael@0: michael@0: class Argument(): michael@0: """ michael@0: A class for outputting the type and name of an argument michael@0: """ michael@0: def __init__(self, argType, name, default=None): michael@0: self.argType = argType michael@0: self.name = name michael@0: self.default = default michael@0: michael@0: def declare(self): michael@0: string = self.argType + ' ' + self.name michael@0: if self.default is not None: michael@0: string += " = " + self.default michael@0: return string michael@0: michael@0: def define(self): michael@0: return self.argType + ' ' + self.name michael@0: michael@0: michael@0: class CGAbstractMethod(CGThing): michael@0: """ michael@0: An abstract class for generating code for a method. Subclasses michael@0: should override definition_body to create the actual code. michael@0: michael@0: descriptor is the descriptor for the interface the method is associated with michael@0: michael@0: name is the name of the method as a string michael@0: michael@0: returnType is the IDLType of the return value michael@0: michael@0: args is a list of Argument objects michael@0: michael@0: inline should be True to generate an inline method, whose body is michael@0: part of the declaration. michael@0: michael@0: alwaysInline should be True to generate an inline method annotated with michael@0: MOZ_ALWAYS_INLINE. michael@0: michael@0: static should be True to generate a static method, which only has michael@0: a definition. michael@0: michael@0: If templateArgs is not None it should be a list of strings containing michael@0: template arguments, and the function will be templatized using those michael@0: arguments. michael@0: """ michael@0: def __init__(self, descriptor, name, returnType, args, inline=False, alwaysInline=False, static=False, templateArgs=None): michael@0: CGThing.__init__(self) michael@0: self.descriptor = descriptor michael@0: self.name = name michael@0: self.returnType = returnType michael@0: self.args = args michael@0: self.inline = inline michael@0: self.alwaysInline = alwaysInline michael@0: self.static = static michael@0: self.templateArgs = templateArgs michael@0: michael@0: def _argstring(self, declare): michael@0: return ', '.join([a.declare() if declare else a.define() for a in self.args]) michael@0: michael@0: def _template(self): michael@0: if self.templateArgs is None: michael@0: return '' michael@0: return 'template <%s>\n' % ', '.join(self.templateArgs) michael@0: michael@0: def _decorators(self): michael@0: decorators = [] michael@0: if self.alwaysInline: michael@0: decorators.append('MOZ_ALWAYS_INLINE') michael@0: elif self.inline: michael@0: decorators.append('inline') michael@0: if self.static: michael@0: decorators.append('static') michael@0: decorators.append(self.returnType) michael@0: maybeNewline = " " if self.inline else "\n" michael@0: return ' '.join(decorators) + maybeNewline michael@0: michael@0: def declare(self): michael@0: if self.inline: michael@0: return self._define(True) michael@0: return "%s%s%s(%s);\n" % (self._template(), self._decorators(), self.name, self._argstring(True)) michael@0: michael@0: def _define(self, fromDeclare=False): michael@0: return self.definition_prologue(fromDeclare) + self.definition_body() + self.definition_epilogue() michael@0: michael@0: def define(self): michael@0: return "" if self.inline else self._define() michael@0: michael@0: def definition_prologue(self, fromDeclare): michael@0: return "%s%s%s(%s)\n{\n" % (self._template(), self._decorators(), michael@0: self.name, self._argstring(fromDeclare)) michael@0: michael@0: def definition_epilogue(self): michael@0: return "}\n" michael@0: michael@0: def definition_body(self): michael@0: assert False # Override me! michael@0: michael@0: michael@0: class CGAbstractStaticMethod(CGAbstractMethod): michael@0: """ michael@0: Abstract base class for codegen of implementation-only (no michael@0: declaration) static methods. michael@0: """ michael@0: def __init__(self, descriptor, name, returnType, args): michael@0: CGAbstractMethod.__init__(self, descriptor, name, returnType, args, michael@0: inline=False, static=True) michael@0: michael@0: def declare(self): michael@0: # We only have implementation michael@0: return "" michael@0: michael@0: michael@0: class CGAbstractClassHook(CGAbstractStaticMethod): michael@0: """ michael@0: Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw michael@0: 'this' unwrapping as it assumes that the unwrapped type is always known. michael@0: """ michael@0: def __init__(self, descriptor, name, returnType, args): michael@0: CGAbstractStaticMethod.__init__(self, descriptor, name, returnType, michael@0: args) michael@0: michael@0: def definition_body_prologue(self): michael@0: return ("\n" # BOGUS extra blank line at start of function michael@0: " %s* self = UnwrapDOMObject<%s>(obj);\n" % michael@0: (self.descriptor.nativeType, self.descriptor.nativeType)) michael@0: michael@0: def definition_body(self): michael@0: return self.definition_body_prologue() + self.generate_code() michael@0: michael@0: def generate_code(self): michael@0: assert False # Override me! michael@0: michael@0: michael@0: class CGGetJSClassMethod(CGAbstractMethod): michael@0: def __init__(self, descriptor): michael@0: CGAbstractMethod.__init__(self, descriptor, 'GetJSClass', 'const JSClass*', michael@0: []) michael@0: michael@0: def definition_body(self): michael@0: return " return Class.ToJSClass();\n" michael@0: michael@0: michael@0: class CGAddPropertyHook(CGAbstractClassHook): michael@0: """ michael@0: A hook for addProperty, used to preserve our wrapper from GC. michael@0: """ michael@0: def __init__(self, descriptor): michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('JS::Handle', 'obj'), michael@0: Argument('JS::Handle', 'id'), michael@0: Argument('JS::MutableHandle', 'vp')] michael@0: CGAbstractClassHook.__init__(self, descriptor, ADDPROPERTY_HOOK_NAME, michael@0: 'bool', args) michael@0: michael@0: def generate_code(self): michael@0: assert self.descriptor.wrapperCache michael@0: return indent(dedent(""" michael@0: // We don't want to preserve if we don't have a wrapper. michael@0: if (self->GetWrapperPreserveColor()) { michael@0: PreserveWrapper(self); michael@0: } michael@0: return true; michael@0: """)) michael@0: michael@0: michael@0: def DeferredFinalizeSmartPtr(descriptor): michael@0: if descriptor.nativeOwnership == 'owned': michael@0: smartPtr = 'nsAutoPtr' michael@0: else: michael@0: smartPtr = 'nsRefPtr' michael@0: return smartPtr michael@0: michael@0: michael@0: def finalizeHook(descriptor, hookName, freeOp): michael@0: finalize = "JSBindingFinalized<%s>::Finalized(self);\n" % descriptor.nativeType michael@0: if descriptor.wrapperCache: michael@0: finalize += "ClearWrapper(self, self);\n" michael@0: if descriptor.interface.getExtendedAttribute('OverrideBuiltins'): michael@0: finalize += "self->mExpandoAndGeneration.expando = JS::UndefinedValue();\n" michael@0: if descriptor.interface.getExtendedAttribute("Global"): michael@0: finalize += "mozilla::dom::FinalizeGlobal(CastToJSFreeOp(%s), obj);\n" % freeOp michael@0: finalize += ("AddForDeferredFinalization<%s, %s >(self);\n" % michael@0: (descriptor.nativeType, DeferredFinalizeSmartPtr(descriptor))) michael@0: return CGIfWrapper(CGGeneric(finalize), "self") michael@0: michael@0: michael@0: class CGClassFinalizeHook(CGAbstractClassHook): michael@0: """ michael@0: A hook for finalize, used to release our native object. michael@0: """ michael@0: def __init__(self, descriptor): michael@0: args = [Argument('js::FreeOp*', 'fop'), Argument('JSObject*', 'obj')] michael@0: CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME, michael@0: 'void', args) michael@0: michael@0: def generate_code(self): michael@0: return indent(finalizeHook(self.descriptor, self.name, self.args[0].name).define()) michael@0: michael@0: michael@0: class CGClassConstructor(CGAbstractStaticMethod): michael@0: """ michael@0: JS-visible constructor for our objects michael@0: """ michael@0: def __init__(self, descriptor, ctor, name=CONSTRUCT_HOOK_NAME): michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('unsigned', 'argc'), michael@0: Argument('JS::Value*', 'vp')] michael@0: CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args) michael@0: self._ctor = ctor michael@0: michael@0: def define(self): michael@0: if not self._ctor: michael@0: return "" michael@0: return CGAbstractStaticMethod.define(self) michael@0: michael@0: def definition_body(self): michael@0: return self.generate_code() michael@0: michael@0: def generate_code(self): michael@0: # [ChromeOnly] interfaces may only be constructed by chrome. michael@0: chromeOnlyCheck = "" michael@0: if isChromeOnly(self._ctor): michael@0: chromeOnlyCheck = dedent(""" michael@0: if (!nsContentUtils::ThreadsafeIsCallerChrome()) { michael@0: return ThrowingConstructor(cx, argc, vp); michael@0: } michael@0: michael@0: """) michael@0: michael@0: # Additionally, we want to throw if a caller does a bareword invocation michael@0: # of a constructor without |new|. We don't enforce this for chrome in michael@0: # realease builds to avoid the addon compat fallout of making that michael@0: # change. See bug 916644. michael@0: # michael@0: # Figure out the name of our constructor for error reporting purposes. michael@0: # For unnamed webidl constructors, identifier.name is "constructor" but michael@0: # the name JS sees is the interface name; for named constructors michael@0: # identifier.name is the actual name. michael@0: name = self._ctor.identifier.name michael@0: if name != "constructor": michael@0: ctorName = name michael@0: else: michael@0: ctorName = self.descriptor.interface.identifier.name michael@0: michael@0: preamble = fill( # BOGUS extra blank line at beginning of function body michael@0: """ michael@0: michael@0: JS::CallArgs args = JS::CallArgsFromVp(argc, vp); michael@0: JS::Rooted obj(cx, &args.callee()); michael@0: $*{chromeOnlyCheck} michael@0: bool mayInvoke = args.isConstructing(); michael@0: #ifdef RELEASE_BUILD michael@0: mayInvoke = mayInvoke || nsContentUtils::ThreadsafeIsCallerChrome(); michael@0: #endif // RELEASE_BUILD michael@0: if (!mayInvoke) { michael@0: // XXXbz wish I could get the name from the callee instead of michael@0: // Adding more relocations michael@0: return ThrowConstructorWithoutNew(cx, "${ctorName}"); michael@0: } michael@0: """, michael@0: chromeOnlyCheck=chromeOnlyCheck, michael@0: ctorName=ctorName) michael@0: michael@0: name = self._ctor.identifier.name michael@0: nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name)) michael@0: callGenerator = CGMethodCall(nativeName, True, self.descriptor, michael@0: self._ctor, isConstructor=True, michael@0: constructorName=ctorName) michael@0: return indent(preamble) + callGenerator.define() michael@0: michael@0: michael@0: # Encapsulate the constructor in a helper method to share genConstructorBody with CGJSImplMethod. michael@0: class CGConstructNavigatorObjectHelper(CGAbstractStaticMethod): michael@0: """ michael@0: Construct a new JS-implemented WebIDL DOM object, for use on navigator. michael@0: """ michael@0: def __init__(self, descriptor): michael@0: name = "ConstructNavigatorObjectHelper" michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('GlobalObject&', 'global'), michael@0: Argument('ErrorResult&', 'aRv')] michael@0: rtype = 'already_AddRefed<%s>' % descriptor.name michael@0: CGAbstractStaticMethod.__init__(self, descriptor, name, rtype, args) michael@0: michael@0: def definition_body(self): michael@0: return indent(genConstructorBody(self.descriptor)) michael@0: michael@0: michael@0: class CGConstructNavigatorObject(CGAbstractMethod): michael@0: """ michael@0: Wrap a JS-implemented WebIDL object into a JS value, for use on navigator. michael@0: """ michael@0: def __init__(self, descriptor): michael@0: name = 'ConstructNavigatorObject' michael@0: args = [Argument('JSContext*', 'aCx'), Argument('JS::Handle', 'aObj')] michael@0: CGAbstractMethod.__init__(self, descriptor, name, 'JSObject*', args) michael@0: michael@0: def definition_body(self): michael@0: if not self.descriptor.interface.isJSImplemented(): michael@0: raise TypeError("Only JS-implemented classes are currently supported " michael@0: "on navigator. See bug 856820.") michael@0: return indent(fill( michael@0: """ michael@0: GlobalObject global(aCx, aObj); michael@0: if (global.Failed()) { michael@0: return nullptr; michael@0: } michael@0: ErrorResult rv; michael@0: JS::Rooted v(aCx); michael@0: { // Scope to make sure |result| goes out of scope while |v| is rooted michael@0: nsRefPtr result = ConstructNavigatorObjectHelper(aCx, global, rv); michael@0: rv.WouldReportJSException(); michael@0: if (rv.Failed()) { michael@0: ThrowMethodFailedWithDetails(aCx, rv, "${descriptorName}", "navigatorConstructor"); michael@0: return nullptr; michael@0: } michael@0: if (!WrapNewBindingObject(aCx, result, &v)) { michael@0: //XXX Assertion disabled for now, see bug 991271. michael@0: MOZ_ASSERT(true || JS_IsExceptionPending(aCx)); michael@0: return nullptr; michael@0: } michael@0: } michael@0: return &v.toObject(); michael@0: """, michael@0: descriptorName=self.descriptor.name)) michael@0: michael@0: michael@0: class CGClassConstructHookHolder(CGGeneric): michael@0: def __init__(self, descriptor): michael@0: if descriptor.interface.ctor(): michael@0: constructHook = CONSTRUCT_HOOK_NAME michael@0: else: michael@0: constructHook = "ThrowingConstructor" michael@0: CGGeneric.__init__(self, fill( michael@0: """ michael@0: static const JSNativeHolder ${CONSTRUCT_HOOK_NAME}_holder = { michael@0: ${constructHook}, michael@0: ${hooks} michael@0: }; michael@0: """, michael@0: CONSTRUCT_HOOK_NAME=CONSTRUCT_HOOK_NAME, michael@0: constructHook=constructHook, michael@0: hooks=NativePropertyHooks(descriptor))) michael@0: michael@0: michael@0: def NamedConstructorName(m): michael@0: return '_' + m.identifier.name michael@0: michael@0: michael@0: class CGNamedConstructors(CGThing): michael@0: def __init__(self, descriptor): michael@0: self.descriptor = descriptor michael@0: CGThing.__init__(self) michael@0: michael@0: def declare(self): michael@0: return "" michael@0: michael@0: def define(self): michael@0: if len(self.descriptor.interface.namedConstructors) == 0: michael@0: return "" michael@0: michael@0: constructorID = "constructors::id::" michael@0: if self.descriptor.interface.hasInterfaceObject(): michael@0: constructorID += self.descriptor.name michael@0: else: michael@0: constructorID += "_ID_Count" michael@0: michael@0: namedConstructors = "" michael@0: for n in self.descriptor.interface.namedConstructors: michael@0: namedConstructors += ( michael@0: "{ \"%s\", { %s, &sNamedConstructorNativePropertyHooks }, %i },\n" % michael@0: (n.identifier.name, NamedConstructorName(n), methodLength(n))) michael@0: michael@0: return fill( michael@0: """ michael@0: const NativePropertyHooks sNamedConstructorNativePropertyHooks = { michael@0: nullptr, michael@0: nullptr, michael@0: { nullptr, nullptr }, michael@0: prototypes::id::${name}, michael@0: ${constructorID}, michael@0: nullptr michael@0: }; michael@0: michael@0: static const NamedConstructor namedConstructors[] = { michael@0: $*{namedConstructors} michael@0: { nullptr, { nullptr, nullptr }, 0 } michael@0: }; michael@0: """, michael@0: name=self.descriptor.name, michael@0: constructorID=constructorID, michael@0: namedConstructors=namedConstructors) michael@0: michael@0: michael@0: class CGClassHasInstanceHook(CGAbstractStaticMethod): michael@0: def __init__(self, descriptor): michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('JS::Handle', 'obj'), michael@0: Argument('JS::MutableHandle', 'vp'), michael@0: Argument('bool*', 'bp')] michael@0: CGAbstractStaticMethod.__init__(self, descriptor, HASINSTANCE_HOOK_NAME, michael@0: 'bool', args) michael@0: michael@0: def define(self): michael@0: if not NeedsGeneratedHasInstance(self.descriptor): michael@0: return "" michael@0: return CGAbstractStaticMethod.define(self) michael@0: michael@0: def definition_body(self): michael@0: return self.generate_code() michael@0: michael@0: def generate_code(self): michael@0: # BOGUS extra blank line at start of function michael@0: header = dedent(""" michael@0: michael@0: if (!vp.isObject()) { michael@0: *bp = false; michael@0: return true; michael@0: } michael@0: michael@0: JS::Rooted instance(cx, &vp.toObject()); michael@0: """) michael@0: if self.descriptor.interface.hasInterfacePrototypeObject(): michael@0: return indent( michael@0: header + michael@0: fill( michael@0: """ michael@0: michael@0: static_assert(IsBaseOf::value, michael@0: "HasInstance only works for nsISupports-based classes."); michael@0: michael@0: bool ok = InterfaceHasInstance(cx, obj, instance, bp); michael@0: if (!ok || *bp) { michael@0: return ok; michael@0: } michael@0: michael@0: // FIXME Limit this to chrome by checking xpc::AccessCheck::isChrome(obj). michael@0: nsISupports* native = michael@0: nsContentUtils::XPConnect()->GetNativeOfWrapper(cx, michael@0: js::UncheckedUnwrap(instance)); michael@0: nsCOMPtr qiResult = do_QueryInterface(native); michael@0: *bp = !!qiResult; michael@0: return true; michael@0: michael@0: """, # BOGUS extra blank line at end of function michael@0: nativeType=self.descriptor.nativeType, michael@0: name=self.descriptor.interface.identifier.name)) michael@0: michael@0: hasInstanceCode = dedent(""" michael@0: michael@0: const DOMClass* domClass = GetDOMClass(js::UncheckedUnwrap(instance)); michael@0: *bp = false; michael@0: if (!domClass) { michael@0: // Not a DOM object, so certainly not an instance of this interface michael@0: return true; michael@0: } michael@0: """) michael@0: if self.descriptor.interface.identifier.name == "ChromeWindow": michael@0: setBp = "*bp = UnwrapDOMObject(js::UncheckedUnwrap(instance))->IsChromeWindow()" michael@0: else: michael@0: setBp = "*bp = true" michael@0: # Sort interaces implementing self by name so we get stable output. michael@0: for iface in sorted(self.descriptor.interface.interfacesImplementingSelf, michael@0: key=lambda iface: iface.identifier.name): michael@0: hasInstanceCode += fill( michael@0: """ michael@0: michael@0: if (domClass->mInterfaceChain[PrototypeTraits::Depth] == prototypes::id::${name}) { michael@0: ${setBp}; michael@0: return true; michael@0: } michael@0: """, michael@0: name=iface.identifier.name, michael@0: setBp=setBp) michael@0: hasInstanceCode += "return true;\n" michael@0: return indent(header + hasInstanceCode) michael@0: michael@0: michael@0: def isChromeOnly(m): michael@0: return m.getExtendedAttribute("ChromeOnly") michael@0: michael@0: michael@0: def getAvailableInTestFunc(obj): michael@0: availableIn = obj.getExtendedAttribute("AvailableIn") michael@0: if availableIn is None: michael@0: return None michael@0: assert isinstance(availableIn, list) and len(availableIn) == 1 michael@0: if availableIn[0] == "PrivilegedApps": michael@0: return "IsInPrivilegedApp" michael@0: if availableIn[0] == "CertifiedApps": michael@0: return "IsInCertifiedApp" michael@0: raise TypeError("Unknown AvailableIn value '%s'" % availableIn[0]) michael@0: michael@0: michael@0: class MemberCondition: michael@0: """ michael@0: An object representing the condition for a member to actually be michael@0: exposed. Any of pref, func, and available can be None. If not michael@0: None, they should be strings that have the pref name (for "pref") michael@0: or function name (for "func" and "available"). michael@0: """ michael@0: def __init__(self, pref, func, available=None): michael@0: assert pref is None or isinstance(pref, str) michael@0: assert func is None or isinstance(func, str) michael@0: assert available is None or isinstance(available, str) michael@0: self.pref = pref michael@0: michael@0: def toFuncPtr(val): michael@0: if val is None: michael@0: return "nullptr" michael@0: return "&" + val michael@0: self.func = toFuncPtr(func) michael@0: self.available = toFuncPtr(available) michael@0: michael@0: def __eq__(self, other): michael@0: return (self.pref == other.pref and self.func == other.func and michael@0: self.available == other.available) michael@0: michael@0: def __ne__(self, other): michael@0: return not self.__eq__(other) michael@0: michael@0: michael@0: class PropertyDefiner: michael@0: """ michael@0: A common superclass for defining things on prototype objects. michael@0: michael@0: Subclasses should implement generateArray to generate the actual arrays of michael@0: things we're defining. They should also set self.chrome to the list of michael@0: things only exposed to chrome and self.regular to the list of things exposed michael@0: to both chrome and web pages. michael@0: """ michael@0: def __init__(self, descriptor, name): michael@0: self.descriptor = descriptor michael@0: self.name = name michael@0: # self.prefCacheData will store an array of (prefname, bool*) michael@0: # pairs for our bool var caches. generateArray will fill it michael@0: # in as needed. michael@0: self.prefCacheData = [] michael@0: michael@0: def hasChromeOnly(self): michael@0: return len(self.chrome) > 0 michael@0: michael@0: def hasNonChromeOnly(self): michael@0: return len(self.regular) > 0 michael@0: michael@0: def variableName(self, chrome): michael@0: if chrome: michael@0: if self.hasChromeOnly(): michael@0: return "sChrome" + self.name michael@0: else: michael@0: if self.hasNonChromeOnly(): michael@0: return "s" + self.name michael@0: return "nullptr" michael@0: michael@0: def usedForXrays(self): michael@0: # No Xrays in workers. michael@0: return not self.descriptor.workers michael@0: michael@0: def __str__(self): michael@0: # We only need to generate id arrays for things that will end michael@0: # up used via ResolveProperty or EnumerateProperties. michael@0: str = self.generateArray(self.regular, self.variableName(False), michael@0: self.usedForXrays()) michael@0: if self.hasChromeOnly(): michael@0: str += self.generateArray(self.chrome, self.variableName(True), michael@0: self.usedForXrays()) michael@0: return str michael@0: michael@0: @staticmethod michael@0: def getStringAttr(member, name): michael@0: attr = member.getExtendedAttribute(name) michael@0: if attr is None: michael@0: return None michael@0: # It's a list of strings michael@0: assert len(attr) == 1 michael@0: assert attr[0] is not None michael@0: return attr[0] michael@0: michael@0: @staticmethod michael@0: def getControllingCondition(interfaceMember): michael@0: return MemberCondition(PropertyDefiner.getStringAttr(interfaceMember, michael@0: "Pref"), michael@0: PropertyDefiner.getStringAttr(interfaceMember, michael@0: "Func"), michael@0: getAvailableInTestFunc(interfaceMember)) michael@0: michael@0: def generatePrefableArray(self, array, name, specTemplate, specTerminator, michael@0: specType, getCondition, getDataTuple, doIdArrays): michael@0: """ michael@0: This method generates our various arrays. michael@0: michael@0: array is an array of interface members as passed to generateArray michael@0: michael@0: name is the name as passed to generateArray michael@0: michael@0: specTemplate is a template for each entry of the spec array michael@0: michael@0: specTerminator is a terminator for the spec array (inserted every time michael@0: our controlling pref changes and at the end of the array) michael@0: michael@0: specType is the actual typename of our spec michael@0: michael@0: getCondition is a callback function that takes an array entry and michael@0: returns the corresponding MemberCondition. michael@0: michael@0: getDataTuple is a callback function that takes an array entry and michael@0: returns a tuple suitable for substitution into specTemplate. michael@0: """ michael@0: michael@0: # We want to generate a single list of specs, but with specTerminator michael@0: # inserted at every point where the pref name controlling the member michael@0: # changes. That will make sure the order of the properties as exposed michael@0: # on the interface and interface prototype objects does not change when michael@0: # pref control is added to members while still allowing us to define all michael@0: # the members in the smallest number of JSAPI calls. michael@0: assert len(array) != 0 michael@0: lastCondition = getCondition(array[0]) # So we won't put a specTerminator michael@0: # at the very front of the list. michael@0: specs = [] michael@0: prefableSpecs = [] michael@0: michael@0: prefableTemplate = ' { true, %s, %s, &%s[%d] }' michael@0: prefCacheTemplate = '&%s[%d].enabled' michael@0: michael@0: def switchToCondition(props, condition): michael@0: # Remember the info about where our pref-controlled michael@0: # booleans live. michael@0: if condition.pref is not None: michael@0: props.prefCacheData.append( michael@0: (condition.pref, michael@0: prefCacheTemplate % (name, len(prefableSpecs)))) michael@0: # Set up pointers to the new sets of specs inside prefableSpecs michael@0: prefableSpecs.append(prefableTemplate % michael@0: (condition.func, michael@0: condition.available, michael@0: name + "_specs", len(specs))) michael@0: michael@0: switchToCondition(self, lastCondition) michael@0: michael@0: for member in array: michael@0: curCondition = getCondition(member) michael@0: if lastCondition != curCondition: michael@0: # Terminate previous list michael@0: specs.append(specTerminator) michael@0: # And switch to our new pref michael@0: switchToCondition(self, curCondition) michael@0: lastCondition = curCondition michael@0: # And the actual spec michael@0: specs.append(specTemplate % getDataTuple(member)) michael@0: specs.append(specTerminator) michael@0: prefableSpecs.append(" { false, nullptr }") michael@0: michael@0: specType = "const " + specType michael@0: arrays = fill( michael@0: """ michael@0: static ${specType} ${name}_specs[] = { michael@0: ${specs} michael@0: }; michael@0: michael@0: // Can't be const because the pref-enabled boolean needs to be writable michael@0: static Prefable<${specType}> ${name}[] = { michael@0: ${prefableSpecs} michael@0: }; michael@0: michael@0: """, michael@0: specType=specType, michael@0: name=name, michael@0: specs=',\n'.join(specs), michael@0: prefableSpecs=',\n'.join(prefableSpecs)) michael@0: if doIdArrays: michael@0: arrays += "static jsid %s_ids[%i];\n\n" % (name, len(specs)) michael@0: return arrays michael@0: michael@0: michael@0: # The length of a method is the minimum of the lengths of the michael@0: # argument lists of all its overloads. michael@0: def overloadLength(arguments): michael@0: i = len(arguments) michael@0: while i > 0 and arguments[i - 1].optional: michael@0: i -= 1 michael@0: return i michael@0: michael@0: michael@0: def methodLength(method): michael@0: signatures = method.signatures() michael@0: return min(overloadLength(arguments) for retType, arguments in signatures) michael@0: michael@0: michael@0: class MethodDefiner(PropertyDefiner): michael@0: """ michael@0: A class for defining methods on a prototype object. michael@0: """ michael@0: def __init__(self, descriptor, name, static): michael@0: PropertyDefiner.__init__(self, descriptor, name) michael@0: michael@0: # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822 michael@0: # We should be able to check for special operations without an michael@0: # identifier. For now we check if the name starts with __ michael@0: michael@0: # Ignore non-static methods for interfaces without a proto object michael@0: if descriptor.interface.hasInterfacePrototypeObject() or static: michael@0: methods = [m for m in descriptor.interface.members if michael@0: m.isMethod() and m.isStatic() == static and michael@0: not m.isIdentifierLess()] michael@0: else: michael@0: methods = [] michael@0: self.chrome = [] michael@0: self.regular = [] michael@0: for m in methods: michael@0: if m.identifier.name == 'queryInterface': michael@0: if self.descriptor.workers: michael@0: continue michael@0: if m.isStatic(): michael@0: raise TypeError("Legacy queryInterface member shouldn't be static") michael@0: signatures = m.signatures() michael@0: michael@0: def argTypeIsIID(arg): michael@0: return arg.type.inner.isExternal() and arg.type.inner.identifier.name == 'IID' michael@0: if len(signatures) > 1 or len(signatures[0][1]) > 1 or not argTypeIsIID(signatures[0][1][0]): michael@0: raise TypeError("There should be only one queryInterface method with 1 argument of type IID") michael@0: michael@0: # Make sure to not stick QueryInterface on abstract interfaces that michael@0: # have hasXPConnectImpls (like EventTarget). So only put it on michael@0: # interfaces that are concrete and all of whose ancestors are abstract. michael@0: def allAncestorsAbstract(iface): michael@0: if not iface.parent: michael@0: return True michael@0: desc = self.descriptor.getDescriptor(iface.parent.identifier.name) michael@0: if desc.concrete: michael@0: return False michael@0: return allAncestorsAbstract(iface.parent) michael@0: if (not self.descriptor.interface.hasInterfacePrototypeObject() or michael@0: not self.descriptor.concrete or michael@0: not allAncestorsAbstract(self.descriptor.interface)): michael@0: raise TypeError("QueryInterface is only supported on " michael@0: "interfaces that are concrete and all " michael@0: "of whose ancestors are abstract: " + michael@0: self.descriptor.name) michael@0: condition = "WantsQueryInterface<%s>::Enabled" % descriptor.nativeType michael@0: self.regular.append({ michael@0: "name": 'QueryInterface', michael@0: "methodInfo": False, michael@0: "length": 1, michael@0: "flags": "0", michael@0: "condition": MemberCondition(None, condition) michael@0: }) michael@0: continue michael@0: michael@0: method = { michael@0: "name": m.identifier.name, michael@0: "methodInfo": not m.isStatic(), michael@0: "length": methodLength(m), michael@0: "flags": "JSPROP_ENUMERATE", michael@0: "condition": PropertyDefiner.getControllingCondition(m), michael@0: "allowCrossOriginThis": m.getExtendedAttribute("CrossOriginCallable"), michael@0: "returnsPromise": m.returnsPromise() michael@0: } michael@0: if isChromeOnly(m): michael@0: self.chrome.append(method) michael@0: else: michael@0: self.regular.append(method) michael@0: michael@0: # FIXME Check for an existing iterator on the interface first. michael@0: if any(m.isGetter() and m.isIndexed() for m in methods): michael@0: self.regular.append({ michael@0: "name": "@@iterator", michael@0: "methodInfo": False, michael@0: "selfHostedName": "ArrayValues", michael@0: "length": 0, michael@0: "flags": "JSPROP_ENUMERATE", michael@0: "condition": MemberCondition(None, None) michael@0: }) michael@0: michael@0: if not static: michael@0: stringifier = descriptor.operations['Stringifier'] michael@0: if stringifier: michael@0: toStringDesc = { michael@0: "name": "toString", michael@0: "nativeName": stringifier.identifier.name, michael@0: "length": 0, michael@0: "flags": "JSPROP_ENUMERATE", michael@0: "condition": PropertyDefiner.getControllingCondition(stringifier) michael@0: } michael@0: if isChromeOnly(stringifier): michael@0: self.chrome.append(toStringDesc) michael@0: else: michael@0: self.regular.append(toStringDesc) michael@0: jsonifier = descriptor.operations['Jsonifier'] michael@0: if jsonifier: michael@0: toJSONDesc = { michael@0: "name": "toJSON", michael@0: "nativeName": jsonifier.identifier.name, michael@0: "length": 0, michael@0: "flags": "JSPROP_ENUMERATE", michael@0: "condition": PropertyDefiner.getControllingCondition(jsonifier) michael@0: } michael@0: if isChromeOnly(jsonifier): michael@0: self.chrome.append(toJSONDesc) michael@0: else: michael@0: self.regular.append(toJSONDesc) michael@0: elif (descriptor.interface.isJSImplemented() and michael@0: descriptor.interface.hasInterfaceObject()): michael@0: self.chrome.append({ michael@0: "name": '_create', michael@0: "nativeName": ("%s::_Create" % descriptor.name), michael@0: "methodInfo": False, michael@0: "length": 2, michael@0: "flags": "0", michael@0: "condition": MemberCondition(None, None) michael@0: }) michael@0: michael@0: if static: michael@0: if not descriptor.interface.hasInterfaceObject(): michael@0: # static methods go on the interface object michael@0: assert not self.hasChromeOnly() and not self.hasNonChromeOnly() michael@0: else: michael@0: if not descriptor.interface.hasInterfacePrototypeObject(): michael@0: # non-static methods go on the interface prototype object michael@0: assert not self.hasChromeOnly() and not self.hasNonChromeOnly() michael@0: michael@0: def generateArray(self, array, name, doIdArrays): michael@0: if len(array) == 0: michael@0: return "" michael@0: michael@0: def condition(m): michael@0: return m["condition"] michael@0: michael@0: def specData(m): michael@0: if "selfHostedName" in m: michael@0: selfHostedName = '"%s"' % m["selfHostedName"] michael@0: assert not m.get("methodInfo", True) michael@0: accessor = "nullptr" michael@0: jitinfo = "nullptr" michael@0: else: michael@0: selfHostedName = "nullptr" michael@0: accessor = m.get("nativeName", m["name"]) michael@0: if m.get("methodInfo", True): michael@0: # Cast this in case the methodInfo is a michael@0: # JSTypedMethodJitInfo. michael@0: jitinfo = ("reinterpret_cast(&%s_methodinfo)" % accessor) michael@0: if m.get("allowCrossOriginThis", False): michael@0: if m.get("returnsPromise", False): michael@0: raise TypeError("%s returns a Promise but should " michael@0: "be allowed cross-origin?" % michael@0: accessor) michael@0: accessor = "genericCrossOriginMethod" michael@0: elif self.descriptor.needsSpecialGenericOps(): michael@0: if m.get("returnsPromise", False): michael@0: raise TypeError("%s returns a Promise but needs " michael@0: "special generic ops?" % michael@0: accessor) michael@0: accessor = "genericMethod" michael@0: elif m.get("returnsPromise", False): michael@0: accessor = "GenericPromiseReturningBindingMethod" michael@0: else: michael@0: accessor = "GenericBindingMethod" michael@0: else: michael@0: if m.get("returnsPromise", False): michael@0: jitinfo = "&%s_methodinfo" % accessor michael@0: accessor = "StaticMethodPromiseWrapper" michael@0: else: michael@0: jitinfo = "nullptr" michael@0: michael@0: return (m["name"], accessor, jitinfo, m["length"], m["flags"], selfHostedName) michael@0: michael@0: return self.generatePrefableArray( michael@0: array, name, michael@0: ' JS_FNSPEC("%s", %s, %s, %s, %s, %s)', michael@0: ' JS_FS_END', michael@0: 'JSFunctionSpec', michael@0: condition, specData, doIdArrays) michael@0: michael@0: michael@0: class AttrDefiner(PropertyDefiner): michael@0: def __init__(self, descriptor, name, static, unforgeable=False): michael@0: assert not (static and unforgeable) michael@0: PropertyDefiner.__init__(self, descriptor, name) michael@0: self.name = name michael@0: # Ignore non-static attributes for interfaces without a proto object michael@0: if descriptor.interface.hasInterfacePrototypeObject() or static: michael@0: attributes = [m for m in descriptor.interface.members if michael@0: m.isAttr() and m.isStatic() == static and michael@0: m.isUnforgeable() == unforgeable] michael@0: else: michael@0: attributes = [] michael@0: self.chrome = [m for m in attributes if isChromeOnly(m)] michael@0: self.regular = [m for m in attributes if not isChromeOnly(m)] michael@0: self.static = static michael@0: self.unforgeable = unforgeable michael@0: michael@0: if static: michael@0: if not descriptor.interface.hasInterfaceObject(): michael@0: # static attributes go on the interface object michael@0: assert not self.hasChromeOnly() and not self.hasNonChromeOnly() michael@0: else: michael@0: if not descriptor.interface.hasInterfacePrototypeObject(): michael@0: # non-static attributes go on the interface prototype object michael@0: assert not self.hasChromeOnly() and not self.hasNonChromeOnly() michael@0: michael@0: def generateArray(self, array, name, doIdArrays): michael@0: if len(array) == 0: michael@0: return "" michael@0: michael@0: def flags(attr): michael@0: unforgeable = " | JSPROP_PERMANENT" if self.unforgeable else "" michael@0: return ("JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS" + michael@0: unforgeable) michael@0: michael@0: def getter(attr): michael@0: if self.static: michael@0: accessor = 'get_' + attr.identifier.name michael@0: jitinfo = "nullptr" michael@0: else: michael@0: if attr.hasLenientThis(): michael@0: accessor = "genericLenientGetter" michael@0: elif attr.getExtendedAttribute("CrossOriginReadable"): michael@0: accessor = "genericCrossOriginGetter" michael@0: elif self.descriptor.needsSpecialGenericOps(): michael@0: accessor = "genericGetter" michael@0: else: michael@0: accessor = "GenericBindingGetter" michael@0: jitinfo = "&%s_getterinfo" % attr.identifier.name michael@0: return "{ { JS_CAST_NATIVE_TO(%s, JSPropertyOp), %s } }" % \ michael@0: (accessor, jitinfo) michael@0: michael@0: def setter(attr): michael@0: if (attr.readonly and michael@0: attr.getExtendedAttribute("PutForwards") is None and michael@0: attr.getExtendedAttribute("Replaceable") is None): michael@0: return "JSOP_NULLWRAPPER" michael@0: if self.static: michael@0: accessor = 'set_' + attr.identifier.name michael@0: jitinfo = "nullptr" michael@0: else: michael@0: if attr.hasLenientThis(): michael@0: accessor = "genericLenientSetter" michael@0: elif attr.getExtendedAttribute("CrossOriginWritable"): michael@0: accessor = "genericCrossOriginSetter" michael@0: elif self.descriptor.needsSpecialGenericOps(): michael@0: accessor = "genericSetter" michael@0: else: michael@0: accessor = "GenericBindingSetter" michael@0: jitinfo = "&%s_setterinfo" % attr.identifier.name michael@0: return "{ { JS_CAST_NATIVE_TO(%s, JSStrictPropertyOp), %s } }" % \ michael@0: (accessor, jitinfo) michael@0: michael@0: def specData(attr): michael@0: return (attr.identifier.name, flags(attr), getter(attr), michael@0: setter(attr)) michael@0: michael@0: return self.generatePrefableArray( michael@0: array, name, michael@0: ' { "%s", %s, %s, %s}', michael@0: ' JS_PS_END', michael@0: 'JSPropertySpec', michael@0: PropertyDefiner.getControllingCondition, specData, doIdArrays) michael@0: michael@0: michael@0: class ConstDefiner(PropertyDefiner): michael@0: """ michael@0: A class for definining constants on the interface object michael@0: """ michael@0: def __init__(self, descriptor, name): michael@0: PropertyDefiner.__init__(self, descriptor, name) michael@0: self.name = name michael@0: constants = [m for m in descriptor.interface.members if m.isConst()] michael@0: self.chrome = [m for m in constants if isChromeOnly(m)] michael@0: self.regular = [m for m in constants if not isChromeOnly(m)] michael@0: michael@0: def generateArray(self, array, name, doIdArrays): michael@0: if len(array) == 0: michael@0: return "" michael@0: michael@0: def specData(const): michael@0: return (const.identifier.name, michael@0: convertConstIDLValueToJSVal(const.value)) michael@0: michael@0: return self.generatePrefableArray( michael@0: array, name, michael@0: ' { "%s", %s }', michael@0: ' { 0, JS::UndefinedValue() }', michael@0: 'ConstantSpec', michael@0: PropertyDefiner.getControllingCondition, specData, doIdArrays) michael@0: michael@0: michael@0: class PropertyArrays(): michael@0: def __init__(self, descriptor): michael@0: self.staticMethods = MethodDefiner(descriptor, "StaticMethods", michael@0: static=True) michael@0: self.staticAttrs = AttrDefiner(descriptor, "StaticAttributes", michael@0: static=True) michael@0: self.methods = MethodDefiner(descriptor, "Methods", static=False) michael@0: self.attrs = AttrDefiner(descriptor, "Attributes", static=False) michael@0: self.unforgeableAttrs = AttrDefiner(descriptor, "UnforgeableAttributes", michael@0: static=False, unforgeable=True) michael@0: self.consts = ConstDefiner(descriptor, "Constants") michael@0: michael@0: @staticmethod michael@0: def arrayNames(): michael@0: return ["staticMethods", "staticAttrs", "methods", "attrs", michael@0: "unforgeableAttrs", "consts"] michael@0: michael@0: def hasChromeOnly(self): michael@0: return any(getattr(self, a).hasChromeOnly() for a in self.arrayNames()) michael@0: michael@0: def hasNonChromeOnly(self): michael@0: return any(getattr(self, a).hasNonChromeOnly() for a in self.arrayNames()) michael@0: michael@0: def __str__(self): michael@0: define = "" michael@0: for array in self.arrayNames(): michael@0: define += str(getattr(self, array)) michael@0: return define michael@0: michael@0: michael@0: class CGNativeProperties(CGList): michael@0: def __init__(self, descriptor, properties): michael@0: def generateNativeProperties(name, chrome): michael@0: def check(p): michael@0: return p.hasChromeOnly() if chrome else p.hasNonChromeOnly() michael@0: michael@0: nativeProps = [] michael@0: for array in properties.arrayNames(): michael@0: propertyArray = getattr(properties, array) michael@0: if check(propertyArray): michael@0: if propertyArray.usedForXrays(): michael@0: ids = "%(name)s_ids" michael@0: else: michael@0: ids = "nullptr" michael@0: props = "%(name)s, " + ids + ", %(name)s_specs" michael@0: props = (props % {'name': propertyArray.variableName(chrome)}) michael@0: else: michael@0: props = "nullptr, nullptr, nullptr" michael@0: nativeProps.append(CGGeneric(props)) michael@0: return CGWrapper(CGIndenter(CGList(nativeProps, ",\n")), michael@0: pre="static const NativeProperties %s = {\n" % name, michael@0: post="\n};\n") michael@0: michael@0: nativeProperties = [] michael@0: if properties.hasNonChromeOnly(): michael@0: nativeProperties.append( michael@0: generateNativeProperties("sNativeProperties", False)) michael@0: if properties.hasChromeOnly(): michael@0: nativeProperties.append( michael@0: generateNativeProperties("sChromeOnlyNativeProperties", True)) michael@0: michael@0: CGList.__init__(self, nativeProperties, "\n") michael@0: michael@0: def declare(self): michael@0: return "" michael@0: michael@0: def define(self): michael@0: # BOGUSly strip off a newline michael@0: return CGList.define(self).rstrip() michael@0: michael@0: michael@0: class CGCreateInterfaceObjectsMethod(CGAbstractMethod): michael@0: """ michael@0: Generate the CreateInterfaceObjects method for an interface descriptor. michael@0: michael@0: properties should be a PropertyArrays instance. michael@0: """ michael@0: def __init__(self, descriptor, properties): michael@0: args = [Argument('JSContext*', 'aCx'), michael@0: Argument('JS::Handle', 'aGlobal'), michael@0: Argument('ProtoAndIfaceCache&', 'aProtoAndIfaceCache'), michael@0: Argument('bool', 'aDefineOnGlobal')] michael@0: CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'void', args) michael@0: self.properties = properties michael@0: michael@0: def definition_body(self): michael@0: if len(self.descriptor.prototypeChain) == 1: michael@0: parentProtoType = "Rooted" michael@0: if self.descriptor.interface.getExtendedAttribute("ArrayClass"): michael@0: getParentProto = "aCx, JS_GetArrayPrototype(aCx, aGlobal)" michael@0: else: michael@0: getParentProto = "aCx, JS_GetObjectPrototype(aCx, aGlobal)" michael@0: else: michael@0: parentProtoName = self.descriptor.prototypeChain[-2] michael@0: parentDesc = self.descriptor.getDescriptor(parentProtoName) michael@0: if parentDesc.workers: michael@0: parentProtoName += '_workers' michael@0: getParentProto = ("%s::GetProtoObject(aCx, aGlobal)" % michael@0: toBindingNamespace(parentProtoName)) michael@0: parentProtoType = "Handle" michael@0: michael@0: parentWithInterfaceObject = self.descriptor.interface.parent michael@0: while (parentWithInterfaceObject and michael@0: not parentWithInterfaceObject.hasInterfaceObject()): michael@0: parentWithInterfaceObject = parentWithInterfaceObject.parent michael@0: if parentWithInterfaceObject: michael@0: parentIfaceName = parentWithInterfaceObject.identifier.name michael@0: parentDesc = self.descriptor.getDescriptor(parentIfaceName) michael@0: if parentDesc.workers: michael@0: parentIfaceName += "_workers" michael@0: getConstructorProto = ("%s::GetConstructorObject(aCx, aGlobal)" % michael@0: toBindingNamespace(parentIfaceName)) michael@0: constructorProtoType = "Handle" michael@0: else: michael@0: getConstructorProto = "aCx, JS_GetFunctionPrototype(aCx, aGlobal)" michael@0: constructorProtoType = "Rooted" michael@0: michael@0: needInterfaceObject = self.descriptor.interface.hasInterfaceObject() michael@0: needInterfacePrototypeObject = self.descriptor.interface.hasInterfacePrototypeObject() michael@0: michael@0: # if we don't need to create anything, why are we generating this? michael@0: assert needInterfaceObject or needInterfacePrototypeObject michael@0: michael@0: idsToInit = [] michael@0: # There is no need to init any IDs in workers, because worker bindings michael@0: # don't have Xrays. michael@0: if not self.descriptor.workers: michael@0: for var in self.properties.arrayNames(): michael@0: props = getattr(self.properties, var) michael@0: # We only have non-chrome ids to init if we have no chrome ids. michael@0: if props.hasChromeOnly(): michael@0: idsToInit.append(props.variableName(True)) michael@0: if props.hasNonChromeOnly(): michael@0: idsToInit.append(props.variableName(False)) michael@0: if len(idsToInit) > 0: michael@0: initIdCalls = ["!InitIds(aCx, %s, %s_ids)" % (varname, varname) michael@0: for varname in idsToInit] michael@0: idsInitedFlag = CGGeneric("static bool sIdsInited = false;\n") michael@0: setFlag = CGGeneric("sIdsInited = true;\n") michael@0: initIdConditionals = [CGIfWrapper(CGGeneric("return;\n"), call) michael@0: for call in initIdCalls] michael@0: initIds = CGList([idsInitedFlag, michael@0: CGIfWrapper(CGList(initIdConditionals + [setFlag]), michael@0: "!sIdsInited && NS_IsMainThread()")]) michael@0: else: michael@0: initIds = None michael@0: michael@0: prefCacheData = [] michael@0: for var in self.properties.arrayNames(): michael@0: props = getattr(self.properties, var) michael@0: prefCacheData.extend(props.prefCacheData) michael@0: if len(prefCacheData) != 0: michael@0: prefCacheData = [ michael@0: CGGeneric('Preferences::AddBoolVarCache(%s, "%s");\n' % (ptr, pref)) michael@0: for pref, ptr in prefCacheData] michael@0: prefCache = CGWrapper(CGIndenter(CGList(prefCacheData)), michael@0: pre=("static bool sPrefCachesInited = false;\n" michael@0: "if (!sPrefCachesInited) {\n" michael@0: " sPrefCachesInited = true;\n"), michael@0: post="}\n") michael@0: else: michael@0: prefCache = None michael@0: michael@0: if UseHolderForUnforgeable(self.descriptor): michael@0: createUnforgeableHolder = CGGeneric(dedent(""" michael@0: JS::Rooted unforgeableHolder(aCx, michael@0: JS_NewObjectWithGivenProto(aCx, nullptr, JS::NullPtr(), JS::NullPtr())); michael@0: if (!unforgeableHolder) { michael@0: return; michael@0: } michael@0: """)) michael@0: defineUnforgeables = InitUnforgeablePropertiesOnObject(self.descriptor, michael@0: "unforgeableHolder", michael@0: self.properties) michael@0: createUnforgeableHolder = CGList( michael@0: [createUnforgeableHolder, defineUnforgeables]) michael@0: else: michael@0: createUnforgeableHolder = None michael@0: michael@0: getParentProto = fill( michael@0: """ michael@0: JS::${type} parentProto(${getParentProto}); michael@0: if (!parentProto) { michael@0: return; michael@0: } michael@0: """, michael@0: type=parentProtoType, michael@0: getParentProto=getParentProto) michael@0: michael@0: getConstructorProto = fill( michael@0: """ michael@0: JS::${type} constructorProto(${getConstructorProto}); michael@0: if (!constructorProto) { michael@0: return; michael@0: } michael@0: """, michael@0: type=constructorProtoType, michael@0: getConstructorProto=getConstructorProto) michael@0: michael@0: if (needInterfaceObject and michael@0: self.descriptor.needsConstructHookHolder()): michael@0: constructHookHolder = "&" + CONSTRUCT_HOOK_NAME + "_holder" michael@0: else: michael@0: constructHookHolder = "nullptr" michael@0: if self.descriptor.interface.ctor(): michael@0: constructArgs = methodLength(self.descriptor.interface.ctor()) michael@0: else: michael@0: constructArgs = 0 michael@0: if len(self.descriptor.interface.namedConstructors) > 0: michael@0: namedConstructors = "namedConstructors" michael@0: else: michael@0: namedConstructors = "nullptr" michael@0: michael@0: if needInterfacePrototypeObject: michael@0: protoClass = "&PrototypeClass.mBase" michael@0: protoCache = "&aProtoAndIfaceCache.EntrySlotOrCreate(prototypes::id::%s)" % self.descriptor.name michael@0: else: michael@0: protoClass = "nullptr" michael@0: protoCache = "nullptr" michael@0: if needInterfaceObject: michael@0: interfaceClass = "&InterfaceObjectClass.mBase" michael@0: interfaceCache = "&aProtoAndIfaceCache.EntrySlotOrCreate(constructors::id::%s)" % self.descriptor.name michael@0: else: michael@0: # We don't have slots to store the named constructors. michael@0: assert len(self.descriptor.interface.namedConstructors) == 0 michael@0: interfaceClass = "nullptr" michael@0: interfaceCache = "nullptr" michael@0: michael@0: if self.descriptor.concrete: michael@0: domClass = "&Class.mClass" michael@0: else: michael@0: domClass = "nullptr" michael@0: michael@0: if self.properties.hasNonChromeOnly(): michael@0: properties = "&sNativeProperties" michael@0: else: michael@0: properties = "nullptr" michael@0: if self.properties.hasChromeOnly(): michael@0: accessCheck = "nsContentUtils::ThreadsafeIsCallerChrome()" michael@0: chromeProperties = accessCheck + " ? &sChromeOnlyNativeProperties : nullptr" michael@0: else: michael@0: chromeProperties = "nullptr" michael@0: michael@0: call = fill( michael@0: """ michael@0: dom::CreateInterfaceObjects(aCx, aGlobal, parentProto, michael@0: ${protoClass}, ${protoCache}, michael@0: constructorProto, ${interfaceClass}, ${constructHookHolder}, ${constructArgs}, ${namedConstructors}, michael@0: ${interfaceCache}, michael@0: ${domClass}, michael@0: ${properties}, michael@0: ${chromeProperties}, michael@0: ${name}, aDefineOnGlobal); michael@0: """, michael@0: protoClass=protoClass, michael@0: protoCache=protoCache, michael@0: interfaceClass=interfaceClass, michael@0: constructHookHolder=constructHookHolder, michael@0: constructArgs=constructArgs, michael@0: namedConstructors=namedConstructors, michael@0: interfaceCache=interfaceCache, michael@0: domClass=domClass, michael@0: properties=properties, michael@0: chromeProperties=chromeProperties, michael@0: name='"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "nullptr") michael@0: michael@0: if UseHolderForUnforgeable(self.descriptor): michael@0: assert needInterfacePrototypeObject michael@0: setUnforgeableHolder = CGGeneric(fill( michael@0: """ michael@0: JSObject* proto = aProtoAndIfaceCache.EntrySlotOrCreate(prototypes::id::${name}); michael@0: if (proto) { michael@0: js::SetReservedSlot(proto, DOM_INTERFACE_PROTO_SLOTS_BASE, michael@0: JS::ObjectValue(*unforgeableHolder)); michael@0: } michael@0: """, michael@0: name=self.descriptor.name)) michael@0: else: michael@0: setUnforgeableHolder = None michael@0: functionBody = CGList( michael@0: [CGGeneric(getParentProto), CGGeneric(getConstructorProto), initIds, michael@0: prefCache, createUnforgeableHolder, CGGeneric(call), setUnforgeableHolder], michael@0: "\n") michael@0: return CGIndenter(functionBody).define() michael@0: michael@0: michael@0: class CGGetPerInterfaceObject(CGAbstractMethod): michael@0: """ michael@0: A method for getting a per-interface object (a prototype object or interface michael@0: constructor object). michael@0: """ michael@0: def __init__(self, descriptor, name, idPrefix="", extraArgs=[]): michael@0: args = [Argument('JSContext*', 'aCx'), michael@0: Argument('JS::Handle', 'aGlobal')] + extraArgs michael@0: CGAbstractMethod.__init__(self, descriptor, name, michael@0: 'JS::Handle', args) michael@0: self.id = idPrefix + "id::" + self.descriptor.name michael@0: michael@0: def definition_body(self): michael@0: # BOGUS extra blank line at the beginning of the code below michael@0: # BOGUS - should be a blank line between an if-block and following comment below michael@0: return indent(fill( michael@0: """ michael@0: michael@0: /* Make sure our global is sane. Hopefully we can remove this sometime */ michael@0: if (!(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL)) { michael@0: return JS::NullPtr(); michael@0: } michael@0: /* Check to see whether the interface objects are already installed */ michael@0: ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(aGlobal); michael@0: if (!protoAndIfaceCache.EntrySlotIfExists(${id})) { michael@0: CreateInterfaceObjects(aCx, aGlobal, protoAndIfaceCache, aDefineOnGlobal); michael@0: } michael@0: michael@0: /* michael@0: * The object might _still_ be null, but that's OK. michael@0: * michael@0: * Calling fromMarkedLocation() is safe because protoAndIfaceCache is michael@0: * traced by TraceProtoAndIfaceCache() and its contents are never michael@0: * changed after they have been set. michael@0: */ michael@0: return JS::Handle::fromMarkedLocation(protoAndIfaceCache.EntrySlotMustExist(${id}).address()); michael@0: """, michael@0: id=self.id)) michael@0: michael@0: michael@0: class CGGetProtoObjectMethod(CGGetPerInterfaceObject): michael@0: """ michael@0: A method for getting the interface prototype object. michael@0: """ michael@0: def __init__(self, descriptor): michael@0: CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject", michael@0: "prototypes::") michael@0: michael@0: def definition_body(self): michael@0: # BOGUS extra blank line at start of method michael@0: return indent(dedent(""" michael@0: michael@0: /* Get the interface prototype object for this class. This will create the michael@0: object as needed. */ michael@0: bool aDefineOnGlobal = true; michael@0: """)) + CGGetPerInterfaceObject.definition_body(self) michael@0: michael@0: michael@0: class CGGetConstructorObjectMethod(CGGetPerInterfaceObject): michael@0: """ michael@0: A method for getting the interface constructor object. michael@0: """ michael@0: def __init__(self, descriptor): michael@0: CGGetPerInterfaceObject.__init__( michael@0: self, descriptor, "GetConstructorObject", michael@0: "constructors::", michael@0: extraArgs=[Argument("bool", "aDefineOnGlobal", "true")]) michael@0: michael@0: def definition_body(self): michael@0: # BOGUS extra blank line at start of method michael@0: return indent(dedent(""" michael@0: michael@0: /* Get the interface object for this class. This will create the object as michael@0: needed. */ michael@0: """)) + CGGetPerInterfaceObject.definition_body(self) michael@0: michael@0: michael@0: class CGDefineDOMInterfaceMethod(CGAbstractMethod): michael@0: """ michael@0: A method for resolve hooks to try to lazily define the interface object for michael@0: a given interface. michael@0: """ michael@0: def __init__(self, descriptor): michael@0: args = [Argument('JSContext*', 'aCx'), michael@0: Argument('JS::Handle', 'aGlobal'), michael@0: Argument('JS::Handle', 'id'), michael@0: Argument('bool', 'aDefineOnGlobal')] michael@0: CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface', 'JSObject*', args) michael@0: michael@0: def declare(self): michael@0: if self.descriptor.workers: michael@0: return '' michael@0: return CGAbstractMethod.declare(self) michael@0: michael@0: def define(self): michael@0: if self.descriptor.workers: michael@0: return '' michael@0: return CGAbstractMethod.define(self) michael@0: michael@0: def definition_body(self): michael@0: if len(self.descriptor.interface.namedConstructors) > 0: michael@0: getConstructor = indent(dedent(""" michael@0: JSObject* interfaceObject = GetConstructorObject(aCx, aGlobal, aDefineOnGlobal); michael@0: if (!interfaceObject) { michael@0: return nullptr; michael@0: } michael@0: for (unsigned slot = DOM_INTERFACE_SLOTS_BASE; slot < JSCLASS_RESERVED_SLOTS(&InterfaceObjectClass.mBase); ++slot) { michael@0: JSObject* constructor = &js::GetReservedSlot(interfaceObject, slot).toObject(); michael@0: if (JS_GetFunctionId(JS_GetObjectFunction(constructor)) == JSID_TO_STRING(id)) { michael@0: return constructor; michael@0: } michael@0: } michael@0: return interfaceObject; michael@0: """)) michael@0: else: michael@0: getConstructor = " return GetConstructorObject(aCx, aGlobal, aDefineOnGlobal);\n" michael@0: return getConstructor michael@0: michael@0: michael@0: class CGConstructorEnabled(CGAbstractMethod): michael@0: """ michael@0: A method for testing whether we should be exposing this interface michael@0: object or navigator property. This can perform various tests michael@0: depending on what conditions are specified on the interface. michael@0: """ michael@0: def __init__(self, descriptor): michael@0: CGAbstractMethod.__init__(self, descriptor, michael@0: 'ConstructorEnabled', 'bool', michael@0: [Argument("JSContext*", "aCx"), michael@0: Argument("JS::Handle", "aObj")]) michael@0: michael@0: def definition_body(self): michael@0: conditions = [] michael@0: iface = self.descriptor.interface michael@0: pref = iface.getExtendedAttribute("Pref") michael@0: if pref: michael@0: assert isinstance(pref, list) and len(pref) == 1 michael@0: conditions.append('Preferences::GetBool("%s")' % pref[0]) michael@0: if iface.getExtendedAttribute("ChromeOnly"): michael@0: conditions.append("nsContentUtils::ThreadsafeIsCallerChrome()") michael@0: func = iface.getExtendedAttribute("Func") michael@0: if func: michael@0: assert isinstance(func, list) and len(func) == 1 michael@0: conditions.append("%s(aCx, aObj)" % func[0]) michael@0: availableIn = getAvailableInTestFunc(iface) michael@0: if availableIn: michael@0: conditions.append("%s(aCx, aObj)" % availableIn) michael@0: # We should really have some conditions michael@0: assert len(conditions) michael@0: body = CGWrapper(CGList((CGGeneric(cond) for cond in conditions), michael@0: " &&\n"), michael@0: pre="return ", post=";\n", reindent=True) michael@0: return CGIndenter(body).define() michael@0: michael@0: michael@0: def CreateBindingJSObject(descriptor, properties, parent): michael@0: # We don't always need to root obj, but there are a variety michael@0: # of cases where we do, so for simplicity, just always root it. michael@0: objDecl = "JS::Rooted obj(aCx);\n" michael@0: if descriptor.proxy: michael@0: create = fill( michael@0: """ michael@0: JS::Rooted proxyPrivateVal(aCx, JS::PrivateValue(aObject)); michael@0: js::ProxyOptions options; michael@0: options.setClass(&Class.mBase); michael@0: obj = NewProxyObject(aCx, DOMProxyHandler::getInstance(), michael@0: proxyPrivateVal, proto, ${parent}, options); michael@0: if (!obj) { michael@0: return nullptr; michael@0: } michael@0: michael@0: """, michael@0: parent=parent) michael@0: if descriptor.interface.getExtendedAttribute('OverrideBuiltins'): michael@0: create += dedent(""" michael@0: js::SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, michael@0: JS::PrivateValue(&aObject->mExpandoAndGeneration)); michael@0: michael@0: """) michael@0: else: michael@0: create = fill( michael@0: """ michael@0: obj = JS_NewObject(aCx, Class.ToJSClass(), proto, ${parent}); michael@0: if (!obj) { michael@0: return nullptr; michael@0: } michael@0: michael@0: js::SetReservedSlot(obj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(aObject)); michael@0: """, michael@0: parent=parent) michael@0: if "Window" in descriptor.interface.identifier.name: michael@0: create = dedent(""" michael@0: MOZ_ASSERT(false, michael@0: "Our current reserved slot situation is unsafe for globals. Fix " michael@0: "bug 760095!"); michael@0: """) + create michael@0: create = objDecl + create michael@0: michael@0: if descriptor.nativeOwnership == 'refcounted': michael@0: create += "NS_ADDREF(aObject);\n" michael@0: else: michael@0: create += dedent(""" michael@0: // Make sure the native objects inherit from NonRefcountedDOMObject so that we michael@0: // log their ctor and dtor. michael@0: MustInheritFromNonRefcountedDOMObject(aObject); michael@0: *aTookOwnership = true; michael@0: """) michael@0: return create michael@0: michael@0: michael@0: def InitUnforgeablePropertiesOnObject(descriptor, obj, properties, failureReturnValue=""): michael@0: """ michael@0: properties is a PropertyArrays instance michael@0: """ michael@0: defineUnforgeables = fill( michael@0: """ michael@0: if (!DefineUnforgeableAttributes(aCx, ${obj}, %s)) { michael@0: return${rv}; michael@0: } michael@0: """, michael@0: obj=obj, michael@0: rv=" " + failureReturnValue if failureReturnValue else "") michael@0: michael@0: unforgeableAttrs = properties.unforgeableAttrs michael@0: unforgeables = [] michael@0: if unforgeableAttrs.hasNonChromeOnly(): michael@0: unforgeables.append(CGGeneric(defineUnforgeables % michael@0: unforgeableAttrs.variableName(False))) michael@0: if unforgeableAttrs.hasChromeOnly(): michael@0: unforgeables.append( michael@0: CGIfWrapper(CGGeneric(defineUnforgeables % michael@0: unforgeableAttrs.variableName(True)), michael@0: "nsContentUtils::ThreadsafeIsCallerChrome()")) michael@0: return CGList(unforgeables) michael@0: michael@0: michael@0: def InitUnforgeableProperties(descriptor, properties): michael@0: """ michael@0: properties is a PropertyArrays instance michael@0: """ michael@0: unforgeableAttrs = properties.unforgeableAttrs michael@0: if not unforgeableAttrs.hasNonChromeOnly() and not unforgeableAttrs.hasChromeOnly(): michael@0: return "" michael@0: michael@0: if descriptor.proxy: michael@0: unforgeableProperties = CGGeneric( michael@0: "// Unforgeable properties on proxy-based bindings are stored in an object held\n" michael@0: "// by the interface prototype object.\n" michael@0: "\n") # BOGUS extra blank line michael@0: else: michael@0: unforgeableProperties = CGWrapper( michael@0: InitUnforgeablePropertiesOnObject(descriptor, "obj", properties, "nullptr"), michael@0: pre=( michael@0: "// Important: do unforgeable property setup after we have handed\n" michael@0: "// over ownership of the C++ object to obj as needed, so that if\n" michael@0: "// we fail and it ends up GCed it won't have problems in the\n" michael@0: "// finalizer trying to drop its ownership of the C++ object.\n")) michael@0: return CGWrapper(unforgeableProperties, pre="\n").define() michael@0: michael@0: michael@0: def AssertInheritanceChain(descriptor): michael@0: asserts = "" michael@0: iface = descriptor.interface michael@0: while iface: michael@0: desc = descriptor.getDescriptor(iface.identifier.name) michael@0: asserts += ( michael@0: " MOZ_ASSERT(static_cast<%s*>(aObject) == \n" michael@0: " reinterpret_cast<%s*>(aObject));\n" % michael@0: (desc.nativeType, desc.nativeType)) michael@0: iface = iface.parent michael@0: asserts += " MOZ_ASSERT(ToSupportsIsCorrect(aObject));\n" michael@0: return asserts michael@0: michael@0: michael@0: def InitMemberSlots(descriptor, wrapperCache): michael@0: """ michael@0: Initialize member slots on our JS object if we're supposed to have some. michael@0: michael@0: Note that this is called after the SetWrapper() call in the michael@0: wrapperCache case, since that can affect how our getters behave michael@0: and we plan to invoke them here. So if we fail, we need to michael@0: ClearWrapper. michael@0: """ michael@0: if not descriptor.interface.hasMembersInSlots(): michael@0: return "\n" # BOGUS blank line only if this returns empty michael@0: if wrapperCache: michael@0: clearWrapper = " aCache->ClearWrapper();\n" michael@0: else: michael@0: clearWrapper = "" michael@0: return ("if (!UpdateMemberSlots(aCx, obj, aObject)) {\n" michael@0: "%s" michael@0: " return nullptr;\n" michael@0: "}\n" % clearWrapper) michael@0: michael@0: michael@0: class CGWrapWithCacheMethod(CGAbstractMethod): michael@0: """ michael@0: Create a wrapper JSObject for a given native that implements nsWrapperCache. michael@0: michael@0: properties should be a PropertyArrays instance. michael@0: """ michael@0: def __init__(self, descriptor, properties): michael@0: assert descriptor.interface.hasInterfacePrototypeObject() michael@0: args = [Argument('JSContext*', 'aCx'), michael@0: Argument(descriptor.nativeType + '*', 'aObject'), michael@0: Argument('nsWrapperCache*', 'aCache')] michael@0: CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args) michael@0: self.properties = properties michael@0: michael@0: def definition_body(self): michael@0: return fill( michael@0: """ michael@0: ${assertion} michael@0: MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache), michael@0: "nsISupports must be on our primary inheritance chain"); michael@0: michael@0: JS::Rooted parent(aCx, michael@0: GetRealParentObject(aObject, michael@0: WrapNativeParent(aCx, aObject->GetParentObject()))); michael@0: if (!parent) { michael@0: return nullptr; michael@0: } michael@0: michael@0: // That might have ended up wrapping us already, due to the wonders michael@0: // of XBL. Check for that, and bail out as needed. Scope so we don't michael@0: // collide with the "obj" we declare in CreateBindingJSObject. michael@0: { michael@0: JSObject* obj = aCache->GetWrapper(); michael@0: if (obj) { michael@0: return obj; michael@0: } michael@0: } michael@0: michael@0: JSAutoCompartment ac(aCx, parent); michael@0: JS::Rooted global(aCx, JS_GetGlobalForObject(aCx, parent)); michael@0: JS::Handle proto = GetProtoObject(aCx, global); michael@0: if (!proto) { michael@0: return nullptr; michael@0: } michael@0: michael@0: $*{parent} michael@0: michael@0: $*{unforgeable} michael@0: michael@0: aCache->SetWrapper(obj); michael@0: $*{slots} michael@0: return obj; michael@0: """, michael@0: assertion=AssertInheritanceChain(self.descriptor), michael@0: parent=CreateBindingJSObject(self.descriptor, self.properties, michael@0: "parent"), michael@0: unforgeable=InitUnforgeableProperties(self.descriptor, self.properties), michael@0: slots=InitMemberSlots(self.descriptor, True)) michael@0: michael@0: michael@0: class CGWrapMethod(CGAbstractMethod): michael@0: def __init__(self, descriptor): michael@0: # XXX can we wrap if we don't have an interface prototype object? michael@0: assert descriptor.interface.hasInterfacePrototypeObject() michael@0: args = [Argument('JSContext*', 'aCx'), michael@0: Argument('T*', 'aObject')] michael@0: CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args, michael@0: inline=True, templateArgs=["class T"]) michael@0: michael@0: def definition_body(self): michael@0: return " return Wrap(aCx, aObject, aObject);\n" michael@0: michael@0: michael@0: class CGWrapNonWrapperCacheMethod(CGAbstractMethod): michael@0: """ michael@0: Create a wrapper JSObject for a given native that does not implement michael@0: nsWrapperCache. michael@0: michael@0: properties should be a PropertyArrays instance. michael@0: """ michael@0: def __init__(self, descriptor, properties): michael@0: # XXX can we wrap if we don't have an interface prototype object? michael@0: assert descriptor.interface.hasInterfacePrototypeObject() michael@0: args = [Argument('JSContext*', 'aCx'), michael@0: Argument(descriptor.nativeType + '*', 'aObject')] michael@0: if descriptor.nativeOwnership == 'owned': michael@0: args.append(Argument('bool*', 'aTookOwnership')) michael@0: CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args) michael@0: self.properties = properties michael@0: michael@0: def definition_body(self): michael@0: return fill( michael@0: """ michael@0: ${assertions} michael@0: JS::Rooted global(aCx, JS::CurrentGlobalOrNull(aCx)); michael@0: JS::Handle proto = GetProtoObject(aCx, global); michael@0: if (!proto) { michael@0: return nullptr; michael@0: } michael@0: michael@0: $*{global_} michael@0: michael@0: $*{unforgeable} michael@0: michael@0: $*{slots} michael@0: return obj; michael@0: """, michael@0: assertions=AssertInheritanceChain(self.descriptor), michael@0: global_=CreateBindingJSObject(self.descriptor, self.properties, michael@0: "global"), michael@0: unforgeable=InitUnforgeableProperties(self.descriptor, self.properties), michael@0: slots=InitMemberSlots(self.descriptor, False)) michael@0: michael@0: michael@0: class CGWrapGlobalMethod(CGAbstractMethod): michael@0: """ michael@0: Create a wrapper JSObject for a global. The global must implement michael@0: nsWrapperCache. michael@0: michael@0: properties should be a PropertyArrays instance. michael@0: """ michael@0: def __init__(self, descriptor, properties): michael@0: assert descriptor.interface.hasInterfacePrototypeObject() michael@0: args = [Argument('JSContext*', 'aCx'), michael@0: Argument(descriptor.nativeType + '*', 'aObject'), michael@0: Argument('nsWrapperCache*', 'aCache'), michael@0: Argument('JS::CompartmentOptions&', 'aOptions'), michael@0: Argument('JSPrincipals*', 'aPrincipal')] michael@0: CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args) michael@0: self.descriptor = descriptor michael@0: self.properties = properties michael@0: michael@0: def definition_body(self): michael@0: return fill( michael@0: """ michael@0: ${assertions} michael@0: MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache), michael@0: "nsISupports must be on our primary inheritance chain"); michael@0: michael@0: JS::Rooted obj(aCx); michael@0: obj = CreateGlobal<${nativeType}, GetProtoObject>(aCx, michael@0: aObject, michael@0: aCache, michael@0: Class.ToJSClass(), michael@0: aOptions, michael@0: aPrincipal); michael@0: michael@0: $*{unforgeable} michael@0: michael@0: $*{slots} michael@0: michael@0: // XXXkhuey can't do this yet until workers can lazy resolve. michael@0: // JS_FireOnNewGlobalObject(aCx, obj); michael@0: michael@0: return obj; michael@0: """, michael@0: assertions=AssertInheritanceChain(self.descriptor), michael@0: nativeType=self.descriptor.nativeType, michael@0: unforgeable=InitUnforgeableProperties(self.descriptor, self.properties), michael@0: slots=InitMemberSlots(self.descriptor, True)) michael@0: michael@0: michael@0: class CGUpdateMemberSlotsMethod(CGAbstractStaticMethod): michael@0: def __init__(self, descriptor): michael@0: args = [Argument('JSContext*', 'aCx'), michael@0: Argument('JS::Handle', 'aWrapper'), michael@0: Argument(descriptor.nativeType + '*', 'aObject')] michael@0: CGAbstractStaticMethod.__init__(self, descriptor, 'UpdateMemberSlots', 'bool', args) michael@0: michael@0: def definition_body(self): michael@0: body = ("JS::Rooted temp(aCx);\n" michael@0: "JSJitGetterCallArgs args(&temp);\n") michael@0: for m in self.descriptor.interface.members: michael@0: if m.isAttr() and m.getExtendedAttribute("StoreInSlot"): michael@0: body += fill( michael@0: """ michael@0: michael@0: static_assert(${slot} < js::shadow::Object::MAX_FIXED_SLOTS, michael@0: "Not enough fixed slots to fit '${interface}.${member}'"); michael@0: if (!get_${member}(aCx, aWrapper, aObject, args)) { michael@0: return false; michael@0: } michael@0: // Getter handled setting our reserved slots michael@0: """, michael@0: slot=memberReservedSlot(m), michael@0: interface=self.descriptor.interface.identifier.name, michael@0: member=m.identifier.name) michael@0: michael@0: body += "\nreturn true;\n" michael@0: return indent(body) michael@0: michael@0: michael@0: class CGClearCachedValueMethod(CGAbstractMethod): michael@0: def __init__(self, descriptor, member): michael@0: self.member = member michael@0: # If we're StoreInSlot, we'll need to call the getter michael@0: if member.getExtendedAttribute("StoreInSlot"): michael@0: args = [Argument('JSContext*', 'aCx')] michael@0: returnType = 'bool' michael@0: else: michael@0: args = [] michael@0: returnType = 'void' michael@0: args.append(Argument(descriptor.nativeType + '*', 'aObject')) michael@0: name = ("ClearCached%sValue" % MakeNativeName(member.identifier.name)) michael@0: CGAbstractMethod.__init__(self, descriptor, name, returnType, args) michael@0: michael@0: def definition_body(self): michael@0: slotIndex = memberReservedSlot(self.member) michael@0: if self.member.getExtendedAttribute("StoreInSlot"): michael@0: # We have to root things and save the old value in case michael@0: # regetting fails, so we can restore it. michael@0: declObj = "JS::Rooted obj(aCx);\n" michael@0: noopRetval = " true" michael@0: saveMember = ( michael@0: "JS::Rooted oldValue(aCx, js::GetReservedSlot(obj, %s));\n" % michael@0: slotIndex) michael@0: regetMember = fill( michael@0: """ michael@0: JS::Rooted temp(aCx); michael@0: JSJitGetterCallArgs args(&temp); michael@0: JSAutoCompartment ac(aCx, obj); michael@0: if (!get_${name}(aCx, obj, aObject, args)) { michael@0: js::SetReservedSlot(obj, ${slotIndex}, oldValue); michael@0: nsJSUtils::ReportPendingException(aCx); michael@0: return false; michael@0: } michael@0: return true; michael@0: """, michael@0: name=self.member.identifier.name, michael@0: slotIndex=slotIndex) michael@0: else: michael@0: declObj = "JSObject* obj;\n" michael@0: noopRetval = "" michael@0: saveMember = "" michael@0: regetMember = "" michael@0: michael@0: return indent(fill( michael@0: """ michael@0: $*{declObj} michael@0: obj = aObject->GetWrapper(); michael@0: if (!obj) { michael@0: return${noopRetval}; michael@0: } michael@0: $*{saveMember} michael@0: js::SetReservedSlot(obj, ${slotIndex}, JS::UndefinedValue()); michael@0: $*{regetMember} michael@0: """, michael@0: declObj=declObj, michael@0: noopRetval=noopRetval, michael@0: saveMember=saveMember, michael@0: slotIndex=slotIndex, michael@0: regetMember=regetMember)) michael@0: michael@0: michael@0: class CGIsPermittedMethod(CGAbstractMethod): michael@0: """ michael@0: crossOriginGetters/Setters/Methods are sets of names of the relevant members. michael@0: """ michael@0: def __init__(self, descriptor, crossOriginGetters, crossOriginSetters, michael@0: crossOriginMethods): michael@0: self.crossOriginGetters = crossOriginGetters michael@0: self.crossOriginSetters = crossOriginSetters michael@0: self.crossOriginMethods = crossOriginMethods michael@0: args = [Argument("JSFlatString*", "prop"), michael@0: Argument("jschar", "propFirstChar"), michael@0: Argument("bool", "set")] michael@0: CGAbstractMethod.__init__(self, descriptor, "IsPermitted", "bool", args, michael@0: inline=True) michael@0: michael@0: def definition_body(self): michael@0: allNames = self.crossOriginGetters | self.crossOriginSetters | self.crossOriginMethods michael@0: readwrite = self.crossOriginGetters & self.crossOriginSetters michael@0: readonly = (self.crossOriginGetters - self.crossOriginSetters) | self.crossOriginMethods michael@0: writeonly = self.crossOriginSetters - self.crossOriginGetters michael@0: cases = {} michael@0: for name in sorted(allNames): michael@0: cond = 'JS_FlatStringEqualsAscii(prop, "%s")' % name michael@0: if name in readonly: michael@0: cond = "!set && %s" % cond michael@0: elif name in writeonly: michael@0: cond = "set && %s" % cond michael@0: else: michael@0: assert name in readwrite michael@0: firstLetter = name[0] michael@0: case = cases.get(firstLetter, CGList([])) michael@0: case.append(CGGeneric("if (%s) {\n" michael@0: " return true;\n" michael@0: "}\n" % cond)) michael@0: cases[firstLetter] = case michael@0: caseList = [] michael@0: for firstLetter in sorted(cases.keys()): michael@0: caseList.append(CGCase("'%s'" % firstLetter, cases[firstLetter])) michael@0: switch = CGSwitch("propFirstChar", caseList) michael@0: return indent(switch.define() + "\nreturn false;\n") michael@0: michael@0: builtinNames = { michael@0: IDLType.Tags.bool: 'bool', michael@0: IDLType.Tags.int8: 'int8_t', michael@0: IDLType.Tags.int16: 'int16_t', michael@0: IDLType.Tags.int32: 'int32_t', michael@0: IDLType.Tags.int64: 'int64_t', michael@0: IDLType.Tags.uint8: 'uint8_t', michael@0: IDLType.Tags.uint16: 'uint16_t', michael@0: IDLType.Tags.uint32: 'uint32_t', michael@0: IDLType.Tags.uint64: 'uint64_t', michael@0: IDLType.Tags.unrestricted_float: 'float', michael@0: IDLType.Tags.float: 'float', michael@0: IDLType.Tags.unrestricted_double: 'double', michael@0: IDLType.Tags.double: 'double' michael@0: } michael@0: michael@0: numericSuffixes = { michael@0: IDLType.Tags.int8: '', michael@0: IDLType.Tags.uint8: '', michael@0: IDLType.Tags.int16: '', michael@0: IDLType.Tags.uint16: '', michael@0: IDLType.Tags.int32: '', michael@0: IDLType.Tags.uint32: 'U', michael@0: IDLType.Tags.int64: 'LL', michael@0: IDLType.Tags.uint64: 'ULL', michael@0: IDLType.Tags.unrestricted_float: 'F', michael@0: IDLType.Tags.float: 'F', michael@0: IDLType.Tags.unrestricted_double: '', michael@0: IDLType.Tags.double: '' michael@0: } michael@0: michael@0: michael@0: def numericValue(t, v): michael@0: if (t == IDLType.Tags.unrestricted_double or michael@0: t == IDLType.Tags.unrestricted_float): michael@0: typeName = builtinNames[t] michael@0: if v == float("inf"): michael@0: return "mozilla::PositiveInfinity<%s>()" % typeName michael@0: if v == float("-inf"): michael@0: return "mozilla::NegativeInfinity<%s>()" % typeName michael@0: if math.isnan(v): michael@0: return "mozilla::UnspecifiedNaN<%s>()" % typeName michael@0: return "%s%s" % (v, numericSuffixes[t]) michael@0: michael@0: michael@0: class CastableObjectUnwrapper(): michael@0: """ michael@0: A class for unwrapping an object named by the "source" argument michael@0: based on the passed-in descriptor and storing it in a variable michael@0: called by the name in the "target" argument. michael@0: michael@0: codeOnFailure is the code to run if unwrapping fails. michael@0: michael@0: If isCallbackReturnValue is "JSImpl" and our descriptor is also michael@0: JS-implemented, fall back to just creating the right object if what we michael@0: have isn't one already. michael@0: michael@0: If allowCrossOriginObj is True, then we'll first do an michael@0: UncheckedUnwrap and then operate on the result. michael@0: """ michael@0: def __init__(self, descriptor, source, target, codeOnFailure, michael@0: exceptionCode=None, isCallbackReturnValue=False, michael@0: allowCrossOriginObj=False): michael@0: self.substitution = { michael@0: "type": descriptor.nativeType, michael@0: "protoID": "prototypes::id::" + descriptor.name, michael@0: "target": target, michael@0: "codeOnFailure": codeOnFailure, michael@0: } michael@0: if allowCrossOriginObj: michael@0: self.substitution["uncheckedObjDecl"] = ( michael@0: "JS::Rooted uncheckedObj(cx, js::UncheckedUnwrap(%s));\n" % source) michael@0: self.substitution["source"] = "uncheckedObj" michael@0: xpconnectUnwrap = dedent(""" michael@0: nsresult rv; michael@0: { // Scope for the JSAutoCompartment, because we only michael@0: // want to be in that compartment for the UnwrapArg call. michael@0: JSAutoCompartment ac(cx, ${source}); michael@0: rv = UnwrapArg<${type}>(cx, val, &objPtr, &objRef.ptr, &val); michael@0: } michael@0: """) michael@0: else: michael@0: self.substitution["uncheckedObjDecl"] = "" michael@0: self.substitution["source"] = source michael@0: xpconnectUnwrap = "nsresult rv = UnwrapArg<${type}>(cx, val, &objPtr, &objRef.ptr, &val);\n" michael@0: michael@0: if descriptor.hasXPConnectImpls: michael@0: # We don't use xpc_qsUnwrapThis because it will always throw on michael@0: # unwrap failure, whereas we want to control whether we throw or michael@0: # not. michael@0: self.substitution["codeOnFailure"] = string.Template( michael@0: "${type} *objPtr;\n" michael@0: "SelfRef objRef;\n" michael@0: "JS::Rooted val(cx, JS::ObjectValue(*${source}));\n" + michael@0: xpconnectUnwrap + michael@0: "if (NS_FAILED(rv)) {\n" michael@0: "${indentedCodeOnFailure}" michael@0: "}\n" michael@0: "// We should be castable!\n" michael@0: "MOZ_ASSERT(!objRef.ptr);\n" michael@0: "// We should have an object, too!\n" michael@0: "MOZ_ASSERT(objPtr);\n" michael@0: "${target} = objPtr;\n" michael@0: ).substitute(self.substitution, michael@0: indentedCodeOnFailure=indent(codeOnFailure)) michael@0: elif (isCallbackReturnValue == "JSImpl" and michael@0: descriptor.interface.isJSImplemented()): michael@0: exceptionCode = exceptionCode or codeOnFailure michael@0: self.substitution["codeOnFailure"] = fill( michael@0: """ michael@0: // Be careful to not wrap random DOM objects here, even if michael@0: // they're wrapped in opaque security wrappers for some reason. michael@0: // XXXbz Wish we could check for a JS-implemented object michael@0: // that already has a content reflection... michael@0: if (!IsDOMObject(js::UncheckedUnwrap(${source}))) { michael@0: nsCOMPtr ourWindow; michael@0: if (!GetWindowForJSImplementedObject(cx, Callback(), getter_AddRefs(ourWindow))) { michael@0: $*{exceptionCode} michael@0: } michael@0: JS::Rooted jsImplSourceObj(cx, ${source}); michael@0: ${target} = new ${type}(jsImplSourceObj, ourWindow); michael@0: } else { michael@0: $*{codeOnFailure} michael@0: } michael@0: """, michael@0: exceptionCode=exceptionCode, michael@0: **self.substitution) michael@0: else: michael@0: self.substitution["codeOnFailure"] = codeOnFailure michael@0: michael@0: def __str__(self): michael@0: substitution = self.substitution.copy() michael@0: substitution["codeOnFailure"] %= { michael@0: 'securityError': 'rv == NS_ERROR_XPC_SECURITY_MANAGER_VETO' michael@0: } michael@0: return fill( michael@0: """ michael@0: { michael@0: $*{uncheckedObjDecl} michael@0: nsresult rv = UnwrapObject<${protoID}, ${type}>(${source}, ${target}); michael@0: if (NS_FAILED(rv)) { michael@0: $*{codeOnFailure} michael@0: } michael@0: } michael@0: """, michael@0: **substitution) michael@0: michael@0: michael@0: class FailureFatalCastableObjectUnwrapper(CastableObjectUnwrapper): michael@0: """ michael@0: As CastableObjectUnwrapper, but defaulting to throwing if unwrapping fails michael@0: """ michael@0: def __init__(self, descriptor, source, target, exceptionCode, michael@0: isCallbackReturnValue, sourceDescription): michael@0: CastableObjectUnwrapper.__init__( michael@0: self, descriptor, source, target, michael@0: 'ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s", "%s");\n' michael@0: '%s' % (sourceDescription, descriptor.interface.identifier.name, michael@0: exceptionCode), michael@0: exceptionCode, michael@0: isCallbackReturnValue) michael@0: michael@0: michael@0: class CGCallbackTempRoot(CGGeneric): michael@0: def __init__(self, name): michael@0: define = dedent(""" michael@0: { // Scope for tempRoot michael@0: JS::Rooted tempRoot(cx, &${val}.toObject()); michael@0: ${declName} = new %s(tempRoot, mozilla::dom::GetIncumbentGlobal()); michael@0: } michael@0: """) % name michael@0: CGGeneric.__init__(self, define=define) michael@0: michael@0: michael@0: class JSToNativeConversionInfo(): michael@0: """ michael@0: An object representing information about a JS-to-native conversion. michael@0: """ michael@0: def __init__(self, template, declType=None, holderType=None, michael@0: dealWithOptional=False, declArgs=None, michael@0: holderArgs=None): michael@0: """ michael@0: template: A string representing the conversion code. This will have michael@0: template substitution performed on it as follows: michael@0: michael@0: ${val} is a handle to the JS::Value in question michael@0: ${mutableVal} is a mutable handle to the JS::Value in question michael@0: ${holderName} replaced by the holder's name, if any michael@0: ${declName} replaced by the declaration's name michael@0: ${haveValue} replaced by an expression that evaluates to a boolean michael@0: for whether we have a JS::Value. Only used when michael@0: defaultValue is not None or when True is passed for michael@0: checkForValue to instantiateJSToNativeConversion. michael@0: michael@0: declType: A CGThing representing the native C++ type we're converting michael@0: to. This is allowed to be None if the conversion code is michael@0: supposed to be used as-is. michael@0: michael@0: holderType: A CGThing representing the type of a "holder" which will michael@0: hold a possible reference to the C++ thing whose type we michael@0: returned in declType, or None if no such holder is needed. michael@0: michael@0: dealWithOptional: A boolean indicating whether the caller has to do michael@0: optional-argument handling. This should only be set michael@0: to true if the JS-to-native conversion is being done michael@0: for an optional argument or dictionary member with no michael@0: default value and if the returned template expects michael@0: both declType and holderType to be wrapped in michael@0: Optional<>, with ${declName} and ${holderName} michael@0: adjusted to point to the Value() of the Optional, and michael@0: Construct() calls to be made on the Optional<>s as michael@0: needed. michael@0: michael@0: declArgs: If not None, the arguments to pass to the ${declName} michael@0: constructor. These will have template substitution performed michael@0: on them so you can use things like ${val}. This is a michael@0: single string, not a list of strings. michael@0: michael@0: holderArgs: If not None, the arguments to pass to the ${holderName} michael@0: constructor. These will have template substitution michael@0: performed on them so you can use things like ${val}. michael@0: This is a single string, not a list of strings. michael@0: michael@0: ${declName} must be in scope before the code from 'template' is entered. michael@0: michael@0: If holderType is not None then ${holderName} must be in scope before michael@0: the code from 'template' is entered. michael@0: """ michael@0: assert isinstance(template, str) michael@0: assert declType is None or isinstance(declType, CGThing) michael@0: assert holderType is None or isinstance(holderType, CGThing) michael@0: self.template = template michael@0: self.declType = declType michael@0: self.holderType = holderType michael@0: self.dealWithOptional = dealWithOptional michael@0: self.declArgs = declArgs michael@0: self.holderArgs = holderArgs michael@0: michael@0: michael@0: def getHandleDefault(defaultValue): michael@0: tag = defaultValue.type.tag() michael@0: if tag in numericSuffixes: michael@0: # Some numeric literals require a suffix to compile without warnings michael@0: return numericValue(tag, defaultValue.value) michael@0: assert tag == IDLType.Tags.bool michael@0: return toStringBool(defaultValue.value) michael@0: michael@0: michael@0: def handleDefaultStringValue(defaultValue, method): michael@0: """ michael@0: Returns a string which ends up calling 'method' with a (char16_t*, length) michael@0: pair that sets this string default value. This string is suitable for michael@0: passing as the second argument of handleDefault; in particular it does not michael@0: end with a ';' michael@0: """ michael@0: assert defaultValue.type.isDOMString() michael@0: return ("static const char16_t data[] = { %s };\n" michael@0: "%s(data, ArrayLength(data) - 1)" % michael@0: (", ".join(["'" + char + "'" for char in michael@0: defaultValue.value] + ["0"]), michael@0: method)) michael@0: michael@0: michael@0: # If this function is modified, modify CGNativeMember.getArg and michael@0: # CGNativeMember.getRetvalInfo accordingly. The latter cares about the decltype michael@0: # and holdertype we end up using, because it needs to be able to return the code michael@0: # that will convert those to the actual return value of the callback function. michael@0: def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, michael@0: isDefinitelyObject=False, michael@0: isMember=False, michael@0: isOptional=False, michael@0: invalidEnumValueFatal=True, michael@0: defaultValue=None, michael@0: treatNullAs="Default", michael@0: isEnforceRange=False, michael@0: isClamp=False, michael@0: isNullOrUndefined=False, michael@0: exceptionCode=None, michael@0: lenientFloatCode=None, michael@0: allowTreatNonCallableAsNull=False, michael@0: isCallbackReturnValue=False, michael@0: sourceDescription="value"): michael@0: """ michael@0: Get a template for converting a JS value to a native object based on the michael@0: given type and descriptor. If failureCode is given, then we're actually michael@0: testing whether we can convert the argument to the desired type. That michael@0: means that failures to convert due to the JS value being the wrong type of michael@0: value need to use failureCode instead of throwing exceptions. Failures to michael@0: convert that are due to JS exceptions (from toString or valueOf methods) or michael@0: out of memory conditions need to throw exceptions no matter what michael@0: failureCode is. However what actually happens when throwing an exception michael@0: can be controlled by exceptionCode. The only requirement on that is that michael@0: exceptionCode must end up doing a return, and every return from this michael@0: function must happen via exceptionCode if exceptionCode is not None. michael@0: michael@0: If isDefinitelyObject is True, that means we know the value michael@0: isObject() and we have no need to recheck that. michael@0: michael@0: if isMember is not False, we're being converted from a property of some JS michael@0: object, not from an actual method argument, so we can't rely on our jsval michael@0: being rooted or outliving us in any way. Callers can pass "Dictionary", michael@0: "Variadic", "Sequence", or "OwningUnion" to indicate that the conversion is michael@0: for something that is a dictionary member, a variadic argument, a sequence, michael@0: or an owning union respectively. michael@0: michael@0: If isOptional is true, then we are doing conversion of an optional michael@0: argument with no default value. michael@0: michael@0: invalidEnumValueFatal controls whether an invalid enum value conversion michael@0: attempt will throw (if true) or simply return without doing anything (if michael@0: false). michael@0: michael@0: If defaultValue is not None, it's the IDL default value for this conversion michael@0: michael@0: If isEnforceRange is true, we're converting an integer and throwing if the michael@0: value is out of range. michael@0: michael@0: If isClamp is true, we're converting an integer and clamping if the michael@0: value is out of range. michael@0: michael@0: If lenientFloatCode is not None, it should be used in cases when michael@0: we're a non-finite float that's not unrestricted. michael@0: michael@0: If allowTreatNonCallableAsNull is true, then [TreatNonCallableAsNull] and michael@0: [TreatNonObjectAsNull] extended attributes on nullable callback functions michael@0: will be honored. michael@0: michael@0: If isCallbackReturnValue is "JSImpl" or "Callback", then the declType may be michael@0: adjusted to make it easier to return from a callback. Since that type is michael@0: never directly observable by any consumers of the callback code, this is OK. michael@0: Furthermore, if isCallbackReturnValue is "JSImpl", that affects the behavior michael@0: of the FailureFatalCastableObjectUnwrapper conversion; this is used for michael@0: implementing auto-wrapping of JS-implemented return values from a michael@0: JS-implemented interface. michael@0: michael@0: sourceDescription is a description of what this JS value represents, to be michael@0: used in error reporting. Callers should assume that it might get placed in michael@0: the middle of a sentence. If it ends up at the beginning of a sentence, its michael@0: first character will be automatically uppercased. michael@0: michael@0: The return value from this function is a JSToNativeConversionInfo. michael@0: """ michael@0: # If we have a defaultValue then we're not actually optional for michael@0: # purposes of what we need to be declared as. michael@0: assert defaultValue is None or not isOptional michael@0: michael@0: # Also, we should not have a defaultValue if we know we're an object michael@0: assert not isDefinitelyObject or defaultValue is None michael@0: michael@0: # And we can't both be an object and be null or undefined michael@0: assert not isDefinitelyObject or not isNullOrUndefined michael@0: michael@0: # If exceptionCode is not set, we'll just rethrow the exception we got. michael@0: # Note that we can't just set failureCode to exceptionCode, because setting michael@0: # failureCode will prevent pending exceptions from being set in cases when michael@0: # they really should be! michael@0: if exceptionCode is None: michael@0: exceptionCode = "return false;\n" michael@0: # We often want exceptionCode to be indented, since it often appears in an michael@0: # if body. michael@0: exceptionCodeIndented = CGIndenter(CGGeneric(exceptionCode)) michael@0: michael@0: # Unfortunately, .capitalize() on a string will lowercase things inside the michael@0: # string, which we do not want. michael@0: def firstCap(string): michael@0: return string[0].upper() + string[1:] michael@0: michael@0: # Helper functions for dealing with failures due to the JS value being the michael@0: # wrong type of value michael@0: def onFailureNotAnObject(failureCode): michael@0: return CGGeneric( michael@0: failureCode or michael@0: ('ThrowErrorMessage(cx, MSG_NOT_OBJECT, "%s");\n' michael@0: '%s' % (firstCap(sourceDescription), exceptionCode))) michael@0: michael@0: def onFailureBadType(failureCode, typeName): michael@0: return CGGeneric( michael@0: failureCode or michael@0: ('ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s", "%s");\n' michael@0: '%s' % (firstCap(sourceDescription), typeName, exceptionCode))) michael@0: michael@0: def onFailureNotCallable(failureCode): michael@0: return CGGeneric( michael@0: failureCode or michael@0: ('ThrowErrorMessage(cx, MSG_NOT_CALLABLE, "%s");\n' michael@0: '%s' % (firstCap(sourceDescription), exceptionCode))) michael@0: michael@0: # A helper function for handling default values. Takes a template michael@0: # body and the C++ code to set the default value and wraps the michael@0: # given template body in handling for the default value. michael@0: def handleDefault(template, setDefault): michael@0: if defaultValue is None: michael@0: return template michael@0: return ( michael@0: "if (${haveValue}) {\n" + michael@0: indent(template) + michael@0: "} else {\n" + michael@0: indent(setDefault) + michael@0: "}\n") michael@0: michael@0: # A helper function for handling null default values. Much like michael@0: # handleDefault, but checks that the default value, if it exists, is null. michael@0: def handleDefaultNull(template, codeToSetNull): michael@0: if (defaultValue is not None and michael@0: not isinstance(defaultValue, IDLNullValue)): michael@0: raise TypeError("Can't handle non-null default value here") michael@0: return handleDefault(template, codeToSetNull) michael@0: michael@0: # A helper function for wrapping up the template body for michael@0: # possibly-nullable objecty stuff michael@0: def wrapObjectTemplate(templateBody, type, codeToSetNull, failureCode=None): michael@0: if isNullOrUndefined and type.nullable(): michael@0: # Just ignore templateBody and set ourselves to null. michael@0: # Note that we don't have to worry about default values michael@0: # here either, since we already examined this value. michael@0: return codeToSetNull michael@0: michael@0: if not isDefinitelyObject: michael@0: # Handle the non-object cases by wrapping up the whole michael@0: # thing in an if cascade. michael@0: if type.nullable(): michael@0: elifLine = "} else if (${val}.isNullOrUndefined()) {\n" michael@0: elifBody = codeToSetNull michael@0: else: michael@0: elifLine = "" michael@0: elifBody = "" michael@0: michael@0: # Note that $${val} below expands to ${val}. This string is michael@0: # used as a template later, and val will be filled in then. michael@0: templateBody = fill( michael@0: """ michael@0: if ($${val}.isObject()) { michael@0: $*{templateBody} michael@0: $*{elifLine} michael@0: $*{elifBody} michael@0: } else { michael@0: $*{failureBody} michael@0: } michael@0: """, michael@0: templateBody=templateBody, michael@0: elifLine=elifLine, michael@0: elifBody=elifBody, michael@0: failureBody=onFailureNotAnObject(failureCode).define()) michael@0: michael@0: if type.nullable(): michael@0: templateBody = handleDefaultNull(templateBody, codeToSetNull) michael@0: else: michael@0: assert defaultValue is None michael@0: michael@0: return templateBody michael@0: michael@0: # A helper function for converting things that look like a JSObject*. michael@0: def handleJSObjectType(type, isMember, failureCode): michael@0: if not isMember: michael@0: if isOptional: michael@0: # We have a specialization of Optional that will use a michael@0: # Rooted for the storage here. michael@0: declType = CGGeneric("JS::Handle") michael@0: else: michael@0: declType = CGGeneric("JS::Rooted") michael@0: declArgs = "cx" michael@0: else: michael@0: assert (isMember in michael@0: ("Sequence", "Variadic", "Dictionary", "OwningUnion", "MozMap")) michael@0: # We'll get traced by the sequence or dictionary or union tracer michael@0: declType = CGGeneric("JSObject*") michael@0: declArgs = None michael@0: templateBody = "${declName} = &${val}.toObject();\n" michael@0: setToNullCode = "${declName} = nullptr;\n" michael@0: template = wrapObjectTemplate(templateBody, type, setToNullCode, michael@0: failureCode) michael@0: return JSToNativeConversionInfo(template, declType=declType, michael@0: dealWithOptional=isOptional, michael@0: declArgs=declArgs) michael@0: michael@0: assert not (isEnforceRange and isClamp) # These are mutually exclusive michael@0: michael@0: if type.isArray(): michael@0: raise TypeError("Can't handle array arguments yet") michael@0: michael@0: if type.isSequence(): michael@0: assert not isEnforceRange and not isClamp michael@0: michael@0: if failureCode is None: michael@0: notSequence = ('ThrowErrorMessage(cx, MSG_NOT_SEQUENCE, "%s");\n' michael@0: "%s" % (firstCap(sourceDescription), exceptionCode)) michael@0: else: michael@0: notSequence = failureCode michael@0: michael@0: nullable = type.nullable() michael@0: # Be very careful not to change "type": we need it later michael@0: if nullable: michael@0: elementType = type.inner.inner michael@0: else: michael@0: elementType = type.inner michael@0: michael@0: # We want to use auto arrays if we can, but we have to be careful with michael@0: # reallocation behavior for arrays. In particular, if we use auto michael@0: # arrays for sequences and have a sequence of elements which are michael@0: # themselves sequences or have sequences as members, we have a problem. michael@0: # In that case, resizing the outermost nsAutoTarray to the right size michael@0: # will memmove its elements, but nsAutoTArrays are not memmovable and michael@0: # hence will end up with pointers to bogus memory, which is bad. To michael@0: # deal with this, we typically map WebIDL sequences to our Sequence michael@0: # type, which is in fact memmovable. The one exception is when we're michael@0: # passing in a sequence directly as an argument without any sort of michael@0: # optional or nullable complexity going on. In that situation, we can michael@0: # use an AutoSequence instead. We have to keep using Sequence in the michael@0: # nullable and optional cases because we don't want to leak the michael@0: # AutoSequence type to consumers, which would be unavoidable with michael@0: # Nullable or Optional. michael@0: if isMember or isOptional or nullable or isCallbackReturnValue: michael@0: sequenceClass = "Sequence" michael@0: else: michael@0: sequenceClass = "binding_detail::AutoSequence" michael@0: michael@0: # XXXbz we can't include the index in the sourceDescription, because michael@0: # we don't really have a way to pass one in dynamically at runtime... michael@0: elementInfo = getJSToNativeConversionInfo( michael@0: elementType, descriptorProvider, isMember="Sequence", michael@0: exceptionCode=exceptionCode, lenientFloatCode=lenientFloatCode, michael@0: isCallbackReturnValue=isCallbackReturnValue, michael@0: sourceDescription="element of %s" % sourceDescription) michael@0: if elementInfo.dealWithOptional: michael@0: raise TypeError("Shouldn't have optional things in sequences") michael@0: if elementInfo.holderType is not None: michael@0: raise TypeError("Shouldn't need holders for sequences") michael@0: michael@0: typeName = CGTemplatedType(sequenceClass, elementInfo.declType) michael@0: sequenceType = typeName.define() michael@0: if nullable: michael@0: typeName = CGTemplatedType("Nullable", typeName) michael@0: arrayRef = "${declName}.SetValue()" michael@0: else: michael@0: arrayRef = "${declName}" michael@0: michael@0: elementConversion = string.Template(elementInfo.template).substitute({ michael@0: "val": "temp", michael@0: "mutableVal": "&temp", michael@0: "declName": "slot", michael@0: # We only need holderName here to handle isExternal() michael@0: # interfaces, which use an internal holder for the michael@0: # conversion even when forceOwningType ends up true. michael@0: "holderName": "tempHolder" michael@0: }) michael@0: michael@0: # NOTE: Keep this in sync with variadic conversions as needed michael@0: templateBody = fill( michael@0: """ michael@0: JS::ForOfIterator iter(cx); michael@0: if (!iter.init($${val}, JS::ForOfIterator::AllowNonIterable)) { michael@0: $*{exceptionCode} michael@0: } michael@0: if (!iter.valueIsIterable()) { michael@0: $*{notSequence} michael@0: } michael@0: ${sequenceType} &arr = ${arrayRef}; michael@0: JS::Rooted temp(cx); michael@0: while (true) { michael@0: bool done; michael@0: if (!iter.next(&temp, &done)) { michael@0: $*{exceptionCode} michael@0: } michael@0: if (done) { michael@0: break; michael@0: } michael@0: ${elementType}* slotPtr = arr.AppendElement(); michael@0: if (!slotPtr) { michael@0: JS_ReportOutOfMemory(cx); michael@0: $*{exceptionCode} michael@0: } michael@0: ${elementType}& slot = *slotPtr; michael@0: $*{elementConversion} michael@0: } michael@0: """, michael@0: exceptionCode=exceptionCode, michael@0: notSequence=notSequence, michael@0: sequenceType=sequenceType, michael@0: arrayRef=arrayRef, michael@0: elementType=elementInfo.declType.define(), michael@0: elementConversion=elementConversion) michael@0: michael@0: templateBody = wrapObjectTemplate(templateBody, type, michael@0: "${declName}.SetNull();\n", notSequence) michael@0: # Sequence arguments that might contain traceable things need michael@0: # to get traced michael@0: if not isMember and typeNeedsRooting(elementType): michael@0: holderType = CGTemplatedType("SequenceRooter", elementInfo.declType) michael@0: # If our sequence is nullable, this will set the Nullable to be michael@0: # not-null, but that's ok because we make an explicit SetNull() call michael@0: # on it as needed if our JS value is actually null. michael@0: holderArgs = "cx, &%s" % arrayRef michael@0: else: michael@0: holderType = None michael@0: holderArgs = None michael@0: michael@0: return JSToNativeConversionInfo(templateBody, declType=typeName, michael@0: holderType=holderType, michael@0: dealWithOptional=isOptional, michael@0: holderArgs=holderArgs) michael@0: michael@0: if type.isMozMap(): michael@0: assert not isEnforceRange and not isClamp michael@0: if failureCode is None: michael@0: notMozMap = ('ThrowErrorMessage(cx, MSG_NOT_OBJECT, "%s");\n' michael@0: "%s" % (firstCap(sourceDescription), exceptionCode)) michael@0: else: michael@0: notMozMap = failureCode michael@0: michael@0: nullable = type.nullable() michael@0: # Be very careful not to change "type": we need it later michael@0: if nullable: michael@0: valueType = type.inner.inner michael@0: else: michael@0: valueType = type.inner michael@0: michael@0: valueInfo = getJSToNativeConversionInfo( michael@0: valueType, descriptorProvider, isMember="MozMap", michael@0: exceptionCode=exceptionCode, lenientFloatCode=lenientFloatCode, michael@0: isCallbackReturnValue=isCallbackReturnValue, michael@0: sourceDescription="value in %s" % sourceDescription) michael@0: if valueInfo.dealWithOptional: michael@0: raise TypeError("Shouldn't have optional things in MozMap") michael@0: if valueInfo.holderType is not None: michael@0: raise TypeError("Shouldn't need holders for MozMap") michael@0: michael@0: typeName = CGTemplatedType("MozMap", valueInfo.declType) michael@0: mozMapType = typeName.define() michael@0: if nullable: michael@0: typeName = CGTemplatedType("Nullable", typeName) michael@0: mozMapRef = "${declName}.SetValue()" michael@0: else: michael@0: mozMapRef = "${declName}" michael@0: michael@0: valueConversion = string.Template(valueInfo.template).substitute({ michael@0: "val": "temp", michael@0: "mutableVal": "&temp", michael@0: "declName": "slot", michael@0: # We only need holderName here to handle isExternal() michael@0: # interfaces, which use an internal holder for the michael@0: # conversion even when forceOwningType ends up true. michael@0: "holderName": "tempHolder" michael@0: }) michael@0: michael@0: templateBody = fill( michael@0: """ michael@0: ${mozMapType} &mozMap = ${mozMapRef}; michael@0: michael@0: JS::Rooted mozMapObj(cx, &$${val}.toObject()); michael@0: JS::AutoIdArray ids(cx, JS_Enumerate(cx, mozMapObj)); michael@0: if (!ids) { michael@0: $*{exceptionCode} michael@0: } michael@0: JS::Rooted propNameValue(cx); michael@0: JS::Rooted temp(cx); michael@0: JS::Rooted curId(cx); michael@0: for (size_t i = 0; i < ids.length(); ++i) { michael@0: // Make sure we get the value before converting the name, since michael@0: // getting the value can trigger GC but our name is a dependent michael@0: // string. michael@0: curId = ids[i]; michael@0: binding_detail::FakeDependentString propName; michael@0: if (!JS_GetPropertyById(cx, mozMapObj, curId, &temp) || michael@0: !JS_IdToValue(cx, curId, &propNameValue) || michael@0: !ConvertJSValueToString(cx, propNameValue, &propNameValue, michael@0: eStringify, eStringify, propName)) { michael@0: $*{exceptionCode} michael@0: } michael@0: michael@0: ${valueType}* slotPtr = mozMap.AddEntry(propName); michael@0: if (!slotPtr) { michael@0: JS_ReportOutOfMemory(cx); michael@0: $*{exceptionCode} michael@0: } michael@0: ${valueType}& slot = *slotPtr; michael@0: $*{valueConversion} michael@0: } michael@0: """, michael@0: exceptionCode=exceptionCode, michael@0: mozMapType=mozMapType, michael@0: mozMapRef=mozMapRef, michael@0: valueType=valueInfo.declType.define(), michael@0: valueConversion=valueConversion) michael@0: michael@0: templateBody = wrapObjectTemplate(templateBody, type, michael@0: "${declName}.SetNull();\n", michael@0: notMozMap) michael@0: michael@0: declType = typeName michael@0: declArgs = None michael@0: holderType = None michael@0: holderArgs = None michael@0: # MozMap arguments that might contain traceable things need michael@0: # to get traced michael@0: if not isMember and isCallbackReturnValue: michael@0: # Go ahead and just convert directly into our actual return value michael@0: declType = CGWrapper(declType, post="&") michael@0: declArgs = "aRetVal" michael@0: elif not isMember and typeNeedsRooting(valueType): michael@0: holderType = CGTemplatedType("MozMapRooter", valueInfo.declType) michael@0: # If our MozMap is nullable, this will set the Nullable to be michael@0: # not-null, but that's ok because we make an explicit SetNull() call michael@0: # on it as needed if our JS value is actually null. michael@0: holderArgs = "cx, &%s" % mozMapRef michael@0: michael@0: return JSToNativeConversionInfo(templateBody, declType=declType, michael@0: declArgs=declArgs, michael@0: holderType=holderType, michael@0: dealWithOptional=isOptional, michael@0: holderArgs=holderArgs) michael@0: michael@0: if type.isUnion(): michael@0: nullable = type.nullable() michael@0: if nullable: michael@0: type = type.inner michael@0: michael@0: unionArgumentObj = "${declName}" if isMember else "${holderName}" michael@0: if nullable: michael@0: # If we're a member, we're a Nullable, which hasn't been told it has michael@0: # a value. Otherwise we're an already-constructed Maybe. michael@0: unionArgumentObj += ".SetValue()" if isMember else ".ref()" michael@0: michael@0: memberTypes = type.flatMemberTypes michael@0: names = [] michael@0: michael@0: interfaceMemberTypes = filter(lambda t: t.isNonCallbackInterface(), memberTypes) michael@0: if len(interfaceMemberTypes) > 0: michael@0: interfaceObject = [] michael@0: for memberType in interfaceMemberTypes: michael@0: if type.isGeckoInterface(): michael@0: name = memberType.inner.identifier.name michael@0: else: michael@0: name = memberType.name michael@0: interfaceObject.append( michael@0: CGGeneric("(failed = !%s.TrySetTo%s(cx, ${val}, ${mutableVal}, tryNext)) || !tryNext" % michael@0: (unionArgumentObj, name))) michael@0: names.append(name) michael@0: interfaceObject = CGWrapper(CGList(interfaceObject, " ||\n"), michael@0: pre="done = ", post=";\n\n", reindent=True) michael@0: else: michael@0: interfaceObject = None michael@0: michael@0: arrayObjectMemberTypes = filter(lambda t: t.isArray() or t.isSequence(), memberTypes) michael@0: if len(arrayObjectMemberTypes) > 0: michael@0: raise TypeError("Bug 767924: We don't support sequences in unions yet") michael@0: else: michael@0: arrayObject = None michael@0: michael@0: dateObjectMemberTypes = filter(lambda t: t.isDate(), memberTypes) michael@0: if len(dateObjectMemberTypes) > 0: michael@0: assert len(dateObjectMemberTypes) == 1 michael@0: memberType = dateObjectMemberTypes[0] michael@0: name = memberType.name michael@0: dateObject = CGGeneric("%s.SetTo%s(cx, ${val}, ${mutableVal});\n" michael@0: "done = true;\n" % (unionArgumentObj, name)) michael@0: dateObject = CGIfWrapper(dateObject, "JS_ObjectIsDate(cx, argObj)") michael@0: names.append(name) michael@0: else: michael@0: dateObject = None michael@0: michael@0: callbackMemberTypes = filter(lambda t: t.isCallback() or t.isCallbackInterface(), memberTypes) michael@0: if len(callbackMemberTypes) > 0: michael@0: assert len(callbackMemberTypes) == 1 michael@0: memberType = callbackMemberTypes[0] michael@0: name = memberType.name michael@0: callbackObject = CGGeneric( michael@0: "done = (failed = !%s.TrySetTo%s(cx, ${val}, ${mutableVal}, tryNext)) || !tryNext;\n" % michael@0: (unionArgumentObj, name)) michael@0: names.append(name) michael@0: else: michael@0: callbackObject = None michael@0: michael@0: dictionaryMemberTypes = filter(lambda t: t.isDictionary(), memberTypes) michael@0: if len(dictionaryMemberTypes) > 0: michael@0: assert len(dictionaryMemberTypes) == 1 michael@0: name = dictionaryMemberTypes[0].inner.identifier.name michael@0: setDictionary = CGGeneric( michael@0: "done = (failed = !%s.TrySetTo%s(cx, ${val}, ${mutableVal}, tryNext)) || !tryNext;\n" % michael@0: (unionArgumentObj, name)) michael@0: names.append(name) michael@0: else: michael@0: setDictionary = None michael@0: michael@0: mozMapMemberTypes = filter(lambda t: t.isMozMap(), memberTypes) michael@0: if len(mozMapMemberTypes) > 0: michael@0: raise TypeError("We don't support MozMap in unions yet") michael@0: michael@0: objectMemberTypes = filter(lambda t: t.isObject(), memberTypes) michael@0: if len(objectMemberTypes) > 0: michael@0: assert len(objectMemberTypes) == 1 michael@0: # Very important to NOT construct a temporary Rooted here, since the michael@0: # SetToObject call can call a Rooted constructor and we need to keep michael@0: # stack discipline for Rooted. michael@0: object = CGGeneric("%s.SetToObject(cx, &${val}.toObject());\n" michael@0: "done = true;\n" % unionArgumentObj) michael@0: names.append(objectMemberTypes[0].name) michael@0: else: michael@0: object = None michael@0: michael@0: hasObjectTypes = interfaceObject or arrayObject or dateObject or callbackObject or object michael@0: if hasObjectTypes: michael@0: # "object" is not distinguishable from other types michael@0: assert not object or not (interfaceObject or arrayObject or dateObject or callbackObject) michael@0: if arrayObject or dateObject or callbackObject: michael@0: # An object can be both an array object and a callback or michael@0: # dictionary, but we shouldn't have both in the union's members michael@0: # because they are not distinguishable. michael@0: assert not (arrayObject and callbackObject) michael@0: templateBody = CGElseChain([arrayObject, dateObject, callbackObject]) michael@0: else: michael@0: templateBody = None michael@0: if interfaceObject: michael@0: assert not object michael@0: if templateBody: michael@0: templateBody = CGIfWrapper(templateBody, "!done") michael@0: templateBody = CGList([interfaceObject, templateBody]) michael@0: else: michael@0: templateBody = CGList([templateBody, object]) michael@0: michael@0: if dateObject: michael@0: templateBody.prepend(CGGeneric("JS::Rooted argObj(cx, &${val}.toObject());\n")) michael@0: templateBody = CGIfWrapper(templateBody, "${val}.isObject()") michael@0: else: michael@0: templateBody = CGGeneric() michael@0: michael@0: if setDictionary: michael@0: assert not object michael@0: templateBody = CGList([templateBody, michael@0: CGIfWrapper(setDictionary, "!done")]) michael@0: michael@0: stringTypes = [t for t in memberTypes if t.isString() or t.isEnum()] michael@0: numericTypes = [t for t in memberTypes if t.isNumeric()] michael@0: booleanTypes = [t for t in memberTypes if t.isBoolean()] michael@0: if stringTypes or numericTypes or booleanTypes: michael@0: assert len(stringTypes) <= 1 michael@0: assert len(numericTypes) <= 1 michael@0: assert len(booleanTypes) <= 1 michael@0: michael@0: # We will wrap all this stuff in a do { } while (0); so we michael@0: # can use "break" for flow control. michael@0: def getStringOrPrimitiveConversion(memberType): michael@0: if memberType.isEnum(): michael@0: name = memberType.inner.identifier.name michael@0: else: michael@0: name = memberType.name michael@0: return CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${mutableVal}, tryNext)) || !tryNext;\n" michael@0: "break;\n" % (unionArgumentObj, name)) michael@0: other = CGList([]) michael@0: stringConversion = map(getStringOrPrimitiveConversion, stringTypes) michael@0: numericConversion = map(getStringOrPrimitiveConversion, numericTypes) michael@0: booleanConversion = map(getStringOrPrimitiveConversion, booleanTypes) michael@0: if stringConversion: michael@0: if booleanConversion: michael@0: other.append(CGIfWrapper(booleanConversion[0], michael@0: "${val}.isBoolean()")) michael@0: if numericConversion: michael@0: other.append(CGIfWrapper(numericConversion[0], michael@0: "${val}.isNumber()")) michael@0: other.append(stringConversion[0]) michael@0: elif numericConversion: michael@0: if booleanConversion: michael@0: other.append(CGIfWrapper(booleanConversion[0], michael@0: "${val}.isBoolean()")) michael@0: other.append(numericConversion[0]) michael@0: else: michael@0: assert booleanConversion michael@0: other.append(booleanConversion[0]) michael@0: michael@0: other = CGWrapper(CGIndenter(other), pre="do {\n", post="} while (0);\n") michael@0: if hasObjectTypes or setDictionary: michael@0: other = CGWrapper(CGIndenter(other), "{\n", post="}\n") michael@0: if object: michael@0: templateBody = CGElseChain([templateBody, other]) michael@0: else: michael@0: other = CGWrapper(other, pre="if (!done) ") michael@0: templateBody = CGList([templateBody, other]) michael@0: else: michael@0: assert templateBody.define() == "" michael@0: templateBody = other michael@0: else: michael@0: other = None michael@0: michael@0: templateBody = CGWrapper(templateBody, pre="bool done = false, failed = false, tryNext;\n") michael@0: throw = CGGeneric(fill( michael@0: """ michael@0: if (failed) { michael@0: $*{exceptionCode} michael@0: } michael@0: if (!done) { michael@0: ThrowErrorMessage(cx, MSG_NOT_IN_UNION, "${desc}", "${names}"); michael@0: $*{exceptionCode} michael@0: } michael@0: """, michael@0: exceptionCode=exceptionCode, michael@0: desc=firstCap(sourceDescription), michael@0: names=", ".join(names))) michael@0: michael@0: templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw])), pre="{\n", post="}\n") michael@0: michael@0: typeName = CGUnionStruct.unionTypeDecl(type, isMember) michael@0: argumentTypeName = typeName + "Argument" michael@0: if nullable: michael@0: typeName = "Nullable<" + typeName + " >" michael@0: michael@0: def handleNull(templateBody, setToNullVar, extraConditionForNull=""): michael@0: nullTest = "%s${val}.isNullOrUndefined()" % extraConditionForNull michael@0: return CGIfElseWrapper(nullTest, michael@0: CGGeneric("%s.SetNull();\n" % setToNullVar), michael@0: templateBody) michael@0: michael@0: if type.hasNullableType: michael@0: assert not nullable michael@0: # Make sure to handle a null default value here michael@0: if defaultValue and isinstance(defaultValue, IDLNullValue): michael@0: assert defaultValue.type == type michael@0: extraConditionForNull = "!(${haveValue}) || " michael@0: else: michael@0: extraConditionForNull = "" michael@0: templateBody = handleNull(templateBody, unionArgumentObj, michael@0: extraConditionForNull=extraConditionForNull) michael@0: michael@0: declType = CGGeneric(typeName) michael@0: if isMember: michael@0: holderType = None michael@0: else: michael@0: holderType = CGGeneric(argumentTypeName) michael@0: if nullable: michael@0: holderType = CGTemplatedType("Maybe", holderType) michael@0: michael@0: # If we're isOptional and not nullable the normal optional handling will michael@0: # handle lazy construction of our holder. If we're nullable and not michael@0: # isMember we do it all by hand because we do not want our holder michael@0: # constructed if we're null. But if we're isMember we don't have a michael@0: # holder anyway, so we can do the normal Optional codepath. michael@0: declLoc = "${declName}" michael@0: constructDecl = None michael@0: if nullable: michael@0: if isOptional and not isMember: michael@0: holderArgs = "${declName}.Value().SetValue()" michael@0: declType = CGTemplatedType("Optional", declType) michael@0: constructDecl = CGGeneric("${declName}.Construct();\n") michael@0: declLoc = "${declName}.Value()" michael@0: else: michael@0: holderArgs = "${declName}.SetValue()" michael@0: if holderType is not None: michael@0: constructHolder = CGGeneric("${holderName}.construct(%s);\n" % holderArgs) michael@0: else: michael@0: constructHolder = None michael@0: # Don't need to pass those args when the holder is being constructed michael@0: holderArgs = None michael@0: else: michael@0: holderArgs = "${declName}" michael@0: constructHolder = None michael@0: michael@0: if defaultValue and not isinstance(defaultValue, IDLNullValue): michael@0: tag = defaultValue.type.tag() michael@0: michael@0: if tag in numericSuffixes or tag is IDLType.Tags.bool: michael@0: defaultStr = getHandleDefault(defaultValue) michael@0: value = declLoc + (".Value()" if nullable else "") michael@0: default = CGGeneric("%s.RawSetAs%s() = %s;\n" % michael@0: (value, defaultValue.type, defaultStr)) michael@0: else: michael@0: default = CGGeneric( michael@0: handleDefaultStringValue( michael@0: defaultValue, "%s.SetStringData" % unionArgumentObj) + michael@0: ";\n") michael@0: michael@0: templateBody = CGIfElseWrapper("!(${haveValue})", default, templateBody) michael@0: michael@0: templateBody = CGList([constructHolder, templateBody]) michael@0: michael@0: if nullable: michael@0: if defaultValue: michael@0: if isinstance(defaultValue, IDLNullValue): michael@0: extraConditionForNull = "!(${haveValue}) || " michael@0: else: michael@0: extraConditionForNull = "${haveValue} && " michael@0: else: michael@0: extraConditionForNull = "" michael@0: templateBody = handleNull(templateBody, declLoc, michael@0: extraConditionForNull=extraConditionForNull) michael@0: elif (not type.hasNullableType and defaultValue and michael@0: isinstance(defaultValue, IDLNullValue)): michael@0: assert type.hasDictionaryType michael@0: assert defaultValue.type.isDictionary() michael@0: if not isMember and typeNeedsRooting(defaultValue.type): michael@0: ctorArgs = "cx" michael@0: else: michael@0: ctorArgs = "" michael@0: initDictionaryWithNull = CGIfWrapper( michael@0: CGGeneric("return false;\n"), michael@0: ('!%s.RawSetAs%s(%s).Init(cx, JS::NullHandleValue, "Member of %s")' michael@0: % (declLoc, getUnionMemberName(defaultValue.type), michael@0: ctorArgs, type))) michael@0: templateBody = CGIfElseWrapper("!(${haveValue})", michael@0: initDictionaryWithNull, michael@0: templateBody) michael@0: michael@0: templateBody = CGList([constructDecl, templateBody]) michael@0: michael@0: return JSToNativeConversionInfo(templateBody.define(), michael@0: declType=declType, michael@0: holderType=holderType, michael@0: holderArgs=holderArgs, michael@0: dealWithOptional=isOptional and (not nullable or isMember)) michael@0: michael@0: if type.isGeckoInterface(): michael@0: assert not isEnforceRange and not isClamp michael@0: michael@0: descriptor = descriptorProvider.getDescriptor( michael@0: type.unroll().inner.identifier.name) michael@0: michael@0: if descriptor.nativeType == 'JSObject': michael@0: # XXXbz Workers code does this sometimes michael@0: assert descriptor.workers michael@0: return handleJSObjectType(type, isMember, failureCode) michael@0: michael@0: if descriptor.interface.isCallback(): michael@0: name = descriptor.interface.identifier.name michael@0: if type.nullable() or isCallbackReturnValue: michael@0: declType = CGGeneric("nsRefPtr<%s>" % name) michael@0: else: michael@0: declType = CGGeneric("OwningNonNull<%s>" % name) michael@0: # BOGUS extra blank line here turns out to be at the end of a block: michael@0: conversion = indent(CGCallbackTempRoot(name).define()) + "\n" michael@0: michael@0: template = wrapObjectTemplate(conversion, type, michael@0: "${declName} = nullptr;\n", michael@0: failureCode) michael@0: return JSToNativeConversionInfo(template, declType=declType, michael@0: dealWithOptional=isOptional) michael@0: michael@0: # This is an interface that we implement as a concrete class michael@0: # or an XPCOM interface. michael@0: michael@0: # Allow null pointers for nullable types and old-binding classes, and michael@0: # use an nsRefPtr or raw pointer for callback return values to make michael@0: # them easier to return. michael@0: argIsPointer = (type.nullable() or type.unroll().inner.isExternal() or michael@0: isCallbackReturnValue) michael@0: michael@0: # Sequences and non-worker callbacks have to hold a strong ref to the michael@0: # thing being passed down. Union return values must hold a strong ref michael@0: # because they may be returning an addrefed pointer. michael@0: # Also, callback return values always end up michael@0: # addrefing anyway, so there is no point trying to avoid it here and it michael@0: # makes other things simpler since we can assume the return value is a michael@0: # strong ref. michael@0: forceOwningType = ((descriptor.interface.isCallback() and michael@0: not descriptor.workers) or michael@0: isMember or michael@0: isCallbackReturnValue) michael@0: michael@0: if forceOwningType and descriptor.nativeOwnership == 'owned': michael@0: raise TypeError("Interface %s has 'owned' nativeOwnership, so we " michael@0: "don't know how to keep it alive in %s" % michael@0: (descriptor.interface.identifier.name, michael@0: sourceDescription)) michael@0: michael@0: typeName = descriptor.nativeType michael@0: typePtr = typeName + "*" michael@0: michael@0: # Compute a few things: michael@0: # - declType is the type we want to return as the first element of our michael@0: # tuple. michael@0: # - holderType is the type we want to return as the third element michael@0: # of our tuple. michael@0: michael@0: # Set up some sensible defaults for these things insofar as we can. michael@0: holderType = None michael@0: if argIsPointer: michael@0: if forceOwningType: michael@0: declType = "nsRefPtr<" + typeName + ">" michael@0: else: michael@0: declType = typePtr michael@0: else: michael@0: if forceOwningType: michael@0: declType = "OwningNonNull<" + typeName + ">" michael@0: else: michael@0: declType = "NonNull<" + typeName + ">" michael@0: michael@0: templateBody = "" michael@0: if not descriptor.skipGen and not descriptor.interface.isConsequential() and not descriptor.interface.isExternal(): michael@0: if failureCode is not None: michael@0: templateBody += str(CastableObjectUnwrapper( michael@0: descriptor, michael@0: "&${val}.toObject()", michael@0: "${declName}", michael@0: failureCode)) michael@0: else: michael@0: templateBody += str(FailureFatalCastableObjectUnwrapper( michael@0: descriptor, michael@0: "&${val}.toObject()", michael@0: "${declName}", michael@0: exceptionCode, michael@0: isCallbackReturnValue, michael@0: firstCap(sourceDescription))) michael@0: elif descriptor.workers: michael@0: return handleJSObjectType(type, isMember, failureCode) michael@0: else: michael@0: # Either external, or new-binding non-castable. We always have a michael@0: # holder for these, because we don't actually know whether we have michael@0: # to addref when unwrapping or not. So we just pass an michael@0: # getter_AddRefs(nsRefPtr) to XPConnect and if we'll need a release michael@0: # it'll put a non-null pointer in there. michael@0: if forceOwningType: michael@0: # Don't return a holderType in this case; our declName michael@0: # will just own stuff. michael@0: templateBody += "nsRefPtr<" + typeName + "> ${holderName};\n" michael@0: else: michael@0: holderType = "nsRefPtr<" + typeName + ">" michael@0: templateBody += ( michael@0: "JS::Rooted tmpVal(cx, ${val});\n" + michael@0: typePtr + " tmp;\n" michael@0: "if (NS_FAILED(UnwrapArg<" + typeName + ">(cx, ${val}, &tmp, static_cast<" + typeName + "**>(getter_AddRefs(${holderName})), &tmpVal))) {\n") michael@0: templateBody += CGIndenter(onFailureBadType(failureCode, michael@0: descriptor.interface.identifier.name)).define() michael@0: templateBody += ("}\n" michael@0: "MOZ_ASSERT(tmp);\n") michael@0: michael@0: if not isDefinitelyObject and not forceOwningType: michael@0: # Our tmpVal will go out of scope, so we can't rely on it michael@0: # for rooting michael@0: templateBody += dedent(""" michael@0: if (tmpVal != ${val} && !${holderName}) { michael@0: // We have to have a strong ref, because we got this off michael@0: // some random object that might get GCed michael@0: ${holderName} = tmp; michael@0: } michael@0: """) michael@0: michael@0: # And store our tmp, before it goes out of scope. michael@0: templateBody += "${declName} = tmp;\n" michael@0: michael@0: # Just pass failureCode, not onFailureBadType, here, so we'll report the michael@0: # thing as not an object as opposed to not implementing whatever our michael@0: # interface is. michael@0: templateBody = wrapObjectTemplate(templateBody, type, michael@0: "${declName} = nullptr;\n", failureCode) michael@0: michael@0: declType = CGGeneric(declType) michael@0: if holderType is not None: michael@0: holderType = CGGeneric(holderType) michael@0: return JSToNativeConversionInfo(templateBody, michael@0: declType=declType, michael@0: holderType=holderType, michael@0: dealWithOptional=isOptional) michael@0: michael@0: if type.isSpiderMonkeyInterface(): michael@0: assert not isEnforceRange and not isClamp michael@0: name = type.name michael@0: arrayType = CGGeneric(name) michael@0: declType = arrayType michael@0: if type.nullable(): michael@0: declType = CGTemplatedType("Nullable", declType) michael@0: objRef = "${declName}.SetValue()" michael@0: else: michael@0: objRef = "${declName}" michael@0: michael@0: # Again, this is a bit strange since we are actually building a michael@0: # template string here. ${objRef} and $*{badType} below are filled in michael@0: # right now; $${val} expands to ${val}, to be filled in later. michael@0: template = fill( michael@0: """ michael@0: if (!${objRef}.Init(&$${val}.toObject())) { michael@0: $*{badType} michael@0: } michael@0: michael@0: """, # BOGUS extra blank line michael@0: objRef=objRef, michael@0: badType=onFailureBadType(failureCode, type.name).define()) michael@0: template = wrapObjectTemplate(template, type, "${declName}.SetNull();\n", michael@0: failureCode) michael@0: if not isMember: michael@0: # This is a bit annoying. In a union we don't want to have a michael@0: # holder, since unions don't support that. But if we're optional we michael@0: # want to have a holder, so that the callee doesn't see michael@0: # Optional >. So do a holder if we're michael@0: # optional and use a RootedTypedArray otherwise. michael@0: if isOptional: michael@0: holderType = CGTemplatedType("TypedArrayRooter", arrayType) michael@0: # If our typed array is nullable, this will set the Nullable to michael@0: # be not-null, but that's ok because we make an explicit michael@0: # SetNull() call on it as needed if our JS value is actually michael@0: # null. XXXbz Because "Maybe" takes const refs for constructor michael@0: # arguments, we can't pass a reference here; have to pass a michael@0: # pointer. michael@0: holderArgs = "cx, &%s" % objRef michael@0: declArgs = None michael@0: else: michael@0: holderType = None michael@0: holderArgs = None michael@0: declType = CGTemplatedType("RootedTypedArray", declType) michael@0: declArgs = "cx" michael@0: else: michael@0: holderType = None michael@0: holderArgs = None michael@0: declArgs = None michael@0: return JSToNativeConversionInfo(template, michael@0: declType=declType, michael@0: holderType=holderType, michael@0: dealWithOptional=isOptional, michael@0: declArgs=declArgs, michael@0: holderArgs=holderArgs) michael@0: michael@0: if type.isDOMString(): michael@0: assert not isEnforceRange and not isClamp michael@0: michael@0: treatAs = { michael@0: "Default": "eStringify", michael@0: "EmptyString": "eEmpty", michael@0: "Null": "eNull", michael@0: } michael@0: if type.nullable(): michael@0: # For nullable strings null becomes a null string. michael@0: treatNullAs = "Null" michael@0: # For nullable strings undefined also becomes a null string. michael@0: undefinedBehavior = "eNull" michael@0: else: michael@0: undefinedBehavior = "eStringify" michael@0: nullBehavior = treatAs[treatNullAs] michael@0: michael@0: def getConversionCode(varName): michael@0: conversionCode = ( michael@0: "if (!ConvertJSValueToString(cx, ${val}, ${mutableVal}, %s, %s, %s)) {\n" michael@0: "%s" michael@0: "}\n" % (nullBehavior, undefinedBehavior, varName, michael@0: exceptionCodeIndented.define())) michael@0: if defaultValue is None: michael@0: return conversionCode michael@0: michael@0: if isinstance(defaultValue, IDLNullValue): michael@0: assert(type.nullable()) michael@0: defaultCode = "%s.SetNull()" % varName michael@0: else: michael@0: defaultCode = handleDefaultStringValue(defaultValue, michael@0: "%s.SetData" % varName) michael@0: return handleDefault(conversionCode, defaultCode + ";\n") michael@0: michael@0: if isMember: michael@0: # We have to make a copy, except in the variadic case, because our michael@0: # jsval may well not live as long as our string needs to. michael@0: declType = CGGeneric("nsString") michael@0: if isMember == "Variadic": michael@0: # The string is kept alive by the argument, so we can just michael@0: # depend on it. michael@0: assignString = "${declName}.Rebind(str.Data(), str.Length());\n" michael@0: else: michael@0: assignString = "${declName} = str;\n" michael@0: return JSToNativeConversionInfo( michael@0: fill( michael@0: """ michael@0: { michael@0: binding_detail::FakeDependentString str; michael@0: $*{convert} michael@0: $*{assign} michael@0: } michael@0: michael@0: """, # BOGUS extra newline michael@0: convert=getConversionCode("str"), michael@0: assign=assignString), michael@0: declType=declType, michael@0: dealWithOptional=isOptional) michael@0: michael@0: if isOptional: michael@0: declType = "Optional" michael@0: holderType = CGGeneric("binding_detail::FakeDependentString") michael@0: conversionCode = ("%s" michael@0: "${declName} = &${holderName};\n" % michael@0: getConversionCode("${holderName}")) michael@0: else: michael@0: declType = "binding_detail::FakeDependentString" michael@0: holderType = None michael@0: conversionCode = getConversionCode("${declName}") michael@0: michael@0: # No need to deal with optional here; we handled it already michael@0: return JSToNativeConversionInfo( michael@0: conversionCode, michael@0: declType=CGGeneric(declType), michael@0: holderType=holderType) michael@0: michael@0: if type.isByteString(): michael@0: assert not isEnforceRange and not isClamp michael@0: michael@0: nullable = toStringBool(type.nullable()) michael@0: michael@0: conversionCode = ( michael@0: "if (!ConvertJSValueToByteString(cx, ${val}, ${mutableVal}, %s, ${declName})) {\n" michael@0: "%s" michael@0: "}\n" % (nullable, exceptionCodeIndented.define())) michael@0: # ByteString arguments cannot have a default value. michael@0: assert defaultValue is None michael@0: michael@0: return JSToNativeConversionInfo( michael@0: conversionCode, michael@0: declType=CGGeneric("nsCString"), michael@0: dealWithOptional=isOptional) michael@0: michael@0: if type.isEnum(): michael@0: assert not isEnforceRange and not isClamp michael@0: michael@0: enumName = type.unroll().inner.identifier.name michael@0: declType = CGGeneric(enumName) michael@0: if type.nullable(): michael@0: declType = CGTemplatedType("Nullable", declType) michael@0: declType = declType.define() michael@0: enumLoc = "${declName}.SetValue()" michael@0: else: michael@0: enumLoc = "${declName}" michael@0: declType = declType.define() michael@0: michael@0: if invalidEnumValueFatal: michael@0: handleInvalidEnumValueCode = "MOZ_ASSERT(index >= 0);\n" michael@0: else: michael@0: # invalidEnumValueFatal is false only for attributes. So we won't michael@0: # have a non-default exceptionCode here unless attribute "arg michael@0: # conversion" code starts passing in an exceptionCode. At which michael@0: # point we'll need to figure out what that even means. michael@0: assert exceptionCode == "return false;\n" michael@0: handleInvalidEnumValueCode = dedent(""" michael@0: if (index < 0) { michael@0: return true; michael@0: } michael@0: """) michael@0: michael@0: template = fill( michael@0: """ michael@0: { michael@0: bool ok; michael@0: int index = FindEnumStringIndex<${invalidEnumValueFatal}>(cx, $${val}, ${values}, "${enumtype}", "${sourceDescription}", &ok); michael@0: if (!ok) { michael@0: $*{exceptionCode} michael@0: } michael@0: $*{handleInvalidEnumValueCode} michael@0: ${enumLoc} = static_cast<${enumtype}>(index); michael@0: } michael@0: """, michael@0: enumtype=enumName, michael@0: values=enumName + "Values::" + ENUM_ENTRY_VARIABLE_NAME, michael@0: invalidEnumValueFatal=toStringBool(invalidEnumValueFatal), michael@0: handleInvalidEnumValueCode=handleInvalidEnumValueCode, michael@0: exceptionCode=exceptionCode, michael@0: enumLoc=enumLoc, michael@0: sourceDescription=firstCap(sourceDescription)) michael@0: michael@0: setNull = "${declName}.SetNull();\n" michael@0: michael@0: if type.nullable(): michael@0: template = CGIfElseWrapper("${val}.isNullOrUndefined()", michael@0: CGGeneric(setNull), michael@0: CGGeneric(template)).define() michael@0: michael@0: if defaultValue is not None: michael@0: if isinstance(defaultValue, IDLNullValue): michael@0: assert type.nullable() michael@0: template = handleDefault(template, setNull) michael@0: else: michael@0: assert(defaultValue.type.tag() == IDLType.Tags.domstring) michael@0: template = handleDefault(template, michael@0: ("%s = %s::%s;\n" % michael@0: (enumLoc, enumName, michael@0: getEnumValueName(defaultValue.value)))) michael@0: return JSToNativeConversionInfo(template, declType=CGGeneric(declType), michael@0: dealWithOptional=isOptional) michael@0: michael@0: if type.isCallback(): michael@0: assert not isEnforceRange and not isClamp michael@0: assert not type.treatNonCallableAsNull() or type.nullable() michael@0: assert not type.treatNonObjectAsNull() or type.nullable() michael@0: assert not type.treatNonObjectAsNull() or not type.treatNonCallableAsNull() michael@0: michael@0: name = type.unroll().identifier.name michael@0: if type.nullable(): michael@0: declType = CGGeneric("nsRefPtr<%s>" % name) michael@0: else: michael@0: declType = CGGeneric("OwningNonNull<%s>" % name) michael@0: conversion = indent(CGCallbackTempRoot(name).define()) michael@0: michael@0: if allowTreatNonCallableAsNull and type.treatNonCallableAsNull(): michael@0: haveCallable = "JS_ObjectIsCallable(cx, &${val}.toObject())" michael@0: if not isDefinitelyObject: michael@0: haveCallable = "${val}.isObject() && " + haveCallable michael@0: if defaultValue is not None: michael@0: assert(isinstance(defaultValue, IDLNullValue)) michael@0: haveCallable = "${haveValue} && " + haveCallable michael@0: template = ( michael@0: ("if (%s) {\n" % haveCallable) + michael@0: conversion + michael@0: "} else {\n" michael@0: " ${declName} = nullptr;\n" michael@0: "}\n") michael@0: elif allowTreatNonCallableAsNull and type.treatNonObjectAsNull(): michael@0: if not isDefinitelyObject: michael@0: haveObject = "${val}.isObject()" michael@0: if defaultValue is not None: michael@0: assert(isinstance(defaultValue, IDLNullValue)) michael@0: haveObject = "${haveValue} && " + haveObject michael@0: template = CGIfElseWrapper(haveObject, michael@0: CGGeneric(conversion + "\n"), # BOGUS extra blank line michael@0: CGGeneric("${declName} = nullptr;\n")).define() michael@0: else: michael@0: template = conversion michael@0: else: michael@0: template = wrapObjectTemplate( michael@0: "if (JS_ObjectIsCallable(cx, &${val}.toObject())) {\n" + michael@0: conversion + michael@0: "} else {\n" + michael@0: indent(onFailureNotCallable(failureCode).define()) + michael@0: "}\n", michael@0: type, michael@0: "${declName} = nullptr;\n", michael@0: failureCode) michael@0: return JSToNativeConversionInfo(template, declType=declType, michael@0: dealWithOptional=isOptional) michael@0: michael@0: if type.isAny(): michael@0: assert not isEnforceRange and not isClamp michael@0: michael@0: declArgs = None michael@0: if isMember in ("Variadic", "Sequence", "Dictionary", "MozMap"): michael@0: # Rooting is handled by the sequence and dictionary tracers. michael@0: declType = "JS::Value" michael@0: else: michael@0: assert not isMember michael@0: declType = "JS::Rooted" michael@0: declArgs = "cx" michael@0: michael@0: assert not isOptional michael@0: templateBody = "${declName} = ${val};\n" michael@0: # We may not have a default value if we're being converted for michael@0: # a setter, say. michael@0: if defaultValue: michael@0: if isinstance(defaultValue, IDLNullValue): michael@0: defaultHandling = "${declName} = JS::NullValue();\n" michael@0: else: michael@0: assert isinstance(defaultValue, IDLUndefinedValue) michael@0: defaultHandling = "${declName} = JS::UndefinedValue();\n" michael@0: templateBody = handleDefault(templateBody, defaultHandling) michael@0: return JSToNativeConversionInfo(templateBody, michael@0: declType=CGGeneric(declType), michael@0: declArgs=declArgs) michael@0: michael@0: if type.isObject(): michael@0: assert not isEnforceRange and not isClamp michael@0: return handleJSObjectType(type, isMember, failureCode) michael@0: michael@0: if type.isDictionary(): michael@0: # There are no nullable dictionaries michael@0: assert not type.nullable() or isCallbackReturnValue michael@0: # All optional dictionaries always have default values, so we michael@0: # should be able to assume not isOptional here. michael@0: assert not isOptional michael@0: # In the callback return value case we never have to worry michael@0: # about a default value; we always have a value. michael@0: assert not isCallbackReturnValue or defaultValue is None michael@0: michael@0: typeName = CGDictionary.makeDictionaryName(type.unroll().inner) michael@0: if not isMember and not isCallbackReturnValue: michael@0: # Since we're not a member and not nullable or optional, no one will michael@0: # see our real type, so we can do the fast version of the dictionary michael@0: # that doesn't pre-initialize members. michael@0: typeName = "binding_detail::Fast" + typeName michael@0: michael@0: declType = CGGeneric(typeName) michael@0: michael@0: # We do manual default value handling here, because we michael@0: # actually do want a jsval, and we only handle null anyway michael@0: # NOTE: if isNullOrUndefined or isDefinitelyObject are true, michael@0: # we know we have a value, so we don't have to worry about the michael@0: # default value. michael@0: if (not isNullOrUndefined and not isDefinitelyObject and michael@0: defaultValue is not None): michael@0: assert(isinstance(defaultValue, IDLNullValue)) michael@0: val = "(${haveValue}) ? ${val} : JS::NullHandleValue" michael@0: else: michael@0: val = "${val}" michael@0: michael@0: if failureCode is not None: michael@0: if isDefinitelyObject: michael@0: dictionaryTest = "IsObjectValueConvertibleToDictionary" michael@0: else: michael@0: dictionaryTest = "IsConvertibleToDictionary" michael@0: # Check that the value we have can in fact be converted to michael@0: # a dictionary, and return failureCode if not. michael@0: template = CGIfWrapper( michael@0: CGGeneric(failureCode), michael@0: "!%s(cx, ${val})" % dictionaryTest).define() + "\n" michael@0: else: michael@0: template = "" michael@0: michael@0: dictLoc = "${declName}" michael@0: if type.nullable(): michael@0: dictLoc += ".SetValue()" michael@0: michael@0: template += ('if (!%s.Init(cx, %s, "%s")) {\n' michael@0: "%s" michael@0: "}\n" % (dictLoc, val, firstCap(sourceDescription), michael@0: exceptionCodeIndented.define())) michael@0: michael@0: if type.nullable(): michael@0: declType = CGTemplatedType("Nullable", declType) michael@0: template = CGIfElseWrapper("${val}.isNullOrUndefined()", michael@0: CGGeneric("${declName}.SetNull();\n"), michael@0: CGGeneric(template)).define() michael@0: michael@0: # Dictionary arguments that might contain traceable things need to get michael@0: # traced michael@0: if not isMember and isCallbackReturnValue: michael@0: # Go ahead and just convert directly into our actual return value michael@0: declType = CGWrapper(declType, post="&") michael@0: declArgs = "aRetVal" michael@0: elif not isMember and typeNeedsRooting(type): michael@0: declType = CGTemplatedType("RootedDictionary", declType) michael@0: declArgs = "cx" michael@0: else: michael@0: declArgs = None michael@0: michael@0: return JSToNativeConversionInfo(template, declType=declType, michael@0: declArgs=declArgs) michael@0: michael@0: if type.isVoid(): michael@0: assert not isOptional michael@0: # This one only happens for return values, and its easy: Just michael@0: # ignore the jsval. michael@0: return JSToNativeConversionInfo("") michael@0: michael@0: if type.isDate(): michael@0: assert not isEnforceRange and not isClamp michael@0: michael@0: declType = CGGeneric("Date") michael@0: if type.nullable(): michael@0: declType = CGTemplatedType("Nullable", declType) michael@0: dateVal = "${declName}.SetValue()" michael@0: else: michael@0: dateVal = "${declName}" michael@0: michael@0: if failureCode is None: michael@0: notDate = ('ThrowErrorMessage(cx, MSG_NOT_DATE, "%s");\n' michael@0: "%s" % (firstCap(sourceDescription), exceptionCode)) michael@0: else: michael@0: notDate = failureCode michael@0: michael@0: conversion = fill( michael@0: """ michael@0: JS::Rooted possibleDateObject(cx, &$${val}.toObject()); michael@0: if (!JS_ObjectIsDate(cx, possibleDateObject) || michael@0: !${dateVal}.SetTimeStamp(cx, possibleDateObject)) { michael@0: $*{notDate} michael@0: } michael@0: """, michael@0: dateVal=dateVal, michael@0: notDate=notDate) michael@0: michael@0: conversion = wrapObjectTemplate(conversion, type, michael@0: "${declName}.SetNull();\n", notDate) michael@0: return JSToNativeConversionInfo(conversion, michael@0: declType=declType, michael@0: dealWithOptional=isOptional) michael@0: michael@0: if not type.isPrimitive(): michael@0: raise TypeError("Need conversion for argument type '%s'" % str(type)) michael@0: michael@0: typeName = builtinNames[type.tag()] michael@0: michael@0: conversionBehavior = "eDefault" michael@0: if isEnforceRange: michael@0: assert type.isInteger() michael@0: conversionBehavior = "eEnforceRange" michael@0: elif isClamp: michael@0: assert type.isInteger() michael@0: conversionBehavior = "eClamp" michael@0: michael@0: if type.nullable(): michael@0: declType = CGGeneric("Nullable<" + typeName + ">") michael@0: writeLoc = "${declName}.SetValue()" michael@0: readLoc = "${declName}.Value()" michael@0: nullCondition = "${val}.isNullOrUndefined()" michael@0: if defaultValue is not None and isinstance(defaultValue, IDLNullValue): michael@0: nullCondition = "!(${haveValue}) || " + nullCondition michael@0: template = ( michael@0: "if (%s) {\n" michael@0: " ${declName}.SetNull();\n" michael@0: "} else if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n" michael@0: "%s" michael@0: "}\n" % (nullCondition, typeName, conversionBehavior, michael@0: writeLoc, exceptionCodeIndented.define())) michael@0: else: michael@0: assert(defaultValue is None or michael@0: not isinstance(defaultValue, IDLNullValue)) michael@0: writeLoc = "${declName}" michael@0: readLoc = writeLoc michael@0: template = ( michael@0: "if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n" michael@0: "%s" michael@0: "}\n" % (typeName, conversionBehavior, writeLoc, michael@0: exceptionCodeIndented.define())) michael@0: declType = CGGeneric(typeName) michael@0: michael@0: if type.isFloat() and not type.isUnrestricted(): michael@0: if lenientFloatCode is not None: michael@0: nonFiniteCode = lenientFloatCode michael@0: else: michael@0: nonFiniteCode = ('ThrowErrorMessage(cx, MSG_NOT_FINITE, "%s");\n' michael@0: "%s" % (firstCap(sourceDescription), exceptionCode)) michael@0: template = template.rstrip() michael@0: template += fill( michael@0: """ michael@0: else if (!mozilla::IsFinite(${readLoc})) { michael@0: // Note: mozilla::IsFinite will do the right thing michael@0: // when passed a non-finite float too. michael@0: $*{nonFiniteCode} michael@0: } michael@0: """, michael@0: readLoc=readLoc, michael@0: nonFiniteCode=nonFiniteCode) michael@0: michael@0: if (defaultValue is not None and michael@0: # We already handled IDLNullValue, so just deal with the other ones michael@0: not isinstance(defaultValue, IDLNullValue)): michael@0: tag = defaultValue.type.tag() michael@0: defaultStr = getHandleDefault(defaultValue) michael@0: template = CGIfElseWrapper( michael@0: "${haveValue}", michael@0: CGGeneric(template), michael@0: CGGeneric("%s = %s;\n" % (writeLoc, defaultStr))).define() michael@0: michael@0: return JSToNativeConversionInfo(template, declType=declType, michael@0: dealWithOptional=isOptional) michael@0: michael@0: michael@0: def instantiateJSToNativeConversion(info, replacements, checkForValue=False): michael@0: """ michael@0: Take a JSToNativeConversionInfo as returned by getJSToNativeConversionInfo michael@0: and a set of replacements as required by the strings in such an object, and michael@0: generate code to convert into stack C++ types. michael@0: michael@0: If checkForValue is True, then the conversion will get wrapped in michael@0: a check for ${haveValue}. michael@0: """ michael@0: templateBody, declType, holderType, dealWithOptional = ( michael@0: info.template, info.declType, info.holderType, info.dealWithOptional) michael@0: michael@0: if dealWithOptional and not checkForValue: michael@0: raise TypeError("Have to deal with optional things, but don't know how") michael@0: if checkForValue and declType is None: michael@0: raise TypeError("Need to predeclare optional things, so they will be " michael@0: "outside the check for big enough arg count!") michael@0: michael@0: # We can't precompute our holder constructor arguments, since michael@0: # those might depend on ${declName}, which we change below. Just michael@0: # compute arguments at the point when we need them as we go. michael@0: def getArgsCGThing(args): michael@0: return CGGeneric(string.Template(args).substitute(replacements)) michael@0: michael@0: result = CGList([]) michael@0: # Make a copy of "replacements" since we may be about to start modifying it michael@0: replacements = dict(replacements) michael@0: originalDeclName = replacements["declName"] michael@0: if declType is not None: michael@0: if dealWithOptional: michael@0: replacements["declName"] = "%s.Value()" % originalDeclName michael@0: declType = CGTemplatedType("Optional", declType) michael@0: declCtorArgs = None michael@0: elif info.declArgs is not None: michael@0: declCtorArgs = CGWrapper(getArgsCGThing(info.declArgs), michael@0: pre="(", post=")") michael@0: else: michael@0: declCtorArgs = None michael@0: result.append( michael@0: CGList([declType, CGGeneric(" "), michael@0: CGGeneric(originalDeclName), michael@0: declCtorArgs, CGGeneric(";\n")])) michael@0: michael@0: originalHolderName = replacements["holderName"] michael@0: if holderType is not None: michael@0: if dealWithOptional: michael@0: replacements["holderName"] = "%s.ref()" % originalHolderName michael@0: holderType = CGTemplatedType("Maybe", holderType) michael@0: holderCtorArgs = None michael@0: elif info.holderArgs is not None: michael@0: holderCtorArgs = CGWrapper(getArgsCGThing(info.holderArgs), michael@0: pre="(", post=")") michael@0: else: michael@0: holderCtorArgs = None michael@0: result.append( michael@0: CGList([holderType, CGGeneric(" "), michael@0: CGGeneric(originalHolderName), michael@0: holderCtorArgs, CGGeneric(";\n")])) michael@0: michael@0: conversion = CGGeneric( michael@0: string.Template(templateBody).substitute(replacements)) michael@0: michael@0: if checkForValue: michael@0: if dealWithOptional: michael@0: declConstruct = CGIndenter( michael@0: CGGeneric("%s.Construct(%s);\n" % michael@0: (originalDeclName, michael@0: getArgsCGThing(info.declArgs).define() if michael@0: info.declArgs else ""))) michael@0: if holderType is not None: michael@0: holderConstruct = CGIndenter( michael@0: CGGeneric("%s.construct(%s);\n" % michael@0: (originalHolderName, michael@0: getArgsCGThing(info.holderArgs).define() if michael@0: info.holderArgs else ""))) michael@0: else: michael@0: holderConstruct = None michael@0: else: michael@0: declConstruct = None michael@0: holderConstruct = None michael@0: michael@0: conversion = CGList([ michael@0: CGGeneric( michael@0: string.Template("if (${haveValue}) {\n").substitute(replacements)), michael@0: declConstruct, michael@0: holderConstruct, michael@0: CGIndenter(conversion), michael@0: CGGeneric("}\n") michael@0: ]) michael@0: michael@0: result.append(conversion) michael@0: return result michael@0: michael@0: michael@0: def convertConstIDLValueToJSVal(value): michael@0: if isinstance(value, IDLNullValue): michael@0: return "JS::NullValue()" michael@0: if isinstance(value, IDLUndefinedValue): michael@0: return "JS::UndefinedValue()" michael@0: tag = value.type.tag() michael@0: if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16, michael@0: IDLType.Tags.uint16, IDLType.Tags.int32]: michael@0: return "INT_TO_JSVAL(%s)" % (value.value) michael@0: if tag == IDLType.Tags.uint32: michael@0: return "UINT_TO_JSVAL(%sU)" % (value.value) michael@0: if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]: michael@0: return "DOUBLE_TO_JSVAL(%s)" % numericValue(tag, value.value) michael@0: if tag == IDLType.Tags.bool: michael@0: return "JSVAL_TRUE" if value.value else "JSVAL_FALSE" michael@0: if tag in [IDLType.Tags.float, IDLType.Tags.double]: michael@0: return "DOUBLE_TO_JSVAL(%s)" % (value.value) michael@0: raise TypeError("Const value of unhandled type: %s" % value.type) michael@0: michael@0: michael@0: class CGArgumentConverter(CGThing): michael@0: """ michael@0: A class that takes an IDL argument object and its index in the michael@0: argument list and generates code to unwrap the argument to the michael@0: right native type. michael@0: michael@0: argDescription is a description of the argument for error-reporting michael@0: purposes. Callers should assume that it might get placed in the middle of a michael@0: sentence. If it ends up at the beginning of a sentence, its first character michael@0: will be automatically uppercased. michael@0: """ michael@0: def __init__(self, argument, index, descriptorProvider, michael@0: argDescription, michael@0: invalidEnumValueFatal=True, lenientFloatCode=None): michael@0: CGThing.__init__(self) michael@0: self.argument = argument michael@0: self.argDescription = argDescription michael@0: assert(not argument.defaultValue or argument.optional) michael@0: michael@0: replacer = { michael@0: "index": index, michael@0: "argc": "args.length()" michael@0: } michael@0: self.replacementVariables = { michael@0: "declName": "arg%d" % index, michael@0: "holderName": ("arg%d" % index) + "_holder", michael@0: "obj": "obj" michael@0: } michael@0: self.replacementVariables["val"] = string.Template( michael@0: "args[${index}]").substitute(replacer) michael@0: self.replacementVariables["mutableVal"] = self.replacementVariables["val"] michael@0: haveValueCheck = string.Template( michael@0: "args.hasDefined(${index})").substitute(replacer) michael@0: self.replacementVariables["haveValue"] = haveValueCheck michael@0: self.descriptorProvider = descriptorProvider michael@0: if self.argument.optional and not self.argument.defaultValue: michael@0: self.argcAndIndex = replacer michael@0: else: michael@0: self.argcAndIndex = None michael@0: self.invalidEnumValueFatal = invalidEnumValueFatal michael@0: self.lenientFloatCode = lenientFloatCode michael@0: michael@0: def define(self): michael@0: typeConversion = getJSToNativeConversionInfo( michael@0: self.argument.type, michael@0: self.descriptorProvider, michael@0: isOptional=(self.argcAndIndex is not None and michael@0: not self.argument.variadic), michael@0: invalidEnumValueFatal=self.invalidEnumValueFatal, michael@0: defaultValue=self.argument.defaultValue, michael@0: treatNullAs=self.argument.treatNullAs, michael@0: isEnforceRange=self.argument.enforceRange, michael@0: isClamp=self.argument.clamp, michael@0: lenientFloatCode=self.lenientFloatCode, michael@0: isMember="Variadic" if self.argument.variadic else False, michael@0: allowTreatNonCallableAsNull=self.argument.allowTreatNonCallableAsNull(), michael@0: sourceDescription=self.argDescription) michael@0: michael@0: if not self.argument.variadic: michael@0: return instantiateJSToNativeConversion( michael@0: typeConversion, michael@0: self.replacementVariables, michael@0: self.argcAndIndex is not None).define() michael@0: michael@0: # Variadic arguments get turned into a sequence. michael@0: if typeConversion.dealWithOptional: michael@0: raise TypeError("Shouldn't have optional things in variadics") michael@0: if typeConversion.holderType is not None: michael@0: raise TypeError("Shouldn't need holders for variadics") michael@0: michael@0: replacer = dict(self.argcAndIndex, **self.replacementVariables) michael@0: replacer["seqType"] = CGTemplatedType("binding_detail::AutoSequence", michael@0: typeConversion.declType).define() michael@0: if typeNeedsRooting(self.argument.type): michael@0: rooterDecl = ("SequenceRooter<%s> ${holderName}(cx, &${declName});\n" % michael@0: typeConversion.declType.define()) michael@0: else: michael@0: rooterDecl = "" michael@0: replacer["elemType"] = typeConversion.declType.define() michael@0: michael@0: # NOTE: Keep this in sync with sequence conversions as needed michael@0: variadicConversion = string.Template( michael@0: "${seqType} ${declName};\n" + michael@0: rooterDecl + michael@0: dedent(""" michael@0: if (${argc} > ${index}) { michael@0: if (!${declName}.SetCapacity(${argc} - ${index})) { michael@0: JS_ReportOutOfMemory(cx); michael@0: return false; michael@0: } michael@0: for (uint32_t variadicArg = ${index}; variadicArg < ${argc}; ++variadicArg) { michael@0: ${elemType}& slot = *${declName}.AppendElement(); michael@0: """) michael@0: ).substitute(replacer) michael@0: michael@0: val = string.Template("args[variadicArg]").substitute(replacer) michael@0: variadicConversion += indent( michael@0: string.Template(typeConversion.template).substitute({ michael@0: "val": val, michael@0: "mutableVal": val, michael@0: "declName": "slot", michael@0: # We only need holderName here to handle isExternal() michael@0: # interfaces, which use an internal holder for the michael@0: # conversion even when forceOwningType ends up true. michael@0: "holderName": "tempHolder", michael@0: # Use the same ${obj} as for the variadic arg itself michael@0: "obj": replacer["obj"] michael@0: }), 4) michael@0: michael@0: variadicConversion += (" }\n" michael@0: "}\n") michael@0: return variadicConversion michael@0: michael@0: michael@0: def getMaybeWrapValueFuncForType(type): michael@0: # Callbacks might actually be DOM objects; nothing prevents a page from michael@0: # doing that. michael@0: if type.isCallback() or type.isCallbackInterface() or type.isObject(): michael@0: if type.nullable(): michael@0: return "MaybeWrapObjectOrNullValue" michael@0: return "MaybeWrapObjectValue" michael@0: # Spidermonkey interfaces are never DOM objects. Neither are sequences or michael@0: # dictionaries, since those are always plain JS objects. michael@0: if type.isSpiderMonkeyInterface() or type.isDictionary() or type.isSequence(): michael@0: if type.nullable(): michael@0: return "MaybeWrapNonDOMObjectOrNullValue" michael@0: return "MaybeWrapNonDOMObjectValue" michael@0: if type.isAny(): michael@0: return "MaybeWrapValue" michael@0: michael@0: # For other types, just go ahead an fall back on MaybeWrapValue for now: michael@0: # it's always safe to do, and shouldn't be particularly slow for any of michael@0: # them michael@0: return "MaybeWrapValue" michael@0: michael@0: michael@0: sequenceWrapLevel = 0 michael@0: mozMapWrapLevel = 0 michael@0: michael@0: michael@0: def getWrapTemplateForType(type, descriptorProvider, result, successCode, michael@0: returnsNewObject, exceptionCode, typedArraysAreStructs): michael@0: """ michael@0: Reflect a C++ value stored in "result", of IDL type "type" into JS. The michael@0: "successCode" is the code to run once we have successfully done the michael@0: conversion and must guarantee that execution of the conversion template michael@0: stops once the successCode has executed (e.g. by doing a 'return', or by michael@0: doing a 'break' if the entire conversion template is inside a block that michael@0: the 'break' will exit). michael@0: michael@0: If typedArraysAreStructs is true, then if the type is a typed array, michael@0: "result" is one of the dom::TypedArray subclasses, not a JSObject*. michael@0: michael@0: The resulting string should be used with string.Template. It michael@0: needs the following keys when substituting: michael@0: michael@0: jsvalHandle: something that can be passed to methods taking a michael@0: JS::MutableHandle. This can be a michael@0: JS::MutableHandle or a JS::Rooted*. michael@0: jsvalRef: something that can have .address() called on it to get a michael@0: JS::Value* and .set() called on it to set it to a JS::Value. michael@0: This can be a JS::MutableHandle or a michael@0: JS::Rooted. michael@0: obj: a JS::Handle. michael@0: michael@0: Returns (templateString, infallibility of conversion template) michael@0: """ michael@0: if successCode is None: michael@0: successCode = "return true;\n" michael@0: michael@0: def setUndefined(): michael@0: return _setValue("", setter="setUndefined") michael@0: michael@0: def setNull(): michael@0: return _setValue("", setter="setNull") michael@0: michael@0: def setInt32(value): michael@0: return _setValue(value, setter="setInt32") michael@0: michael@0: def setString(value): michael@0: return _setValue(value, setter="setString") michael@0: michael@0: def setObject(value, wrapAsType=None): michael@0: return _setValue(value, wrapAsType=wrapAsType, setter="setObject") michael@0: michael@0: def setObjectOrNull(value, wrapAsType=None): michael@0: return _setValue(value, wrapAsType=wrapAsType, setter="setObjectOrNull") michael@0: michael@0: def setUint32(value): michael@0: return _setValue(value, setter="setNumber") michael@0: michael@0: def setDouble(value): michael@0: return _setValue("JS_NumberValue(%s)" % value) michael@0: michael@0: def setBoolean(value): michael@0: return _setValue(value, setter="setBoolean") michael@0: michael@0: def _setValue(value, wrapAsType=None, setter="set"): michael@0: """ michael@0: Returns the code to set the jsval to value. michael@0: michael@0: If wrapAsType is not None, then will wrap the resulting value using the michael@0: function that getMaybeWrapValueFuncForType(wrapAsType) returns. michael@0: Otherwise, no wrapping will be done. michael@0: """ michael@0: if wrapAsType is None: michael@0: tail = successCode michael@0: else: michael@0: tail = fill( michael@0: """ michael@0: if (!${maybeWrap}(cx, $${jsvalHandle})) { michael@0: $*{exceptionCode} michael@0: } michael@0: $*{successCode} michael@0: """, michael@0: maybeWrap=getMaybeWrapValueFuncForType(wrapAsType), michael@0: exceptionCode=exceptionCode, michael@0: successCode=successCode) michael@0: return ("${jsvalRef}.%s(%s);\n" % (setter, value)) + tail michael@0: michael@0: def wrapAndSetPtr(wrapCall, failureCode=None): michael@0: """ michael@0: Returns the code to set the jsval by calling "wrapCall". "failureCode" michael@0: is the code to run if calling "wrapCall" fails michael@0: """ michael@0: if failureCode is None: michael@0: failureCode = exceptionCode michael@0: return fill( michael@0: """ michael@0: if (!${wrapCall}) { michael@0: $*{failureCode} michael@0: } michael@0: $*{successCode} michael@0: """, michael@0: wrapCall=wrapCall, michael@0: failureCode=failureCode, michael@0: successCode=successCode) michael@0: michael@0: if type is None or type.isVoid(): michael@0: return (setUndefined(), True) michael@0: michael@0: if type.isArray(): michael@0: raise TypeError("Can't handle array return values yet") michael@0: michael@0: if (type.isSequence() or type.isMozMap()) and type.nullable(): michael@0: # These are both wrapped in Nullable<> michael@0: recTemplate, recInfall = getWrapTemplateForType(type.inner, descriptorProvider, michael@0: "%s.Value()" % result, successCode, michael@0: returnsNewObject, exceptionCode, michael@0: typedArraysAreStructs) michael@0: code = fill( michael@0: """ michael@0: michael@0: if (${result}.IsNull()) { michael@0: $*{setNull} michael@0: } michael@0: $*{recTemplate} michael@0: """, michael@0: result=result, michael@0: setNull=setNull(), michael@0: recTemplate=recTemplate) michael@0: return code, recInfall michael@0: michael@0: if type.isSequence(): michael@0: # Now do non-nullable sequences. Our success code is just to break to michael@0: # where we set the element in the array. Note that we bump the michael@0: # sequenceWrapLevel around this call so that nested sequence conversions michael@0: # will use different iteration variables. michael@0: global sequenceWrapLevel michael@0: index = "sequenceIdx%d" % sequenceWrapLevel michael@0: sequenceWrapLevel += 1 michael@0: innerTemplate = wrapForType( michael@0: type.inner, descriptorProvider, michael@0: { michael@0: 'result': "%s[%s]" % (result, index), michael@0: 'successCode': "break;\n", michael@0: 'jsvalRef': "tmp", michael@0: 'jsvalHandle': "&tmp", michael@0: 'returnsNewObject': returnsNewObject, michael@0: 'exceptionCode': exceptionCode, michael@0: 'obj': "returnArray" michael@0: }) michael@0: sequenceWrapLevel -= 1 michael@0: code = fill( michael@0: """ michael@0: michael@0: uint32_t length = ${result}.Length(); michael@0: JS::Rooted returnArray(cx, JS_NewArrayObject(cx, length)); michael@0: if (!returnArray) { michael@0: $*{exceptionCode} michael@0: } michael@0: // Scope for 'tmp' michael@0: { michael@0: JS::Rooted tmp(cx); michael@0: for (uint32_t ${index} = 0; ${index} < length; ++${index}) { michael@0: // Control block to let us common up the JS_DefineElement calls when there michael@0: // are different ways to succeed at wrapping the object. michael@0: do { michael@0: $*{innerTemplate} michael@0: } while (0); michael@0: if (!JS_DefineElement(cx, returnArray, ${index}, tmp, michael@0: nullptr, nullptr, JSPROP_ENUMERATE)) { michael@0: $*{exceptionCode} michael@0: } michael@0: } michael@0: } michael@0: $*{set} michael@0: """, michael@0: result=result, michael@0: exceptionCode=exceptionCode, michael@0: index=index, michael@0: innerTemplate=innerTemplate, michael@0: set=setObject("*returnArray")) michael@0: michael@0: return (code, False) michael@0: michael@0: if type.isMozMap(): michael@0: # Now do non-nullable MozMap. Our success code is just to break to michael@0: # where we define the property on the object. Note that we bump the michael@0: # mozMapWrapLevel around this call so that nested MozMap conversions michael@0: # will use different temp value names. michael@0: global mozMapWrapLevel michael@0: valueName = "mozMapValue%d" % mozMapWrapLevel michael@0: mozMapWrapLevel += 1 michael@0: innerTemplate = wrapForType( michael@0: type.inner, descriptorProvider, michael@0: { michael@0: 'result': valueName, michael@0: 'successCode': "break;\n", michael@0: 'jsvalRef': "tmp", michael@0: 'jsvalHandle': "&tmp", michael@0: 'returnsNewObject': returnsNewObject, michael@0: 'exceptionCode': exceptionCode, michael@0: 'obj': "returnObj" michael@0: }) michael@0: mozMapWrapLevel -= 1 michael@0: code = fill( michael@0: """ michael@0: michael@0: nsTArray keys; michael@0: ${result}.GetKeys(keys); michael@0: JS::Rooted returnObj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr())); michael@0: if (!returnObj) { michael@0: $*{exceptionCode} michael@0: } michael@0: // Scope for 'tmp' michael@0: { michael@0: JS::Rooted tmp(cx); michael@0: for (size_t idx = 0; idx < keys.Length(); ++idx) { michael@0: auto& ${valueName} = ${result}.Get(keys[idx]); michael@0: // Control block to let us common up the JS_DefineUCProperty calls when there michael@0: // are different ways to succeed at wrapping the value. michael@0: do { michael@0: $*{innerTemplate} michael@0: } while (0); michael@0: if (!JS_DefineUCProperty(cx, returnObj, keys[idx].get(), michael@0: keys[idx].Length(), tmp, michael@0: JS_PropertyStub, JS_StrictPropertyStub, michael@0: JSPROP_ENUMERATE)) { michael@0: $*{exceptionCode} michael@0: } michael@0: } michael@0: } michael@0: $*{set} michael@0: """, michael@0: result=result, michael@0: exceptionCode=exceptionCode, michael@0: valueName=valueName, michael@0: innerTemplate=innerTemplate, michael@0: set=setObject("*returnObj")) michael@0: michael@0: return (code, False) michael@0: michael@0: if type.isGeckoInterface() and not type.isCallbackInterface(): michael@0: descriptor = descriptorProvider.getDescriptor(type.unroll().inner.identifier.name) michael@0: if type.nullable(): michael@0: wrappingCode = ("if (!%s) {\n" % (result) + michael@0: indent(setNull()) + michael@0: "}\n") michael@0: else: michael@0: wrappingCode = "" michael@0: michael@0: if not descriptor.interface.isExternal() and not descriptor.skipGen: michael@0: if descriptor.wrapperCache: michael@0: assert descriptor.nativeOwnership != 'owned' michael@0: wrapMethod = "WrapNewBindingObject" michael@0: else: michael@0: if not returnsNewObject: michael@0: raise MethodNotNewObjectError(descriptor.interface.identifier.name) michael@0: if descriptor.nativeOwnership == 'owned': michael@0: wrapMethod = "WrapNewBindingNonWrapperCachedOwnedObject" michael@0: else: michael@0: wrapMethod = "WrapNewBindingNonWrapperCachedObject" michael@0: wrap = "%s(cx, ${obj}, %s, ${jsvalHandle})" % (wrapMethod, result) michael@0: if not descriptor.hasXPConnectImpls: michael@0: # Can only fail to wrap as a new-binding object michael@0: # if they already threw an exception. michael@0: #XXX Assertion disabled for now, see bug 991271. michael@0: failed = ("MOZ_ASSERT(true || JS_IsExceptionPending(cx));\n" + michael@0: exceptionCode) michael@0: else: michael@0: if descriptor.notflattened: michael@0: raise TypeError("%s has XPConnect impls but not flattened; " michael@0: "fallback won't work correctly" % michael@0: descriptor.interface.identifier.name) michael@0: # Try old-style wrapping for bindings which might be XPConnect impls. michael@0: failed = wrapAndSetPtr("HandleNewBindingWrappingFailure(cx, ${obj}, %s, ${jsvalHandle})" % result) michael@0: else: michael@0: if descriptor.notflattened: michael@0: getIID = "&NS_GET_IID(%s), " % descriptor.nativeType michael@0: else: michael@0: getIID = "" michael@0: wrap = "WrapObject(cx, %s, %s${jsvalHandle})" % (result, getIID) michael@0: failed = None michael@0: michael@0: wrappingCode += wrapAndSetPtr(wrap, failed) michael@0: return (wrappingCode, False) michael@0: michael@0: if type.isDOMString(): michael@0: if type.nullable(): michael@0: return (wrapAndSetPtr("xpc::StringToJsval(cx, %s, ${jsvalHandle})" % result), False) michael@0: else: michael@0: return (wrapAndSetPtr("xpc::NonVoidStringToJsval(cx, %s, ${jsvalHandle})" % result), False) michael@0: michael@0: if type.isByteString(): michael@0: if type.nullable(): michael@0: return (wrapAndSetPtr("ByteStringToJsval(cx, %s, ${jsvalHandle})" % result), False) michael@0: else: michael@0: return (wrapAndSetPtr("NonVoidByteStringToJsval(cx, %s, ${jsvalHandle})" % result), False) michael@0: michael@0: if type.isEnum(): michael@0: if type.nullable(): michael@0: resultLoc = "%s.Value()" % result michael@0: else: michael@0: resultLoc = result michael@0: conversion = fill( michael@0: """ michael@0: { michael@0: // Scope for resultStr michael@0: MOZ_ASSERT(uint32_t(${result}) < ArrayLength(${strings})); michael@0: JSString* resultStr = JS_NewStringCopyN(cx, ${strings}[uint32_t(${result})].value, ${strings}[uint32_t(${result})].length); michael@0: if (!resultStr) { michael@0: $*{exceptionCode} michael@0: } michael@0: $*{setResultStr} michael@0: } michael@0: """, michael@0: result=resultLoc, michael@0: strings=(type.unroll().inner.identifier.name + "Values::" + michael@0: ENUM_ENTRY_VARIABLE_NAME), michael@0: exceptionCode=exceptionCode, michael@0: setResultStr=setString("resultStr")) michael@0: michael@0: if type.nullable(): michael@0: conversion = CGIfElseWrapper( michael@0: "%s.IsNull()" % result, michael@0: CGGeneric(setNull()), michael@0: CGGeneric(conversion)).define() michael@0: return conversion, False michael@0: michael@0: if type.isCallback() or type.isCallbackInterface(): michael@0: wrapCode = setObject( michael@0: "*GetCallbackFromCallbackObject(%(result)s)", michael@0: wrapAsType=type) michael@0: if type.nullable(): michael@0: wrapCode = ( michael@0: "if (%(result)s) {\n" + michael@0: indent(wrapCode) + michael@0: "} else {\n" + michael@0: indent(setNull()) + michael@0: "}\n") michael@0: wrapCode = wrapCode % {"result": result} michael@0: return wrapCode, False michael@0: michael@0: if type.isAny(): michael@0: # See comments in WrapNewBindingObject explaining why we need michael@0: # to wrap here. michael@0: # NB: _setValue(..., type-that-is-any) calls JS_WrapValue(), so is fallible michael@0: return (_setValue(result, wrapAsType=type), False) michael@0: michael@0: if (type.isObject() or (type.isSpiderMonkeyInterface() and michael@0: not typedArraysAreStructs)): michael@0: # See comments in WrapNewBindingObject explaining why we need michael@0: # to wrap here. michael@0: if type.nullable(): michael@0: toValue = "%s" michael@0: setter = setObjectOrNull michael@0: else: michael@0: toValue = "*%s" michael@0: setter = setObject michael@0: # NB: setObject{,OrNull}(..., some-object-type) calls JS_WrapValue(), so is fallible michael@0: return (setter(toValue % result, wrapAsType=type), False) michael@0: michael@0: if not (type.isUnion() or type.isPrimitive() or type.isDictionary() or michael@0: type.isDate() or michael@0: (type.isSpiderMonkeyInterface() and typedArraysAreStructs)): michael@0: raise TypeError("Need to learn to wrap %s" % type) michael@0: michael@0: if type.nullable(): michael@0: recTemplate, recInfal = getWrapTemplateForType(type.inner, descriptorProvider, michael@0: "%s.Value()" % result, successCode, michael@0: returnsNewObject, exceptionCode, michael@0: typedArraysAreStructs) michael@0: return ("if (%s.IsNull()) {\n" % result + michael@0: indent(setNull()) + michael@0: "}\n" + michael@0: recTemplate, recInfal) michael@0: michael@0: if type.isSpiderMonkeyInterface(): michael@0: assert typedArraysAreStructs michael@0: # See comments in WrapNewBindingObject explaining why we need michael@0: # to wrap here. michael@0: # NB: setObject(..., some-object-type) calls JS_WrapValue(), so is fallible michael@0: return (setObject("*%s.Obj()" % result, michael@0: wrapAsType=type), False) michael@0: michael@0: if type.isUnion(): michael@0: return (wrapAndSetPtr("%s.ToJSVal(cx, ${obj}, ${jsvalHandle})" % result), michael@0: False) michael@0: michael@0: if type.isDictionary(): michael@0: return (wrapAndSetPtr("%s.ToObject(cx, ${jsvalHandle})" % result), michael@0: False) michael@0: michael@0: if type.isDate(): michael@0: return (wrapAndSetPtr("%s.ToDateObject(cx, ${jsvalHandle})" % result), michael@0: False) michael@0: michael@0: tag = type.tag() michael@0: michael@0: if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16, michael@0: IDLType.Tags.uint16, IDLType.Tags.int32]: michael@0: return (setInt32("int32_t(%s)" % result), True) michael@0: michael@0: elif tag in [IDLType.Tags.int64, IDLType.Tags.uint64, michael@0: IDLType.Tags.unrestricted_float, IDLType.Tags.float, michael@0: IDLType.Tags.unrestricted_double, IDLType.Tags.double]: michael@0: # XXXbz will cast to double do the "even significand" thing that webidl michael@0: # calls for for 64-bit ints? Do we care? michael@0: return (setDouble("double(%s)" % result), True) michael@0: michael@0: elif tag == IDLType.Tags.uint32: michael@0: return (setUint32(result), True) michael@0: michael@0: elif tag == IDLType.Tags.bool: michael@0: return (setBoolean(result), True) michael@0: michael@0: else: michael@0: raise TypeError("Need to learn to wrap primitive: %s" % type) michael@0: michael@0: michael@0: def wrapForType(type, descriptorProvider, templateValues): michael@0: """ michael@0: Reflect a C++ value of IDL type "type" into JS. TemplateValues is a dict michael@0: that should contain: michael@0: michael@0: * 'jsvalRef': something that can have .address() called on it to get a michael@0: JS::Value* and .set() called on it to set it to a JS::Value. michael@0: This can be a JS::MutableHandle or a michael@0: JS::Rooted. michael@0: * 'jsvalHandle': something that can be passed to methods taking a michael@0: JS::MutableHandle. This can be a michael@0: JS::MutableHandle or a JS::Rooted*. michael@0: * 'obj' (optional): the name of the variable that contains the JSObject to michael@0: use as a scope when wrapping, if not supplied 'obj' michael@0: will be used as the name michael@0: * 'result' (optional): the name of the variable in which the C++ value is michael@0: stored, if not supplied 'result' will be used as michael@0: the name michael@0: * 'successCode' (optional): the code to run once we have successfully michael@0: done the conversion, if not supplied 'return michael@0: true;' will be used as the code. The michael@0: successCode must ensure that once it runs no michael@0: more of the conversion template will be michael@0: executed (e.g. by doing a 'return' or 'break' michael@0: as appropriate). michael@0: * 'returnsNewObject' (optional): If true, we're wrapping for the return michael@0: value of a [NewObject] method. Assumed michael@0: false if not set. michael@0: * 'exceptionCode' (optional): Code to run when a JS exception is thrown. michael@0: The default is "return false;". The code michael@0: passed here must return. michael@0: """ michael@0: wrap = getWrapTemplateForType(type, descriptorProvider, michael@0: templateValues.get('result', 'result'), michael@0: templateValues.get('successCode', None), michael@0: templateValues.get('returnsNewObject', False), michael@0: templateValues.get('exceptionCode', michael@0: "return false;\n"), michael@0: templateValues.get('typedArraysAreStructs', michael@0: False))[0] michael@0: michael@0: defaultValues = {'obj': 'obj'} michael@0: return string.Template(wrap).substitute(defaultValues, **templateValues) michael@0: michael@0: michael@0: def infallibleForMember(member, type, descriptorProvider): michael@0: """ michael@0: Determine the fallibility of changing a C++ value of IDL type "type" into michael@0: JS for the given attribute. Apart from returnsNewObject, all the defaults michael@0: are used, since the fallbility does not change based on the boolean values, michael@0: and the template will be discarded. michael@0: michael@0: CURRENT ASSUMPTIONS: michael@0: We assume that successCode for wrapping up return values cannot contain michael@0: failure conditions. michael@0: """ michael@0: return getWrapTemplateForType(type, descriptorProvider, 'result', None, michael@0: memberReturnsNewObject(member), "return false;\n", michael@0: False)[1] michael@0: michael@0: michael@0: def leafTypeNeedsCx(type, retVal): michael@0: return (type.isAny() or type.isObject() or michael@0: (retVal and type.isSpiderMonkeyInterface())) michael@0: michael@0: michael@0: def leafTypeNeedsScopeObject(type, retVal): michael@0: return retVal and type.isSpiderMonkeyInterface() michael@0: michael@0: michael@0: def leafTypeNeedsRooting(type): michael@0: return leafTypeNeedsCx(type, False) or type.isSpiderMonkeyInterface() michael@0: michael@0: michael@0: def typeNeedsRooting(type): michael@0: return typeMatchesLambda(type, michael@0: lambda t: leafTypeNeedsRooting(t)) michael@0: michael@0: michael@0: def typeNeedsCx(type, retVal=False): michael@0: return typeMatchesLambda(type, michael@0: lambda t: leafTypeNeedsCx(t, retVal)) michael@0: michael@0: michael@0: def typeNeedsScopeObject(type, retVal=False): michael@0: return typeMatchesLambda(type, michael@0: lambda t: leafTypeNeedsScopeObject(t, retVal)) michael@0: michael@0: michael@0: def typeMatchesLambda(type, func): michael@0: if type is None: michael@0: return False michael@0: if type.nullable(): michael@0: return typeMatchesLambda(type.inner, func) michael@0: if type.isSequence() or type.isMozMap() or type.isArray(): michael@0: return typeMatchesLambda(type.inner, func) michael@0: if type.isUnion(): michael@0: return any(typeMatchesLambda(t, func) for t in michael@0: type.unroll().flatMemberTypes) michael@0: if type.isDictionary(): michael@0: return dictionaryMatchesLambda(type.inner, func) michael@0: return func(type) michael@0: michael@0: michael@0: def dictionaryMatchesLambda(dictionary, func): michael@0: return (any(typeMatchesLambda(m.type, func) for m in dictionary.members) or michael@0: (dictionary.parent and dictionaryMatchesLambda(dictionary.parent, func))) michael@0: michael@0: michael@0: # Whenever this is modified, please update CGNativeMember.getRetvalInfo as michael@0: # needed to keep the types compatible. michael@0: def getRetvalDeclarationForType(returnType, descriptorProvider, michael@0: resultAlreadyAddRefed, michael@0: isMember=False): michael@0: """ michael@0: Returns a tuple containing four things: michael@0: michael@0: 1) A CGThing for the type of the return value, or None if there is no need michael@0: for a return value. michael@0: michael@0: 2) A value indicating the kind of ourparam to pass the value as. Valid michael@0: options are None to not pass as an out param at all, "ref" (to pass a michael@0: reference as an out param), and "ptr" (to pass a pointer as an out michael@0: param). michael@0: michael@0: 3) A CGThing for a tracer for the return value, or None if no tracing is michael@0: needed. michael@0: michael@0: 4) An argument string to pass to the retval declaration michael@0: constructor or None if there are no arguments. michael@0: """ michael@0: if returnType is None or returnType.isVoid(): michael@0: # Nothing to declare michael@0: return None, None, None, None michael@0: if returnType.isPrimitive() and returnType.tag() in builtinNames: michael@0: result = CGGeneric(builtinNames[returnType.tag()]) michael@0: if returnType.nullable(): michael@0: result = CGTemplatedType("Nullable", result) michael@0: return result, None, None, None michael@0: if returnType.isDOMString(): michael@0: if isMember: michael@0: return CGGeneric("nsString"), "ref", None, None michael@0: return CGGeneric("DOMString"), "ref", None, None michael@0: if returnType.isByteString(): michael@0: return CGGeneric("nsCString"), "ref", None, None michael@0: if returnType.isEnum(): michael@0: result = CGGeneric(returnType.unroll().inner.identifier.name) michael@0: if returnType.nullable(): michael@0: result = CGTemplatedType("Nullable", result) michael@0: return result, None, None, None michael@0: if returnType.isGeckoInterface(): michael@0: result = CGGeneric(descriptorProvider.getDescriptor( michael@0: returnType.unroll().inner.identifier.name).nativeType) michael@0: if descriptorProvider.getDescriptor( michael@0: returnType.unroll().inner.identifier.name).nativeOwnership == 'owned': michael@0: result = CGTemplatedType("nsAutoPtr", result) michael@0: elif resultAlreadyAddRefed: michael@0: result = CGTemplatedType("nsRefPtr", result) michael@0: else: michael@0: result = CGWrapper(result, post="*") michael@0: return result, None, None, None michael@0: if returnType.isCallback(): michael@0: name = returnType.unroll().identifier.name michael@0: return CGGeneric("nsRefPtr<%s>" % name), None, None, None michael@0: if returnType.isAny(): michael@0: if isMember: michael@0: return CGGeneric("JS::Value"), None, None, None michael@0: return CGGeneric("JS::Rooted"), "ptr", None, "cx" michael@0: if returnType.isObject() or returnType.isSpiderMonkeyInterface(): michael@0: if isMember: michael@0: return CGGeneric("JSObject*"), None, None, None michael@0: return CGGeneric("JS::Rooted"), "ptr", None, "cx" michael@0: if returnType.isSequence(): michael@0: nullable = returnType.nullable() michael@0: if nullable: michael@0: returnType = returnType.inner michael@0: # If our result is already addrefed, use the right type in the michael@0: # sequence argument here. michael@0: result, _, _, _ = getRetvalDeclarationForType(returnType.inner, michael@0: descriptorProvider, michael@0: resultAlreadyAddRefed, michael@0: isMember="Sequence") michael@0: # While we have our inner type, set up our rooter, if needed michael@0: if not isMember and typeNeedsRooting(returnType): michael@0: rooter = CGGeneric("SequenceRooter<%s > resultRooter(cx, &result);\n" % michael@0: result.define()) michael@0: else: michael@0: rooter = None michael@0: result = CGTemplatedType("nsTArray", result) michael@0: if nullable: michael@0: result = CGTemplatedType("Nullable", result) michael@0: return result, "ref", rooter, None michael@0: if returnType.isMozMap(): michael@0: nullable = returnType.nullable() michael@0: if nullable: michael@0: returnType = returnType.inner michael@0: # If our result is already addrefed, use the right type in the michael@0: # MozMap argument here. michael@0: result, _, _, _ = getRetvalDeclarationForType(returnType.inner, michael@0: descriptorProvider, michael@0: resultAlreadyAddRefed, michael@0: isMember="MozMap") michael@0: # While we have our inner type, set up our rooter, if needed michael@0: if not isMember and typeNeedsRooting(returnType): michael@0: rooter = CGGeneric("MozMapRooter<%s> resultRooter(cx, &result);\n" % michael@0: result.define()) michael@0: else: michael@0: rooter = None michael@0: result = CGTemplatedType("MozMap", result) michael@0: if nullable: michael@0: result = CGTemplatedType("Nullable", result) michael@0: return result, "ref", rooter, None michael@0: if returnType.isDictionary(): michael@0: nullable = returnType.nullable() michael@0: dictName = CGDictionary.makeDictionaryName(returnType.unroll().inner) michael@0: result = CGGeneric(dictName) michael@0: if not isMember and typeNeedsRooting(returnType): michael@0: if nullable: michael@0: result = CGTemplatedType("NullableRootedDictionary", result) michael@0: else: michael@0: result = CGTemplatedType("RootedDictionary", result) michael@0: resultArgs = "cx" michael@0: else: michael@0: if nullable: michael@0: result = CGTemplatedType("Nullable", result) michael@0: resultArgs = None michael@0: return result, "ref", None, resultArgs michael@0: if returnType.isUnion(): michael@0: result = CGGeneric(CGUnionStruct.unionTypeName(returnType.unroll(), True)) michael@0: if not isMember and typeNeedsRooting(returnType): michael@0: if returnType.nullable(): michael@0: result = CGTemplatedType("NullableRootedUnion", result) michael@0: else: michael@0: result = CGTemplatedType("RootedUnion", result) michael@0: resultArgs = "cx" michael@0: else: michael@0: if returnType.nullable(): michael@0: result = CGTemplatedType("Nullable", result) michael@0: resultArgs = None michael@0: return result, "ref", None, resultArgs michael@0: if returnType.isDate(): michael@0: result = CGGeneric("Date") michael@0: if returnType.nullable(): michael@0: result = CGTemplatedType("Nullable", result) michael@0: return result, None, None, None michael@0: raise TypeError("Don't know how to declare return value for %s" % michael@0: returnType) michael@0: michael@0: michael@0: def isResultAlreadyAddRefed(extendedAttributes): michael@0: return 'resultNotAddRefed' not in extendedAttributes michael@0: michael@0: michael@0: def needCx(returnType, arguments, extendedAttributes, considerTypes): michael@0: return (considerTypes and michael@0: (typeNeedsCx(returnType, True) or michael@0: any(typeNeedsCx(a.type) for a in arguments)) or michael@0: 'implicitJSContext' in extendedAttributes) michael@0: michael@0: michael@0: def needScopeObject(returnType, arguments, extendedAttributes, michael@0: isWrapperCached, considerTypes, isMember): michael@0: """ michael@0: isMember should be true if we're dealing with an attribute michael@0: annotated as [StoreInSlot]. michael@0: """ michael@0: return (considerTypes and not isWrapperCached and michael@0: ((not isMember and typeNeedsScopeObject(returnType, True)) or michael@0: any(typeNeedsScopeObject(a.type) for a in arguments))) michael@0: michael@0: michael@0: class CGCallGenerator(CGThing): michael@0: """ michael@0: A class to generate an actual call to a C++ object. Assumes that the C++ michael@0: object is stored in a variable whose name is given by the |object| argument. michael@0: michael@0: errorReport should be a CGThing for an error report or None if no michael@0: error reporting is needed. michael@0: """ michael@0: def __init__(self, errorReport, arguments, argsPre, returnType, michael@0: extendedAttributes, descriptorProvider, nativeMethodName, michael@0: static, object="self", argsPost=[]): michael@0: CGThing.__init__(self) michael@0: michael@0: assert errorReport is None or isinstance(errorReport, CGThing) michael@0: michael@0: isFallible = errorReport is not None michael@0: michael@0: resultAlreadyAddRefed = isResultAlreadyAddRefed(extendedAttributes) michael@0: result, resultOutParam, resultRooter, resultArgs = \ michael@0: getRetvalDeclarationForType( michael@0: returnType, descriptorProvider, resultAlreadyAddRefed) michael@0: michael@0: args = CGList([CGGeneric(arg) for arg in argsPre], ", ") michael@0: for a, name in arguments: michael@0: arg = CGGeneric(name) michael@0: michael@0: # Now constify the things that need it michael@0: def needsConst(a): michael@0: if a.type.isDictionary(): michael@0: return True michael@0: if a.type.isSequence(): michael@0: return True michael@0: if a.type.isMozMap(): michael@0: return True michael@0: # isObject() types are always a JS::Rooted, whether michael@0: # nullable or not, and it turns out a const JS::Rooted michael@0: # is not very helpful at all (in particular, it won't michael@0: # even convert to a JS::Handle). michael@0: # XXX bz Well, why not??? michael@0: if a.type.nullable() and not a.type.isObject(): michael@0: return True michael@0: if a.type.isString(): michael@0: return True michael@0: if a.optional and not a.defaultValue: michael@0: # If a.defaultValue, then it's not going to use an Optional, michael@0: # so doesn't need to be const just due to being optional. michael@0: # This also covers variadic arguments. michael@0: return True michael@0: if a.type.isUnion(): michael@0: return True michael@0: if a.type.isSpiderMonkeyInterface(): michael@0: return True michael@0: return False michael@0: if needsConst(a): michael@0: arg = CGWrapper(arg, pre="Constify(", post=")") michael@0: # And convert NonNull to T& michael@0: if (((a.type.isGeckoInterface() or a.type.isCallback()) and not a.type.nullable()) or michael@0: a.type.isDOMString()): michael@0: arg = CGWrapper(arg, pre="NonNullHelper(", post=")") michael@0: args.append(arg) michael@0: michael@0: # Return values that go in outparams go here michael@0: if resultOutParam is not None: michael@0: if resultOutParam is "ref": michael@0: args.append(CGGeneric("result")) michael@0: else: michael@0: assert resultOutParam is "ptr" michael@0: args.append(CGGeneric("&result")) michael@0: michael@0: if isFallible: michael@0: args.append(CGGeneric("rv")) michael@0: args.extend(CGGeneric(arg) for arg in argsPost) michael@0: michael@0: # Build up our actual call michael@0: self.cgRoot = CGList([]) michael@0: michael@0: call = CGGeneric(nativeMethodName) michael@0: if not static: michael@0: call = CGWrapper(call, pre="%s->" % object) michael@0: call = CGList([call, CGWrapper(args, pre="(", post=");\n")]) michael@0: if result is not None: michael@0: if resultRooter is not None: michael@0: self.cgRoot.prepend(resultRooter) michael@0: if resultArgs is not None: michael@0: resultArgs = "(%s)" % resultArgs michael@0: else: michael@0: resultArgs = "" michael@0: result = CGWrapper(result, post=(" result%s;\n" % resultArgs)) michael@0: self.cgRoot.prepend(result) michael@0: if not resultOutParam: michael@0: call = CGWrapper(call, pre="result = ") michael@0: michael@0: call = CGWrapper(call) michael@0: self.cgRoot.append(call) michael@0: michael@0: if isFallible: michael@0: self.cgRoot.prepend(CGGeneric("ErrorResult rv;\n")) michael@0: self.cgRoot.append(CGGeneric("rv.WouldReportJSException();\n")) michael@0: self.cgRoot.append(CGGeneric("if (rv.Failed()) {\n")) michael@0: self.cgRoot.append(CGIndenter(errorReport)) michael@0: self.cgRoot.append(CGGeneric("}\n")) michael@0: michael@0: def define(self): michael@0: return self.cgRoot.define() michael@0: michael@0: michael@0: def getUnionMemberName(type): michael@0: if type.isGeckoInterface(): michael@0: return type.inner.identifier.name michael@0: if type.isEnum(): michael@0: return type.inner.identifier.name michael@0: if type.isArray() or type.isSequence(): michael@0: return str(type) michael@0: return type.name michael@0: michael@0: michael@0: class MethodNotNewObjectError(Exception): michael@0: def __init__(self, typename): michael@0: self.typename = typename michael@0: michael@0: # A counter for making sure that when we're wrapping up things in michael@0: # nested sequences we don't use the same variable name to iterate over michael@0: # different sequences. michael@0: sequenceWrapLevel = 0 michael@0: michael@0: michael@0: def wrapTypeIntoCurrentCompartment(type, value, isMember=True): michael@0: """ michael@0: Take the thing named by "value" and if it contains "any", michael@0: "object", or spidermonkey-interface types inside return a CGThing michael@0: that will wrap them into the current compartment. michael@0: """ michael@0: if type.isAny(): michael@0: assert not type.nullable() michael@0: if isMember: michael@0: value = "JS::MutableHandle::fromMarkedLocation(&%s)" % value michael@0: else: michael@0: value = "&" + value michael@0: return CGGeneric("if (!JS_WrapValue(cx, %s)) {\n" michael@0: " return false;\n" michael@0: "}\n" % value) michael@0: michael@0: if type.isObject(): michael@0: if isMember: michael@0: value = "JS::MutableHandle::fromMarkedLocation(&%s)" % value michael@0: else: michael@0: value = "&" + value michael@0: return CGGeneric("if (!JS_WrapObject(cx, %s)) {\n" michael@0: " return false;\n" michael@0: "}\n" % value) michael@0: michael@0: if type.isSpiderMonkeyInterface(): michael@0: origValue = value michael@0: if type.nullable(): michael@0: value = "%s.Value()" % value michael@0: wrapCode = CGGeneric("if (!%s.WrapIntoNewCompartment(cx)) {\n" michael@0: " return false;\n" michael@0: "}\n" % value) michael@0: if type.nullable(): michael@0: wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue) michael@0: return wrapCode michael@0: michael@0: if type.isSequence(): michael@0: origValue = value michael@0: origType = type michael@0: if type.nullable(): michael@0: type = type.inner michael@0: value = "%s.Value()" % value michael@0: global sequenceWrapLevel michael@0: index = "indexName%d" % sequenceWrapLevel michael@0: sequenceWrapLevel += 1 michael@0: wrapElement = wrapTypeIntoCurrentCompartment(type.inner, michael@0: "%s[%s]" % (value, index)) michael@0: sequenceWrapLevel -= 1 michael@0: if not wrapElement: michael@0: return None michael@0: wrapCode = CGWrapper(CGIndenter(wrapElement), michael@0: pre=("for (uint32_t %s = 0; %s < %s.Length(); ++%s) {\n" % michael@0: (index, index, value, index)), michael@0: post="}\n") michael@0: if origType.nullable(): michael@0: wrapCode = CGIfWrapper(wrapCode, "!%s.IsNull()" % origValue) michael@0: return wrapCode michael@0: michael@0: if type.isDictionary(): michael@0: assert not type.nullable() michael@0: myDict = type.inner michael@0: memberWraps = [] michael@0: while myDict: michael@0: for member in myDict.members: michael@0: memberWrap = wrapArgIntoCurrentCompartment( michael@0: member, michael@0: "%s.%s" % (value, CGDictionary.makeMemberName(member.identifier.name))) michael@0: if memberWrap: michael@0: memberWraps.append(memberWrap) michael@0: myDict = myDict.parent michael@0: return CGList(memberWraps) if len(memberWraps) != 0 else None michael@0: michael@0: if type.isUnion(): michael@0: memberWraps = [] michael@0: if type.nullable(): michael@0: type = type.inner michael@0: value = "%s.Value()" % value michael@0: for member in type.flatMemberTypes: michael@0: memberName = getUnionMemberName(member) michael@0: memberWrap = wrapTypeIntoCurrentCompartment( michael@0: member, "%s.GetAs%s()" % (value, memberName)) michael@0: if memberWrap: michael@0: memberWrap = CGIfWrapper( michael@0: memberWrap, "%s.Is%s()" % (value, memberName)) michael@0: memberWraps.append(memberWrap) michael@0: return CGList(memberWraps, "else ") if len(memberWraps) != 0 else None michael@0: michael@0: if (type.isString() or type.isPrimitive() or type.isEnum() or michael@0: type.isGeckoInterface() or type.isCallback() or type.isDate()): michael@0: # All of these don't need wrapping michael@0: return None michael@0: michael@0: raise TypeError("Unknown type; we don't know how to wrap it in constructor " michael@0: "arguments: %s" % type) michael@0: michael@0: michael@0: def wrapArgIntoCurrentCompartment(arg, value, isMember=True): michael@0: """ michael@0: As wrapTypeIntoCurrentCompartment but handles things being optional michael@0: """ michael@0: origValue = value michael@0: isOptional = arg.optional and not arg.defaultValue michael@0: if isOptional: michael@0: value = value + ".Value()" michael@0: wrap = wrapTypeIntoCurrentCompartment(arg.type, value, isMember) michael@0: if wrap and isOptional: michael@0: wrap = CGIfWrapper(wrap, "%s.WasPassed()" % origValue) michael@0: return wrap michael@0: michael@0: michael@0: class CGPerSignatureCall(CGThing): michael@0: """ michael@0: This class handles the guts of generating code for a particular michael@0: call signature. A call signature consists of four things: michael@0: michael@0: 1) A return type, which can be None to indicate that there is no michael@0: actual return value (e.g. this is an attribute setter) or an michael@0: IDLType if there's an IDL type involved (including |void|). michael@0: 2) An argument list, which is allowed to be empty. michael@0: 3) A name of a native method to call. michael@0: 4) Whether or not this method is static. michael@0: michael@0: We also need to know whether this is a method or a getter/setter michael@0: to do error reporting correctly. michael@0: michael@0: The idlNode parameter can be either a method or an attr. We can query michael@0: |idlNode.identifier| in both cases, so we can be agnostic between the two. michael@0: """ michael@0: # XXXbz For now each entry in the argument list is either an michael@0: # IDLArgument or a FakeArgument, but longer-term we may want to michael@0: # have ways of flagging things like JSContext* or optional_argc in michael@0: # there. michael@0: michael@0: def __init__(self, returnType, arguments, nativeMethodName, static, michael@0: descriptor, idlNode, argConversionStartsAt=0, getter=False, michael@0: setter=False, isConstructor=False): michael@0: assert idlNode.isMethod() == (not getter and not setter) michael@0: assert idlNode.isAttr() == (getter or setter) michael@0: # Constructors are always static michael@0: assert not isConstructor or static michael@0: michael@0: CGThing.__init__(self) michael@0: self.returnType = returnType michael@0: self.descriptor = descriptor michael@0: self.idlNode = idlNode michael@0: self.extendedAttributes = descriptor.getExtendedAttributes(idlNode, michael@0: getter=getter, michael@0: setter=setter) michael@0: self.arguments = arguments michael@0: self.argCount = len(arguments) michael@0: cgThings = [] michael@0: lenientFloatCode = None michael@0: if idlNode.getExtendedAttribute('LenientFloat') is not None: michael@0: if setter: michael@0: lenientFloatCode = "return true;\n" michael@0: elif idlNode.isMethod(): michael@0: lenientFloatCode = ("args.rval().setUndefined();\n" michael@0: "return true;\n") michael@0: michael@0: argsPre = [] michael@0: if static: michael@0: nativeMethodName = "%s::%s" % (descriptor.nativeType, michael@0: nativeMethodName) michael@0: # If we're a constructor, "obj" may not be a function, so calling michael@0: # XrayAwareCalleeGlobal() on it is not safe. Of course in the michael@0: # constructor case either "obj" is an Xray or we're already in the michael@0: # content compartment, not the Xray compartment, so just michael@0: # constructing the GlobalObject from "obj" is fine. michael@0: if isConstructor: michael@0: objForGlobalObject = "obj" michael@0: else: michael@0: objForGlobalObject = "xpc::XrayAwareCalleeGlobal(obj)" michael@0: cgThings.append(CGGeneric(fill( michael@0: """ michael@0: GlobalObject global(cx, ${obj}); michael@0: if (global.Failed()) { michael@0: return false; michael@0: } michael@0: michael@0: """, michael@0: obj=objForGlobalObject))) michael@0: argsPre.append("global") michael@0: michael@0: # For JS-implemented interfaces we do not want to base the michael@0: # needsCx decision on the types involved, just on our extended michael@0: # attributes. michael@0: needsCx = needCx(returnType, arguments, self.extendedAttributes, michael@0: not descriptor.interface.isJSImplemented()) michael@0: if needsCx and not (static and descriptor.workers): michael@0: argsPre.append("cx") michael@0: michael@0: needsUnwrap = False michael@0: argsPost = [] michael@0: if isConstructor: michael@0: needsUnwrap = True michael@0: needsUnwrappedVar = False michael@0: unwrappedVar = "obj" michael@0: elif descriptor.interface.isJSImplemented(): michael@0: needsUnwrap = True michael@0: needsUnwrappedVar = True michael@0: argsPost.append("js::GetObjectCompartment(unwrappedObj.empty() ? obj : unwrappedObj.ref())") michael@0: elif needScopeObject(returnType, arguments, self.extendedAttributes, michael@0: descriptor.wrapperCache, True, michael@0: idlNode.getExtendedAttribute("StoreInSlot")): michael@0: needsUnwrap = True michael@0: needsUnwrappedVar = True michael@0: argsPre.append("unwrappedObj.empty() ? obj : unwrappedObj.ref()") michael@0: michael@0: if needsUnwrap and needsUnwrappedVar: michael@0: # We cannot assign into obj because it's a Handle, not a michael@0: # MutableHandle, so we need a separate Rooted. michael@0: cgThings.append(CGGeneric("Maybe > unwrappedObj;\n")) michael@0: unwrappedVar = "unwrappedObj.ref()" michael@0: michael@0: if idlNode.isMethod() and idlNode.isLegacycaller(): michael@0: # If we can have legacycaller with identifier, we can't michael@0: # just use the idlNode to determine whether we're michael@0: # generating code for the legacycaller or not. michael@0: assert idlNode.isIdentifierLess() michael@0: # Pass in our thisVal michael@0: argsPre.append("args.thisv()") michael@0: michael@0: ourName = "%s.%s" % (descriptor.interface.identifier.name, michael@0: idlNode.identifier.name) michael@0: if idlNode.isMethod(): michael@0: argDescription = "argument %(index)d of " + ourName michael@0: elif setter: michael@0: argDescription = "value being assigned to %s" % ourName michael@0: else: michael@0: assert self.argCount == 0 michael@0: michael@0: if needsUnwrap: michael@0: # It's very important that we construct our unwrappedObj, if we need michael@0: # to do it, before we might start setting up Rooted things for our michael@0: # arguments, so that we don't violate the stack discipline Rooted michael@0: # depends on. michael@0: cgThings.append(CGGeneric( michael@0: "bool objIsXray = xpc::WrapperFactory::IsXrayWrapper(obj);\n")) michael@0: if needsUnwrappedVar: michael@0: cgThings.append(CGIfWrapper( michael@0: CGGeneric("unwrappedObj.construct(cx, obj);\n"), michael@0: "objIsXray")) michael@0: michael@0: for i in range(argConversionStartsAt, self.argCount): michael@0: cgThings.append( michael@0: CGArgumentConverter(arguments[i], i, self.descriptor, michael@0: argDescription % {"index": i + 1}, michael@0: invalidEnumValueFatal=not setter, michael@0: lenientFloatCode=lenientFloatCode)) michael@0: michael@0: if needsUnwrap: michael@0: # Something depends on having the unwrapped object, so unwrap it now. michael@0: xraySteps = [] michael@0: # XXXkhuey we should be able to MOZ_ASSERT that ${obj} is michael@0: # not null. michael@0: xraySteps.append( michael@0: CGGeneric(string.Template(dedent(""" michael@0: ${obj} = js::CheckedUnwrap(${obj}); michael@0: if (!${obj}) { michael@0: return false; michael@0: } michael@0: """)).substitute({'obj': unwrappedVar}))) michael@0: if isConstructor: michael@0: # If we're called via an xray, we need to enter the underlying michael@0: # object's compartment and then wrap up all of our arguments into michael@0: # that compartment as needed. This is all happening after we've michael@0: # already done the conversions from JS values to WebIDL (C++) michael@0: # values, so we only need to worry about cases where there are 'any' michael@0: # or 'object' types, or other things that we represent as actual michael@0: # JSAPI types, present. Effectively, we're emulating a michael@0: # CrossCompartmentWrapper, but working with the C++ types, not the michael@0: # original list of JS::Values. michael@0: cgThings.append(CGGeneric("Maybe ac;\n")) michael@0: xraySteps.append(CGGeneric("ac.construct(cx, obj);\n")) michael@0: xraySteps.extend( michael@0: wrapArgIntoCurrentCompartment(arg, argname, isMember=False) michael@0: for arg, argname in self.getArguments()) michael@0: michael@0: cgThings.append( michael@0: CGIfWrapper(CGList(xraySteps), michael@0: "objIsXray")) michael@0: michael@0: cgThings.append(CGCallGenerator( michael@0: self.getErrorReport() if self.isFallible() else None, michael@0: self.getArguments(), argsPre, returnType, michael@0: self.extendedAttributes, descriptor, nativeMethodName, michael@0: static, argsPost=argsPost)) michael@0: self.cgRoot = CGList(cgThings) michael@0: michael@0: def getArguments(self): michael@0: return [(a, "arg" + str(i)) for i, a in enumerate(self.arguments)] michael@0: michael@0: def isFallible(self): michael@0: return 'infallible' not in self.extendedAttributes michael@0: michael@0: def wrap_return_value(self): michael@0: returnsNewObject = memberReturnsNewObject(self.idlNode) michael@0: if returnsNewObject: michael@0: # We better be returning addrefed things! michael@0: assert(isResultAlreadyAddRefed(self.extendedAttributes) or michael@0: # NewObject can return raw pointers to owned objects michael@0: (self.returnType.isGeckoInterface() and michael@0: self.descriptor.getDescriptor(self.returnType.unroll().inner.identifier.name).nativeOwnership == 'owned')) michael@0: michael@0: setSlot = self.idlNode.isAttr() and self.idlNode.slotIndex is not None michael@0: if setSlot: michael@0: # For attributes in slots, we want to do some michael@0: # post-processing once we've wrapped them. michael@0: successCode = "break;\n" michael@0: else: michael@0: successCode = None michael@0: michael@0: resultTemplateValues = { michael@0: 'jsvalRef': 'args.rval()', michael@0: 'jsvalHandle': 'args.rval()', michael@0: 'returnsNewObject': returnsNewObject, michael@0: 'successCode': successCode, michael@0: 'obj': "reflector" if setSlot else "obj" michael@0: } michael@0: try: michael@0: wrapCode = wrapForType(self.returnType, self.descriptor, resultTemplateValues) michael@0: except MethodNotNewObjectError, err: michael@0: assert not returnsNewObject michael@0: raise TypeError("%s being returned from non-NewObject method or property %s.%s" % michael@0: (err.typename, michael@0: self.descriptor.interface.identifier.name, michael@0: self.idlNode.identifier.name)) michael@0: if setSlot: michael@0: # We need to make sure that our initial wrapping is done in the michael@0: # reflector compartment, but that we finally set args.rval() in the michael@0: # caller compartment. We also need to make sure that the actual michael@0: # wrapping steps happen inside a do/while that they can break out michael@0: # of. michael@0: michael@0: # postSteps are the steps that run while we're still in the michael@0: # reflector compartment but after we've finished the initial michael@0: # wrapping into args.rval(). michael@0: postSteps = "" michael@0: if self.idlNode.getExtendedAttribute("Frozen"): michael@0: assert self.idlNode.type.isSequence() or self.idlNode.type.isDictionary() michael@0: freezeValue = CGGeneric( michael@0: "JS::Rooted rvalObj(cx, &args.rval().toObject());\n" michael@0: "if (!JS_FreezeObject(cx, rvalObj)) {\n" michael@0: " return false;\n" michael@0: "}\n") michael@0: if self.idlNode.type.nullable(): michael@0: freezeValue = CGIfWrapper(freezeValue, michael@0: "args.rval().isObject()") michael@0: postSteps += freezeValue.define() michael@0: postSteps += ("js::SetReservedSlot(reflector, %s, args.rval());\n" % michael@0: memberReservedSlot(self.idlNode)) michael@0: # For the case of Cached attributes, go ahead and preserve our michael@0: # wrapper if needed. We need to do this because otherwise the michael@0: # wrapper could get garbage-collected and the cached value would michael@0: # suddenly disappear, but the whole premise of cached values is that michael@0: # they never change without explicit action on someone's part. We michael@0: # don't do this for StoreInSlot, since those get dealt with during michael@0: # wrapper setup, and failure would involve us trying to clear an michael@0: # already-preserved wrapper. michael@0: if (self.idlNode.getExtendedAttribute("Cached") and michael@0: self.descriptor.wrapperCache): michael@0: postSteps += "PreserveWrapper(self);\n" michael@0: michael@0: wrapCode = fill( michael@0: """ michael@0: { // Make sure we wrap and store in the slot in reflector's compartment michael@0: JSAutoCompartment ac(cx, reflector); michael@0: do { // block we break out of when done wrapping michael@0: $*{wrapCode} michael@0: } while (0); michael@0: $*{postSteps} michael@0: } michael@0: // And now make sure args.rval() is in the caller compartment michael@0: return ${maybeWrap}(cx, args.rval()); michael@0: """, michael@0: wrapCode=wrapCode, michael@0: postSteps=postSteps, michael@0: maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type)) michael@0: return wrapCode michael@0: michael@0: def getErrorReport(self): michael@0: jsImplemented = "" michael@0: if self.descriptor.interface.isJSImplemented(): michael@0: jsImplemented = ", true" michael@0: return CGGeneric('return ThrowMethodFailedWithDetails(cx, rv, "%s", "%s"%s);\n' michael@0: % (self.descriptor.interface.identifier.name, michael@0: self.idlNode.identifier.name, michael@0: jsImplemented)) michael@0: michael@0: def define(self): michael@0: return (self.cgRoot.define() + self.wrap_return_value()) michael@0: michael@0: michael@0: class CGSwitch(CGList): michael@0: """ michael@0: A class to generate code for a switch statement. michael@0: michael@0: Takes three constructor arguments: an expression, a list of cases, michael@0: and an optional default. michael@0: michael@0: Each case is a CGCase. The default is a CGThing for the body of michael@0: the default case, if any. michael@0: """ michael@0: def __init__(self, expression, cases, default=None): michael@0: CGList.__init__(self, [CGIndenter(c) for c in cases]) michael@0: self.prepend(CGGeneric("switch (" + expression + ") {\n")) michael@0: if default is not None: michael@0: self.append( michael@0: CGIndenter( michael@0: CGWrapper( michael@0: CGIndenter(default), michael@0: pre="default: {\n", michael@0: post=" break;\n}\n"))) michael@0: michael@0: self.append(CGGeneric("}\n")) michael@0: michael@0: michael@0: class CGCase(CGList): michael@0: """ michael@0: A class to generate code for a case statement. michael@0: michael@0: Takes three constructor arguments: an expression, a CGThing for michael@0: the body (allowed to be None if there is no body), and an optional michael@0: argument (defaulting to False) for whether to fall through. michael@0: """ michael@0: def __init__(self, expression, body, fallThrough=False): michael@0: CGList.__init__(self, []) michael@0: self.append(CGGeneric("case " + expression + ": {\n")) michael@0: bodyList = CGList([body]) michael@0: if fallThrough: michael@0: bodyList.append(CGGeneric("/* Fall through */\n")) michael@0: else: michael@0: bodyList.append(CGGeneric("break;\n")) michael@0: self.append(CGIndenter(bodyList)) michael@0: self.append(CGGeneric("}\n")) michael@0: michael@0: michael@0: class CGMethodCall(CGThing): michael@0: """ michael@0: A class to generate selection of a method signature from a set of michael@0: signatures and generation of a call to that signature. michael@0: """ michael@0: def __init__(self, nativeMethodName, static, descriptor, method, michael@0: isConstructor=False, constructorName=None): michael@0: CGThing.__init__(self) michael@0: michael@0: if isConstructor: michael@0: assert constructorName is not None michael@0: methodName = constructorName michael@0: else: michael@0: methodName = "%s.%s" % (descriptor.interface.identifier.name, method.identifier.name) michael@0: argDesc = "argument %d of " + methodName michael@0: michael@0: def requiredArgCount(signature): michael@0: arguments = signature[1] michael@0: if len(arguments) == 0: michael@0: return 0 michael@0: requiredArgs = len(arguments) michael@0: while requiredArgs and arguments[requiredArgs-1].optional: michael@0: requiredArgs -= 1 michael@0: return requiredArgs michael@0: michael@0: def getPerSignatureCall(signature, argConversionStartsAt=0): michael@0: return CGPerSignatureCall(signature[0], signature[1], michael@0: nativeMethodName, static, descriptor, michael@0: method, michael@0: argConversionStartsAt=argConversionStartsAt, michael@0: isConstructor=isConstructor) michael@0: michael@0: signatures = method.signatures() michael@0: if len(signatures) == 1: michael@0: # Special case: we can just do a per-signature method call michael@0: # here for our one signature and not worry about switching michael@0: # on anything. michael@0: signature = signatures[0] michael@0: self.cgRoot = CGList([CGIndenter(getPerSignatureCall(signature))]) michael@0: requiredArgs = requiredArgCount(signature) michael@0: michael@0: if requiredArgs > 0: michael@0: code = indent(fill( # BOGUS extra blank line michael@0: """ michael@0: michael@0: if (args.length() < ${requiredArgs}) { michael@0: return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${methodName}"); michael@0: } michael@0: """, michael@0: requiredArgs=requiredArgs, michael@0: methodName=methodName)) michael@0: self.cgRoot.prepend(CGGeneric(code)) michael@0: return michael@0: michael@0: # Need to find the right overload michael@0: maxArgCount = method.maxArgCount michael@0: allowedArgCounts = method.allowedArgCounts michael@0: michael@0: argCountCases = [] michael@0: for argCountIdx, argCount in enumerate(allowedArgCounts): michael@0: possibleSignatures = method.signaturesForArgCount(argCount) michael@0: michael@0: # Try to optimize away cases when the next argCount in the list michael@0: # will have the same code as us; if it does, we can fall through to michael@0: # that case. michael@0: if argCountIdx+1 < len(allowedArgCounts): michael@0: nextPossibleSignatures = \ michael@0: method.signaturesForArgCount(allowedArgCounts[argCountIdx+1]) michael@0: else: michael@0: nextPossibleSignatures = None michael@0: if possibleSignatures == nextPossibleSignatures: michael@0: # Same set of signatures means we better have the same michael@0: # distinguishing index. So we can in fact just fall through to michael@0: # the next case here. michael@0: assert (len(possibleSignatures) == 1 or michael@0: (method.distinguishingIndexForArgCount(argCount) == michael@0: method.distinguishingIndexForArgCount(allowedArgCounts[argCountIdx+1]))) michael@0: argCountCases.append(CGCase(str(argCount), None, True)) michael@0: continue michael@0: michael@0: if len(possibleSignatures) == 1: michael@0: # easy case! michael@0: signature = possibleSignatures[0] michael@0: argCountCases.append( michael@0: CGCase(str(argCount), getPerSignatureCall(signature))) michael@0: continue michael@0: michael@0: distinguishingIndex = method.distinguishingIndexForArgCount(argCount) michael@0: michael@0: def distinguishingArgument(signature): michael@0: args = signature[1] michael@0: if distinguishingIndex < len(args): michael@0: return args[distinguishingIndex] michael@0: assert args[-1].variadic michael@0: return args[-1] michael@0: michael@0: def distinguishingType(signature): michael@0: return distinguishingArgument(signature).type michael@0: michael@0: for sig in possibleSignatures: michael@0: # We should not have "any" args at distinguishingIndex, michael@0: # since we have multiple possible signatures remaining, michael@0: # but "any" is never distinguishable from anything else. michael@0: assert not distinguishingType(sig).isAny() michael@0: # We can't handle unions at the distinguishing index. michael@0: if distinguishingType(sig).isUnion(): michael@0: raise TypeError("No support for unions as distinguishing " michael@0: "arguments yet: %s" % michael@0: distinguishingArgument(sig).location) michael@0: # We don't support variadics as the distinguishingArgument yet. michael@0: # If you want to add support, consider this case: michael@0: # michael@0: # void(long... foo); michael@0: # void(long bar, Int32Array baz); michael@0: # michael@0: # in which we have to convert argument 0 to long before picking michael@0: # an overload... but all the variadic stuff needs to go into a michael@0: # single array in case we pick that overload, so we have to have michael@0: # machinery for converting argument 0 to long and then either michael@0: # placing it in the variadic bit or not. Or something. We may michael@0: # be able to loosen this restriction if the variadic arg is in michael@0: # fact at distinguishingIndex, perhaps. Would need to michael@0: # double-check. michael@0: if distinguishingArgument(sig).variadic: michael@0: raise TypeError("No support for variadics as distinguishing " michael@0: "arguments yet: %s" % michael@0: distinguishingArgument(sig).location) michael@0: michael@0: # Convert all our arguments up to the distinguishing index. michael@0: # Doesn't matter which of the possible signatures we use, since michael@0: # they all have the same types up to that point; just use michael@0: # possibleSignatures[0] michael@0: caseBody = [CGArgumentConverter(possibleSignatures[0][1][i], michael@0: i, descriptor, michael@0: argDesc % (i + 1)) michael@0: for i in range(0, distinguishingIndex)] michael@0: michael@0: # Select the right overload from our set. michael@0: distinguishingArg = "args[%d]" % distinguishingIndex michael@0: michael@0: def tryCall(signature, indent, isDefinitelyObject=False, michael@0: isNullOrUndefined=False): michael@0: assert not isDefinitelyObject or not isNullOrUndefined michael@0: assert isDefinitelyObject or isNullOrUndefined michael@0: if isDefinitelyObject: michael@0: failureCode = "break;\n" michael@0: else: michael@0: failureCode = None michael@0: type = distinguishingType(signature) michael@0: # The argument at index distinguishingIndex can't possibly be michael@0: # unset here, because we've already checked that argc is large michael@0: # enough that we can examine this argument. But note that we michael@0: # still want to claim that optional arguments are optional, in michael@0: # case undefined was passed in. michael@0: argIsOptional = (distinguishingArgument(signature).optional and michael@0: not distinguishingArgument(signature).defaultValue) michael@0: testCode = instantiateJSToNativeConversion( michael@0: getJSToNativeConversionInfo(type, descriptor, michael@0: failureCode=failureCode, michael@0: isDefinitelyObject=isDefinitelyObject, michael@0: isNullOrUndefined=isNullOrUndefined, michael@0: isOptional=argIsOptional, michael@0: sourceDescription=(argDesc % (distinguishingIndex + 1))), michael@0: { michael@0: "declName": "arg%d" % distinguishingIndex, michael@0: "holderName": ("arg%d" % distinguishingIndex) + "_holder", michael@0: "val": distinguishingArg, michael@0: "mutableVal": distinguishingArg, michael@0: "obj": "obj", michael@0: "haveValue": "args.hasDefined(%d)" % distinguishingIndex michael@0: }, michael@0: checkForValue=argIsOptional) michael@0: caseBody.append(CGIndenter(testCode, indent)) michael@0: michael@0: # If we got this far, we know we unwrapped to the right michael@0: # C++ type, so just do the call. Start conversion with michael@0: # distinguishingIndex + 1, since we already converted michael@0: # distinguishingIndex. michael@0: caseBody.append(CGIndenter( michael@0: getPerSignatureCall(signature, distinguishingIndex + 1), michael@0: indent)) michael@0: michael@0: def hasConditionalConversion(type): michael@0: """ michael@0: Return whether the argument conversion for this type will be michael@0: conditional on the type of incoming JS value. For example, for michael@0: interface types the conversion is conditional on the incoming michael@0: value being isObject(). michael@0: michael@0: For the types for which this returns false, we do not have to michael@0: output extra isUndefined() or isNullOrUndefined() cases, because michael@0: null/undefined values will just fall through into our michael@0: unconditional conversion. michael@0: """ michael@0: if type.isString() or type.isEnum(): michael@0: return False michael@0: if type.isBoolean(): michael@0: distinguishingTypes = (distinguishingType(s) for s in michael@0: possibleSignatures) michael@0: return any(t.isString() or t.isEnum() or t.isNumeric() michael@0: for t in distinguishingTypes) michael@0: if type.isNumeric(): michael@0: distinguishingTypes = (distinguishingType(s) for s in michael@0: possibleSignatures) michael@0: return any(t.isString() or t.isEnum() michael@0: for t in distinguishingTypes) michael@0: return True michael@0: michael@0: def needsNullOrUndefinedCase(type): michael@0: """ michael@0: Return true if the type needs a special isNullOrUndefined() case michael@0: """ michael@0: return ((type.nullable() and michael@0: hasConditionalConversion(type)) or michael@0: type.isDictionary()) michael@0: michael@0: # First check for undefined and optional distinguishing arguments michael@0: # and output a special branch for that case. Note that we don't michael@0: # use distinguishingArgument here because we actualy want to michael@0: # exclude variadic arguments. Also note that we skip this check if michael@0: # we plan to output a isNullOrUndefined() special case for this michael@0: # argument anyway, since that will subsume our isUndefined() check. michael@0: # This is safe, because there can be at most one nullable michael@0: # distinguishing argument, so if we're it we'll definitely get michael@0: # picked up by the nullable handling. Also, we can skip this check michael@0: # if the argument has an unconditional conversion later on. michael@0: undefSigs = [s for s in possibleSignatures if michael@0: distinguishingIndex < len(s[1]) and michael@0: s[1][distinguishingIndex].optional and michael@0: hasConditionalConversion(s[1][distinguishingIndex].type) and michael@0: not needsNullOrUndefinedCase(s[1][distinguishingIndex].type)] michael@0: # Can't have multiple signatures with an optional argument at the michael@0: # same index. michael@0: assert len(undefSigs) < 2 michael@0: if len(undefSigs) > 0: michael@0: caseBody.append(CGGeneric("if (%s.isUndefined()) {\n" % michael@0: distinguishingArg)) michael@0: tryCall(undefSigs[0], 2, isNullOrUndefined=True) michael@0: caseBody.append(CGGeneric("}\n")) michael@0: michael@0: # Next, check for null or undefined. That means looking for michael@0: # nullable arguments at the distinguishing index and outputting a michael@0: # separate branch for them. But if the nullable argument has an michael@0: # unconditional conversion, we don't need to do that. The reason michael@0: # for that is that at most one argument at the distinguishing index michael@0: # is nullable (since two nullable arguments are not michael@0: # distinguishable), and null/undefined values will always fall michael@0: # through to the unconditional conversion we have, if any, since michael@0: # they will fail whatever the conditions on the input value are for michael@0: # our other conversions. michael@0: nullOrUndefSigs = [s for s in possibleSignatures michael@0: if needsNullOrUndefinedCase(distinguishingType(s))] michael@0: # Can't have multiple nullable types here michael@0: assert len(nullOrUndefSigs) < 2 michael@0: if len(nullOrUndefSigs) > 0: michael@0: caseBody.append(CGGeneric("if (%s.isNullOrUndefined()) {\n" % michael@0: distinguishingArg)) michael@0: tryCall(nullOrUndefSigs[0], 2, isNullOrUndefined=True) michael@0: caseBody.append(CGGeneric("}\n")) michael@0: michael@0: # Now check for distinguishingArg being various kinds of objects. michael@0: # The spec says to check for the following things in order: michael@0: # 1) A platform object that's not a platform array object, being michael@0: # passed to an interface or "object" arg. michael@0: # 2) A Date object being passed to a Date or "object" arg. michael@0: # 3) A RegExp object being passed to a RegExp or "object" arg. michael@0: # 4) A callable object being passed to a callback or "object" arg. michael@0: # 5) Any non-Date and non-RegExp object being passed to a michael@0: # array or sequence or callback interface dictionary or michael@0: # "object" arg. michael@0: # michael@0: # We can can coalesce these five cases together, as long as we make michael@0: # sure to check whether our object works as an interface argument michael@0: # before checking whether it works as an arraylike or dictionary or michael@0: # callback function or callback interface. michael@0: michael@0: # First grab all the overloads that have a non-callback interface michael@0: # (which includes typed arrays and arraybuffers) at the michael@0: # distinguishing index. We can also include the ones that have an michael@0: # "object" here, since if those are present no other object-typed michael@0: # argument will be. michael@0: objectSigs = [ michael@0: s for s in possibleSignatures michael@0: if (distinguishingType(s).isObject() or michael@0: distinguishingType(s).isNonCallbackInterface())] michael@0: michael@0: # And all the overloads that take Date michael@0: objectSigs.extend(s for s in possibleSignatures michael@0: if distinguishingType(s).isDate()) michael@0: michael@0: # And all the overloads that take callbacks michael@0: objectSigs.extend(s for s in possibleSignatures michael@0: if distinguishingType(s).isCallback()) michael@0: michael@0: # Now append all the overloads that take an array or sequence or michael@0: # dictionary or callback interface: michael@0: objectSigs.extend(s for s in possibleSignatures michael@0: if (distinguishingType(s).isArray() or michael@0: distinguishingType(s).isSequence() or michael@0: distinguishingType(s).isDictionary() or michael@0: distinguishingType(s).isCallbackInterface())) michael@0: michael@0: # There might be more than one thing in objectSigs; we need to check michael@0: # which ones we unwrap to. michael@0: if len(objectSigs) > 0: michael@0: # Here it's enough to guard on our argument being an object. The michael@0: # code for unwrapping non-callback interfaces, typed arrays, michael@0: # sequences, arrays, and Dates will just bail out and move on to michael@0: # the next overload if the object fails to unwrap correctly, michael@0: # while "object" accepts any object anyway. We could even not michael@0: # do the isObject() check up front here, but in cases where we michael@0: # have multiple object overloads it makes sense to do it only michael@0: # once instead of for each overload. That will also allow the michael@0: # unwrapping test to skip having to do codegen for the michael@0: # null-or-undefined case, which we already handled above. michael@0: caseBody.append(CGGeneric("if (%s.isObject()) {\n" % michael@0: distinguishingArg)) michael@0: for sig in objectSigs: michael@0: caseBody.append(CGIndenter(CGGeneric("do {\n"))) michael@0: # Indent by 4, since we need to indent further michael@0: # than our "do" statement michael@0: tryCall(sig, 4, isDefinitelyObject=True) michael@0: caseBody.append(CGIndenter(CGGeneric("} while (0);\n"))) michael@0: michael@0: caseBody.append(CGGeneric("}\n")) michael@0: michael@0: # Now we only have to consider booleans, numerics, and strings. If michael@0: # we only have one of them, then we can just output it. But if not, michael@0: # then we need to output some of the cases conditionally: if we have michael@0: # a string overload, then boolean and numeric are conditional, and michael@0: # if not then boolean is conditional if we have a numeric overload. michael@0: def findUniqueSignature(filterLambda): michael@0: sigs = filter(filterLambda, possibleSignatures) michael@0: assert len(sigs) < 2 michael@0: if len(sigs) > 0: michael@0: return sigs[0] michael@0: return None michael@0: michael@0: stringSignature = findUniqueSignature( michael@0: lambda s: (distinguishingType(s).isString() or michael@0: distinguishingType(s).isEnum())) michael@0: numericSignature = findUniqueSignature( michael@0: lambda s: distinguishingType(s).isNumeric()) michael@0: booleanSignature = findUniqueSignature( michael@0: lambda s: distinguishingType(s).isBoolean()) michael@0: michael@0: if stringSignature or numericSignature: michael@0: booleanCondition = "%s.isBoolean()" michael@0: else: michael@0: booleanCondition = None michael@0: michael@0: if stringSignature: michael@0: numericCondition = "%s.isNumber()" michael@0: else: michael@0: numericCondition = None michael@0: michael@0: def addCase(sig, condition): michael@0: sigCode = getPerSignatureCall(sig, distinguishingIndex) michael@0: if condition: michael@0: sigCode = CGIfWrapper(sigCode, michael@0: condition % distinguishingArg) michael@0: caseBody.append(sigCode) michael@0: michael@0: if booleanSignature: michael@0: addCase(booleanSignature, booleanCondition) michael@0: if numericSignature: michael@0: addCase(numericSignature, numericCondition) michael@0: if stringSignature: michael@0: addCase(stringSignature, None) michael@0: michael@0: if (not booleanSignature and not numericSignature and michael@0: not stringSignature): michael@0: # Just throw; we have no idea what we're supposed to michael@0: # do with this. michael@0: caseBody.append(CGGeneric( michael@0: 'return ThrowErrorMessage(cx, MSG_OVERLOAD_RESOLUTION_FAILED, "%d", "%d", "%s");\n' % michael@0: (distinguishingIndex + 1, argCount, methodName))) michael@0: michael@0: argCountCases.append(CGCase(str(argCount), CGList(caseBody))) michael@0: michael@0: overloadCGThings = [] michael@0: overloadCGThings.append( michael@0: CGGeneric("unsigned argcount = std::min(args.length(), %du);\n" % michael@0: maxArgCount)) michael@0: overloadCGThings.append( michael@0: CGSwitch("argcount", michael@0: argCountCases, michael@0: # BOGUS extra blank line at end of default block michael@0: CGGeneric('return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "%s");\n\n' % michael@0: methodName))) michael@0: overloadCGThings.append( michael@0: CGGeneric('MOZ_CRASH("We have an always-returning default case");\n' michael@0: 'return false;\n')) michael@0: self.cgRoot = CGWrapper(CGIndenter(CGList(overloadCGThings)), michael@0: pre="\n") michael@0: michael@0: def define(self): michael@0: return self.cgRoot.define() michael@0: michael@0: michael@0: class CGGetterCall(CGPerSignatureCall): michael@0: """ michael@0: A class to generate a native object getter call for a particular IDL michael@0: getter. michael@0: """ michael@0: def __init__(self, returnType, nativeMethodName, descriptor, attr): michael@0: CGPerSignatureCall.__init__(self, returnType, [], nativeMethodName, michael@0: attr.isStatic(), descriptor, attr, michael@0: getter=True) michael@0: michael@0: michael@0: class FakeArgument(): michael@0: """ michael@0: A class that quacks like an IDLArgument. This is used to make michael@0: setters look like method calls or for special operations. michael@0: """ michael@0: def __init__(self, type, interfaceMember, name="arg", allowTreatNonCallableAsNull=False): michael@0: self.type = type michael@0: self.optional = False michael@0: self.variadic = False michael@0: self.defaultValue = None michael@0: self._allowTreatNonCallableAsNull = allowTreatNonCallableAsNull michael@0: self.treatNullAs = interfaceMember.treatNullAs michael@0: if isinstance(interfaceMember, IDLAttribute): michael@0: self.enforceRange = interfaceMember.enforceRange michael@0: self.clamp = interfaceMember.clamp michael@0: else: michael@0: self.enforceRange = False michael@0: self.clamp = False michael@0: michael@0: class FakeIdentifier(): michael@0: def __init__(self): michael@0: self.name = name michael@0: self.identifier = FakeIdentifier() michael@0: michael@0: def allowTreatNonCallableAsNull(self): michael@0: return self._allowTreatNonCallableAsNull michael@0: michael@0: michael@0: class CGSetterCall(CGPerSignatureCall): michael@0: """ michael@0: A class to generate a native object setter call for a particular IDL michael@0: setter. michael@0: """ michael@0: def __init__(self, argType, nativeMethodName, descriptor, attr): michael@0: CGPerSignatureCall.__init__(self, None, [FakeArgument(argType, attr, allowTreatNonCallableAsNull=True)], michael@0: nativeMethodName, attr.isStatic(), michael@0: descriptor, attr, setter=True) michael@0: michael@0: def wrap_return_value(self): michael@0: attr = self.idlNode michael@0: if self.descriptor.wrapperCache and attr.slotIndex is not None: michael@0: if attr.getExtendedAttribute("StoreInSlot"): michael@0: args = "cx, self" michael@0: else: michael@0: args = "self" michael@0: clearSlot = ("ClearCached%sValue(%s);\n" % michael@0: (MakeNativeName(self.idlNode.identifier.name), args)) michael@0: else: michael@0: clearSlot = "" michael@0: michael@0: # We have no return value michael@0: return ("\n" michael@0: "%s" michael@0: "return true;\n" % clearSlot) michael@0: michael@0: michael@0: class CGAbstractBindingMethod(CGAbstractStaticMethod): michael@0: """ michael@0: Common class to generate the JSNatives for all our methods, getters, and michael@0: setters. This will generate the function declaration and unwrap the michael@0: |this| object. Subclasses are expected to override the generate_code michael@0: function to do the rest of the work. This function should return a michael@0: CGThing which is already properly indented. michael@0: michael@0: getThisObj should be code for getting a JSObject* for the binding michael@0: object. If this is None, we will auto-generate code based on michael@0: descriptor to do the right thing. "" can be passed in if the michael@0: binding object is already stored in 'obj'. michael@0: michael@0: callArgs should be code for getting a JS::CallArgs into a variable michael@0: called 'args'. This can be "" if there is already such a variable michael@0: around. michael@0: michael@0: If allowCrossOriginThis is true, then this-unwrapping will first do an michael@0: UncheckedUnwrap and after that operate on the result. michael@0: """ michael@0: def __init__(self, descriptor, name, args, unwrapFailureCode=None, michael@0: getThisObj=None, michael@0: callArgs="JS::CallArgs args = JS::CallArgsFromVp(argc, vp);\n", michael@0: allowCrossOriginThis=False): michael@0: CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args) michael@0: michael@0: if unwrapFailureCode is None: michael@0: self.unwrapFailureCode = 'return ThrowErrorMessage(cx, MSG_THIS_DOES_NOT_IMPLEMENT_INTERFACE, "Value", "%s");\n' % descriptor.interface.identifier.name michael@0: else: michael@0: self.unwrapFailureCode = unwrapFailureCode michael@0: michael@0: if getThisObj == "": michael@0: self.getThisObj = None michael@0: else: michael@0: if getThisObj is None: michael@0: if descriptor.interface.isOnGlobalProtoChain(): michael@0: ensureCondition = "!args.thisv().isNullOrUndefined() && !args.thisv().isObject()" michael@0: getThisObj = "args.thisv().isObject() ? &args.thisv().toObject() : js::GetGlobalForObjectCrossCompartment(&args.callee())" michael@0: else: michael@0: ensureCondition = "!args.thisv().isObject()" michael@0: getThisObj = "&args.thisv().toObject()" michael@0: unwrapFailureCode = self.unwrapFailureCode % {'securityError': 'false'} michael@0: ensureThisObj = CGIfWrapper(CGGeneric(unwrapFailureCode), michael@0: ensureCondition) michael@0: else: michael@0: ensureThisObj = None michael@0: self.getThisObj = CGList( michael@0: [ensureThisObj, michael@0: CGGeneric("JS::Rooted obj(cx, %s);\n" % michael@0: getThisObj)]) michael@0: self.callArgs = callArgs michael@0: self.allowCrossOriginThis = allowCrossOriginThis michael@0: michael@0: def definition_body(self): michael@0: body = self.callArgs michael@0: if self.getThisObj is not None: michael@0: body += self.getThisObj.define() + "\n" michael@0: body += "%s* self;\n" % self.descriptor.nativeType michael@0: michael@0: # Our descriptor might claim that we're not castable, simply because michael@0: # we're someone's consequential interface. But for this-unwrapping, we michael@0: # know that we're the real deal. So fake a descriptor here for michael@0: # consumption by CastableObjectUnwrapper. michael@0: body += str(CastableObjectUnwrapper( michael@0: self.descriptor, michael@0: "obj", "self", self.unwrapFailureCode, michael@0: allowCrossOriginObj=self.allowCrossOriginThis)) michael@0: michael@0: return indent(body) + self.generate_code().define() michael@0: michael@0: def generate_code(self): michael@0: assert False # Override me michael@0: michael@0: michael@0: class CGAbstractStaticBindingMethod(CGAbstractStaticMethod): michael@0: """ michael@0: Common class to generate the JSNatives for all our static methods, getters michael@0: and setters. This will generate the function declaration and unwrap the michael@0: global object. Subclasses are expected to override the generate_code michael@0: function to do the rest of the work. This function should return a michael@0: CGThing which is already properly indented. michael@0: """ michael@0: def __init__(self, descriptor, name): michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('unsigned', 'argc'), michael@0: Argument('JS::Value*', 'vp')] michael@0: CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args) michael@0: michael@0: def definition_body(self): michael@0: # Make sure that "obj" is in the same compartment as "cx", since we'll michael@0: # later use it to wrap return values. michael@0: unwrap = indent(dedent(""" michael@0: JS::CallArgs args = JS::CallArgsFromVp(argc, vp); michael@0: JS::Rooted obj(cx, &args.callee()); michael@0: michael@0: """)) michael@0: return unwrap + self.generate_code().define() michael@0: michael@0: def generate_code(self): michael@0: assert False # Override me michael@0: michael@0: michael@0: def MakeNativeName(name): michael@0: return name[0].upper() + name[1:] michael@0: michael@0: michael@0: class CGGenericMethod(CGAbstractBindingMethod): michael@0: """ michael@0: A class for generating the C++ code for an IDL method. michael@0: michael@0: If allowCrossOriginThis is true, then this-unwrapping will first do an michael@0: UncheckedUnwrap and after that operate on the result. michael@0: """ michael@0: def __init__(self, descriptor, allowCrossOriginThis=False): michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('unsigned', 'argc'), michael@0: Argument('JS::Value*', 'vp')] michael@0: unwrapFailureCode = ( michael@0: 'return ThrowInvalidThis(cx, args, GetInvalidThisErrorForMethod(%%(securityError)s), "%s");\n' % michael@0: descriptor.interface.identifier.name) michael@0: name = "genericCrossOriginMethod" if allowCrossOriginThis else "genericMethod" michael@0: CGAbstractBindingMethod.__init__(self, descriptor, name, michael@0: args, michael@0: unwrapFailureCode=unwrapFailureCode, michael@0: allowCrossOriginThis=allowCrossOriginThis) michael@0: michael@0: def generate_code(self): michael@0: return CGGeneric(indent(dedent(""" michael@0: const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); michael@0: MOZ_ASSERT(info->type() == JSJitInfo::Method); michael@0: JSJitMethodOp method = info->method; michael@0: return method(cx, obj, self, JSJitMethodCallArgs(args)); michael@0: """))) michael@0: michael@0: michael@0: class CGSpecializedMethod(CGAbstractStaticMethod): michael@0: """ michael@0: A class for generating the C++ code for a specialized method that the JIT michael@0: can call with lower overhead. michael@0: """ michael@0: def __init__(self, descriptor, method): michael@0: self.method = method michael@0: name = CppKeywords.checkMethodName(method.identifier.name) michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('JS::Handle', 'obj'), michael@0: Argument('%s*' % descriptor.nativeType, 'self'), michael@0: Argument('const JSJitMethodCallArgs&', 'args')] michael@0: CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args) michael@0: michael@0: def definition_body(self): michael@0: nativeName = CGSpecializedMethod.makeNativeName(self.descriptor, michael@0: self.method) michael@0: return CGMethodCall(nativeName, self.method.isStatic(), self.descriptor, michael@0: self.method).define() michael@0: michael@0: @staticmethod michael@0: def makeNativeName(descriptor, method): michael@0: name = method.identifier.name michael@0: return MakeNativeName(descriptor.binaryNames.get(name, name)) michael@0: michael@0: michael@0: class CGMethodPromiseWrapper(CGAbstractStaticMethod): michael@0: """ michael@0: A class for generating a wrapper around another method that will michael@0: convert exceptions to promises. michael@0: """ michael@0: def __init__(self, descriptor, methodToWrap): michael@0: self.method = methodToWrap michael@0: name = self.makeName(methodToWrap.name) michael@0: args = list(methodToWrap.args) michael@0: CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args) michael@0: michael@0: def definition_body(self): michael@0: return indent(fill( michael@0: """ michael@0: // Make sure to save the callee before someone maybe messes michael@0: // with rval(). michael@0: JS::Rooted callee(cx, &args.callee()); michael@0: bool ok = ${methodName}(${args}); michael@0: if (ok) { michael@0: return true; michael@0: } michael@0: return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee), michael@0: args.rval()); michael@0: """, michael@0: methodName=self.method.name, michael@0: args=", ".join(arg.name for arg in self.args))) michael@0: michael@0: @staticmethod michael@0: def makeName(methodName): michael@0: return methodName + "_promiseWrapper" michael@0: michael@0: michael@0: class CGJsonifierMethod(CGSpecializedMethod): michael@0: def __init__(self, descriptor, method): michael@0: assert method.isJsonifier() michael@0: CGSpecializedMethod.__init__(self, descriptor, method) michael@0: michael@0: def definition_body(self): michael@0: ret = dedent(""" michael@0: JS::Rooted result(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr())); michael@0: if (!result) { michael@0: return false; michael@0: } michael@0: """) michael@0: for m in self.descriptor.interface.members: michael@0: if m.isAttr() and not m.isStatic() and m.type.isSerializable(): michael@0: ret += fill( michael@0: """ michael@0: { // scope for "temp" michael@0: JS::Rooted temp(cx); michael@0: if (!get_${name}(cx, obj, self, JSJitGetterCallArgs(&temp))) { michael@0: return false; michael@0: } michael@0: if (!JS_DefineProperty(cx, result, "${name}", temp, JSPROP_ENUMERATE)) { michael@0: return false; michael@0: } michael@0: } michael@0: """, michael@0: name=m.identifier.name) michael@0: michael@0: ret += ('args.rval().setObject(*result);\n' michael@0: 'return true;\n') michael@0: return indent(ret) michael@0: michael@0: michael@0: class CGLegacyCallHook(CGAbstractBindingMethod): michael@0: """ michael@0: Call hook for our object michael@0: """ michael@0: def __init__(self, descriptor): michael@0: self._legacycaller = descriptor.operations["LegacyCaller"] michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('unsigned', 'argc'), michael@0: Argument('JS::Value*', 'vp')] michael@0: # Our "self" is actually the callee in this case, not the thisval. michael@0: CGAbstractBindingMethod.__init__( michael@0: self, descriptor, LEGACYCALLER_HOOK_NAME, michael@0: args, getThisObj="&args.callee()") michael@0: michael@0: def define(self): michael@0: if not self._legacycaller: michael@0: return "" michael@0: return CGAbstractBindingMethod.define(self) michael@0: michael@0: def generate_code(self): michael@0: name = self._legacycaller.identifier.name michael@0: nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name)) michael@0: return CGMethodCall(nativeName, False, self.descriptor, michael@0: self._legacycaller) michael@0: michael@0: michael@0: class CGNewResolveHook(CGAbstractBindingMethod): michael@0: """ michael@0: NewResolve hook for objects with custom hooks. michael@0: """ michael@0: def __init__(self, descriptor): michael@0: assert descriptor.interface.getExtendedAttribute("NeedNewResolve") michael@0: michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('JS::Handle', 'obj'), michael@0: Argument('JS::Handle', 'id'), michael@0: Argument('JS::MutableHandle', 'objp')] michael@0: # Our "self" is actually the "obj" argument in this case, not the thisval. michael@0: CGAbstractBindingMethod.__init__( michael@0: self, descriptor, NEWRESOLVE_HOOK_NAME, michael@0: args, getThisObj="", callArgs="") michael@0: michael@0: def generate_code(self): michael@0: return CGGeneric(indent(dedent(""" michael@0: JS::Rooted desc(cx); michael@0: if (!self->DoNewResolve(cx, obj, id, &desc)) { michael@0: return false; michael@0: } michael@0: if (!desc.object()) { michael@0: return true; michael@0: } michael@0: // If desc.value() is undefined, then the DoNewResolve call michael@0: // has already defined it on the object. Don't try to also michael@0: // define it. michael@0: if (!desc.value().isUndefined() && michael@0: !JS_DefinePropertyById(cx, obj, id, desc.value(), michael@0: desc.getter(), desc.setter(), michael@0: desc.attributes())) { michael@0: return false; michael@0: } michael@0: objp.set(obj); michael@0: return true; michael@0: """))) michael@0: michael@0: def definition_body(self): michael@0: if self.descriptor.interface.getExtendedAttribute("Global"): michael@0: # Resolve standard classes michael@0: prefix = indent(dedent(""" michael@0: if (!ResolveGlobal(cx, obj, id, objp)) { michael@0: return false; michael@0: } michael@0: if (objp) { michael@0: return true; michael@0: } michael@0: michael@0: """)) michael@0: else: michael@0: prefix = "" michael@0: return prefix + CGAbstractBindingMethod.definition_body(self) michael@0: michael@0: michael@0: class CGEnumerateHook(CGAbstractBindingMethod): michael@0: """ michael@0: Enumerate hook for objects with custom hooks. michael@0: """ michael@0: def __init__(self, descriptor): michael@0: assert descriptor.interface.getExtendedAttribute("NeedNewResolve") michael@0: michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('JS::Handle', 'obj')] michael@0: # Our "self" is actually the "obj" argument in this case, not the thisval. michael@0: CGAbstractBindingMethod.__init__( michael@0: self, descriptor, ENUMERATE_HOOK_NAME, michael@0: args, getThisObj="", callArgs="") michael@0: michael@0: def generate_code(self): michael@0: return CGGeneric(indent(dedent(""" michael@0: nsAutoTArray names; michael@0: ErrorResult rv; michael@0: self->GetOwnPropertyNames(cx, names, rv); michael@0: rv.WouldReportJSException(); michael@0: if (rv.Failed()) { michael@0: return ThrowMethodFailedWithDetails(cx, rv, "%s", "enumerate"); michael@0: } michael@0: JS::Rooted dummy(cx); michael@0: for (uint32_t i = 0; i < names.Length(); ++i) { michael@0: if (!JS_LookupUCProperty(cx, obj, names[i].get(), names[i].Length(), &dummy)) { michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: """))) michael@0: michael@0: def definition_body(self): michael@0: if self.descriptor.interface.getExtendedAttribute("Global"): michael@0: # Enumerate standard classes michael@0: prefix = indent(dedent(""" michael@0: if (!EnumerateGlobal(cx, obj)) { michael@0: return false; michael@0: } michael@0: michael@0: """)) michael@0: else: michael@0: prefix = "" michael@0: return prefix + CGAbstractBindingMethod.definition_body(self) michael@0: michael@0: michael@0: class CppKeywords(): michael@0: """ michael@0: A class for checking if method names declared in webidl michael@0: are not in conflict with C++ keywords. michael@0: """ michael@0: keywords = frozenset([ michael@0: 'alignas', 'alignof', 'and', 'and_eq', 'asm', 'assert', 'auto', 'bitand', 'bitor', 'bool', michael@0: 'break', 'case', 'catch', 'char', 'char16_t', 'char32_t', 'class', 'compl', 'const', michael@0: 'constexpr', 'const_cast', 'continue', 'decltype', 'default', 'delete', 'do', 'double', michael@0: 'dynamic_cast', 'else', 'enum', 'explicit', 'export', 'extern', 'false', 'final', 'float', michael@0: 'for', 'friend', 'goto', 'if', 'inline', 'int', 'long', 'mutable', 'namespace', 'new', michael@0: 'noexcept', 'not', 'not_eq', 'nullptr', 'operator', 'or', 'or_eq', 'override', 'private', michael@0: 'protected', 'public', 'register', 'reinterpret_cast', 'return', 'short', 'signed', michael@0: 'sizeof', 'static', 'static_assert', 'static_cast', 'struct', 'switch', 'template', 'this', michael@0: 'thread_local', 'throw', 'true', 'try', 'typedef', 'typeid', 'typename', 'union', michael@0: 'unsigned', 'using', 'virtual', 'void', 'volatile', 'wchar_t', 'while', 'xor', 'xor_eq']) michael@0: michael@0: @staticmethod michael@0: def checkMethodName(name): michael@0: # Double '_' because 'assert' and '_assert' cannot be used in MS2013 compiler. michael@0: # Bug 964892 and bug 963560. michael@0: if name in CppKeywords.keywords: michael@0: name = '_' + name + '_' michael@0: return name michael@0: michael@0: michael@0: class CGStaticMethod(CGAbstractStaticBindingMethod): michael@0: """ michael@0: A class for generating the C++ code for an IDL static method. michael@0: """ michael@0: def __init__(self, descriptor, method): michael@0: self.method = method michael@0: name = method.identifier.name michael@0: CGAbstractStaticBindingMethod.__init__(self, descriptor, name) michael@0: michael@0: def generate_code(self): michael@0: nativeName = CGSpecializedMethod.makeNativeName(self.descriptor, michael@0: self.method) michael@0: return CGMethodCall(nativeName, True, self.descriptor, self.method) michael@0: michael@0: michael@0: class CGGenericGetter(CGAbstractBindingMethod): michael@0: """ michael@0: A class for generating the C++ code for an IDL attribute getter. michael@0: """ michael@0: def __init__(self, descriptor, lenientThis=False, allowCrossOriginThis=False): michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('unsigned', 'argc'), michael@0: Argument('JS::Value*', 'vp')] michael@0: if lenientThis: michael@0: name = "genericLenientGetter" michael@0: unwrapFailureCode = dedent(""" michael@0: MOZ_ASSERT(!JS_IsExceptionPending(cx)); michael@0: if (!ReportLenientThisUnwrappingFailure(cx, &args.callee())) { michael@0: return false; michael@0: } michael@0: args.rval().set(JS::UndefinedValue()); michael@0: return true; michael@0: """) michael@0: else: michael@0: if allowCrossOriginThis: michael@0: name = "genericCrossOriginGetter" michael@0: else: michael@0: name = "genericGetter" michael@0: unwrapFailureCode = ( michael@0: 'return ThrowInvalidThis(cx, args, GetInvalidThisErrorForGetter(%%(securityError)s), "%s");\n' % michael@0: descriptor.interface.identifier.name) michael@0: CGAbstractBindingMethod.__init__(self, descriptor, name, args, michael@0: unwrapFailureCode, michael@0: allowCrossOriginThis=allowCrossOriginThis) michael@0: michael@0: def generate_code(self): michael@0: return CGGeneric(indent(dedent(""" michael@0: const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); michael@0: MOZ_ASSERT(info->type() == JSJitInfo::Getter); michael@0: JSJitGetterOp getter = info->getter; michael@0: return getter(cx, obj, self, JSJitGetterCallArgs(args)); michael@0: """))) michael@0: michael@0: michael@0: class CGSpecializedGetter(CGAbstractStaticMethod): michael@0: """ michael@0: A class for generating the code for a specialized attribute getter michael@0: that the JIT can call with lower overhead. michael@0: """ michael@0: def __init__(self, descriptor, attr): michael@0: self.attr = attr michael@0: name = 'get_' + attr.identifier.name michael@0: args = [ michael@0: Argument('JSContext*', 'cx'), michael@0: Argument('JS::Handle', 'obj'), michael@0: Argument('%s*' % descriptor.nativeType, 'self'), michael@0: Argument('JSJitGetterCallArgs', 'args') michael@0: ] michael@0: CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args) michael@0: michael@0: def definition_body(self): michael@0: nativeName = CGSpecializedGetter.makeNativeName(self.descriptor, michael@0: self.attr) michael@0: if self.attr.slotIndex is not None: michael@0: if (self.descriptor.hasXPConnectImpls and michael@0: (self.descriptor.interface.identifier.name != 'Window' or michael@0: self.attr.identifier.name != 'document')): michael@0: raise TypeError("Interface '%s' has XPConnect impls, so we " michael@0: "can't use our slot for property '%s'!" % michael@0: (self.descriptor.interface.identifier.name, michael@0: self.attr.identifier.name)) michael@0: prefix = indent(fill( michael@0: """ michael@0: // Have to either root across the getter call or reget after. michael@0: JS::Rooted reflector(cx); michael@0: // Safe to do an unchecked unwrap, since we've gotten this far. michael@0: // Also make sure to unwrap outer windows, since we want the michael@0: // real DOM object. michael@0: reflector = IsDOMObject(obj) ? obj : js::UncheckedUnwrap(obj, /* stopAtOuter = */ false); michael@0: { michael@0: // Scope for cachedVal michael@0: JS::Value cachedVal = js::GetReservedSlot(reflector, ${slot}); michael@0: if (!cachedVal.isUndefined()) { michael@0: args.rval().set(cachedVal); michael@0: // The cached value is in the compartment of reflector, michael@0: // so wrap into the caller compartment as needed. michael@0: return ${maybeWrap}(cx, args.rval()); michael@0: } michael@0: } michael@0: michael@0: """, michael@0: slot=memberReservedSlot(self.attr), michael@0: maybeWrap=getMaybeWrapValueFuncForType(self.attr.type))) michael@0: else: michael@0: prefix = "" michael@0: michael@0: return (prefix + michael@0: indent(CGGetterCall(self.attr.type, nativeName, michael@0: self.descriptor, self.attr).define())) michael@0: michael@0: @staticmethod michael@0: def makeNativeName(descriptor, attr): michael@0: name = attr.identifier.name michael@0: nativeName = MakeNativeName(descriptor.binaryNames.get(name, name)) michael@0: # resultOutParam does not depend on whether resultAlreadyAddRefed is set michael@0: _, resultOutParam, _, _ = getRetvalDeclarationForType(attr.type, michael@0: descriptor, michael@0: False) michael@0: infallible = ('infallible' in michael@0: descriptor.getExtendedAttributes(attr, getter=True)) michael@0: if resultOutParam or attr.type.nullable() or not infallible: michael@0: nativeName = "Get" + nativeName michael@0: return nativeName michael@0: michael@0: michael@0: class CGStaticGetter(CGAbstractStaticBindingMethod): michael@0: """ michael@0: A class for generating the C++ code for an IDL static attribute getter. michael@0: """ michael@0: def __init__(self, descriptor, attr): michael@0: self.attr = attr michael@0: name = 'get_' + attr.identifier.name michael@0: CGAbstractStaticBindingMethod.__init__(self, descriptor, name) michael@0: michael@0: def generate_code(self): michael@0: nativeName = CGSpecializedGetter.makeNativeName(self.descriptor, michael@0: self.attr) michael@0: return CGIndenter(CGGetterCall(self.attr.type, nativeName, michael@0: self.descriptor, self.attr)) michael@0: michael@0: michael@0: class CGGenericSetter(CGAbstractBindingMethod): michael@0: """ michael@0: A class for generating the C++ code for an IDL attribute setter. michael@0: """ michael@0: def __init__(self, descriptor, lenientThis=False, allowCrossOriginThis=False): michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('unsigned', 'argc'), michael@0: Argument('JS::Value*', 'vp')] michael@0: if lenientThis: michael@0: name = "genericLenientSetter" michael@0: unwrapFailureCode = dedent(""" michael@0: MOZ_ASSERT(!JS_IsExceptionPending(cx)); michael@0: if (!ReportLenientThisUnwrappingFailure(cx, &args.callee())) { michael@0: return false; michael@0: } michael@0: args.rval().set(JS::UndefinedValue()); michael@0: return true; michael@0: """) michael@0: else: michael@0: if allowCrossOriginThis: michael@0: name = "genericCrossOriginSetter" michael@0: else: michael@0: name = "genericSetter" michael@0: unwrapFailureCode = ( michael@0: 'return ThrowInvalidThis(cx, args, GetInvalidThisErrorForSetter(%%(securityError)s), "%s");\n' % michael@0: descriptor.interface.identifier.name) michael@0: michael@0: CGAbstractBindingMethod.__init__(self, descriptor, name, args, michael@0: unwrapFailureCode, michael@0: allowCrossOriginThis=allowCrossOriginThis) michael@0: michael@0: def generate_code(self): michael@0: return CGGeneric(indent(fill( michael@0: """ michael@0: if (args.length() == 0) { michael@0: return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${name} attribute setter"); michael@0: } michael@0: const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev()); michael@0: MOZ_ASSERT(info->type() == JSJitInfo::Setter); michael@0: JSJitSetterOp setter = info->setter; michael@0: if (!setter(cx, obj, self, JSJitSetterCallArgs(args))) { michael@0: return false; michael@0: } michael@0: args.rval().set(JSVAL_VOID); michael@0: return true; michael@0: """, michael@0: name=self.descriptor.interface.identifier.name))) michael@0: michael@0: michael@0: class CGSpecializedSetter(CGAbstractStaticMethod): michael@0: """ michael@0: A class for generating the code for a specialized attribute setter michael@0: that the JIT can call with lower overhead. michael@0: """ michael@0: def __init__(self, descriptor, attr): michael@0: self.attr = attr michael@0: name = 'set_' + attr.identifier.name michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('JS::Handle', 'obj'), michael@0: Argument('%s*' % descriptor.nativeType, 'self'), michael@0: Argument('JSJitSetterCallArgs', 'args')] michael@0: CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args) michael@0: michael@0: def definition_body(self): michael@0: nativeName = CGSpecializedSetter.makeNativeName(self.descriptor, michael@0: self.attr) michael@0: return CGIndenter(CGSetterCall(self.attr.type, nativeName, michael@0: self.descriptor, self.attr)).define() michael@0: michael@0: @staticmethod michael@0: def makeNativeName(descriptor, attr): michael@0: name = attr.identifier.name michael@0: return "Set" + MakeNativeName(descriptor.binaryNames.get(name, name)) michael@0: michael@0: michael@0: class CGStaticSetter(CGAbstractStaticBindingMethod): michael@0: """ michael@0: A class for generating the C++ code for an IDL static attribute setter. michael@0: """ michael@0: def __init__(self, descriptor, attr): michael@0: self.attr = attr michael@0: name = 'set_' + attr.identifier.name michael@0: CGAbstractStaticBindingMethod.__init__(self, descriptor, name) michael@0: michael@0: def generate_code(self): michael@0: nativeName = CGSpecializedSetter.makeNativeName(self.descriptor, michael@0: self.attr) michael@0: checkForArg = CGGeneric(fill( michael@0: """ michael@0: if (args.length() == 0) { michael@0: return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${name} setter"); michael@0: } michael@0: """, michael@0: name=self.attr.identifier.name)) michael@0: call = CGSetterCall(self.attr.type, nativeName, self.descriptor, michael@0: self.attr) michael@0: return CGIndenter(CGList([checkForArg, call])) michael@0: michael@0: michael@0: class CGSpecializedForwardingSetter(CGSpecializedSetter): michael@0: """ michael@0: A class for generating the code for a specialized attribute setter with michael@0: PutForwards that the JIT can call with lower overhead. michael@0: """ michael@0: def __init__(self, descriptor, attr): michael@0: CGSpecializedSetter.__init__(self, descriptor, attr) michael@0: michael@0: def definition_body(self): michael@0: attrName = self.attr.identifier.name michael@0: forwardToAttrName = self.attr.getExtendedAttribute("PutForwards")[0] michael@0: # JS_GetProperty and JS_SetProperty can only deal with ASCII michael@0: assert all(ord(c) < 128 for c in attrName) michael@0: assert all(ord(c) < 128 for c in forwardToAttrName) michael@0: return indent(fill( michael@0: """ michael@0: JS::Rooted v(cx); michael@0: if (!JS_GetProperty(cx, obj, "${attr}", &v)) { michael@0: return false; michael@0: } michael@0: michael@0: if (!v.isObject()) { michael@0: return ThrowErrorMessage(cx, MSG_NOT_OBJECT, "${interface}.${attr}"); michael@0: } michael@0: michael@0: JS::Rooted targetObj(cx, &v.toObject()); michael@0: return JS_SetProperty(cx, targetObj, "${forwardToAttrName}", args[0]); michael@0: """, michael@0: attr=attrName, michael@0: interface=self.descriptor.interface.identifier.name, michael@0: forwardToAttrName=forwardToAttrName)) michael@0: michael@0: michael@0: class CGSpecializedReplaceableSetter(CGSpecializedSetter): michael@0: """ michael@0: A class for generating the code for a specialized attribute setter with michael@0: Replaceable that the JIT can call with lower overhead. michael@0: """ michael@0: def __init__(self, descriptor, attr): michael@0: CGSpecializedSetter.__init__(self, descriptor, attr) michael@0: michael@0: def definition_body(self): michael@0: attrName = self.attr.identifier.name michael@0: # JS_DefineProperty can only deal with ASCII michael@0: assert all(ord(c) < 128 for c in attrName) michael@0: return indent('return JS_DefineProperty(cx, obj, "%s", args[0], JSPROP_ENUMERATE);\n' % michael@0: attrName) michael@0: michael@0: michael@0: def memberReturnsNewObject(member): michael@0: return member.getExtendedAttribute("NewObject") is not None michael@0: michael@0: michael@0: class CGMemberJITInfo(CGThing): michael@0: """ michael@0: A class for generating the JITInfo for a property that points to michael@0: our specialized getter and setter. michael@0: """ michael@0: def __init__(self, descriptor, member): michael@0: self.member = member michael@0: self.descriptor = descriptor michael@0: michael@0: def declare(self): michael@0: return "" michael@0: michael@0: def defineJitInfo(self, infoName, opName, opType, infallible, movable, michael@0: aliasSet, hasSlot, slotIndex, returnTypes, args): michael@0: """ michael@0: aliasSet is a JSJitInfo::AliasSet value, without the "JSJitInfo::" bit. michael@0: michael@0: args is None if we don't want to output argTypes for some michael@0: reason (e.g. we have overloads or we're not a method) and michael@0: otherwise an iterable of the arguments for this method. michael@0: """ michael@0: assert(not movable or aliasSet != "AliasEverything") # Can't move write-aliasing things michael@0: assert(not hasSlot or movable) # Things with slots had better be movable michael@0: michael@0: def jitInfoInitializer(isTypedMethod): michael@0: initializer = fill( michael@0: """ michael@0: { michael@0: { ${opName} }, michael@0: prototypes::id::${name}, michael@0: PrototypeTraits::Depth, michael@0: JSJitInfo::${opType}, michael@0: JSJitInfo::${aliasSet}, /* aliasSet. Not relevant for setters. */ michael@0: ${returnType}, /* returnType. Not relevant for setters. */ michael@0: ${isInfallible}, /* isInfallible. False in setters. */ michael@0: ${isMovable}, /* isMovable. Not relevant for setters. */ michael@0: ${isInSlot}, /* isInSlot. Only relevant for getters. */ michael@0: ${isTypedMethod}, /* isTypedMethod. Only relevant for methods. */ michael@0: ${slotIndex} /* Reserved slot index, if we're stored in a slot, else 0. */ michael@0: } michael@0: """, michael@0: opName=opName, michael@0: name=self.descriptor.name, michael@0: opType=opType, michael@0: aliasSet=aliasSet, michael@0: returnType=reduce(CGMemberJITInfo.getSingleReturnType, returnTypes, michael@0: ""), michael@0: isInfallible=toStringBool(infallible), michael@0: isMovable=toStringBool(movable), michael@0: isInSlot=toStringBool(hasSlot), michael@0: isTypedMethod=toStringBool(isTypedMethod), michael@0: slotIndex=slotIndex) michael@0: return initializer.rstrip() michael@0: michael@0: if args is not None: michael@0: argTypes = "%s_argTypes" % infoName michael@0: args = [CGMemberJITInfo.getJSArgType(arg.type) for arg in args] michael@0: args.append("JSJitInfo::ArgTypeListEnd") michael@0: argTypesDecl = ( michael@0: "static const JSJitInfo::ArgType %s[] = { %s };\n" % michael@0: (argTypes, ", ".join(args))) michael@0: return fill( michael@0: """ michael@0: michael@0: $*{argTypesDecl} michael@0: static const JSTypedMethodJitInfo ${infoName} = { michael@0: ${jitInfo}, michael@0: ${argTypes} michael@0: }; michael@0: """, michael@0: argTypesDecl=argTypesDecl, michael@0: infoName=infoName, michael@0: jitInfo=jitInfoInitializer(True), michael@0: argTypes=argTypes) michael@0: michael@0: return ("\n" michael@0: "static const JSJitInfo %s = %s;\n" michael@0: % (infoName, jitInfoInitializer(False))) michael@0: michael@0: def define(self): michael@0: if self.member.isAttr(): michael@0: getterinfo = ("%s_getterinfo" % self.member.identifier.name) michael@0: # We need the cast here because JSJitGetterOp has a "void* self" michael@0: # while we have the right type. michael@0: getter = ("(JSJitGetterOp)get_%s" % self.member.identifier.name) michael@0: getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True) michael@0: getterconst = (self.member.getExtendedAttribute("SameObject") or michael@0: self.member.getExtendedAttribute("Constant")) michael@0: getterpure = getterconst or self.member.getExtendedAttribute("Pure") michael@0: if getterconst: michael@0: aliasSet = "AliasNone" michael@0: elif getterpure: michael@0: aliasSet = "AliasDOMSets" michael@0: else: michael@0: aliasSet = "AliasEverything" michael@0: movable = getterpure and getterinfal michael@0: michael@0: getterinfal = getterinfal and infallibleForMember(self.member, self.member.type, self.descriptor) michael@0: isInSlot = self.member.getExtendedAttribute("StoreInSlot") michael@0: if isInSlot: michael@0: slotIndex = memberReservedSlot(self.member) michael@0: # We'll statically assert that this is not too big in michael@0: # CGUpdateMemberSlotsMethod michael@0: else: michael@0: slotIndex = "0" michael@0: michael@0: result = self.defineJitInfo(getterinfo, getter, "Getter", michael@0: getterinfal, movable, aliasSet, michael@0: isInSlot, slotIndex, michael@0: [self.member.type], None) michael@0: if (not self.member.readonly or michael@0: self.member.getExtendedAttribute("PutForwards") is not None or michael@0: self.member.getExtendedAttribute("Replaceable") is not None): michael@0: setterinfo = ("%s_setterinfo" % self.member.identifier.name) michael@0: # Actually a JSJitSetterOp, but JSJitGetterOp is first in the michael@0: # union. michael@0: setter = ("(JSJitGetterOp)set_%s" % self.member.identifier.name) michael@0: # Setters are always fallible, since they have to do a typed unwrap. michael@0: result += self.defineJitInfo(setterinfo, setter, "Setter", michael@0: False, False, "AliasEverything", michael@0: False, "0", michael@0: [BuiltinTypes[IDLBuiltinType.Types.void]], michael@0: None) michael@0: return result michael@0: if self.member.isMethod(): michael@0: methodinfo = ("%s_methodinfo" % self.member.identifier.name) michael@0: name = CppKeywords.checkMethodName(self.member.identifier.name) michael@0: if self.member.returnsPromise(): michael@0: name = CGMethodPromiseWrapper.makeName(name) michael@0: # Actually a JSJitMethodOp, but JSJitGetterOp is first in the union. michael@0: method = ("(JSJitGetterOp)%s" % name) michael@0: methodPure = self.member.getExtendedAttribute("Pure") michael@0: michael@0: # Methods are infallible if they are infallible, have no arguments michael@0: # to unwrap, and have a return type that's infallible to wrap up for michael@0: # return. michael@0: sigs = self.member.signatures() michael@0: if len(sigs) != 1: michael@0: # Don't handle overloading. If there's more than one signature, michael@0: # one of them must take arguments. michael@0: methodInfal = False michael@0: args = None michael@0: movable = False michael@0: else: michael@0: sig = sigs[0] michael@0: # For pure methods, it's OK to set movable to our notion of michael@0: # infallible on the C++ side, without considering argument michael@0: # conversions, since argument conversions that can reliably michael@0: # throw would be effectful anyway and the jit doesn't move michael@0: # effectful things. michael@0: hasInfallibleImpl = "infallible" in self.descriptor.getExtendedAttributes(self.member) michael@0: movable = methodPure and hasInfallibleImpl michael@0: # XXXbz can we move the smarts about fallibility due to arg michael@0: # conversions into the JIT, using our new args stuff? michael@0: if (len(sig[1]) != 0 or michael@0: not infallibleForMember(self.member, sig[0], self.descriptor)): michael@0: # We have arguments or our return-value boxing can fail michael@0: methodInfal = False michael@0: else: michael@0: methodInfal = hasInfallibleImpl michael@0: # For now, only bother to output args if we're pure michael@0: if methodPure: michael@0: args = sig[1] michael@0: else: michael@0: args = None michael@0: michael@0: if args is not None: michael@0: aliasSet = "AliasDOMSets" michael@0: else: michael@0: aliasSet = "AliasEverything" michael@0: result = self.defineJitInfo(methodinfo, method, "Method", michael@0: methodInfal, movable, aliasSet, False, "0", michael@0: [s[0] for s in sigs], args) michael@0: return result michael@0: raise TypeError("Illegal member type to CGPropertyJITInfo") michael@0: michael@0: @staticmethod michael@0: def getJSReturnTypeTag(t): michael@0: if t.nullable(): michael@0: # Sometimes it might return null, sometimes not michael@0: return "JSVAL_TYPE_UNKNOWN" michael@0: if t.isVoid(): michael@0: # No return, every time michael@0: return "JSVAL_TYPE_UNDEFINED" michael@0: if t.isArray(): michael@0: # No idea yet michael@0: assert False michael@0: if t.isSequence(): michael@0: return "JSVAL_TYPE_OBJECT" michael@0: if t.isMozMap(): michael@0: return "JSVAL_TYPE_OBJECT" michael@0: if t.isGeckoInterface(): michael@0: return "JSVAL_TYPE_OBJECT" michael@0: if t.isString(): michael@0: return "JSVAL_TYPE_STRING" michael@0: if t.isEnum(): michael@0: return "JSVAL_TYPE_STRING" michael@0: if t.isCallback(): michael@0: return "JSVAL_TYPE_OBJECT" michael@0: if t.isAny(): michael@0: # The whole point is to return various stuff michael@0: return "JSVAL_TYPE_UNKNOWN" michael@0: if t.isObject(): michael@0: return "JSVAL_TYPE_OBJECT" michael@0: if t.isSpiderMonkeyInterface(): michael@0: return "JSVAL_TYPE_OBJECT" michael@0: if t.isUnion(): michael@0: u = t.unroll() michael@0: if u.hasNullableType: michael@0: # Might be null or not michael@0: return "JSVAL_TYPE_UNKNOWN" michael@0: return reduce(CGMemberJITInfo.getSingleReturnType, michael@0: u.flatMemberTypes, "") michael@0: if t.isDictionary(): michael@0: return "JSVAL_TYPE_OBJECT" michael@0: if t.isDate(): michael@0: return "JSVAL_TYPE_OBJECT" michael@0: if not t.isPrimitive(): michael@0: raise TypeError("No idea what type " + str(t) + " is.") michael@0: tag = t.tag() michael@0: if tag == IDLType.Tags.bool: michael@0: return "JSVAL_TYPE_BOOLEAN" michael@0: if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, michael@0: IDLType.Tags.int16, IDLType.Tags.uint16, michael@0: IDLType.Tags.int32]: michael@0: return "JSVAL_TYPE_INT32" michael@0: if tag in [IDLType.Tags.int64, IDLType.Tags.uint64, michael@0: IDLType.Tags.unrestricted_float, IDLType.Tags.float, michael@0: IDLType.Tags.unrestricted_double, IDLType.Tags.double]: michael@0: # These all use JS_NumberValue, which can return int or double. michael@0: # But TI treats "double" as meaning "int or double", so we're michael@0: # good to return JSVAL_TYPE_DOUBLE here. michael@0: return "JSVAL_TYPE_DOUBLE" michael@0: if tag != IDLType.Tags.uint32: michael@0: raise TypeError("No idea what type " + str(t) + " is.") michael@0: # uint32 is sometimes int and sometimes double. michael@0: return "JSVAL_TYPE_DOUBLE" michael@0: michael@0: @staticmethod michael@0: def getSingleReturnType(existingType, t): michael@0: type = CGMemberJITInfo.getJSReturnTypeTag(t) michael@0: if existingType == "": michael@0: # First element of the list; just return its type michael@0: return type michael@0: michael@0: if type == existingType: michael@0: return existingType michael@0: if ((type == "JSVAL_TYPE_DOUBLE" and michael@0: existingType == "JSVAL_TYPE_INT32") or michael@0: (existingType == "JSVAL_TYPE_DOUBLE" and michael@0: type == "JSVAL_TYPE_INT32")): michael@0: # Promote INT32 to DOUBLE as needed michael@0: return "JSVAL_TYPE_DOUBLE" michael@0: # Different types michael@0: return "JSVAL_TYPE_UNKNOWN" michael@0: michael@0: @staticmethod michael@0: def getJSArgType(t): michael@0: assert not t.isVoid() michael@0: if t.nullable(): michael@0: # Sometimes it might return null, sometimes not michael@0: return "JSJitInfo::ArgType(JSJitInfo::Null | %s)" % CGMemberJITInfo.getJSArgType(t.inner) michael@0: if t.isArray(): michael@0: # No idea yet michael@0: assert False michael@0: if t.isSequence(): michael@0: return "JSJitInfo::Object" michael@0: if t.isGeckoInterface(): michael@0: return "JSJitInfo::Object" michael@0: if t.isString(): michael@0: return "JSJitInfo::String" michael@0: if t.isEnum(): michael@0: return "JSJitInfo::String" michael@0: if t.isCallback(): michael@0: return "JSJitInfo::Object" michael@0: if t.isAny(): michael@0: # The whole point is to return various stuff michael@0: return "JSJitInfo::Any" michael@0: if t.isObject(): michael@0: return "JSJitInfo::Object" michael@0: if t.isSpiderMonkeyInterface(): michael@0: return "JSJitInfo::Object" michael@0: if t.isUnion(): michael@0: u = t.unroll() michael@0: type = "JSJitInfo::Null" if u.hasNullableType else "" michael@0: return ("JSJitInfo::ArgType(%s)" % michael@0: reduce(CGMemberJITInfo.getSingleArgType, michael@0: u.flatMemberTypes, type)) michael@0: if t.isDictionary(): michael@0: return "JSJitInfo::Object" michael@0: if t.isDate(): michael@0: return "JSJitInfo::Object" michael@0: if not t.isPrimitive(): michael@0: raise TypeError("No idea what type " + str(t) + " is.") michael@0: tag = t.tag() michael@0: if tag == IDLType.Tags.bool: michael@0: return "JSJitInfo::Boolean" michael@0: if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, michael@0: IDLType.Tags.int16, IDLType.Tags.uint16, michael@0: IDLType.Tags.int32]: michael@0: return "JSJitInfo::Integer" michael@0: if tag in [IDLType.Tags.int64, IDLType.Tags.uint64, michael@0: IDLType.Tags.unrestricted_float, IDLType.Tags.float, michael@0: IDLType.Tags.unrestricted_double, IDLType.Tags.double]: michael@0: # These all use JS_NumberValue, which can return int or double. michael@0: # But TI treats "double" as meaning "int or double", so we're michael@0: # good to return JSVAL_TYPE_DOUBLE here. michael@0: return "JSJitInfo::Double" michael@0: if tag != IDLType.Tags.uint32: michael@0: raise TypeError("No idea what type " + str(t) + " is.") michael@0: # uint32 is sometimes int and sometimes double. michael@0: return "JSJitInfo::Double" michael@0: michael@0: @staticmethod michael@0: def getSingleArgType(existingType, t): michael@0: type = CGMemberJITInfo.getJSArgType(t) michael@0: if existingType == "": michael@0: # First element of the list; just return its type michael@0: return type michael@0: michael@0: if type == existingType: michael@0: return existingType michael@0: return "%s | %s" % (existingType, type) michael@0: michael@0: michael@0: class CGStaticMethodJitinfo(CGGeneric): michael@0: """ michael@0: A class for generating the JITInfo for a promise-returning static method. michael@0: """ michael@0: def __init__(self, method): michael@0: CGGeneric.__init__( michael@0: self, michael@0: "\n" michael@0: "static const JSJitInfo %s_methodinfo = {\n" michael@0: " { (JSJitGetterOp)%s },\n" michael@0: " prototypes::id::_ID_Count, 0, JSJitInfo::StaticMethod,\n" michael@0: " JSJitInfo::AliasEverything, JSVAL_TYPE_MISSING, false, false,\n" michael@0: " false, false, 0\n" michael@0: "};\n" % michael@0: (method.identifier.name, method.identifier.name)) michael@0: michael@0: michael@0: def getEnumValueName(value): michael@0: # Some enum values can be empty strings. Others might have weird michael@0: # characters in them. Deal with the former by returning "_empty", michael@0: # deal with possible name collisions from that by throwing if the michael@0: # enum value is actually "_empty", and throw on any value michael@0: # containing non-ASCII chars for now. Replace all chars other than michael@0: # [0-9A-Za-z_] with '_'. michael@0: if re.match("[^\x20-\x7E]", value): michael@0: raise SyntaxError('Enum value "' + value + '" contains non-ASCII characters') michael@0: if re.match("^[0-9]", value): michael@0: return '_' + value michael@0: value = re.sub(r'[^0-9A-Za-z_]', '_', value) michael@0: if re.match("^_[A-Z]|__", value): michael@0: raise SyntaxError('Enum value "' + value + '" is reserved by the C++ spec') michael@0: if value == "_empty": michael@0: raise SyntaxError('"_empty" is not an IDL enum value we support yet') michael@0: if value == "": michael@0: return "_empty" michael@0: return MakeNativeName(value) michael@0: michael@0: michael@0: class CGEnum(CGThing): michael@0: def __init__(self, enum): michael@0: CGThing.__init__(self) michael@0: self.enum = enum michael@0: michael@0: def stringsNamespace(self): michael@0: return self.enum.identifier.name + "Values" michael@0: michael@0: def nEnumStrings(self): michael@0: return len(self.enum.values()) + 1 michael@0: michael@0: def declare(self): michael@0: decl = fill( # BOGUS extra newline at top michael@0: """ michael@0: michael@0: MOZ_BEGIN_ENUM_CLASS(${name}, uint32_t) michael@0: $*{enums} michael@0: MOZ_END_ENUM_CLASS(${name}) michael@0: """, michael@0: name=self.enum.identifier.name, michael@0: enums=",\n".join(map(getEnumValueName, self.enum.values())) + "\n") michael@0: strings = CGNamespace(self.stringsNamespace(), michael@0: CGGeneric(declare="extern const EnumEntry %s[%d];\n" michael@0: % (ENUM_ENTRY_VARIABLE_NAME, self.nEnumStrings()))) michael@0: return decl + "\n" + strings.declare() michael@0: michael@0: def define(self): michael@0: strings = fill( # BOGUS extra newline at top michael@0: """ michael@0: michael@0: extern const EnumEntry ${name}[${count}] = { michael@0: $*{entries} michael@0: { nullptr, 0 } michael@0: }; michael@0: """, michael@0: name=ENUM_ENTRY_VARIABLE_NAME, michael@0: count=self.nEnumStrings(), michael@0: entries=''.join('{"%s", %d},\n' % (val, len(val)) michael@0: for val in self.enum.values())) michael@0: # BOGUS - CGNamespace automatically indents; the extra indent() below causes michael@0: # the output to be indented 4 spaces. michael@0: return CGNamespace(self.stringsNamespace(), michael@0: CGGeneric(define=indent(strings))).define() michael@0: michael@0: def deps(self): michael@0: return self.enum.getDeps() michael@0: michael@0: michael@0: def getUnionAccessorSignatureType(type, descriptorProvider): michael@0: """ michael@0: Returns the types that are used in the getter and setter signatures for michael@0: union types michael@0: """ michael@0: if type.isArray(): michael@0: raise TypeError("Can't handle array arguments yet") michael@0: michael@0: if type.isSequence(): michael@0: nullable = type.nullable() michael@0: if nullable: michael@0: type = type.inner.inner michael@0: else: michael@0: type = type.inner michael@0: # We don't use the returned template here, so it's OK to just pass no michael@0: # sourceDescription. michael@0: elementInfo = getJSToNativeConversionInfo(type, descriptorProvider, michael@0: isMember="Sequence") michael@0: typeName = CGTemplatedType("Sequence", elementInfo.declType, michael@0: isReference=True) michael@0: if nullable: michael@0: typeName = CGTemplatedType("Nullable", typeName, isReference=True) michael@0: michael@0: return typeName michael@0: michael@0: if type.isUnion(): michael@0: typeName = CGGeneric(type.name) michael@0: if type.nullable(): michael@0: typeName = CGTemplatedType("Nullable", typeName, isReference=True) michael@0: michael@0: return typeName michael@0: michael@0: if type.isGeckoInterface(): michael@0: descriptor = descriptorProvider.getDescriptor( michael@0: type.unroll().inner.identifier.name) michael@0: typeName = CGGeneric(descriptor.nativeType) michael@0: # Allow null pointers for nullable types and old-binding classes michael@0: if type.nullable() or type.unroll().inner.isExternal(): michael@0: typeName = CGWrapper(typeName, post="*") michael@0: else: michael@0: typeName = CGWrapper(typeName, post="&") michael@0: return typeName michael@0: michael@0: if type.isSpiderMonkeyInterface(): michael@0: typeName = CGGeneric(type.name) michael@0: if type.nullable(): michael@0: typeName = CGTemplatedType("Nullable", typeName) michael@0: return CGWrapper(typeName, post="&") michael@0: michael@0: if type.isDOMString(): michael@0: return CGGeneric("const nsAString&") michael@0: michael@0: if type.isByteString(): michael@0: return CGGeneric("const nsCString&") michael@0: michael@0: if type.isEnum(): michael@0: if type.nullable(): michael@0: raise TypeError("We don't support nullable enumerated arguments or " michael@0: "union members yet") michael@0: return CGGeneric(type.inner.identifier.name) michael@0: michael@0: if type.isCallback(): michael@0: if type.nullable(): michael@0: typeName = "%s*" michael@0: else: michael@0: typeName = "%s&" michael@0: return CGGeneric(typeName % type.unroll().identifier.name) michael@0: michael@0: if type.isAny(): michael@0: return CGGeneric("JS::Value") michael@0: michael@0: if type.isObject(): michael@0: return CGGeneric("JSObject*") michael@0: michael@0: if type.isDictionary(): michael@0: return CGGeneric("const %s&" % type.inner.identifier.name) michael@0: michael@0: if not type.isPrimitive(): michael@0: raise TypeError("Need native type for argument type '%s'" % str(type)) michael@0: michael@0: typeName = CGGeneric(builtinNames[type.tag()]) michael@0: if type.nullable(): michael@0: typeName = CGTemplatedType("Nullable", typeName, isReference=True) michael@0: return typeName michael@0: michael@0: michael@0: def getUnionTypeTemplateVars(unionType, type, descriptorProvider, michael@0: ownsMembers=False): michael@0: # For dictionaries and sequences we need to pass None as the failureCode michael@0: # for getJSToNativeConversionInfo. michael@0: # Also, for dictionaries we would need to handle conversion of michael@0: # null/undefined to the dictionary correctly. michael@0: if type.isSequence(): michael@0: raise TypeError("Can't handle sequences in unions") michael@0: michael@0: name = getUnionMemberName(type) michael@0: michael@0: # By the time tryNextCode is invoked, we're guaranteed the union has been michael@0: # constructed as some type, since we've been trying to convert into the michael@0: # corresponding member. michael@0: prefix = "" if ownsMembers else "mUnion." michael@0: tryNextCode = ("%sDestroy%s();\n" michael@0: "tryNext = true;\n" michael@0: "return true;\n" % (prefix, name)) michael@0: conversionInfo = getJSToNativeConversionInfo( michael@0: type, descriptorProvider, failureCode=tryNextCode, michael@0: isDefinitelyObject=not type.isDictionary(), michael@0: isMember=("OwningUnion" if ownsMembers else None), michael@0: sourceDescription="member of %s" % unionType) michael@0: michael@0: ctorNeedsCx = conversionInfo.declArgs == "cx" michael@0: ctorArgs = "cx" if ctorNeedsCx else "" michael@0: michael@0: # This is ugly, but UnionMember needs to call a constructor with no michael@0: # arguments so the type can't be const. michael@0: structType = conversionInfo.declType.define() michael@0: if structType.startswith("const "): michael@0: structType = structType[6:] michael@0: externalType = getUnionAccessorSignatureType(type, descriptorProvider).define() michael@0: michael@0: if type.isObject(): michael@0: if ownsMembers: michael@0: body = dedent(""" michael@0: MOZ_ASSERT(mType == eUninitialized); michael@0: mValue.mObject.SetValue(obj); michael@0: mType = eObject; michael@0: """) michael@0: else: michael@0: body = dedent(""" michael@0: MOZ_ASSERT(mUnion.mType == mUnion.eUninitialized); michael@0: mUnion.mValue.mObject.SetValue(cx, obj); michael@0: mUnion.mType = mUnion.eObject; michael@0: """) michael@0: setter = ClassMethod("SetToObject", "void", michael@0: [Argument("JSContext*", "cx"), michael@0: Argument("JSObject*", "obj")], michael@0: inline=True, bodyInHeader=True, michael@0: body=body) michael@0: michael@0: else: michael@0: # Important: we need to not have our declName involve michael@0: # maybe-GCing operations. michael@0: jsConversion = string.Template(conversionInfo.template).substitute({ michael@0: "val": "value", michael@0: "mutableVal": "pvalue", michael@0: "declName": "memberSlot", michael@0: "holderName": "m" + name + "Holder", michael@0: }) michael@0: jsConversion = fill( michael@0: """ michael@0: tryNext = false; michael@0: { // scope for memberSlot michael@0: ${structType}& memberSlot = RawSetAs${name}(${ctorArgs}); michael@0: $*{jsConversion} michael@0: } michael@0: return true; michael@0: """, michael@0: structType=structType, michael@0: name=name, michael@0: ctorArgs=ctorArgs, michael@0: jsConversion=jsConversion) michael@0: michael@0: setter = ClassMethod("TrySetTo" + name, "bool", michael@0: [Argument("JSContext*", "cx"), michael@0: Argument("JS::Handle", "value"), michael@0: Argument("JS::MutableHandle", "pvalue"), michael@0: Argument("bool&", "tryNext")], michael@0: inline=not ownsMembers, michael@0: bodyInHeader=not ownsMembers, michael@0: body=jsConversion) michael@0: michael@0: return { michael@0: "name": name, michael@0: "structType": structType, michael@0: "externalType": externalType, michael@0: "setter": setter, michael@0: "holderType": conversionInfo.holderType.define() if conversionInfo.holderType else None, michael@0: "ctorArgs": ctorArgs, michael@0: "ctorArgList": [Argument("JSContext*", "cx")] if ctorNeedsCx else [] michael@0: } michael@0: michael@0: michael@0: class CGUnionStruct(CGThing): michael@0: def __init__(self, type, descriptorProvider, ownsMembers=False): michael@0: CGThing.__init__(self) michael@0: self.type = type.unroll() michael@0: self.descriptorProvider = descriptorProvider michael@0: self.ownsMembers = ownsMembers michael@0: self.struct = self.getStruct() michael@0: michael@0: def declare(self): michael@0: return self.struct.declare() michael@0: michael@0: def define(self): michael@0: return self.struct.define() michael@0: michael@0: def getStruct(self): michael@0: michael@0: members = [ClassMember("mType", "Type", body="eUninitialized"), michael@0: ClassMember("mValue", "Value")] michael@0: ctor = ClassConstructor([], bodyInHeader=True, visibility="public", michael@0: explicit=True) michael@0: michael@0: methods = [] michael@0: enumValues = ["eUninitialized"] michael@0: toJSValCases = [CGCase("eUninitialized", CGGeneric("return false;\n"))] michael@0: destructorCases = [CGCase("eUninitialized", None)] michael@0: assignmentCases = [ michael@0: CGCase("eUninitialized", michael@0: CGGeneric('MOZ_ASSERT(mType == eUninitialized,\n' michael@0: ' "We need to destroy ourselves?");\n'))] michael@0: traceCases = [] michael@0: unionValues = [] michael@0: if self.type.hasNullableType: michael@0: enumValues.append("eNull") michael@0: methods.append(ClassMethod("IsNull", "bool", [], const=True, inline=True, michael@0: body="return mType == eNull;\n", michael@0: bodyInHeader=True)) michael@0: methods.append(ClassMethod("SetNull", "void", [], inline=True, michael@0: body=("Uninit();\n" michael@0: "mType = eNull;\n"), michael@0: bodyInHeader=True)) michael@0: destructorCases.append(CGCase("eNull", None)) michael@0: assignmentCases.append(CGCase("eNull", michael@0: CGGeneric("MOZ_ASSERT(mType == eUninitialized);\n" michael@0: "mType = eNull;\n"))) michael@0: toJSValCases.append(CGCase("eNull", CGGeneric("rval.setNull();\n" michael@0: "return true;\n"))) michael@0: michael@0: hasObjectType = any(t.isObject() for t in self.type.flatMemberTypes) michael@0: for t in self.type.flatMemberTypes: michael@0: vars = getUnionTypeTemplateVars(self.type, michael@0: t, self.descriptorProvider, michael@0: ownsMembers=self.ownsMembers) michael@0: if vars["name"] != "Object" or self.ownsMembers: michael@0: body = fill( michael@0: """ michael@0: if (mType == e${name}) { michael@0: return mValue.m${name}.Value(); michael@0: } michael@0: %s michael@0: mType = e${name}; michael@0: return mValue.m${name}.SetValue(${ctorArgs}); michael@0: """, michael@0: **vars) michael@0: michael@0: # bodyInHeader must be false for return values because they own michael@0: # their union members and we don't want include headers in michael@0: # UnionTypes.h just to call Addref/Release michael@0: methods.append(ClassMethod( michael@0: "RawSetAs" + vars["name"], michael@0: vars["structType"] + "&", michael@0: vars["ctorArgList"], michael@0: bodyInHeader=not self.ownsMembers, michael@0: body=body % "MOZ_ASSERT(mType == eUninitialized);")) michael@0: uninit = "Uninit();" michael@0: if hasObjectType and not self.ownsMembers: michael@0: uninit = 'MOZ_ASSERT(mType != eObject, "This will not play well with Rooted");\n' + uninit michael@0: methods.append(ClassMethod( michael@0: "SetAs" + vars["name"], michael@0: vars["structType"] + "&", michael@0: vars["ctorArgList"], michael@0: bodyInHeader=not self.ownsMembers, michael@0: body=body % uninit)) michael@0: if self.ownsMembers: michael@0: methods.append(vars["setter"]) michael@0: if t.isString(): michael@0: methods.append( michael@0: ClassMethod("SetStringData", "void", michael@0: [Argument("const nsString::char_type*", "aData"), michael@0: Argument("nsString::size_type", "aLength")], michael@0: inline=True, bodyInHeader=True, michael@0: body="RawSetAsString().Assign(aData, aLength);\n")) michael@0: michael@0: body = fill( michael@0: """ michael@0: MOZ_ASSERT(Is${name}(), "Wrong type!"); michael@0: mValue.m${name}.Destroy(); michael@0: mType = eUninitialized; michael@0: """, michael@0: **vars) michael@0: methods.append(ClassMethod("Destroy" + vars["name"], michael@0: "void", michael@0: [], michael@0: visibility="private", michael@0: bodyInHeader=not self.ownsMembers, michael@0: body=body)) michael@0: michael@0: body = fill("return mType == e${name};\n", **vars) michael@0: methods.append(ClassMethod("Is" + vars["name"], michael@0: "bool", michael@0: [], michael@0: const=True, michael@0: bodyInHeader=True, michael@0: body=body)) michael@0: michael@0: body = fill( michael@0: """ michael@0: MOZ_ASSERT(Is${name}(), "Wrong type!"); michael@0: return const_cast<${structType}&>(mValue.m${name}.Value()); michael@0: """, michael@0: **vars) michael@0: if self.ownsMembers: michael@0: getterReturnType = "%s&" % vars["structType"] michael@0: else: michael@0: getterReturnType = vars["externalType"] michael@0: methods.append(ClassMethod("GetAs" + vars["name"], michael@0: getterReturnType, michael@0: [], michael@0: const=True, michael@0: bodyInHeader=True, michael@0: body=body)) michael@0: michael@0: unionValues.append( michael@0: fill("UnionMember<${structType} > m${name}", **vars)) michael@0: enumValues.append("e" + vars["name"]) michael@0: michael@0: toJSValCases.append( michael@0: CGCase("e" + vars["name"], michael@0: self.getConversionToJS(vars, t))) michael@0: destructorCases.append( michael@0: CGCase("e" + vars["name"], michael@0: CGGeneric("Destroy%s();\n" % vars["name"]))) michael@0: assignmentCases.append( michael@0: CGCase("e" + vars["name"], michael@0: CGGeneric("SetAs%s() = aOther.GetAs%s();\n" % michael@0: (vars["name"], vars["name"])))) michael@0: if self.ownsMembers and typeNeedsRooting(t): michael@0: if t.isObject(): michael@0: traceCases.append( michael@0: CGCase("e" + vars["name"], michael@0: CGGeneric('JS_CallObjectTracer(trc, %s, "%s");\n' % michael@0: ("&mValue.m" + vars["name"] + ".Value()", michael@0: "mValue.m" + vars["name"])))) michael@0: elif t.isDictionary(): michael@0: traceCases.append( michael@0: CGCase("e" + vars["name"], michael@0: CGGeneric("mValue.m%s.Value().TraceDictionary(trc);\n" % michael@0: vars["name"]))) michael@0: else: michael@0: assert t.isSpiderMonkeyInterface() michael@0: traceCases.append( michael@0: CGCase("e" + vars["name"], michael@0: CGGeneric("mValue.m%s.Value().TraceSelf(trc);\n" % michael@0: vars["name"]))) michael@0: michael@0: dtor = CGSwitch("mType", destructorCases).define() michael@0: michael@0: methods.append(ClassMethod("Uninit", "void", [], michael@0: visibility="private", body=dtor, michael@0: bodyInHeader=not self.ownsMembers, michael@0: inline=not self.ownsMembers)) michael@0: michael@0: methods.append( michael@0: ClassMethod( michael@0: "ToJSVal", michael@0: "bool", michael@0: [ michael@0: Argument("JSContext*", "cx"), michael@0: Argument("JS::Handle", "scopeObj"), michael@0: Argument("JS::MutableHandle", "rval") michael@0: ], michael@0: body=CGSwitch("mType", toJSValCases, michael@0: default=CGGeneric("return false;\n")).define(), michael@0: const=True)) michael@0: michael@0: constructors = [ctor] michael@0: selfName = CGUnionStruct.unionTypeName(self.type, self.ownsMembers) michael@0: if self.ownsMembers: michael@0: if traceCases: michael@0: # BOGUS blank line in default case michael@0: traceBody = CGSwitch("mType", traceCases, michael@0: default=CGGeneric("\n")).define() michael@0: else: michael@0: # BOGUS blank line in method michael@0: traceBody = "\n" michael@0: methods.append(ClassMethod("TraceUnion", "void", michael@0: [Argument("JSTracer*", "trc")], michael@0: body=traceBody)) michael@0: if CGUnionStruct.isUnionCopyConstructible(self.type): michael@0: constructors.append( michael@0: ClassConstructor( michael@0: [Argument("const %s&" % selfName, "aOther")], michael@0: bodyInHeader=True, michael@0: visibility="public", michael@0: explicit=True, michael@0: body="*this = aOther;\n")) michael@0: methods.append(ClassMethod( michael@0: "operator=", "void", michael@0: [Argument("const %s&" % selfName, "aOther")], michael@0: body=CGSwitch("aOther.mType", assignmentCases).define())) michael@0: disallowCopyConstruction = False michael@0: else: michael@0: disallowCopyConstruction = True michael@0: else: michael@0: disallowCopyConstruction = True michael@0: michael@0: friend = " friend class %sArgument;\n" % str(self.type) if not self.ownsMembers else "" michael@0: bases = [ClassBase("AllOwningUnionBase")] if self.ownsMembers else [] michael@0: return CGClass(selfName, michael@0: bases=bases, michael@0: members=members, michael@0: constructors=constructors, michael@0: methods=methods, michael@0: disallowCopyConstruction=disallowCopyConstruction, michael@0: extradeclarations=friend, michael@0: destructor=ClassDestructor(visibility="public", michael@0: body="Uninit();", michael@0: bodyInHeader=True), michael@0: enums=[ClassEnum("Type", enumValues, visibility="private")], michael@0: unions=[ClassUnion("Value", unionValues, visibility="private")]) michael@0: michael@0: def getConversionToJS(self, templateVars, type): michael@0: assert not type.nullable() # flatMemberTypes never has nullable types michael@0: val = "mValue.m%(name)s.Value()" % templateVars michael@0: wrapCode = wrapForType( michael@0: type, self.descriptorProvider, michael@0: { michael@0: "jsvalRef": "rval", michael@0: "jsvalHandle": "rval", michael@0: "obj": "scopeObj", michael@0: "result": val, michael@0: "typedArraysAreStructs": True michael@0: }) michael@0: return CGGeneric(wrapCode) michael@0: michael@0: @staticmethod michael@0: def isUnionCopyConstructible(type): michael@0: return all(isTypeCopyConstructible(t) for t in type.flatMemberTypes) michael@0: michael@0: @staticmethod michael@0: def unionTypeName(type, ownsMembers): michael@0: """ michael@0: Returns a string name for this known union type. michael@0: """ michael@0: assert type.isUnion() and not type.nullable() michael@0: return ("Owning" if ownsMembers else "") + type.name michael@0: michael@0: @staticmethod michael@0: def unionTypeDecl(type, ownsMembers): michael@0: """ michael@0: Returns a string for declaring this possibly-nullable union type. michael@0: """ michael@0: assert type.isUnion() michael@0: nullable = type.nullable() michael@0: if nullable: michael@0: type = type.inner michael@0: decl = CGGeneric(CGUnionStruct.unionTypeName(type, ownsMembers)) michael@0: if nullable: michael@0: decl = CGTemplatedType("Nullable", decl) michael@0: return decl.define() michael@0: michael@0: michael@0: class CGUnionConversionStruct(CGThing): michael@0: def __init__(self, type, descriptorProvider): michael@0: CGThing.__init__(self) michael@0: self.type = type.unroll() michael@0: self.descriptorProvider = descriptorProvider michael@0: michael@0: def declare(self): michael@0: michael@0: structName = str(self.type) michael@0: members = [ClassMember("mUnion", structName + "&", michael@0: body="const_cast<%s&>(aUnion)" % structName)] michael@0: # Argument needs to be a const ref because that's all Maybe<> allows michael@0: ctor = ClassConstructor([Argument("const %s&" % structName, "aUnion")], michael@0: bodyInHeader=True, michael@0: visibility="public", michael@0: explicit=True) michael@0: methods = [] michael@0: michael@0: if self.type.hasNullableType: michael@0: methods.append(ClassMethod("SetNull", "bool", [], michael@0: body=("MOZ_ASSERT(mUnion.mType == mUnion.eUninitialized);\n" michael@0: "mUnion.mType = mUnion.eNull;\n" michael@0: "return true;\n"), michael@0: inline=True, bodyInHeader=True)) michael@0: michael@0: for t in self.type.flatMemberTypes: michael@0: vars = getUnionTypeTemplateVars(self.type, michael@0: t, self.descriptorProvider) michael@0: methods.append(vars["setter"]) michael@0: if vars["name"] != "Object": michael@0: body = fill( michael@0: """ michael@0: MOZ_ASSERT(mUnion.mType == mUnion.eUninitialized); michael@0: mUnion.mType = mUnion.e${name}; michael@0: return mUnion.mValue.m${name}.SetValue(${ctorArgs}); michael@0: """, michael@0: **vars) michael@0: methods.append(ClassMethod("RawSetAs" + vars["name"], michael@0: vars["structType"] + "&", michael@0: vars["ctorArgList"], michael@0: bodyInHeader=True, michael@0: body=body, michael@0: visibility="private")) michael@0: if t.isString(): michael@0: methods.append( michael@0: ClassMethod("SetStringData", "void", michael@0: [Argument("const nsDependentString::char_type*", "aData"), michael@0: Argument("nsDependentString::size_type", "aLength")], michael@0: inline=True, bodyInHeader=True, michael@0: body="RawSetAsString().SetData(aData, aLength);\n")) michael@0: michael@0: if vars["holderType"] is not None: michael@0: members.append(ClassMember("m%sHolder" % vars["name"], michael@0: vars["holderType"])) michael@0: michael@0: return CGClass(structName + "Argument", michael@0: members=members, michael@0: constructors=[ctor], michael@0: methods=methods, michael@0: disallowCopyConstruction=True).declare() michael@0: michael@0: def define(self): michael@0: return "\n" michael@0: michael@0: michael@0: class ClassItem: michael@0: """ Use with CGClass """ michael@0: def __init__(self, name, visibility): michael@0: self.name = name michael@0: self.visibility = visibility michael@0: michael@0: def declare(self, cgClass): michael@0: assert False michael@0: michael@0: def define(self, cgClass): michael@0: assert False michael@0: michael@0: michael@0: class ClassBase(ClassItem): michael@0: def __init__(self, name, visibility='public'): michael@0: ClassItem.__init__(self, name, visibility) michael@0: michael@0: def declare(self, cgClass): michael@0: return '%s %s' % (self.visibility, self.name) michael@0: michael@0: def define(self, cgClass): michael@0: # Only in the header michael@0: return '' michael@0: michael@0: michael@0: class ClassMethod(ClassItem): michael@0: def __init__(self, name, returnType, args, inline=False, static=False, michael@0: virtual=False, const=False, bodyInHeader=False, michael@0: templateArgs=None, visibility='public', body=None, michael@0: breakAfterReturnDecl="\n", michael@0: breakAfterSelf="\n", override=False): michael@0: """ michael@0: override indicates whether to flag the method as MOZ_OVERRIDE michael@0: """ michael@0: assert not override or virtual michael@0: assert not (override and static) michael@0: self.returnType = returnType michael@0: self.args = args michael@0: self.inline = inline or bodyInHeader michael@0: self.static = static michael@0: self.virtual = virtual michael@0: self.const = const michael@0: self.bodyInHeader = bodyInHeader michael@0: self.templateArgs = templateArgs michael@0: self.body = body michael@0: self.breakAfterReturnDecl = breakAfterReturnDecl michael@0: self.breakAfterSelf = breakAfterSelf michael@0: self.override = override michael@0: ClassItem.__init__(self, name, visibility) michael@0: michael@0: def getDecorators(self, declaring): michael@0: decorators = [] michael@0: if self.inline: michael@0: decorators.append('inline') michael@0: if declaring: michael@0: if self.static: michael@0: decorators.append('static') michael@0: if self.virtual: michael@0: decorators.append('virtual') michael@0: if decorators: michael@0: return ' '.join(decorators) + ' ' michael@0: return '' michael@0: michael@0: def getBody(self): michael@0: # Override me or pass a string to constructor michael@0: assert self.body is not None michael@0: return self.body michael@0: michael@0: def declare(self, cgClass): michael@0: templateClause = ('template <%s>\n' % ', '.join(self.templateArgs) michael@0: if self.bodyInHeader and self.templateArgs else '') michael@0: args = ', '.join([a.declare() for a in self.args]) michael@0: if self.bodyInHeader: michael@0: body = indent(self.getBody()) michael@0: body = '\n{\n' + body + '}\n' michael@0: else: michael@0: body = ';\n' michael@0: michael@0: return fill( michael@0: "${templateClause}${decorators}${returnType}${breakAfterReturnDecl}" michael@0: "${name}(${args})${const}${override}${body}" michael@0: "${breakAfterSelf}", michael@0: templateClause=templateClause, michael@0: decorators=self.getDecorators(True), michael@0: returnType=self.returnType, michael@0: breakAfterReturnDecl=self.breakAfterReturnDecl, michael@0: name=self.name, michael@0: args=args, michael@0: const=' const' if self.const else '', michael@0: override=' MOZ_OVERRIDE' if self.override else '', michael@0: body=body, michael@0: breakAfterSelf=self.breakAfterSelf) michael@0: michael@0: def define(self, cgClass): michael@0: if self.bodyInHeader: michael@0: return '' michael@0: michael@0: templateArgs = cgClass.templateArgs michael@0: if templateArgs: michael@0: if cgClass.templateSpecialization: michael@0: templateArgs = \ michael@0: templateArgs[len(cgClass.templateSpecialization):] michael@0: michael@0: if templateArgs: michael@0: templateClause = \ michael@0: 'template <%s>\n' % ', '.join([str(a) for a in templateArgs]) michael@0: else: michael@0: templateClause = '' michael@0: michael@0: return fill( michael@0: """ michael@0: ${templateClause}${decorators}${returnType} michael@0: ${className}::${name}(${args})${const} michael@0: { michael@0: $*{body} michael@0: } michael@0: """, michael@0: templateClause=templateClause, michael@0: decorators=self.getDecorators(False), michael@0: returnType=self.returnType, michael@0: className=cgClass.getNameString(), michael@0: name=self.name, michael@0: args=', '.join([a.define() for a in self.args]), michael@0: const=' const' if self.const else '', michael@0: body=self.getBody()) michael@0: michael@0: michael@0: class ClassUsingDeclaration(ClassItem): michael@0: """ michael@0: Used for importing a name from a base class into a CGClass michael@0: michael@0: baseClass is the name of the base class to import the name from michael@0: michael@0: name is the name to import michael@0: michael@0: visibility determines the visibility of the name (public, michael@0: protected, private), defaults to public. michael@0: """ michael@0: def __init__(self, baseClass, name, visibility='public'): michael@0: self.baseClass = baseClass michael@0: ClassItem.__init__(self, name, visibility) michael@0: michael@0: def declare(self, cgClass): michael@0: return "using %s::%s;\n\n" % (self.baseClass, self.name) michael@0: michael@0: def define(self, cgClass): michael@0: return '' michael@0: michael@0: michael@0: class ClassConstructor(ClassItem): michael@0: """ michael@0: Used for adding a constructor to a CGClass. michael@0: michael@0: args is a list of Argument objects that are the arguments taken by the michael@0: constructor. michael@0: michael@0: inline should be True if the constructor should be marked inline. michael@0: michael@0: bodyInHeader should be True if the body should be placed in the class michael@0: declaration in the header. michael@0: michael@0: visibility determines the visibility of the constructor (public, michael@0: protected, private), defaults to private. michael@0: michael@0: explicit should be True if the constructor should be marked explicit. michael@0: michael@0: baseConstructors is a list of strings containing calls to base constructors, michael@0: defaults to None. michael@0: michael@0: body contains a string with the code for the constructor, defaults to empty. michael@0: """ michael@0: def __init__(self, args, inline=False, bodyInHeader=False, michael@0: visibility="private", explicit=False, baseConstructors=None, michael@0: body=""): michael@0: self.args = args michael@0: self.inline = inline or bodyInHeader michael@0: self.bodyInHeader = bodyInHeader michael@0: self.explicit = explicit michael@0: self.baseConstructors = baseConstructors or [] michael@0: self.body = body michael@0: ClassItem.__init__(self, None, visibility) michael@0: michael@0: def getDecorators(self, declaring): michael@0: decorators = [] michael@0: if self.explicit: michael@0: decorators.append('explicit') michael@0: if self.inline and declaring: michael@0: decorators.append('inline') michael@0: if decorators: michael@0: return ' '.join(decorators) + ' ' michael@0: return '' michael@0: michael@0: def getInitializationList(self, cgClass): michael@0: items = [str(c) for c in self.baseConstructors] michael@0: for m in cgClass.members: michael@0: if not m.static: michael@0: initialize = m.body michael@0: if initialize: michael@0: items.append(m.name + "(" + initialize + ")") michael@0: michael@0: if len(items) > 0: michael@0: return '\n : ' + ',\n '.join(items) michael@0: return '' michael@0: michael@0: def getBody(self): michael@0: return self.body michael@0: michael@0: def declare(self, cgClass): michael@0: args = ', '.join([a.declare() for a in self.args]) michael@0: if self.bodyInHeader: michael@0: body = self.getInitializationList(cgClass) + '\n{\n' + indent(self.getBody()) + '}\n' michael@0: else: michael@0: body = ';\n' michael@0: michael@0: return fill( michael@0: "${decorators}${className}(${args})${body}\n", michael@0: decorators=self.getDecorators(True), michael@0: className=cgClass.getNameString(), michael@0: args=args, michael@0: body=body) michael@0: michael@0: def define(self, cgClass): michael@0: if self.bodyInHeader: michael@0: return '' michael@0: michael@0: return fill( michael@0: """ michael@0: ${decorators} michael@0: ${className}::${className}(${args})${initializationList} michael@0: { michael@0: $*{body} michael@0: } michael@0: """, michael@0: decorators=self.getDecorators(False), michael@0: className=cgClass.getNameString(), michael@0: args=', '.join([a.define() for a in self.args]), michael@0: initializationList=self.getInitializationList(cgClass), michael@0: body=self.getBody()) michael@0: michael@0: michael@0: class ClassDestructor(ClassItem): michael@0: """ michael@0: Used for adding a destructor to a CGClass. michael@0: michael@0: inline should be True if the destructor should be marked inline. michael@0: michael@0: bodyInHeader should be True if the body should be placed in the class michael@0: declaration in the header. michael@0: michael@0: visibility determines the visibility of the destructor (public, michael@0: protected, private), defaults to private. michael@0: michael@0: body contains a string with the code for the destructor, defaults to empty. michael@0: michael@0: virtual determines whether the destructor is virtual, defaults to False. michael@0: """ michael@0: def __init__(self, inline=False, bodyInHeader=False, michael@0: visibility="private", body='', virtual=False): michael@0: self.inline = inline or bodyInHeader michael@0: self.bodyInHeader = bodyInHeader michael@0: self.body = body michael@0: self.virtual = virtual michael@0: ClassItem.__init__(self, None, visibility) michael@0: michael@0: def getDecorators(self, declaring): michael@0: decorators = [] michael@0: if self.virtual and declaring: michael@0: decorators.append('virtual') michael@0: if self.inline and declaring: michael@0: decorators.append('inline') michael@0: if decorators: michael@0: return ' '.join(decorators) + ' ' michael@0: return '' michael@0: michael@0: def getBody(self): michael@0: return self.body michael@0: michael@0: def declare(self, cgClass): michael@0: if self.bodyInHeader: michael@0: body = '\n{\n' + indent(self.getBody()) + '}\n' michael@0: else: michael@0: body = ';\n' michael@0: michael@0: return fill( michael@0: "${decorators}~${className}()${body}\n", michael@0: decorators=self.getDecorators(True), michael@0: className=cgClass.getNameString(), michael@0: body=body) michael@0: michael@0: def define(self, cgClass): michael@0: if self.bodyInHeader: michael@0: return '' michael@0: return fill( michael@0: """ michael@0: ${decorators} michael@0: ${className}::~${className}() michael@0: { michael@0: $*{body} michael@0: } michael@0: """, michael@0: decorators=self.getDecorators(False), michael@0: className=cgClass.getNameString(), michael@0: body=self.getBody() or "\n") # BOGUS extra blank line if empty michael@0: michael@0: michael@0: class ClassMember(ClassItem): michael@0: def __init__(self, name, type, visibility="private", static=False, michael@0: body=None): michael@0: self.type = type michael@0: self.static = static michael@0: self.body = body michael@0: ClassItem.__init__(self, name, visibility) michael@0: michael@0: def declare(self, cgClass): michael@0: return '%s%s %s;\n' % ('static ' if self.static else '', self.type, michael@0: self.name) michael@0: michael@0: def define(self, cgClass): michael@0: if not self.static: michael@0: return '' michael@0: if self.body: michael@0: body = " = " + self.body michael@0: else: michael@0: body = "" michael@0: return '%s %s::%s%s;\n' % (self.type, cgClass.getNameString(), michael@0: self.name, body) michael@0: michael@0: michael@0: class ClassTypedef(ClassItem): michael@0: def __init__(self, name, type, visibility="public"): michael@0: self.type = type michael@0: ClassItem.__init__(self, name, visibility) michael@0: michael@0: def declare(self, cgClass): michael@0: return 'typedef %s %s;\n' % (self.type, self.name) michael@0: michael@0: def define(self, cgClass): michael@0: # Only goes in the header michael@0: return '' michael@0: michael@0: michael@0: class ClassEnum(ClassItem): michael@0: def __init__(self, name, entries, values=None, visibility="public"): michael@0: self.entries = entries michael@0: self.values = values michael@0: ClassItem.__init__(self, name, visibility) michael@0: michael@0: def declare(self, cgClass): michael@0: entries = [] michael@0: for i in range(0, len(self.entries)): michael@0: if not self.values or i >= len(self.values): michael@0: entry = '%s' % self.entries[i] michael@0: else: michael@0: entry = '%s = %s' % (self.entries[i], self.values[i]) michael@0: entries.append(entry) michael@0: name = '' if not self.name else ' ' + self.name michael@0: return 'enum%s\n{\n%s\n};\n' % (name, indent(',\n'.join(entries))) michael@0: michael@0: def define(self, cgClass): michael@0: # Only goes in the header michael@0: return '' michael@0: michael@0: michael@0: class ClassUnion(ClassItem): michael@0: def __init__(self, name, entries, visibility="public"): michael@0: self.entries = [entry + ";\n" for entry in entries] michael@0: ClassItem.__init__(self, name, visibility) michael@0: michael@0: def declare(self, cgClass): michael@0: return "union %s\n{\n%s\n};\n" % (self.name, indent(''.join(self.entries))) michael@0: michael@0: def define(self, cgClass): michael@0: # Only goes in the header michael@0: return '' michael@0: michael@0: michael@0: class CGClass(CGThing): michael@0: def __init__(self, name, bases=[], members=[], constructors=[], michael@0: destructor=None, methods=[], michael@0: typedefs=[], enums=[], unions=[], templateArgs=[], michael@0: templateSpecialization=[], isStruct=False, michael@0: disallowCopyConstruction=False, indent='', michael@0: decorators='', michael@0: extradeclarations='', michael@0: extradefinitions=''): michael@0: CGThing.__init__(self) michael@0: self.name = name michael@0: self.bases = bases michael@0: self.members = members michael@0: self.constructors = constructors michael@0: # We store our single destructor in a list, since all of our michael@0: # code wants lists of members. michael@0: self.destructors = [destructor] if destructor else [] michael@0: self.methods = methods michael@0: self.typedefs = typedefs michael@0: self.enums = enums michael@0: self.unions = unions michael@0: self.templateArgs = templateArgs michael@0: self.templateSpecialization = templateSpecialization michael@0: self.isStruct = isStruct michael@0: self.disallowCopyConstruction = disallowCopyConstruction michael@0: self.indent = indent michael@0: self.defaultVisibility = 'public' if isStruct else 'private' michael@0: self.decorators = decorators michael@0: self.extradeclarations = extradeclarations michael@0: self.extradefinitions = extradefinitions michael@0: michael@0: def getNameString(self): michael@0: className = self.name michael@0: if self.templateSpecialization: michael@0: className += '<%s>' % ', '.join([str(a) michael@0: for a in self.templateSpecialization]) michael@0: return className michael@0: michael@0: def declare(self): michael@0: result = '' michael@0: if self.templateArgs: michael@0: templateArgs = [a.declare() for a in self.templateArgs] michael@0: templateArgs = templateArgs[len(self.templateSpecialization):] michael@0: result += ('template <%s>\n' % michael@0: ','.join([str(a) for a in templateArgs])) michael@0: michael@0: type = 'struct' if self.isStruct else 'class' michael@0: michael@0: if self.templateSpecialization: michael@0: specialization = \ michael@0: '<%s>' % ', '.join([str(a) for a in self.templateSpecialization]) michael@0: else: michael@0: specialization = '' michael@0: michael@0: myself = '%s %s%s' % (type, self.name, specialization) michael@0: if self.decorators != '': michael@0: myself += " " + self.decorators michael@0: result += myself michael@0: michael@0: if self.bases: michael@0: inherit = ' : ' michael@0: result += inherit michael@0: # Grab our first base michael@0: baseItems = [CGGeneric(b.declare(self)) for b in self.bases] michael@0: bases = baseItems[:1] michael@0: # Indent the rest michael@0: bases.extend(CGIndenter(b, len(myself) + len(inherit)) michael@0: for b in baseItems[1:]) michael@0: result += ",\n".join(b.define() for b in bases) michael@0: michael@0: result += '\n{\n' michael@0: michael@0: result += self.extradeclarations michael@0: michael@0: def declareMembers(cgClass, memberList, defaultVisibility): michael@0: members = {'private': [], 'protected': [], 'public': []} michael@0: michael@0: for member in memberList: michael@0: members[member.visibility].append(member) michael@0: michael@0: if defaultVisibility == 'public': michael@0: order = ['public', 'protected', 'private'] michael@0: else: michael@0: order = ['private', 'protected', 'public'] michael@0: michael@0: result = '' michael@0: michael@0: lastVisibility = defaultVisibility michael@0: for visibility in order: michael@0: list = members[visibility] michael@0: if list: michael@0: if visibility != lastVisibility: michael@0: result += visibility + ':\n' michael@0: for member in list: michael@0: result += indent(member.declare(cgClass)) michael@0: lastVisibility = visibility michael@0: return (result, lastVisibility) michael@0: michael@0: if self.disallowCopyConstruction: michael@0: class DisallowedCopyConstructor(object): michael@0: def __init__(self): michael@0: self.visibility = "private" michael@0: michael@0: def declare(self, cgClass): michael@0: name = cgClass.getNameString() michael@0: return ("%s(const %s&) MOZ_DELETE;\n" michael@0: "void operator=(const %s) MOZ_DELETE;\n" % (name, name, name)) michael@0: michael@0: disallowedCopyConstructors = [DisallowedCopyConstructor()] michael@0: else: michael@0: disallowedCopyConstructors = [] michael@0: michael@0: order = [self.enums, self.unions, michael@0: self.typedefs, self.members, michael@0: self.constructors + disallowedCopyConstructors, michael@0: self.destructors, self.methods] michael@0: michael@0: lastVisibility = self.defaultVisibility michael@0: pieces = [] michael@0: for memberList in order: michael@0: code, lastVisibility = declareMembers(self, memberList, lastVisibility) michael@0: michael@0: if code: michael@0: code = code.rstrip() + "\n" # remove extra blank lines at the end michael@0: pieces.append(code) michael@0: michael@0: result += '\n'.join(pieces) michael@0: result += '};\n' michael@0: result = indent(result, len(self.indent)) michael@0: return result michael@0: michael@0: def define(self): michael@0: def defineMembers(cgClass, memberList, itemCount, separator=''): michael@0: result = '' michael@0: for member in memberList: michael@0: if itemCount != 0: michael@0: result = result + separator michael@0: definition = member.define(cgClass) michael@0: if definition: michael@0: # Member variables would only produce empty lines here. michael@0: result += definition michael@0: itemCount += 1 michael@0: return (result, itemCount) michael@0: michael@0: order = [(self.members, ''), (self.constructors, '\n'), michael@0: (self.destructors, '\n'), (self.methods, '\n')] michael@0: michael@0: result = self.extradefinitions michael@0: itemCount = 0 michael@0: for memberList, separator in order: michael@0: memberString, itemCount = defineMembers(self, memberList, michael@0: itemCount, separator) michael@0: result = result + memberString michael@0: return result michael@0: michael@0: michael@0: class CGResolveOwnProperty(CGAbstractStaticMethod): michael@0: def __init__(self, descriptor): michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('JS::Handle', 'wrapper'), michael@0: Argument('JS::Handle', 'obj'), michael@0: Argument('JS::Handle', 'id'), michael@0: Argument('JS::MutableHandle', 'desc'), michael@0: ] michael@0: CGAbstractStaticMethod.__init__(self, descriptor, "ResolveOwnProperty", michael@0: "bool", args) michael@0: michael@0: def definition_body(self): michael@0: # BOGUS extra blank line at end of function michael@0: return " return js::GetProxyHandler(obj)->getOwnPropertyDescriptor(cx, wrapper, id, desc);\n\n" michael@0: michael@0: michael@0: class CGResolveOwnPropertyViaNewresolve(CGAbstractBindingMethod): michael@0: """ michael@0: An implementation of Xray ResolveOwnProperty stuff for things that have a michael@0: newresolve hook. michael@0: """ michael@0: def __init__(self, descriptor): michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('JS::Handle', 'wrapper'), michael@0: Argument('JS::Handle', 'obj'), michael@0: Argument('JS::Handle', 'id'), michael@0: Argument('JS::MutableHandle', 'desc')] michael@0: CGAbstractBindingMethod.__init__(self, descriptor, michael@0: "ResolveOwnPropertyViaNewresolve", michael@0: args, getThisObj="", michael@0: callArgs="") michael@0: michael@0: def generate_code(self): michael@0: return CGGeneric(indent(dedent(""" michael@0: { michael@0: // Since we're dealing with an Xray, do the resolve on the michael@0: // underlying object first. That gives it a chance to michael@0: // define properties on the actual object as needed, and michael@0: // then use the fact that it created the objects as a flag michael@0: // to avoid re-resolving the properties if someone deletes michael@0: // them. michael@0: JSAutoCompartment ac(cx, obj); michael@0: JS::Rooted objDesc(cx); michael@0: if (!self->DoNewResolve(cx, obj, id, &objDesc)) { michael@0: return false; michael@0: } michael@0: // If desc.value() is undefined, then the DoNewResolve call michael@0: // has already defined the property on the object. Don't michael@0: // try to also define it. michael@0: if (objDesc.object() && michael@0: !objDesc.value().isUndefined() && michael@0: !JS_DefinePropertyById(cx, obj, id, objDesc.value(), michael@0: objDesc.getter(), objDesc.setter(), michael@0: objDesc.attributes())) { michael@0: return false; michael@0: } michael@0: } michael@0: return self->DoNewResolve(cx, wrapper, id, desc); michael@0: """))) michael@0: michael@0: michael@0: class CGEnumerateOwnProperties(CGAbstractStaticMethod): michael@0: def __init__(self, descriptor): michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('JS::Handle', 'wrapper'), michael@0: Argument('JS::Handle', 'obj'), michael@0: Argument('JS::AutoIdVector&', 'props')] michael@0: CGAbstractStaticMethod.__init__(self, descriptor, michael@0: "EnumerateOwnProperties", "bool", args) michael@0: michael@0: def definition_body(self): michael@0: # BOGUS extra newline michael@0: return " return js::GetProxyHandler(obj)->getOwnPropertyNames(cx, wrapper, props);\n\n" michael@0: michael@0: michael@0: class CGEnumerateOwnPropertiesViaGetOwnPropertyNames(CGAbstractBindingMethod): michael@0: """ michael@0: An implementation of Xray EnumerateOwnProperties stuff for things michael@0: that have a newresolve hook. michael@0: """ michael@0: def __init__(self, descriptor): michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('JS::Handle', 'wrapper'), michael@0: Argument('JS::Handle', 'obj'), michael@0: Argument('JS::AutoIdVector&', 'props')] michael@0: CGAbstractBindingMethod.__init__(self, descriptor, michael@0: "EnumerateOwnPropertiesViaGetOwnPropertyNames", michael@0: args, getThisObj="", michael@0: callArgs="") michael@0: michael@0: def generate_code(self): michael@0: return CGIndenter(CGGeneric(dedent(""" michael@0: nsAutoTArray names; michael@0: ErrorResult rv; michael@0: self->GetOwnPropertyNames(cx, names, rv); michael@0: rv.WouldReportJSException(); michael@0: if (rv.Failed()) { michael@0: return ThrowMethodFailedWithDetails(cx, rv, "%s", "enumerate"); michael@0: } michael@0: // OK to pass null as "proxy" because it's ignored if michael@0: // shadowPrototypeProperties is true michael@0: return AppendNamedPropertyIds(cx, JS::NullPtr(), names, true, props); michael@0: """))) michael@0: michael@0: michael@0: class CGPrototypeTraitsClass(CGClass): michael@0: def __init__(self, descriptor, indent=''): michael@0: templateArgs = [Argument('prototypes::ID', 'PrototypeID')] michael@0: templateSpecialization = ['prototypes::id::' + descriptor.name] michael@0: enums = [ClassEnum('', ['Depth'], michael@0: [descriptor.interface.inheritanceDepth()])] michael@0: CGClass.__init__(self, 'PrototypeTraits', indent=indent, michael@0: templateArgs=templateArgs, michael@0: templateSpecialization=templateSpecialization, michael@0: enums=enums, isStruct=True) michael@0: michael@0: def deps(self): michael@0: return set() michael@0: michael@0: michael@0: class CGClassForwardDeclare(CGThing): michael@0: def __init__(self, name, isStruct=False): michael@0: CGThing.__init__(self) michael@0: self.name = name michael@0: self.isStruct = isStruct michael@0: michael@0: def declare(self): michael@0: type = 'struct' if self.isStruct else 'class' michael@0: return '%s %s;\n' % (type, self.name) michael@0: michael@0: def define(self): michael@0: # Header only michael@0: return '' michael@0: michael@0: def deps(self): michael@0: return set() michael@0: michael@0: michael@0: class CGProxySpecialOperation(CGPerSignatureCall): michael@0: """ michael@0: Base class for classes for calling an indexed or named special operation michael@0: (don't use this directly, use the derived classes below). michael@0: michael@0: If checkFound is False, will just assert that the prop is found instead of michael@0: checking that it is before wrapping the value. michael@0: """ michael@0: def __init__(self, descriptor, operation, checkFound=True, argumentMutableValue=None): michael@0: self.checkFound = checkFound michael@0: michael@0: nativeName = MakeNativeName(descriptor.binaryNames.get(operation, operation)) michael@0: operation = descriptor.operations[operation] michael@0: assert len(operation.signatures()) == 1 michael@0: signature = operation.signatures()[0] michael@0: michael@0: returnType, arguments = signature michael@0: michael@0: # We pass len(arguments) as the final argument so that the michael@0: # CGPerSignatureCall won't do any argument conversion of its own. michael@0: CGPerSignatureCall.__init__(self, returnType, arguments, nativeName, michael@0: False, descriptor, operation, michael@0: len(arguments)) michael@0: michael@0: if operation.isSetter() or operation.isCreator(): michael@0: # arguments[0] is the index or name of the item that we're setting. michael@0: argument = arguments[1] michael@0: info = getJSToNativeConversionInfo( michael@0: argument.type, descriptor, michael@0: treatNullAs=argument.treatNullAs, michael@0: sourceDescription=("value being assigned to %s setter" % michael@0: descriptor.interface.identifier.name)) michael@0: if argumentMutableValue is None: michael@0: argumentMutableValue = "desc.value()" michael@0: templateValues = { michael@0: "declName": argument.identifier.name, michael@0: "holderName": argument.identifier.name + "_holder", michael@0: "val": argumentMutableValue, michael@0: "mutableVal": argumentMutableValue, michael@0: "obj": "obj" michael@0: } michael@0: self.cgRoot.prepend(instantiateJSToNativeConversion(info, templateValues)) michael@0: elif operation.isGetter() or operation.isDeleter(): michael@0: self.cgRoot.prepend(CGGeneric("bool found;\n")) michael@0: michael@0: def getArguments(self): michael@0: args = [(a, a.identifier.name) for a in self.arguments] michael@0: if self.idlNode.isGetter() or self.idlNode.isDeleter(): michael@0: args.append((FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean], michael@0: self.idlNode), michael@0: "found")) michael@0: return args michael@0: michael@0: def wrap_return_value(self): michael@0: if not self.idlNode.isGetter() or self.templateValues is None: michael@0: return "" michael@0: michael@0: wrap = CGGeneric(wrapForType(self.returnType, self.descriptor, self.templateValues)) michael@0: if self.checkFound: michael@0: wrap = CGIfWrapper(wrap, "found") michael@0: else: michael@0: wrap = CGList([CGGeneric("MOZ_ASSERT(found);\n"), wrap]) michael@0: return "\n" + wrap.define() michael@0: michael@0: michael@0: class CGProxyIndexedOperation(CGProxySpecialOperation): michael@0: """ michael@0: Class to generate a call to an indexed operation. michael@0: michael@0: If doUnwrap is False, the caller is responsible for making sure a variable michael@0: named 'self' holds the C++ object somewhere where the code we generate michael@0: will see it. michael@0: michael@0: If checkFound is False, will just assert that the prop is found instead of michael@0: checking that it is before wrapping the value. michael@0: """ michael@0: def __init__(self, descriptor, name, doUnwrap=True, checkFound=True, michael@0: argumentMutableValue=None): michael@0: self.doUnwrap = doUnwrap michael@0: CGProxySpecialOperation.__init__(self, descriptor, name, checkFound, michael@0: argumentMutableValue=argumentMutableValue) michael@0: michael@0: def define(self): michael@0: # Our first argument is the id we're getting. michael@0: argName = self.arguments[0].identifier.name michael@0: if argName == "index": michael@0: # We already have our index in a variable with that name michael@0: setIndex = "" michael@0: else: michael@0: setIndex = "uint32_t %s = index;\n" % argName michael@0: if self.doUnwrap: michael@0: unwrap = "%s* self = UnwrapProxy(proxy);\n" % self.descriptor.nativeType michael@0: else: michael@0: unwrap = "" michael@0: return (setIndex + unwrap + michael@0: CGProxySpecialOperation.define(self)) michael@0: michael@0: michael@0: class CGProxyIndexedGetter(CGProxyIndexedOperation): michael@0: """ michael@0: Class to generate a call to an indexed getter. If templateValues is not None michael@0: the returned value will be wrapped with wrapForType using templateValues. michael@0: michael@0: If doUnwrap is False, the caller is responsible for making sure a variable michael@0: named 'self' holds the C++ object somewhere where the code we generate michael@0: will see it. michael@0: michael@0: If checkFound is False, will just assert that the prop is found instead of michael@0: checking that it is before wrapping the value. michael@0: """ michael@0: def __init__(self, descriptor, templateValues=None, doUnwrap=True, michael@0: checkFound=True): michael@0: self.templateValues = templateValues michael@0: CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedGetter', michael@0: doUnwrap, checkFound) michael@0: michael@0: michael@0: class CGProxyIndexedPresenceChecker(CGProxyIndexedGetter): michael@0: """ michael@0: Class to generate a call that checks whether an indexed property exists. michael@0: michael@0: For now, we just delegate to CGProxyIndexedGetter michael@0: """ michael@0: def __init__(self, descriptor): michael@0: CGProxyIndexedGetter.__init__(self, descriptor) michael@0: self.cgRoot.append(CGGeneric("(void)result;\n")) michael@0: michael@0: michael@0: class CGProxyIndexedSetter(CGProxyIndexedOperation): michael@0: """ michael@0: Class to generate a call to an indexed setter. michael@0: """ michael@0: def __init__(self, descriptor, argumentMutableValue=None): michael@0: CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedSetter', michael@0: argumentMutableValue=argumentMutableValue) michael@0: michael@0: michael@0: class CGProxyIndexedDeleter(CGProxyIndexedOperation): michael@0: """ michael@0: Class to generate a call to an indexed deleter. michael@0: """ michael@0: def __init__(self, descriptor): michael@0: CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedDeleter') michael@0: michael@0: michael@0: class CGProxyNamedOperation(CGProxySpecialOperation): michael@0: """ michael@0: Class to generate a call to a named operation. michael@0: michael@0: 'value' is the jsval to use for the name; None indicates that it should be michael@0: gotten from the property id. michael@0: """ michael@0: def __init__(self, descriptor, name, value=None, argumentMutableValue=None): michael@0: CGProxySpecialOperation.__init__(self, descriptor, name, michael@0: argumentMutableValue=argumentMutableValue) michael@0: self.value = value michael@0: michael@0: def define(self): michael@0: # Our first argument is the id we're getting. michael@0: argName = self.arguments[0].identifier.name michael@0: if argName == "id": michael@0: # deal with the name collision michael@0: idDecl = "JS::Rooted id_(cx, id);\n" michael@0: idName = "id_" michael@0: else: michael@0: idDecl = "" michael@0: idName = "id" michael@0: unwrapString = fill( michael@0: """ michael@0: if (!ConvertJSValueToString(cx, nameVal, &nameVal, michael@0: eStringify, eStringify, ${argName})) { michael@0: return false; michael@0: } michael@0: """, michael@0: argName=argName) michael@0: if self.value is None: michael@0: # We're just using 'id', and if it's an atom we can take a michael@0: # fast path here. michael@0: unwrapString = fill( michael@0: """ michael@0: if (MOZ_LIKELY(JSID_IS_ATOM(${idName}))) { michael@0: ${argName}.SetData(js::GetAtomChars(JSID_TO_ATOM(${idName})), js::GetAtomLength(JSID_TO_ATOM(${idName}))); michael@0: } else { michael@0: nameVal = js::IdToValue(${idName}); michael@0: $*{unwrapString} michael@0: } michael@0: """, michael@0: idName=idName, michael@0: argName=argName, michael@0: unwrapString=unwrapString) michael@0: else: michael@0: unwrapString = ("nameVal = %s;\n" % self.value) + unwrapString michael@0: michael@0: # Sadly, we have to set up nameVal even if we have an atom id, michael@0: # because we don't know for sure, and we can end up needing it michael@0: # so it needs to be higher up the stack. Using a Maybe here michael@0: # seems like probable overkill. michael@0: return fill( michael@0: """ michael@0: JS::Rooted nameVal(cx); michael@0: $*{idDecl} michael@0: binding_detail::FakeDependentString ${argName}; michael@0: $*{unwrapString} michael@0: michael@0: ${nativeType}* self = UnwrapProxy(proxy); michael@0: $*{op} michael@0: """, michael@0: idDecl=idDecl, michael@0: argName=argName, michael@0: unwrapString=unwrapString, michael@0: nativeType=self.descriptor.nativeType, michael@0: op=CGProxySpecialOperation.define(self)) michael@0: michael@0: michael@0: class CGProxyNamedGetter(CGProxyNamedOperation): michael@0: """ michael@0: Class to generate a call to an named getter. If templateValues is not None michael@0: the returned value will be wrapped with wrapForType using templateValues. michael@0: 'value' is the jsval to use for the name; None indicates that it should be michael@0: gotten from the property id. michael@0: """ michael@0: def __init__(self, descriptor, templateValues=None, value=None): michael@0: self.templateValues = templateValues michael@0: CGProxyNamedOperation.__init__(self, descriptor, 'NamedGetter', value) michael@0: michael@0: michael@0: class CGProxyNamedPresenceChecker(CGProxyNamedGetter): michael@0: """ michael@0: Class to generate a call that checks whether a named property exists. michael@0: michael@0: For now, we just delegate to CGProxyNamedGetter michael@0: """ michael@0: def __init__(self, descriptor): michael@0: CGProxyNamedGetter.__init__(self, descriptor) michael@0: self.cgRoot.append(CGGeneric("(void)result;\n")) michael@0: michael@0: michael@0: class CGProxyNamedSetter(CGProxyNamedOperation): michael@0: """ michael@0: Class to generate a call to a named setter. michael@0: """ michael@0: def __init__(self, descriptor, argumentMutableValue=None): michael@0: CGProxyNamedOperation.__init__(self, descriptor, 'NamedSetter', michael@0: argumentMutableValue=argumentMutableValue) michael@0: michael@0: michael@0: class CGProxyNamedDeleter(CGProxyNamedOperation): michael@0: """ michael@0: Class to generate a call to a named deleter. michael@0: """ michael@0: def __init__(self, descriptor): michael@0: CGProxyNamedOperation.__init__(self, descriptor, 'NamedDeleter') michael@0: michael@0: michael@0: class CGProxyIsProxy(CGAbstractMethod): michael@0: def __init__(self, descriptor): michael@0: args = [Argument('JSObject*', 'obj')] michael@0: CGAbstractMethod.__init__(self, descriptor, "IsProxy", "bool", args, alwaysInline=True) michael@0: michael@0: def declare(self): michael@0: return "" michael@0: michael@0: def definition_body(self): michael@0: return " return js::IsProxy(obj) && js::GetProxyHandler(obj) == DOMProxyHandler::getInstance();\n" michael@0: michael@0: michael@0: class CGProxyUnwrap(CGAbstractMethod): michael@0: def __init__(self, descriptor): michael@0: args = [Argument('JSObject*', 'obj')] michael@0: CGAbstractMethod.__init__(self, descriptor, "UnwrapProxy", descriptor.nativeType + '*', args, alwaysInline=True) michael@0: michael@0: def declare(self): michael@0: return "" michael@0: michael@0: def definition_body(self): michael@0: return indent(fill( michael@0: """ michael@0: MOZ_ASSERT(js::IsProxy(obj)); michael@0: if (js::GetProxyHandler(obj) != DOMProxyHandler::getInstance()) { michael@0: MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(obj)); michael@0: obj = js::UncheckedUnwrap(obj); michael@0: } michael@0: MOZ_ASSERT(IsProxy(obj)); michael@0: return static_cast<${type}*>(js::GetProxyPrivate(obj).toPrivate()); michael@0: """, michael@0: type=self.descriptor.nativeType)) michael@0: michael@0: michael@0: class CGDOMJSProxyHandler_getOwnPropDescriptor(ClassMethod): michael@0: def __init__(self, descriptor): michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('JS::Handle', 'proxy'), michael@0: Argument('JS::Handle', 'id'), michael@0: Argument('bool', 'ignoreNamedProps'), michael@0: Argument('JS::MutableHandle', 'desc')] michael@0: ClassMethod.__init__(self, "getOwnPropDescriptor", "bool", args, michael@0: virtual=True, override=True) michael@0: self.descriptor = descriptor michael@0: michael@0: def getBody(self): michael@0: indexedGetter = self.descriptor.operations['IndexedGetter'] michael@0: indexedSetter = self.descriptor.operations['IndexedSetter'] michael@0: michael@0: if self.descriptor.supportsIndexedProperties(): michael@0: readonly = toStringBool(indexedSetter is None) michael@0: fillDescriptor = "FillPropertyDescriptor(desc, proxy, %s);\nreturn true;\n" % readonly michael@0: templateValues = { michael@0: 'jsvalRef': 'desc.value()', michael@0: 'jsvalHandle': 'desc.value()', michael@0: 'obj': 'proxy', michael@0: 'successCode': fillDescriptor michael@0: } michael@0: getIndexed = fill( michael@0: """ michael@0: int32_t index = GetArrayIndexFromId(cx, id); michael@0: if (IsArrayIndex(index)) { michael@0: $*{callGetter} michael@0: } michael@0: michael@0: """, michael@0: callGetter=CGProxyIndexedGetter(self.descriptor, templateValues).define()) michael@0: else: michael@0: getIndexed = "" michael@0: michael@0: if UseHolderForUnforgeable(self.descriptor): michael@0: tryHolder = dedent(""" michael@0: if (!JS_GetPropertyDescriptorById(cx, ${holder}, id, desc)) { michael@0: return false; michael@0: } michael@0: MOZ_ASSERT_IF(desc.object(), desc.object() == ${holder}); michael@0: """) michael@0: michael@0: # We don't want to look at the unforgeable holder at all michael@0: # in the xray case; that part got handled already. michael@0: getUnforgeable = fill( michael@0: """ michael@0: if (!isXray) { michael@0: $*{callOnUnforgeable} michael@0: if (desc.object()) { michael@0: desc.object().set(proxy); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: """, michael@0: callOnUnforgeable=CallOnUnforgeableHolder(self.descriptor, tryHolder)) michael@0: else: michael@0: getUnforgeable = "" michael@0: michael@0: if self.descriptor.supportsNamedProperties(): michael@0: operations = self.descriptor.operations michael@0: readonly = toStringBool(operations['NamedSetter'] is None) michael@0: enumerable = ( michael@0: "self->NameIsEnumerable(Constify(%s))" % michael@0: # First [0] means first (and only) signature, [1] means michael@0: # "arguments" as opposed to return type, [0] means first (and michael@0: # only) argument. michael@0: operations['NamedGetter'].signatures()[0][1][0].identifier.name) michael@0: fillDescriptor = ( michael@0: "FillPropertyDescriptor(desc, proxy, %s, %s);\n" michael@0: "return true;\n" % (readonly, enumerable)) michael@0: templateValues = {'jsvalRef': 'desc.value()', 'jsvalHandle': 'desc.value()', michael@0: 'obj': 'proxy', 'successCode': fillDescriptor} michael@0: condition = "!HasPropertyOnPrototype(cx, proxy, id)" michael@0: if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'): michael@0: condition = "(!isXray || %s)" % condition michael@0: condition = "!ignoreNamedProps && " + condition michael@0: if self.descriptor.supportsIndexedProperties(): michael@0: condition = "!IsArrayIndex(index) && " + condition michael@0: namedGet = (CGIfWrapper(CGProxyNamedGetter(self.descriptor, templateValues), michael@0: condition).define() + michael@0: "\n") michael@0: else: michael@0: namedGet = "" michael@0: michael@0: return fill( michael@0: """ michael@0: bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy); michael@0: $*{getIndexed} michael@0: $*{getUnforgeable} michael@0: JS::Rooted expando(cx); michael@0: if (!isXray && (expando = GetExpandoObject(proxy))) { michael@0: if (!JS_GetPropertyDescriptorById(cx, expando, id, desc)) { michael@0: return false; michael@0: } michael@0: if (desc.object()) { michael@0: // Pretend the property lives on the wrapper. michael@0: desc.object().set(proxy); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: $*{namedGet} michael@0: desc.object().set(nullptr); michael@0: return true; michael@0: """, michael@0: getIndexed=getIndexed, michael@0: getUnforgeable=getUnforgeable, michael@0: namedGet=namedGet) michael@0: michael@0: michael@0: class CGDOMJSProxyHandler_defineProperty(ClassMethod): michael@0: def __init__(self, descriptor): michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('JS::Handle', 'proxy'), michael@0: Argument('JS::Handle', 'id'), michael@0: Argument('JS::MutableHandle', 'desc'), michael@0: Argument('bool*', 'defined')] michael@0: ClassMethod.__init__(self, "defineProperty", "bool", args, virtual=True, override=True) michael@0: self.descriptor = descriptor michael@0: michael@0: def getBody(self): michael@0: set = "" michael@0: michael@0: indexedSetter = self.descriptor.operations['IndexedSetter'] michael@0: if indexedSetter: michael@0: if self.descriptor.operations['IndexedCreator'] is not indexedSetter: michael@0: raise TypeError("Can't handle creator that's different from the setter") michael@0: set += fill( michael@0: """ michael@0: int32_t index = GetArrayIndexFromId(cx, id); michael@0: if (IsArrayIndex(index)) { michael@0: *defined = true; michael@0: $*{callSetter} michael@0: return true; michael@0: } michael@0: """, michael@0: callSetter=CGProxyIndexedSetter(self.descriptor).define()) michael@0: elif self.descriptor.supportsIndexedProperties(): michael@0: set += fill( michael@0: """ michael@0: if (IsArrayIndex(GetArrayIndexFromId(cx, id))) { michael@0: return js::IsInNonStrictPropertySet(cx) || ThrowErrorMessage(cx, MSG_NO_INDEXED_SETTER, "${name}"); michael@0: } michael@0: """, michael@0: name=self.descriptor.name) michael@0: michael@0: if UseHolderForUnforgeable(self.descriptor): michael@0: defineOnUnforgeable = ("bool hasUnforgeable;\n" michael@0: "if (!JS_HasPropertyById(cx, ${holder}, id, &hasUnforgeable)) {\n" michael@0: " return false;\n" michael@0: "}\n" michael@0: "if (hasUnforgeable) {\n" michael@0: " *defined = true;" # SUPER BOGUS missing newline michael@0: " bool unused;\n" michael@0: " return js_DefineOwnProperty(cx, ${holder}, id, desc, &unused);\n" michael@0: "}\n" michael@0: "\n") # BOGUS extra blank line at end of block or method michael@0: set += CallOnUnforgeableHolder(self.descriptor, michael@0: defineOnUnforgeable, michael@0: "xpc::WrapperFactory::IsXrayWrapper(proxy)") michael@0: michael@0: namedSetter = self.descriptor.operations['NamedSetter'] michael@0: if namedSetter: michael@0: if self.descriptor.operations['NamedCreator'] is not namedSetter: michael@0: raise TypeError("Can't handle creator that's different from the setter") michael@0: # If we support indexed properties, we won't get down here for michael@0: # indices, so we can just do our setter unconditionally here. michael@0: set += fill( michael@0: """ michael@0: *defined = true; michael@0: $*{callSetter} michael@0: michael@0: return true; michael@0: michael@0: """, # BOGUS extra blank line at end of method michael@0: callSetter=CGProxyNamedSetter(self.descriptor).define()) michael@0: else: michael@0: if self.descriptor.supportsNamedProperties(): michael@0: set += fill( michael@0: """ michael@0: $*{presenceChecker} michael@0: michael@0: if (found) { michael@0: return js::IsInNonStrictPropertySet(cx) || ThrowErrorMessage(cx, MSG_NO_NAMED_SETTER, "${name}"); michael@0: } michael@0: """, michael@0: presenceChecker=CGProxyNamedPresenceChecker(self.descriptor).define(), michael@0: name=self.descriptor.name) michael@0: set += ("return mozilla::dom::DOMProxyHandler::defineProperty(%s);\n" % michael@0: ", ".join(a.name for a in self.args)) michael@0: return set michael@0: michael@0: michael@0: class CGDOMJSProxyHandler_delete(ClassMethod): michael@0: def __init__(self, descriptor): michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('JS::Handle', 'proxy'), michael@0: Argument('JS::Handle', 'id'), michael@0: Argument('bool*', 'bp')] michael@0: ClassMethod.__init__(self, "delete_", "bool", args, michael@0: virtual=True, override=True) michael@0: self.descriptor = descriptor michael@0: michael@0: def getBody(self): michael@0: def getDeleterBody(type): michael@0: """ michael@0: type should be "Named" or "Indexed" michael@0: """ michael@0: assert type in ("Named", "Indexed") michael@0: deleter = self.descriptor.operations[type + 'Deleter'] michael@0: if deleter: michael@0: if (not deleter.signatures()[0][0].isPrimitive() or michael@0: deleter.signatures()[0][0].nullable() or michael@0: deleter.signatures()[0][0].tag() != IDLType.Tags.bool): michael@0: setBp = "*bp = true;\n" michael@0: else: michael@0: setBp = dedent(""" michael@0: if (found) { michael@0: *bp = result; michael@0: } else { michael@0: *bp = true; michael@0: } michael@0: """) michael@0: body = (eval("CGProxy%sDeleter" % type)(self.descriptor).define() + michael@0: setBp) michael@0: elif eval("self.descriptor.supports%sProperties()" % type): michael@0: body = (eval("CGProxy%sPresenceChecker" % type)(self.descriptor).define() + michael@0: dedent(""" michael@0: if (found) { michael@0: *bp = false; michael@0: } else { michael@0: *bp = true; michael@0: } michael@0: """)) michael@0: else: michael@0: body = None michael@0: return body michael@0: michael@0: delete = dedent(""" michael@0: MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy), michael@0: "Should not have a XrayWrapper here"); michael@0: michael@0: """) michael@0: michael@0: indexedBody = getDeleterBody("Indexed") michael@0: if indexedBody is not None: michael@0: delete += fill( michael@0: """ michael@0: int32_t index = GetArrayIndexFromId(cx, id); michael@0: if (IsArrayIndex(index)) { michael@0: $*{indexedBody} michael@0: // We always return here, even if the property was not found michael@0: return true; michael@0: } michael@0: """, michael@0: indexedBody=indexedBody) michael@0: michael@0: if UseHolderForUnforgeable(self.descriptor): michael@0: unforgeable = dedent(""" michael@0: bool hasUnforgeable; michael@0: if (!JS_HasPropertyById(cx, ${holder}, id, &hasUnforgeable)) { michael@0: return false; michael@0: } michael@0: if (hasUnforgeable) { michael@0: *bp = false; michael@0: return true; michael@0: } michael@0: """) michael@0: delete += CallOnUnforgeableHolder(self.descriptor, unforgeable) michael@0: delete += "\n" michael@0: michael@0: namedBody = getDeleterBody("Named") michael@0: if namedBody is not None: michael@0: # We always return above for an index id in the case when we support michael@0: # indexed properties, so we can just treat the id as a name michael@0: # unconditionally here. michael@0: delete += (namedBody + michael@0: "if (found) {\n" michael@0: " return true;\n" michael@0: "}\n\n") # BOGUS extra blank line michael@0: if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'): michael@0: delete = CGIfWrapper(CGGeneric(delete), michael@0: "!HasPropertyOnPrototype(cx, proxy, id)").define() michael@0: else: michael@0: delete += "\n" # BOGUS extra blank line michael@0: michael@0: delete += dedent(""" michael@0: michael@0: return dom::DOMProxyHandler::delete_(cx, proxy, id, bp); michael@0: """) michael@0: michael@0: return delete michael@0: michael@0: michael@0: class CGDOMJSProxyHandler_ownPropNames(ClassMethod): michael@0: def __init__(self, descriptor, ): michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('JS::Handle', 'proxy'), michael@0: Argument('unsigned', 'flags'), michael@0: Argument('JS::AutoIdVector&', 'props')] michael@0: ClassMethod.__init__(self, "ownPropNames", "bool", args, michael@0: virtual=True, override=True) michael@0: self.descriptor = descriptor michael@0: michael@0: def getBody(self): michael@0: # Per spec, we do indices, then named props, then everything else michael@0: if self.descriptor.supportsIndexedProperties(): michael@0: addIndices = dedent(""" michael@0: michael@0: uint32_t length = UnwrapProxy(proxy)->Length(); michael@0: MOZ_ASSERT(int32_t(length) >= 0); michael@0: for (int32_t i = 0; i < int32_t(length); ++i) { michael@0: if (!props.append(INT_TO_JSID(i))) { michael@0: return false; michael@0: } michael@0: } michael@0: """) michael@0: else: michael@0: addIndices = "" michael@0: michael@0: if UseHolderForUnforgeable(self.descriptor): michael@0: addUnforgeable = dedent(""" michael@0: if (!js::GetPropertyNames(cx, ${holder}, flags, &props)) { michael@0: return false; michael@0: } michael@0: """) michael@0: addUnforgeable = CallOnUnforgeableHolder(self.descriptor, michael@0: addUnforgeable, michael@0: "isXray") michael@0: else: michael@0: addUnforgeable = "" michael@0: michael@0: if self.descriptor.supportsNamedProperties(): michael@0: if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'): michael@0: shadow = "!isXray" michael@0: else: michael@0: shadow = "false" michael@0: addNames = fill( michael@0: """ michael@0: michael@0: nsTArray names; michael@0: UnwrapProxy(proxy)->GetSupportedNames(flags, names); michael@0: if (!AppendNamedPropertyIds(cx, proxy, names, ${shadow}, props)) { michael@0: return false; michael@0: } michael@0: """, michael@0: shadow=shadow) michael@0: else: michael@0: addNames = "" michael@0: michael@0: return fill( michael@0: """ michael@0: bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy); michael@0: $*{addIndices} michael@0: $*{addUnforgeable} michael@0: $*{addNames} michael@0: michael@0: JS::Rooted expando(cx); michael@0: if (!isXray && (expando = DOMProxyHandler::GetExpandoObject(proxy)) && michael@0: !js::GetPropertyNames(cx, expando, flags, &props)) { michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: """, michael@0: addIndices=addIndices, michael@0: addUnforgeable=addUnforgeable, michael@0: addNames=addNames) michael@0: michael@0: michael@0: class CGDOMJSProxyHandler_hasOwn(ClassMethod): michael@0: def __init__(self, descriptor): michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('JS::Handle', 'proxy'), michael@0: Argument('JS::Handle', 'id'), michael@0: Argument('bool*', 'bp')] michael@0: ClassMethod.__init__(self, "hasOwn", "bool", args, michael@0: virtual=True, override=True) michael@0: self.descriptor = descriptor michael@0: michael@0: def getBody(self): michael@0: if self.descriptor.supportsIndexedProperties(): michael@0: indexed = fill( michael@0: """ michael@0: int32_t index = GetArrayIndexFromId(cx, id); michael@0: if (IsArrayIndex(index)) { michael@0: $*{presenceChecker} michael@0: michael@0: *bp = found; michael@0: return true; michael@0: } michael@0: michael@0: """, michael@0: presenceChecker=CGProxyIndexedPresenceChecker(self.descriptor).define()) michael@0: else: michael@0: indexed = "" michael@0: michael@0: if UseHolderForUnforgeable(self.descriptor): michael@0: unforgeable = dedent(""" michael@0: bool b = true; michael@0: bool ok = JS_AlreadyHasOwnPropertyById(cx, ${holder}, id, &b); michael@0: *bp = !!b; michael@0: if (!ok || *bp) { michael@0: return ok; michael@0: } michael@0: """) michael@0: unforgeable = CallOnUnforgeableHolder(self.descriptor, unforgeable) michael@0: else: michael@0: unforgeable = "" michael@0: michael@0: if self.descriptor.supportsNamedProperties(): michael@0: # If we support indexed properties we always return above for index michael@0: # property names, so no need to check for those here. michael@0: named = (CGProxyNamedPresenceChecker(self.descriptor).define() + michael@0: "\n" + michael@0: "*bp = found;\n") michael@0: if not self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'): michael@0: # BOGUS extra blank line at end of block michael@0: named = CGIfWrapper(CGGeneric(named + "return true;\n\n"), michael@0: "!HasPropertyOnPrototype(cx, proxy, id)").define() michael@0: named += "*bp = false;\n" michael@0: else: michael@0: named += "\n" michael@0: else: michael@0: named = "*bp = false;\n" michael@0: michael@0: return fill( michael@0: """ michael@0: MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy), michael@0: "Should not have a XrayWrapper here"); michael@0: michael@0: $*{indexed} michael@0: $*{unforgeable} michael@0: michael@0: JS::Rooted expando(cx, GetExpandoObject(proxy)); michael@0: if (expando) { michael@0: bool b = true; michael@0: bool ok = JS_HasPropertyById(cx, expando, id, &b); michael@0: *bp = !!b; michael@0: if (!ok || *bp) { michael@0: return ok; michael@0: } michael@0: } michael@0: michael@0: $*{named} michael@0: return true; michael@0: """, michael@0: indexed=indexed, michael@0: unforgeable=unforgeable, michael@0: named=named) michael@0: michael@0: michael@0: class CGDOMJSProxyHandler_get(ClassMethod): michael@0: def __init__(self, descriptor): michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('JS::Handle', 'proxy'), michael@0: Argument('JS::Handle', 'receiver'), michael@0: Argument('JS::Handle', 'id'), michael@0: Argument('JS::MutableHandle', 'vp')] michael@0: ClassMethod.__init__(self, "get", "bool", args, michael@0: virtual=True, override=True) michael@0: self.descriptor = descriptor michael@0: michael@0: def getBody(self): michael@0: getUnforgeableOrExpando = "JS::Rooted sharedRoot(cx);\n" michael@0: if UseHolderForUnforgeable(self.descriptor): michael@0: hasUnforgeable = dedent(""" michael@0: bool hasUnforgeable; michael@0: if (!JS_AlreadyHasOwnPropertyById(cx, ${holder}, id, &hasUnforgeable)) { michael@0: return false; michael@0: } michael@0: if (hasUnforgeable) { michael@0: return JS_ForwardGetPropertyTo(cx, ${holder}, id, proxy, vp); michael@0: } michael@0: """) michael@0: getUnforgeableOrExpando += CallOnUnforgeableHolder(self.descriptor, michael@0: hasUnforgeable, michael@0: useSharedRoot=True) michael@0: getUnforgeableOrExpando += dedent(""" michael@0: { // Scope for expando michael@0: JS::Rooted& expando(sharedRoot); michael@0: expando = DOMProxyHandler::GetExpandoObject(proxy); michael@0: if (expando) { michael@0: bool hasProp; michael@0: if (!JS_HasPropertyById(cx, expando, id, &hasProp)) { michael@0: return false; michael@0: } michael@0: michael@0: if (hasProp) { michael@0: // Forward the get to the expando object, but our receiver is whatever our michael@0: // receiver is. michael@0: return JS_ForwardGetPropertyTo(cx, expando, id, receiver, vp); michael@0: } michael@0: } michael@0: } michael@0: """) michael@0: michael@0: templateValues = {'jsvalRef': 'vp', 'jsvalHandle': 'vp', 'obj': 'proxy'} michael@0: michael@0: if self.descriptor.supportsIndexedProperties(): michael@0: getIndexedOrExpando = fill( michael@0: """ michael@0: int32_t index = GetArrayIndexFromId(cx, id); michael@0: if (IsArrayIndex(index)) { michael@0: $*{callGetter} michael@0: // Even if we don't have this index, we don't forward the michael@0: // get on to our expando object. michael@0: } else { michael@0: $*{getUnforgeableOrExpando} michael@0: } michael@0: """, michael@0: callGetter=CGProxyIndexedGetter(self.descriptor, templateValues).define(), michael@0: getUnforgeableOrExpando=getUnforgeableOrExpando) michael@0: else: michael@0: getIndexedOrExpando = getUnforgeableOrExpando michael@0: michael@0: if self.descriptor.supportsNamedProperties(): michael@0: getNamed = CGProxyNamedGetter(self.descriptor, templateValues) michael@0: if self.descriptor.supportsIndexedProperties(): michael@0: getNamed = CGIfWrapper(getNamed, "!IsArrayIndex(index)") michael@0: getNamed = getNamed.define() + "\n" michael@0: else: michael@0: getNamed = "" michael@0: michael@0: getOnPrototype = dedent(""" michael@0: bool foundOnPrototype; michael@0: if (!GetPropertyOnPrototype(cx, proxy, id, &foundOnPrototype, vp.address())) { michael@0: return false; michael@0: } michael@0: michael@0: if (foundOnPrototype) { michael@0: return true; michael@0: } michael@0: michael@0: """) michael@0: if self.descriptor.interface.getExtendedAttribute('OverrideBuiltins'): michael@0: getNamed = getNamed + getOnPrototype michael@0: else: michael@0: getNamed = getOnPrototype + getNamed michael@0: michael@0: return fill( michael@0: """ michael@0: MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy), michael@0: "Should not have a XrayWrapper here"); michael@0: michael@0: $*{indexedOrExpando} michael@0: michael@0: $*{named} michael@0: vp.setUndefined(); michael@0: return true; michael@0: """, michael@0: indexedOrExpando=getIndexedOrExpando, michael@0: named=getNamed) michael@0: michael@0: michael@0: class CGDOMJSProxyHandler_setCustom(ClassMethod): michael@0: def __init__(self, descriptor): michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('JS::Handle', 'proxy'), michael@0: Argument('JS::Handle', 'id'), michael@0: Argument('JS::MutableHandle', 'vp'), michael@0: Argument('bool*', 'done')] michael@0: ClassMethod.__init__(self, "setCustom", "bool", args, virtual=True, override=True) michael@0: self.descriptor = descriptor michael@0: michael@0: def getBody(self): michael@0: assertion = ("MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),\n" michael@0: ' "Should not have a XrayWrapper here");\n') michael@0: michael@0: # Correctness first. If we have a NamedSetter and [OverrideBuiltins], michael@0: # always call the NamedSetter and never do anything else. michael@0: namedSetter = self.descriptor.operations['NamedSetter'] michael@0: if (namedSetter is not None and michael@0: self.descriptor.interface.getExtendedAttribute('OverrideBuiltins')): michael@0: # Check assumptions. michael@0: if self.descriptor.supportsIndexedProperties(): michael@0: raise ValueError("In interface " + self.descriptor.name + ": " + michael@0: "Can't cope with [OverrideBuiltins] and an indexed getter") michael@0: if self.descriptor.operations['NamedCreator'] is not namedSetter: michael@0: raise ValueError("In interface " + self.descriptor.name + ": " + michael@0: "Can't cope with named setter that is not also a named creator") michael@0: if UseHolderForUnforgeable(self.descriptor): michael@0: raise ValueError("In interface " + self.descriptor.name + ": " + michael@0: "Can't cope with [OverrideBuiltins] and unforgeable members") michael@0: michael@0: callSetter = CGProxyNamedSetter(self.descriptor, argumentMutableValue="vp") michael@0: return (assertion + michael@0: callSetter.define() + michael@0: "*done = true;\n" michael@0: "return true;\n") michael@0: michael@0: # As an optimization, if we are going to call an IndexedSetter, go michael@0: # ahead and call it and have done. michael@0: indexedSetter = self.descriptor.operations['IndexedSetter'] michael@0: if indexedSetter is not None: michael@0: if self.descriptor.operations['IndexedCreator'] is not indexedSetter: michael@0: raise ValueError("In interface " + self.descriptor.name + ": " + michael@0: "Can't cope with indexed setter that is not " + michael@0: "also an indexed creator") michael@0: setIndexed = fill( michael@0: """ michael@0: int32_t index = GetArrayIndexFromId(cx, id); michael@0: if (IsArrayIndex(index)) { michael@0: $*{callSetter} michael@0: *done = true; michael@0: return true; michael@0: } michael@0: michael@0: """, michael@0: callSetter=CGProxyIndexedSetter(self.descriptor, michael@0: argumentMutableValue="vp").define()) michael@0: else: michael@0: setIndexed = "" michael@0: michael@0: return (assertion + michael@0: setIndexed + michael@0: "*done = false;\n" michael@0: "return true;\n") michael@0: michael@0: michael@0: class CGDOMJSProxyHandler_className(ClassMethod): michael@0: def __init__(self, descriptor): michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('JS::Handle', 'proxy')] michael@0: ClassMethod.__init__(self, "className", "const char*", args, michael@0: virtual=True, override=True) michael@0: self.descriptor = descriptor michael@0: michael@0: def getBody(self): michael@0: return 'return "%s";\n' % self.descriptor.name michael@0: michael@0: michael@0: class CGDOMJSProxyHandler_finalizeInBackground(ClassMethod): michael@0: def __init__(self, descriptor): michael@0: args = [Argument('JS::Value', 'priv')] michael@0: ClassMethod.__init__(self, "finalizeInBackground", "bool", args, michael@0: virtual=True, override=True) michael@0: self.descriptor = descriptor michael@0: michael@0: def getBody(self): michael@0: return "return false;\n" michael@0: michael@0: michael@0: class CGDOMJSProxyHandler_finalize(ClassMethod): michael@0: def __init__(self, descriptor): michael@0: args = [Argument('JSFreeOp*', 'fop'), Argument('JSObject*', 'proxy')] michael@0: ClassMethod.__init__(self, "finalize", "void", args, michael@0: virtual=True, override=True) michael@0: self.descriptor = descriptor michael@0: michael@0: def getBody(self): michael@0: return ("%s* self = UnwrapProxy(proxy);\n\n" % self.descriptor.nativeType + michael@0: finalizeHook(self.descriptor, FINALIZE_HOOK_NAME, self.args[0].name).define()) michael@0: michael@0: michael@0: class CGDOMJSProxyHandler_slice(ClassMethod): michael@0: def __init__(self, descriptor): michael@0: assert descriptor.supportsIndexedProperties() michael@0: michael@0: args = [Argument('JSContext*', 'cx'), michael@0: Argument('JS::Handle', 'proxy'), michael@0: Argument('uint32_t', 'begin'), michael@0: Argument('uint32_t', 'end'), michael@0: Argument('JS::Handle', 'array')] michael@0: ClassMethod.__init__(self, "slice", "bool", args, virtual=True, override=True) michael@0: self.descriptor = descriptor michael@0: michael@0: def getBody(self): michael@0: # Just like getOwnPropertyNames we'll assume that we have no holes, so michael@0: # we have all properties from 0 to length. If that ever changes michael@0: # (unlikely), we'll need to do something a bit more clever with how we michael@0: # forward on to our ancestor. michael@0: michael@0: templateValues = { michael@0: 'jsvalRef': 'temp', michael@0: 'jsvalHandle': '&temp', michael@0: 'obj': 'proxy', michael@0: 'successCode': ("js::UnsafeDefineElement(cx, array, index - begin, temp);\n" michael@0: "continue;\n") michael@0: } michael@0: get = CGProxyIndexedGetter(self.descriptor, templateValues, False, False).define() michael@0: michael@0: return fill( michael@0: """ michael@0: JS::Rooted temp(cx); michael@0: MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy), michael@0: "Should not have a XrayWrapper here"); michael@0: michael@0: ${nativeType}* self = UnwrapProxy(proxy); michael@0: uint32_t length = self->Length(); michael@0: // Compute the end of the indices we'll get ourselves michael@0: uint32_t ourEnd = std::max(begin, std::min(end, length)); michael@0: michael@0: for (uint32_t index = begin; index < ourEnd; ++index) { michael@0: $*{get} michael@0: } michael@0: michael@0: if (end > ourEnd) { michael@0: JS::Rooted proto(cx); michael@0: if (!js::GetObjectProto(cx, proxy, &proto)) { michael@0: return false; michael@0: } michael@0: return js::SliceSlowly(cx, proto, proxy, ourEnd, end, array); michael@0: } michael@0: michael@0: return true; michael@0: """, michael@0: nativeType=self.descriptor.nativeType, michael@0: get=get) michael@0: michael@0: michael@0: class CGDOMJSProxyHandler_getInstance(ClassMethod): michael@0: def __init__(self): michael@0: ClassMethod.__init__(self, "getInstance", "DOMProxyHandler*", [], static=True) michael@0: michael@0: def getBody(self): michael@0: return dedent(""" michael@0: static DOMProxyHandler instance; michael@0: return &instance; michael@0: """) michael@0: michael@0: michael@0: class CGDOMJSProxyHandler(CGClass): michael@0: def __init__(self, descriptor): michael@0: assert (descriptor.supportsIndexedProperties() or michael@0: descriptor.supportsNamedProperties()) michael@0: methods = [CGDOMJSProxyHandler_getOwnPropDescriptor(descriptor), michael@0: CGDOMJSProxyHandler_defineProperty(descriptor), michael@0: ClassUsingDeclaration("mozilla::dom::DOMProxyHandler", michael@0: "defineProperty"), michael@0: CGDOMJSProxyHandler_ownPropNames(descriptor), michael@0: CGDOMJSProxyHandler_hasOwn(descriptor), michael@0: CGDOMJSProxyHandler_get(descriptor), michael@0: CGDOMJSProxyHandler_className(descriptor), michael@0: CGDOMJSProxyHandler_finalizeInBackground(descriptor), michael@0: CGDOMJSProxyHandler_finalize(descriptor), michael@0: CGDOMJSProxyHandler_getInstance(), michael@0: CGDOMJSProxyHandler_delete(descriptor)] michael@0: if descriptor.supportsIndexedProperties(): michael@0: methods.append(CGDOMJSProxyHandler_slice(descriptor)) michael@0: if (descriptor.operations['IndexedSetter'] is not None or michael@0: (descriptor.operations['NamedSetter'] is not None and michael@0: descriptor.interface.getExtendedAttribute('OverrideBuiltins'))): michael@0: methods.append(CGDOMJSProxyHandler_setCustom(descriptor)) michael@0: michael@0: CGClass.__init__(self, 'DOMProxyHandler', michael@0: bases=[ClassBase('mozilla::dom::DOMProxyHandler')], michael@0: methods=methods) michael@0: michael@0: michael@0: class CGDOMJSProxyHandlerDeclarer(CGThing): michael@0: """ michael@0: A class for declaring a DOMProxyHandler. michael@0: """ michael@0: def __init__(self, handlerThing): michael@0: self.handlerThing = handlerThing michael@0: michael@0: def declare(self): michael@0: # Our class declaration should happen when we're defining michael@0: return "" michael@0: michael@0: def define(self): michael@0: return self.handlerThing.declare() michael@0: michael@0: michael@0: class CGDOMJSProxyHandlerDefiner(CGThing): michael@0: """ michael@0: A class for defining a DOMProxyHandler. michael@0: """ michael@0: def __init__(self, handlerThing): michael@0: self.handlerThing = handlerThing michael@0: michael@0: def declare(self): michael@0: return "" michael@0: michael@0: def define(self): michael@0: return self.handlerThing.define() michael@0: michael@0: michael@0: def stripTrailingWhitespace(text): michael@0: tail = '\n' if text.endswith('\n') else '' michael@0: lines = text.splitlines() michael@0: return '\n'.join(line.rstrip() for line in lines) + tail michael@0: michael@0: michael@0: class CGDescriptor(CGThing): michael@0: def __init__(self, descriptor): michael@0: CGThing.__init__(self) michael@0: michael@0: assert not descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject() michael@0: michael@0: if descriptor.nativeOwnership == 'owned' and ( michael@0: descriptor.interface.hasChildInterfaces() or michael@0: descriptor.interface.parent): michael@0: raise TypeError("Owned interface cannot have a parent or children") michael@0: michael@0: self._deps = descriptor.interface.getDeps() michael@0: michael@0: cgThings = [] michael@0: cgThings.append(CGGeneric(declare="typedef %s NativeType;\n" % michael@0: descriptor.nativeType)) michael@0: # These are set to true if at least one non-static michael@0: # method/getter/setter or jsonifier exist on the interface. michael@0: (hasMethod, hasGetter, hasLenientGetter, hasSetter, hasJsonifier, michael@0: hasLenientSetter) = False, False, False, False, False, False michael@0: crossOriginMethods, crossOriginGetters, crossOriginSetters = set(), set(), set() michael@0: for n in descriptor.interface.namedConstructors: michael@0: cgThings.append(CGClassConstructor(descriptor, n, michael@0: NamedConstructorName(n))) michael@0: for m in descriptor.interface.members: michael@0: if m.isMethod() and m.identifier.name == 'queryInterface': michael@0: continue michael@0: if m.isMethod() and m == descriptor.operations['Jsonifier']: michael@0: hasJsonifier = True michael@0: hasMethod = descriptor.needsSpecialGenericOps() michael@0: jsonifierMethod = m michael@0: elif (m.isMethod() and michael@0: (not m.isIdentifierLess() or m == descriptor.operations['Stringifier'])): michael@0: if m.isStatic(): michael@0: assert descriptor.interface.hasInterfaceObject michael@0: cgThings.append(CGStaticMethod(descriptor, m)) michael@0: if m.returnsPromise(): michael@0: cgThings.append(CGStaticMethodJitinfo(m)) michael@0: elif descriptor.interface.hasInterfacePrototypeObject(): michael@0: specializedMethod = CGSpecializedMethod(descriptor, m) michael@0: cgThings.append(specializedMethod) michael@0: if m.returnsPromise(): michael@0: cgThings.append(CGMethodPromiseWrapper(descriptor, specializedMethod)) michael@0: cgThings.append(CGMemberJITInfo(descriptor, m)) michael@0: if m.getExtendedAttribute("CrossOriginCallable"): michael@0: crossOriginMethods.add(m.identifier.name) michael@0: elif descriptor.needsSpecialGenericOps(): michael@0: hasMethod = True michael@0: elif m.isAttr(): michael@0: if m.stringifier: michael@0: raise TypeError("Stringifier attributes not supported yet. " michael@0: "See bug 824857.\n" michael@0: "%s" % m.location) michael@0: if m.isStatic(): michael@0: assert descriptor.interface.hasInterfaceObject michael@0: cgThings.append(CGStaticGetter(descriptor, m)) michael@0: elif descriptor.interface.hasInterfacePrototypeObject(): michael@0: cgThings.append(CGSpecializedGetter(descriptor, m)) michael@0: if m.hasLenientThis(): michael@0: hasLenientGetter = True michael@0: elif m.getExtendedAttribute("CrossOriginReadable"): michael@0: crossOriginGetters.add(m.identifier.name) michael@0: elif descriptor.needsSpecialGenericOps(): michael@0: hasGetter = True michael@0: if not m.readonly: michael@0: for extAttr in ["PutForwards", "Replaceable"]: michael@0: if m.getExtendedAttribute(extAttr): michael@0: raise TypeError("Writable attributes should not " michael@0: "have %s specified.\n" michael@0: "%s" % michael@0: (extAttr, m.location)) michael@0: if m.isStatic(): michael@0: assert descriptor.interface.hasInterfaceObject michael@0: cgThings.append(CGStaticSetter(descriptor, m)) michael@0: elif descriptor.interface.hasInterfacePrototypeObject(): michael@0: cgThings.append(CGSpecializedSetter(descriptor, m)) michael@0: if m.hasLenientThis(): michael@0: hasLenientSetter = True michael@0: elif m.getExtendedAttribute("CrossOriginWritable"): michael@0: crossOriginSetters.add(m.identifier.name) michael@0: elif descriptor.needsSpecialGenericOps(): michael@0: hasSetter = True michael@0: elif m.getExtendedAttribute("PutForwards"): michael@0: cgThings.append(CGSpecializedForwardingSetter(descriptor, m)) michael@0: if m.getExtendedAttribute("CrossOriginWritable"): michael@0: crossOriginSetters.add(m.identifier.name) michael@0: elif descriptor.needsSpecialGenericOps(): michael@0: hasSetter = True michael@0: elif m.getExtendedAttribute("Replaceable"): michael@0: cgThings.append(CGSpecializedReplaceableSetter(descriptor, m)) michael@0: if descriptor.needsSpecialGenericOps(): michael@0: hasSetter = True michael@0: if (not m.isStatic() and michael@0: descriptor.interface.hasInterfacePrototypeObject()): michael@0: cgThings.append(CGMemberJITInfo(descriptor, m)) michael@0: if hasJsonifier: michael@0: cgThings.append(CGJsonifierMethod(descriptor, jsonifierMethod)) michael@0: cgThings.append(CGMemberJITInfo(descriptor, jsonifierMethod)) michael@0: if hasMethod: michael@0: cgThings.append(CGGenericMethod(descriptor)) michael@0: if len(crossOriginMethods): michael@0: cgThings.append(CGGenericMethod(descriptor, michael@0: allowCrossOriginThis=True)) michael@0: if hasGetter: michael@0: cgThings.append(CGGenericGetter(descriptor)) michael@0: if hasLenientGetter: michael@0: cgThings.append(CGGenericGetter(descriptor, lenientThis=True)) michael@0: if len(crossOriginGetters): michael@0: cgThings.append(CGGenericGetter(descriptor, michael@0: allowCrossOriginThis=True)) michael@0: if hasSetter: michael@0: cgThings.append(CGGenericSetter(descriptor)) michael@0: if hasLenientSetter: michael@0: cgThings.append(CGGenericSetter(descriptor, lenientThis=True)) michael@0: if len(crossOriginSetters): michael@0: cgThings.append(CGGenericSetter(descriptor, michael@0: allowCrossOriginThis=True)) michael@0: michael@0: if descriptor.interface.getNavigatorProperty(): michael@0: cgThings.append(CGConstructNavigatorObjectHelper(descriptor)) michael@0: cgThings.append(CGConstructNavigatorObject(descriptor)) michael@0: michael@0: if descriptor.concrete and not descriptor.proxy: michael@0: if wantsAddProperty(descriptor): michael@0: cgThings.append(CGAddPropertyHook(descriptor)) michael@0: michael@0: # Always have a finalize hook, regardless of whether the class michael@0: # wants a custom hook. michael@0: cgThings.append(CGClassFinalizeHook(descriptor)) michael@0: michael@0: properties = PropertyArrays(descriptor) michael@0: cgThings.append(CGGeneric(define=str(properties))) michael@0: cgThings.append(CGNativeProperties(descriptor, properties)) michael@0: michael@0: # Set up our Xray callbacks as needed. Note that we don't need to do michael@0: # it in workers. michael@0: if not descriptor.workers and descriptor.concrete and descriptor.proxy: michael@0: cgThings.append(CGResolveOwnProperty(descriptor)) michael@0: cgThings.append(CGEnumerateOwnProperties(descriptor)) michael@0: elif descriptor.needsXrayResolveHooks(): michael@0: cgThings.append(CGResolveOwnPropertyViaNewresolve(descriptor)) michael@0: cgThings.append(CGEnumerateOwnPropertiesViaGetOwnPropertyNames(descriptor)) michael@0: michael@0: # Now that we have our ResolveOwnProperty/EnumerateOwnProperties stuff michael@0: # done, set up our NativePropertyHooks. michael@0: cgThings.append(CGNativePropertyHooks(descriptor, properties)) michael@0: michael@0: if descriptor.interface.hasInterfaceObject(): michael@0: cgThings.append(CGClassConstructor(descriptor, michael@0: descriptor.interface.ctor())) michael@0: cgThings.append(CGClassHasInstanceHook(descriptor)) michael@0: cgThings.append(CGInterfaceObjectJSClass(descriptor, properties)) michael@0: if descriptor.needsConstructHookHolder(): michael@0: cgThings.append(CGClassConstructHookHolder(descriptor)) michael@0: cgThings.append(CGNamedConstructors(descriptor)) michael@0: michael@0: cgThings.append(CGLegacyCallHook(descriptor)) michael@0: if descriptor.interface.getExtendedAttribute("NeedNewResolve"): michael@0: cgThings.append(CGNewResolveHook(descriptor)) michael@0: cgThings.append(CGEnumerateHook(descriptor)) michael@0: michael@0: if descriptor.interface.hasInterfacePrototypeObject(): michael@0: cgThings.append(CGPrototypeJSClass(descriptor, properties)) michael@0: michael@0: if descriptor.interface.hasInterfaceObject(): michael@0: cgThings.append(CGDefineDOMInterfaceMethod(descriptor)) michael@0: michael@0: if ((descriptor.interface.hasInterfaceObject() or descriptor.interface.getNavigatorProperty()) and michael@0: not descriptor.interface.isExternal() and michael@0: descriptor.isExposedConditionally() and michael@0: # Workers stuff is never conditional michael@0: not descriptor.workers): michael@0: cgThings.append(CGConstructorEnabled(descriptor)) michael@0: michael@0: if descriptor.concrete: michael@0: if descriptor.proxy: michael@0: if descriptor.interface.totalMembersInSlots != 0: michael@0: raise TypeError("We can't have extra reserved slots for " michael@0: "proxy interface %s" % michael@0: descriptor.interface.identifier.name) michael@0: cgThings.append(CGGeneric(fill( michael@0: """ michael@0: static_assert(IsBaseOf::value, michael@0: "We don't support non-nsISupports native classes for " michael@0: "proxy-based bindings yet"); michael@0: michael@0: """, michael@0: nativeType=descriptor.nativeType))) michael@0: if not descriptor.wrapperCache: michael@0: raise TypeError("We need a wrappercache to support expandos for proxy-based " michael@0: "bindings (" + descriptor.name + ")") michael@0: handlerThing = CGDOMJSProxyHandler(descriptor) michael@0: cgThings.append(CGDOMJSProxyHandlerDeclarer(handlerThing)) michael@0: cgThings.append(CGProxyIsProxy(descriptor)) michael@0: cgThings.append(CGProxyUnwrap(descriptor)) michael@0: cgThings.append(CGDOMJSProxyHandlerDefiner(handlerThing)) michael@0: cgThings.append(CGDOMProxyJSClass(descriptor)) michael@0: else: michael@0: cgThings.append(CGDOMJSClass(descriptor)) michael@0: cgThings.append(CGGetJSClassMethod(descriptor)) michael@0: if descriptor.interface.hasMembersInSlots(): michael@0: if descriptor.interface.hasChildInterfaces(): michael@0: raise TypeError("We don't support members in slots on " michael@0: "non-leaf interfaces like %s" % michael@0: descriptor.interface.identifier.name) michael@0: cgThings.append(CGUpdateMemberSlotsMethod(descriptor)) michael@0: michael@0: if descriptor.interface.getExtendedAttribute("Global"): michael@0: assert descriptor.wrapperCache michael@0: cgThings.append(CGWrapGlobalMethod(descriptor, properties)) michael@0: elif descriptor.wrapperCache: michael@0: cgThings.append(CGWrapWithCacheMethod(descriptor, properties)) michael@0: cgThings.append(CGWrapMethod(descriptor)) michael@0: else: michael@0: cgThings.append(CGWrapNonWrapperCacheMethod(descriptor, michael@0: properties)) michael@0: michael@0: # If we're not wrappercached, we don't know how to clear our michael@0: # cached values, since we can't get at the JSObject. michael@0: if descriptor.wrapperCache: michael@0: cgThings.extend(CGClearCachedValueMethod(descriptor, m) for michael@0: m in descriptor.interface.members if michael@0: m.isAttr() and michael@0: # Constants should never need clearing! michael@0: not m.getExtendedAttribute("Constant") and michael@0: not m.getExtendedAttribute("SameObject") and michael@0: m.slotIndex is not None) michael@0: michael@0: # CGCreateInterfaceObjectsMethod needs to come after our michael@0: # CGDOMJSClass, if any. michael@0: cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties)) michael@0: michael@0: # CGGetProtoObjectMethod and CGGetConstructorObjectMethod need michael@0: # to come after CGCreateInterfaceObjectsMethod. michael@0: if descriptor.interface.hasInterfacePrototypeObject(): michael@0: cgThings.append(CGGetProtoObjectMethod(descriptor)) michael@0: if descriptor.interface.hasInterfaceObject(): michael@0: cgThings.append(CGGetConstructorObjectMethod(descriptor)) michael@0: michael@0: # See whether we need we need to generate an IsPermitted method michael@0: if crossOriginGetters or crossOriginSetters or crossOriginMethods: michael@0: cgThings.append(CGIsPermittedMethod(descriptor, michael@0: crossOriginGetters, michael@0: crossOriginSetters, michael@0: crossOriginMethods)) michael@0: michael@0: cgThings = CGList((CGIndenter(t, declareOnly=True) for t in cgThings), "\n") michael@0: cgThings = CGWrapper(cgThings, pre='\n', post='\n') michael@0: self.cgRoot = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name), michael@0: cgThings), michael@0: post='\n') michael@0: michael@0: def declare(self): michael@0: return self.cgRoot.declare() michael@0: michael@0: def define(self): michael@0: return self.cgRoot.define() michael@0: michael@0: def deps(self): michael@0: return self._deps michael@0: michael@0: michael@0: class CGNamespacedEnum(CGThing): michael@0: def __init__(self, namespace, enumName, names, values, comment=""): michael@0: michael@0: if not values: michael@0: values = [] michael@0: michael@0: # Account for explicit enum values. michael@0: entries = [] michael@0: for i in range(0, len(names)): michael@0: if len(values) > i and values[i] is not None: michael@0: entry = "%s = %s" % (names[i], values[i]) michael@0: else: michael@0: entry = names[i] michael@0: entries.append(entry) michael@0: michael@0: # Append a Count. michael@0: entries.append('_' + enumName + '_Count') michael@0: michael@0: # Indent. michael@0: entries = [' ' + e for e in entries] michael@0: michael@0: # Build the enum body. michael@0: enumstr = comment + 'enum %s\n{\n%s\n};\n' % (enumName, ',\n'.join(entries)) michael@0: curr = CGGeneric(declare=enumstr) michael@0: michael@0: # Add some whitespace padding. michael@0: curr = CGWrapper(curr, pre='\n', post='\n') michael@0: michael@0: # Add the namespace. michael@0: curr = CGNamespace(namespace, curr) michael@0: michael@0: # Add the typedef michael@0: typedef = '\ntypedef %s::%s %s;\n\n' % (namespace, enumName, enumName) michael@0: curr = CGList([curr, CGGeneric(declare=typedef)]) michael@0: michael@0: # Save the result. michael@0: self.node = curr michael@0: michael@0: def declare(self): michael@0: return self.node.declare() michael@0: michael@0: def define(self): michael@0: return "" michael@0: michael@0: michael@0: class CGDictionary(CGThing): michael@0: def __init__(self, dictionary, descriptorProvider): michael@0: self.dictionary = dictionary michael@0: self.descriptorProvider = descriptorProvider michael@0: self.needToInitIds = len(dictionary.members) > 0 michael@0: self.memberInfo = [ michael@0: (member, michael@0: getJSToNativeConversionInfo( michael@0: member.type, michael@0: descriptorProvider, michael@0: isEnforceRange=member.enforceRange, michael@0: isClamp=member.clamp, michael@0: isMember="Dictionary", michael@0: isOptional=(not member.defaultValue), michael@0: defaultValue=member.defaultValue, michael@0: sourceDescription=("'%s' member of %s" % michael@0: (member.identifier.name, dictionary.identifier.name)))) michael@0: for member in dictionary.members] michael@0: michael@0: # If we have a union member containing something in the same michael@0: # file as us, bail: the C++ includes won't work out. michael@0: for member in dictionary.members: michael@0: type = member.type.unroll() michael@0: if type.isUnion(): michael@0: for t in type.flatMemberTypes: michael@0: if (t.isDictionary() and michael@0: CGHeaders.getDeclarationFilename(t.inner) == michael@0: CGHeaders.getDeclarationFilename(dictionary)): michael@0: raise TypeError( michael@0: "Dictionary contains a union that contains a " michael@0: "dictionary in the same WebIDL file. This won't " michael@0: "compile. Move the inner dictionary to a " michael@0: "different file.\n%s\n%s" % michael@0: (t.location, t.inner.location)) michael@0: self.structs = self.getStructs() michael@0: michael@0: def declare(self): michael@0: return self.structs.declare() michael@0: michael@0: def define(self): michael@0: return self.structs.define() michael@0: michael@0: def base(self): michael@0: if self.dictionary.parent: michael@0: return self.makeClassName(self.dictionary.parent) michael@0: return "DictionaryBase" michael@0: michael@0: def initMethod(self): michael@0: body = dedent(""" michael@0: // Passing a null JSContext is OK only if we're initing from null, michael@0: // Since in that case we will not have to do any property gets michael@0: MOZ_ASSERT_IF(!cx, val.isNull()); michael@0: """) michael@0: michael@0: if self.needToInitIds: michael@0: body += fill( michael@0: """ michael@0: ${dictName}Atoms* atomsCache = nullptr; michael@0: if (cx) { michael@0: atomsCache = GetAtomCache<${dictName}Atoms>(cx); michael@0: if (!*reinterpret_cast(atomsCache) && !InitIds(cx, atomsCache)) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: """, michael@0: dictName=self.makeClassName(self.dictionary)) michael@0: michael@0: if self.dictionary.parent: michael@0: body += fill( michael@0: """ michael@0: // Per spec, we init the parent's members first michael@0: if (!${dictName}::Init(cx, val)) { michael@0: return false; michael@0: } michael@0: MOZ_ASSERT(IsConvertibleToDictionary(cx, val)); michael@0: michael@0: """, michael@0: dictName=self.makeClassName(self.dictionary.parent)) michael@0: else: michael@0: body += fill( michael@0: """ michael@0: if (!IsConvertibleToDictionary(cx, val)) { michael@0: return ThrowErrorMessage(cx, MSG_NOT_DICTIONARY, sourceDescription); michael@0: } michael@0: michael@0: """) michael@0: michael@0: memberInits = [self.getMemberConversion(m).define() michael@0: for m in self.memberInfo] michael@0: if memberInits: michael@0: body += fill( michael@0: """ michael@0: bool isNull = val.isNullOrUndefined(); michael@0: // We only need these if !isNull, in which case we have |cx|. michael@0: Maybe > object; michael@0: Maybe > temp; michael@0: if (!isNull) { michael@0: object.construct(cx, &val.toObject()); michael@0: temp.construct(cx); michael@0: } michael@0: $*{memberInits} michael@0: """, michael@0: memberInits="\n".join(memberInits)) michael@0: michael@0: body += "return true;\n" michael@0: michael@0: return ClassMethod("Init", "bool", [ michael@0: Argument('JSContext*', 'cx'), michael@0: Argument('JS::Handle', 'val'), michael@0: Argument('const char*', 'sourceDescription', default='"Value"') michael@0: ], body=body) michael@0: michael@0: def initFromJSONMethod(self): michael@0: return ClassMethod( michael@0: "Init", "bool", michael@0: [Argument('const nsAString&', 'aJSON')], michael@0: body=dedent(""" michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: AutoSafeJSContext cx; michael@0: JS::Rooted json(cx); michael@0: bool ok = ParseJSON(cx, aJSON, &json); michael@0: NS_ENSURE_TRUE(ok, false); michael@0: return Init(cx, json); michael@0: """)) michael@0: michael@0: def toObjectMethod(self): michael@0: body = "" michael@0: if self.needToInitIds: michael@0: body += fill( michael@0: """ michael@0: ${dictName}Atoms* atomsCache = GetAtomCache<${dictName}Atoms>(cx); michael@0: if (!*reinterpret_cast(atomsCache) && !InitIds(cx, atomsCache)) { michael@0: return false; michael@0: } michael@0: michael@0: """, michael@0: dictName=self.makeClassName(self.dictionary)) michael@0: michael@0: if self.dictionary.parent: michael@0: body += fill( michael@0: """ michael@0: // Per spec, we define the parent's members first michael@0: if (!${dictName}::ToObject(cx, rval)) { michael@0: return false; michael@0: } michael@0: JS::Rooted obj(cx, &rval.toObject()); michael@0: michael@0: """, michael@0: dictName=self.makeClassName(self.dictionary.parent)) michael@0: else: michael@0: body += fill( michael@0: """ michael@0: JS::Rooted obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr())); michael@0: if (!obj) { michael@0: return false; michael@0: } michael@0: rval.set(JS::ObjectValue(*obj)); michael@0: michael@0: """) michael@0: michael@0: if self.memberInfo: michael@0: body += "\n".join(self.getMemberDefinition(m).define() michael@0: for m in self.memberInfo) michael@0: else: michael@0: body += "\n" # BOGUS extra blank line michael@0: body += "\nreturn true;\n" michael@0: michael@0: return ClassMethod("ToObject", "bool", [ michael@0: Argument('JSContext*', 'cx'), michael@0: Argument('JS::MutableHandle', 'rval'), michael@0: ], const=True, body=body) michael@0: michael@0: def initIdsMethod(self): michael@0: assert self.needToInitIds michael@0: idinit = ['!atomsCache->%s.init(cx, "%s")' % michael@0: (CGDictionary.makeIdName(m.identifier.name), michael@0: m.identifier.name) michael@0: for m in self.dictionary.members] michael@0: idinit.reverse() michael@0: body = fill( michael@0: """ michael@0: MOZ_ASSERT(!*reinterpret_cast(atomsCache)); michael@0: michael@0: // Initialize these in reverse order so that any failure leaves the first one michael@0: // uninitialized. michael@0: if (${idinit}) { michael@0: return false; michael@0: } michael@0: return true; michael@0: """, michael@0: idinit=" ||\n ".join(idinit)) michael@0: michael@0: return ClassMethod("InitIds", "bool", [ michael@0: Argument("JSContext*", "cx"), michael@0: Argument("%sAtoms*" % self.makeClassName(self.dictionary), michael@0: "atomsCache"), michael@0: ], static=True, body=body, visibility="private") michael@0: michael@0: def traceDictionaryMethod(self): michael@0: body = "" michael@0: if self.dictionary.parent: michael@0: cls = self.makeClassName(self.dictionary.parent) michael@0: body += "%s::TraceDictionary(trc);\n" % cls michael@0: michael@0: memberTraces = [self.getMemberTrace(m) michael@0: for m in self.dictionary.members michael@0: if typeNeedsRooting(m.type)] michael@0: michael@0: if memberTraces: michael@0: body += "\n".join(memberTraces) michael@0: else: michael@0: body += "\n" # BOGUS extra newline michael@0: michael@0: return ClassMethod("TraceDictionary", "void", [ michael@0: Argument("JSTracer*", "trc"), michael@0: ], body=body) michael@0: michael@0: def assignmentOperator(self): michael@0: body = CGList([]) michael@0: if self.dictionary.parent: michael@0: body.append(CGGeneric( michael@0: "%s::operator=(aOther);\n" % michael@0: self.makeClassName(self.dictionary.parent))) michael@0: for m, _ in self.memberInfo: michael@0: memberName = self.makeMemberName(m.identifier.name) michael@0: if not m.defaultValue: michael@0: memberAssign = CGGeneric(fill( michael@0: """ michael@0: if (aOther.${name}.WasPassed()) { michael@0: ${name}.Construct(); michael@0: ${name}.Value() = aOther.${name}.Value(); michael@0: } else { michael@0: ${name}.Reset(); michael@0: } michael@0: """, michael@0: name=memberName)) michael@0: else: michael@0: memberAssign = CGGeneric( michael@0: "%s = aOther.%s;\n" % (memberName, memberName)) michael@0: body.append(memberAssign) michael@0: return ClassMethod( michael@0: "operator=", "void", michael@0: [Argument("const %s&" % self.makeClassName(self.dictionary), michael@0: "aOther")], michael@0: body=body.define() or "\n") # BOGUS blank line when empty michael@0: michael@0: def getStructs(self): michael@0: d = self.dictionary michael@0: selfName = self.makeClassName(d) michael@0: members = [ClassMember(self.makeMemberName(m[0].identifier.name), michael@0: self.getMemberType(m), michael@0: visibility="public", michael@0: body=self.getMemberInitializer(m)) michael@0: for m in self.memberInfo] michael@0: ctors = [ michael@0: ClassConstructor( michael@0: [], michael@0: visibility="public", michael@0: body=( michael@0: "// Safe to pass a null context if we pass a null value\n" michael@0: "Init(nullptr, JS::NullHandleValue);\n")), michael@0: ClassConstructor( michael@0: [Argument("int", "")], michael@0: visibility="protected", michael@0: explicit=True, michael@0: bodyInHeader=True, michael@0: body='// Do nothing here; this is used by our "Fast" subclass\n') michael@0: ] michael@0: methods = [] michael@0: michael@0: if self.needToInitIds: michael@0: methods.append(self.initIdsMethod()) michael@0: michael@0: methods.append(self.initMethod()) michael@0: methods.append(self.initFromJSONMethod()) michael@0: try: michael@0: methods.append(self.toObjectMethod()) michael@0: except MethodNotNewObjectError: michael@0: # If we can't have a ToObject() because one of our members can only michael@0: # be returned from [NewObject] methods, then just skip generating michael@0: # ToObject(). michael@0: pass michael@0: methods.append(self.traceDictionaryMethod()) michael@0: michael@0: if CGDictionary.isDictionaryCopyConstructible(d): michael@0: disallowCopyConstruction = False michael@0: # Note: no base constructors because our operator= will michael@0: # deal with that. michael@0: ctors.append(ClassConstructor([Argument("const %s&" % selfName, michael@0: "aOther")], michael@0: bodyInHeader=True, michael@0: visibility="public", michael@0: explicit=True, michael@0: body="*this = aOther;\n")) michael@0: methods.append(self.assignmentOperator()) michael@0: else: michael@0: disallowCopyConstruction = True michael@0: michael@0: struct = CGClass(selfName, michael@0: bases=[ClassBase(self.base())], michael@0: members=members, michael@0: constructors=ctors, michael@0: methods=methods, michael@0: isStruct=True, michael@0: disallowCopyConstruction=disallowCopyConstruction) michael@0: michael@0: fastDictionaryCtor = ClassConstructor( michael@0: [], michael@0: visibility="public", michael@0: bodyInHeader=True, michael@0: baseConstructors=["%s(42)" % selfName], michael@0: body="// Doesn't matter what int we pass to the parent constructor\n") michael@0: michael@0: fastStruct = CGClass("Fast" + selfName, michael@0: bases=[ClassBase(selfName)], michael@0: constructors=[fastDictionaryCtor], michael@0: isStruct=True) michael@0: michael@0: return CGList([struct, michael@0: CGNamespace('binding_detail', fastStruct)], michael@0: "\n") michael@0: michael@0: def deps(self): michael@0: return self.dictionary.getDeps() michael@0: michael@0: @staticmethod michael@0: def makeDictionaryName(dictionary): michael@0: return dictionary.identifier.name michael@0: michael@0: def makeClassName(self, dictionary): michael@0: return self.makeDictionaryName(dictionary) michael@0: michael@0: @staticmethod michael@0: def makeMemberName(name): michael@0: return "m" + name[0].upper() + name[1:] michael@0: michael@0: def getMemberType(self, memberInfo): michael@0: _, conversionInfo = memberInfo michael@0: # We can't handle having a holderType here michael@0: assert conversionInfo.holderType is None michael@0: declType = conversionInfo.declType michael@0: if conversionInfo.dealWithOptional: michael@0: declType = CGTemplatedType("Optional", declType) michael@0: return declType.define() michael@0: michael@0: def getMemberConversion(self, memberInfo): michael@0: member, conversionInfo = memberInfo michael@0: replacements = { michael@0: "val": "temp.ref()", michael@0: "mutableVal": "&temp.ref()", michael@0: "declName": self.makeMemberName(member.identifier.name), michael@0: # We need a holder name for external interfaces, but michael@0: # it's scoped down to the conversion so we can just use michael@0: # anything we want. michael@0: "holderName": "holder" michael@0: } michael@0: # We can't handle having a holderType here michael@0: assert conversionInfo.holderType is None michael@0: if conversionInfo.dealWithOptional: michael@0: replacements["declName"] = "(" + replacements["declName"] + ".Value())" michael@0: if member.defaultValue: michael@0: replacements["haveValue"] = "!isNull && !temp.ref().isUndefined()" michael@0: michael@0: propId = self.makeIdName(member.identifier.name) michael@0: propGet = ("JS_GetPropertyById(cx, object.ref(), atomsCache->%s, &temp.ref())" % michael@0: propId) michael@0: michael@0: conversionReplacements = { michael@0: "prop": self.makeMemberName(member.identifier.name), michael@0: "convert": string.Template(conversionInfo.template).substitute(replacements), michael@0: "propGet": propGet michael@0: } michael@0: conversion = ("if (!isNull && !${propGet}) {\n" michael@0: " return false;\n" michael@0: "}\n") michael@0: if member.defaultValue: michael@0: conversion += "${convert}" michael@0: else: michael@0: conversion += ( michael@0: "if (!isNull && !temp.ref().isUndefined()) {\n" michael@0: " ${prop}.Construct();\n" michael@0: "${convert}" michael@0: "}\n") michael@0: conversionReplacements["convert"] = indent(conversionReplacements["convert"]) michael@0: michael@0: return CGGeneric( michael@0: string.Template(conversion).substitute(conversionReplacements)) michael@0: michael@0: def getMemberDefinition(self, memberInfo): michael@0: member = memberInfo[0] michael@0: declType = memberInfo[1].declType michael@0: memberLoc = self.makeMemberName(member.identifier.name) michael@0: if member.defaultValue: michael@0: memberData = memberLoc michael@0: else: michael@0: # The data is inside the Optional<> michael@0: memberData = "%s.InternalValue()" % memberLoc michael@0: michael@0: # If you have to change this list (which you shouldn't!), make sure it michael@0: # continues to match the list in test_Object.prototype_props.html michael@0: if (member.identifier.name in michael@0: ["constructor", "toSource", "toString", "toLocaleString", "valueOf", michael@0: "watch", "unwatch", "hasOwnProperty", "isPrototypeOf", michael@0: "propertyIsEnumerable", "__defineGetter__", "__defineSetter__", michael@0: "__lookupGetter__", "__lookupSetter__", "__proto__"]): michael@0: raise TypeError("'%s' member of %s dictionary shadows " michael@0: "a property of Object.prototype, and Xrays to " michael@0: "Object can't handle that.\n" michael@0: "%s" % michael@0: (member.identifier.name, michael@0: self.dictionary.identifier.name, michael@0: member.location)) michael@0: michael@0: propDef = ( michael@0: 'JS_DefinePropertyById(cx, obj, atomsCache->%s, temp, nullptr, nullptr, JSPROP_ENUMERATE)' % michael@0: self.makeIdName(member.identifier.name)) michael@0: michael@0: innerTemplate = wrapForType( michael@0: member.type, self.descriptorProvider, michael@0: { michael@0: 'result': "currentValue", michael@0: 'successCode': ("if (!%s) {\n" michael@0: " return false;\n" michael@0: "}\n" michael@0: "break;\n" % propDef), michael@0: 'jsvalRef': "temp", michael@0: 'jsvalHandle': "&temp", michael@0: 'returnsNewObject': False, michael@0: # 'obj' can just be allowed to be the string "obj", since that michael@0: # will be our dictionary object, which is presumably itself in michael@0: # the right scope. michael@0: 'typedArraysAreStructs': True michael@0: }) michael@0: conversion = CGGeneric(innerTemplate) michael@0: conversion = CGWrapper(conversion, michael@0: pre=("JS::Rooted temp(cx);\n" michael@0: "%s const & currentValue = %s;\n" % michael@0: (declType.define(), memberData) michael@0: )) michael@0: michael@0: # Now make sure that our successCode can actually break out of the michael@0: # conversion. This incidentally gives us a scope for 'temp' and michael@0: # 'currentValue'. michael@0: conversion = CGWrapper( michael@0: CGIndenter(conversion), michael@0: pre=("do {\n" michael@0: " // block for our 'break' successCode and scope for 'temp' and 'currentValue'\n"), michael@0: post="} while(0);\n") michael@0: if not member.defaultValue: michael@0: # Only do the conversion if we have a value michael@0: conversion = CGIfWrapper(conversion, "%s.WasPassed()" % memberLoc) michael@0: return conversion michael@0: michael@0: def getMemberTrace(self, member): michael@0: type = member.type michael@0: assert typeNeedsRooting(type) michael@0: memberLoc = self.makeMemberName(member.identifier.name) michael@0: if member.defaultValue: michael@0: memberData = memberLoc michael@0: else: michael@0: # The data is inside the Optional<> michael@0: memberData = "%s.Value()" % memberLoc michael@0: michael@0: memberName = "%s.%s" % (self.makeClassName(self.dictionary), michael@0: memberLoc) michael@0: michael@0: if type.isObject(): michael@0: trace = CGGeneric('JS_CallObjectTracer(trc, %s, "%s");\n' % michael@0: ("&"+memberData, memberName)) michael@0: if type.nullable(): michael@0: trace = CGIfWrapper(trace, memberData) michael@0: elif type.isAny(): michael@0: trace = CGGeneric('JS_CallValueTracer(trc, %s, "%s");\n' % michael@0: ("&"+memberData, memberName)) michael@0: elif (type.isSequence() or type.isDictionary() or michael@0: type.isSpiderMonkeyInterface() or type.isUnion()): michael@0: if type.nullable(): michael@0: memberNullable = memberData michael@0: memberData = "%s.Value()" % memberData michael@0: if type.isSequence(): michael@0: trace = CGGeneric('DoTraceSequence(trc, %s);\n' % memberData) michael@0: elif type.isDictionary(): michael@0: trace = CGGeneric('%s.TraceDictionary(trc);\n' % memberData) michael@0: elif type.isUnion(): michael@0: trace = CGGeneric('%s.TraceUnion(trc);\n' % memberData) michael@0: else: michael@0: assert type.isSpiderMonkeyInterface() michael@0: trace = CGGeneric('%s.TraceSelf(trc);\n' % memberData) michael@0: if type.nullable(): michael@0: trace = CGIfWrapper(trace, "!%s.IsNull()" % memberNullable) michael@0: else: michael@0: assert False # unknown type michael@0: michael@0: if not member.defaultValue: michael@0: trace = CGIfWrapper(trace, "%s.WasPassed()" % memberLoc) michael@0: michael@0: return trace.define() michael@0: michael@0: def getMemberInitializer(self, memberInfo): michael@0: """ michael@0: Get the right initializer for the member. Most members don't need one, michael@0: but we need to pre-initialize 'any' and 'object' that have a default michael@0: value, so they're safe to trace at all times. michael@0: """ michael@0: member, _ = memberInfo michael@0: if not member.defaultValue: michael@0: # No default value means no need to set it up front, since it's michael@0: # inside an Optional and won't get traced until it's actually set michael@0: # up. michael@0: return None michael@0: type = member.type michael@0: if type.isAny(): michael@0: return "JS::UndefinedValue()" michael@0: if type.isObject(): michael@0: return "nullptr" michael@0: return None michael@0: michael@0: @staticmethod michael@0: def makeIdName(name): michael@0: return name + "_id" michael@0: michael@0: @staticmethod michael@0: def getDictionaryDependenciesFromType(type): michael@0: if type.isDictionary(): michael@0: return set([type.unroll().inner]) michael@0: if type.isSequence() or type.isArray(): michael@0: return CGDictionary.getDictionaryDependenciesFromType(type.unroll()) michael@0: return set() michael@0: michael@0: @staticmethod michael@0: def getDictionaryDependencies(dictionary): michael@0: deps = set() michael@0: if dictionary.parent: michael@0: deps.add(dictionary.parent) michael@0: for member in dictionary.members: michael@0: deps |= CGDictionary.getDictionaryDependenciesFromType(member.type) michael@0: return deps michael@0: michael@0: @staticmethod michael@0: def isDictionaryCopyConstructible(dictionary): michael@0: if (dictionary.parent and michael@0: not CGDictionary.isDictionaryCopyConstructible(dictionary.parent)): michael@0: return False michael@0: return all(isTypeCopyConstructible(m.type) for m in dictionary.members) michael@0: michael@0: michael@0: class CGRegisterProtos(CGAbstractMethod): michael@0: def __init__(self, config): michael@0: CGAbstractMethod.__init__(self, None, 'Register', 'void', michael@0: [Argument('nsScriptNameSpaceManager*', 'aNameSpaceManager')]) michael@0: self.config = config michael@0: michael@0: def _defineMacro(self): michael@0: return dedent(""" michael@0: #define REGISTER_PROTO(_dom_class, _ctor_check) \\ michael@0: aNameSpaceManager->RegisterDefineDOMInterface(MOZ_UTF16(#_dom_class), _dom_class##Binding::DefineDOMInterface, _ctor_check); michael@0: #define REGISTER_CONSTRUCTOR(_dom_constructor, _dom_class, _ctor_check) \\ michael@0: aNameSpaceManager->RegisterDefineDOMInterface(MOZ_UTF16(#_dom_constructor), _dom_class##Binding::DefineDOMInterface, _ctor_check); michael@0: #define REGISTER_NAVIGATOR_CONSTRUCTOR(_prop, _dom_class, _ctor_check) \\ michael@0: aNameSpaceManager->RegisterNavigatorDOMConstructor(MOZ_UTF16(_prop), _dom_class##Binding::ConstructNavigatorObject, _ctor_check); michael@0: """) michael@0: michael@0: def _undefineMacro(self): michael@0: return dedent(""" michael@0: #undef REGISTER_CONSTRUCTOR michael@0: #undef REGISTER_PROTO michael@0: #undef REGISTER_NAVIGATOR_CONSTRUCTOR michael@0: """) michael@0: michael@0: def _registerProtos(self): michael@0: def getCheck(desc): michael@0: if not desc.isExposedConditionally(): michael@0: return "nullptr" michael@0: return "%sBinding::ConstructorEnabled" % desc.name michael@0: lines = [] michael@0: for desc in self.config.getDescriptors(hasInterfaceObject=True, michael@0: isExternal=False, michael@0: workers=False, michael@0: register=True): michael@0: lines.append("REGISTER_PROTO(%s, %s);\n" % (desc.name, getCheck(desc))) michael@0: lines.extend("REGISTER_CONSTRUCTOR(%s, %s, %s);\n" % (n.identifier.name, desc.name, getCheck(desc)) michael@0: for n in desc.interface.namedConstructors) michael@0: for desc in self.config.getDescriptors(isNavigatorProperty=True, register=True): michael@0: propName = desc.interface.getNavigatorProperty() michael@0: assert propName michael@0: lines.append('REGISTER_NAVIGATOR_CONSTRUCTOR("%s", %s, %s);\n' % (propName, desc.name, getCheck(desc))) michael@0: return ''.join(lines) michael@0: michael@0: def definition_body(self): michael@0: return "\n" + self._defineMacro() + "\n" + self._registerProtos() + "\n" + self._undefineMacro() michael@0: michael@0: michael@0: def dependencySortObjects(objects, dependencyGetter, nameGetter): michael@0: """ michael@0: Sort IDL objects with dependencies on each other such that if A michael@0: depends on B then B will come before A. This is needed for michael@0: declaring C++ classes in the right order, for example. Objects michael@0: that have no dependencies are just sorted by name. michael@0: michael@0: objects should be something that can produce a set of objects michael@0: (e.g. a set, iterator, list, etc). michael@0: michael@0: dependencyGetter is something that, given an object, should return michael@0: the set of objects it depends on. michael@0: """ michael@0: # XXXbz this will fail if we have two webidl files F1 and F2 such that F1 michael@0: # declares an object which depends on an object in F2, and F2 declares an michael@0: # object (possibly a different one!) that depends on an object in F1. The michael@0: # good news is that I expect this to never happen. michael@0: sortedObjects = [] michael@0: objects = set(objects) michael@0: while len(objects) != 0: michael@0: # Find the dictionaries that don't depend on anything else michael@0: # anymore and move them over. michael@0: toMove = [o for o in objects if michael@0: len(dependencyGetter(o) & objects) == 0] michael@0: if len(toMove) == 0: michael@0: raise TypeError("Loop in dependency graph\n" + michael@0: "\n".join(o.location for o in objects)) michael@0: objects = objects - set(toMove) michael@0: sortedObjects.extend(sorted(toMove, key=nameGetter)) michael@0: return sortedObjects michael@0: michael@0: michael@0: class ForwardDeclarationBuilder: michael@0: """ michael@0: Create a canonical representation of a set of namespaced forward michael@0: declarations. michael@0: """ michael@0: def __init__(self): michael@0: """ michael@0: The set of declarations is represented as a tree of nested namespaces. michael@0: Each tree node has a set of declarations |decls| and a dict |children|. michael@0: Each declaration is a pair consisting of the class name and a boolean michael@0: that is true iff the class is really a struct. |children| maps the michael@0: names of inner namespaces to the declarations in that namespace. michael@0: """ michael@0: self.decls = set() michael@0: self.children = {} michael@0: michael@0: def _listAdd(self, namespaces, name, isStruct=False): michael@0: """ michael@0: Add a forward declaration, where |namespaces| is a list of namespaces. michael@0: |name| should not contain any other namespaces. michael@0: """ michael@0: if namespaces: michael@0: child = self.children.setdefault(namespaces[0], ForwardDeclarationBuilder()) michael@0: child._listAdd(namespaces[1:], name, isStruct) michael@0: else: michael@0: assert '::' not in name michael@0: self.decls.add((name, isStruct)) michael@0: michael@0: def addInMozillaDom(self, name, isStruct=False): michael@0: """ michael@0: Add a forward declaration to the mozilla::dom:: namespace. |name| should not michael@0: contain any other namespaces. michael@0: """ michael@0: self._listAdd(["mozilla", "dom"], name, isStruct) michael@0: michael@0: def add(self, nativeType, isStruct=False): michael@0: """ michael@0: Add a forward declaration, where |nativeType| is a string containing michael@0: the type and its namespaces, in the usual C++ way. michael@0: """ michael@0: components = nativeType.split('::') michael@0: self._listAdd(components[:-1], components[-1], isStruct) michael@0: michael@0: def _build(self, atTopLevel): michael@0: """ michael@0: Return a codegenerator for the forward declarations. michael@0: """ michael@0: decls = [] michael@0: if self.decls: michael@0: decls.append(CGList([CGClassForwardDeclare(cname, isStruct) michael@0: for cname, isStruct in sorted(self.decls)])) michael@0: for namespace, child in sorted(self.children.iteritems()): michael@0: decls.append(CGNamespace(namespace, child._build(atTopLevel=False), declareOnly=True)) michael@0: michael@0: cg = CGList(decls, "\n") michael@0: if not atTopLevel and len(decls) + len(self.decls) > 1: michael@0: cg = CGWrapper(cg, pre='\n', post='\n') michael@0: return cg michael@0: michael@0: def build(self): michael@0: return self._build(atTopLevel=True) michael@0: michael@0: michael@0: class CGForwardDeclarations(CGWrapper): michael@0: """ michael@0: Code generate the forward declarations for a header file. michael@0: """ michael@0: def __init__(self, config, descriptors, mainCallbacks, workerCallbacks, michael@0: dictionaries, callbackInterfaces): michael@0: builder = ForwardDeclarationBuilder() michael@0: michael@0: def forwardDeclareForType(t, workerness='both'): michael@0: t = t.unroll() michael@0: if t.isGeckoInterface(): michael@0: name = t.inner.identifier.name michael@0: # Find and add the non-worker implementation, if any. michael@0: if workerness != 'workeronly': michael@0: try: michael@0: desc = config.getDescriptor(name, False) michael@0: builder.add(desc.nativeType) michael@0: except NoSuchDescriptorError: michael@0: pass michael@0: # Find and add the worker implementation, if any. michael@0: if workerness != 'mainthreadonly': michael@0: try: michael@0: desc = config.getDescriptor(name, True) michael@0: builder.add(desc.nativeType) michael@0: except NoSuchDescriptorError: michael@0: pass michael@0: elif t.isCallback(): michael@0: builder.addInMozillaDom(str(t)) michael@0: elif t.isDictionary(): michael@0: builder.addInMozillaDom(t.inner.identifier.name, isStruct=True) michael@0: elif t.isCallbackInterface(): michael@0: builder.addInMozillaDom(t.inner.identifier.name) michael@0: elif t.isUnion(): michael@0: # Forward declare both the owning and non-owning version, michael@0: # since we don't know which one we might want michael@0: builder.addInMozillaDom(CGUnionStruct.unionTypeName(t, False)) michael@0: builder.addInMozillaDom(CGUnionStruct.unionTypeName(t, True)) michael@0: elif t.isMozMap(): michael@0: forwardDeclareForType(t.inner, workerness) michael@0: # Don't need to do anything for void, primitive, string, any or object. michael@0: # There may be some other cases we are missing. michael@0: michael@0: # Needed for at least Wrap. michael@0: for d in descriptors: michael@0: builder.add(d.nativeType) michael@0: michael@0: # We just about always need NativePropertyHooks michael@0: builder.addInMozillaDom("NativePropertyHooks") michael@0: builder.addInMozillaDom("ProtoAndIfaceCache") michael@0: michael@0: for callback in mainCallbacks: michael@0: forwardDeclareForType(callback) michael@0: for t in getTypesFromCallback(callback): michael@0: forwardDeclareForType(t, workerness='mainthreadonly') michael@0: michael@0: for callback in workerCallbacks: michael@0: forwardDeclareForType(callback) michael@0: for t in getTypesFromCallback(callback): michael@0: forwardDeclareForType(t, workerness='workeronly') michael@0: michael@0: for d in callbackInterfaces: michael@0: builder.add(d.nativeType) michael@0: for t in getTypesFromDescriptor(d): michael@0: forwardDeclareForType(t) michael@0: michael@0: for d in dictionaries: michael@0: if len(d.members) > 0: michael@0: builder.addInMozillaDom(d.identifier.name + "Atoms", isStruct=True) michael@0: for t in getTypesFromDictionary(d): michael@0: forwardDeclareForType(t) michael@0: michael@0: CGWrapper.__init__(self, builder.build()) michael@0: michael@0: michael@0: class CGBindingRoot(CGThing): michael@0: """ michael@0: Root codegen class for binding generation. Instantiate the class, and call michael@0: declare or define to generate header or cpp code (respectively). michael@0: """ michael@0: def __init__(self, config, prefix, webIDLFile): michael@0: bindingHeaders = {} michael@0: descriptors = config.getDescriptors(webIDLFile=webIDLFile, michael@0: hasInterfaceOrInterfacePrototypeObject=True, michael@0: skipGen=False) michael@0: michael@0: def descriptorRequiresPreferences(desc): michael@0: iface = desc.interface michael@0: return any(m.getExtendedAttribute("Pref") for m in iface.members + [iface]) michael@0: michael@0: bindingHeaders["mozilla/Preferences.h"] = any( michael@0: descriptorRequiresPreferences(d) for d in descriptors) michael@0: bindingHeaders["mozilla/dom/NonRefcountedDOMObject.h"] = any( michael@0: d.nativeOwnership == 'owned' for d in descriptors) michael@0: bindingHeaders["mozilla/dom/DOMJSProxyHandler.h"] = any( michael@0: d.concrete and d.proxy for d in descriptors) michael@0: michael@0: def descriptorHasChromeOnly(desc): michael@0: return (any(isChromeOnly(a) for a in desc.interface.members) or michael@0: desc.interface.getExtendedAttribute("ChromeOnly") is not None or michael@0: # JS-implemented interfaces with an interface object get a michael@0: # chromeonly _create method. michael@0: (desc.interface.isJSImplemented() and michael@0: desc.interface.hasInterfaceObject())) michael@0: michael@0: bindingHeaders["nsContentUtils.h"] = any( michael@0: descriptorHasChromeOnly(d) for d in descriptors) michael@0: # XXXkhuey ugly hack but this is going away soon. michael@0: bindingHeaders['xpcprivate.h'] = webIDLFile.endswith("EventTarget.webidl") michael@0: hasWorkerStuff = len(config.getDescriptors(webIDLFile=webIDLFile, michael@0: workers=True)) != 0 michael@0: bindingHeaders["WorkerPrivate.h"] = hasWorkerStuff michael@0: bindingHeaders["nsThreadUtils.h"] = hasWorkerStuff michael@0: michael@0: dictionaries = config.getDictionaries(webIDLFile=webIDLFile) michael@0: bindingHeaders["nsCxPusher.h"] = dictionaries michael@0: bindingHeaders["AtomList.h"] = any( michael@0: len(dict.members) > 0 for dict in dictionaries) michael@0: mainCallbacks = config.getCallbacks(webIDLFile=webIDLFile, michael@0: workers=False) michael@0: workerCallbacks = config.getCallbacks(webIDLFile=webIDLFile, michael@0: workers=True) michael@0: callbackDescriptors = config.getDescriptors(webIDLFile=webIDLFile, michael@0: isCallback=True) michael@0: jsImplemented = config.getDescriptors(webIDLFile=webIDLFile, michael@0: isJSImplemented=True) michael@0: bindingHeaders["nsPIDOMWindow.h"] = jsImplemented michael@0: michael@0: def addHeaderBasedOnTypes(header, typeChecker): michael@0: bindingHeaders[header] = ( michael@0: bindingHeaders.get(header, False) or michael@0: any(map(typeChecker, michael@0: getAllTypes(descriptors + callbackDescriptors, michael@0: dictionaries, michael@0: mainCallbacks + workerCallbacks)))) michael@0: michael@0: bindingHeaders["nsDOMQS.h"] = any(d.hasXPConnectImpls for d in descriptors) michael@0: # Only mainthread things can have hasXPConnectImpls michael@0: provider = config.getDescriptorProvider(False) michael@0: michael@0: def checkForXPConnectImpls(typeInfo): michael@0: type, _, _ = typeInfo michael@0: type = type.unroll() michael@0: while type.isMozMap(): michael@0: type = type.inner.unroll() michael@0: if not type.isInterface() or not type.isGeckoInterface(): michael@0: return False michael@0: try: michael@0: typeDesc = provider.getDescriptor(type.inner.identifier.name) michael@0: except NoSuchDescriptorError: michael@0: return False michael@0: return typeDesc.hasXPConnectImpls michael@0: addHeaderBasedOnTypes("nsDOMQS.h", checkForXPConnectImpls) michael@0: michael@0: def descriptorClearsPropsInSlots(descriptor): michael@0: if not descriptor.wrapperCache: michael@0: return False michael@0: return any(m.isAttr() and m.getExtendedAttribute("StoreInSlot") michael@0: for m in descriptor.interface.members) michael@0: bindingHeaders["nsJSUtils.h"] = any(descriptorClearsPropsInSlots(d) for d in descriptors) michael@0: michael@0: # Do codegen for all the enums michael@0: enums = config.getEnums(webIDLFile) michael@0: cgthings = [CGEnum(e) for e in enums] michael@0: michael@0: hasCode = (descriptors or callbackDescriptors or dictionaries or michael@0: mainCallbacks or workerCallbacks) michael@0: bindingHeaders["mozilla/dom/BindingUtils.h"] = hasCode michael@0: bindingHeaders["mozilla/dom/OwningNonNull.h"] = hasCode michael@0: bindingHeaders["mozilla/dom/BindingDeclarations.h"] = ( michael@0: not hasCode and enums) michael@0: michael@0: bindingHeaders["WrapperFactory.h"] = descriptors michael@0: bindingHeaders["mozilla/dom/DOMJSClass.h"] = descriptors michael@0: michael@0: # Do codegen for all the dictionaries. We have to be a bit careful michael@0: # here, because we have to generate these in order from least derived michael@0: # to most derived so that class inheritance works out. We also have to michael@0: # generate members before the dictionary that contains them. michael@0: cgthings.extend([CGDictionary(d, config.getDescriptorProvider(False)) michael@0: for d in michael@0: dependencySortObjects(dictionaries, michael@0: CGDictionary.getDictionaryDependencies, michael@0: lambda d: d.identifier.name)]) michael@0: michael@0: # Do codegen for all the callbacks. michael@0: cgthings.extend(CGCallbackFunction(c, config.getDescriptorProvider(False)) michael@0: for c in mainCallbacks) michael@0: michael@0: cgthings.extend(CGCallbackFunction(c, config.getDescriptorProvider(True)) michael@0: for c in workerCallbacks if c not in mainCallbacks) michael@0: michael@0: # Do codegen for all the descriptors michael@0: cgthings.extend([CGDescriptor(x) for x in descriptors]) michael@0: michael@0: # Do codegen for all the callback interfaces. Skip worker callbacks. michael@0: cgthings.extend([CGCallbackInterface(x) for x in callbackDescriptors if michael@0: not x.workers]) michael@0: michael@0: # Do codegen for JS implemented classes michael@0: def getParentDescriptor(desc): michael@0: if not desc.interface.parent: michael@0: return set() michael@0: return {desc.getDescriptor(desc.interface.parent.identifier.name)} michael@0: for x in dependencySortObjects(jsImplemented, getParentDescriptor, michael@0: lambda d: d.interface.identifier.name): michael@0: cgthings.append(CGCallbackInterface(x)) michael@0: cgthings.append(CGJSImplClass(x)) michael@0: michael@0: # And make sure we have the right number of newlines at the end michael@0: curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n") michael@0: michael@0: # Wrap all of that in our namespaces. michael@0: curr = CGNamespace.build(['mozilla', 'dom'], michael@0: CGWrapper(curr, pre="\n")) michael@0: michael@0: curr = CGList([CGForwardDeclarations(config, descriptors, michael@0: mainCallbacks, workerCallbacks, michael@0: dictionaries, michael@0: callbackDescriptors + jsImplemented), michael@0: curr], michael@0: "\n") michael@0: michael@0: # Add header includes. michael@0: bindingHeaders = [header michael@0: for header, include in bindingHeaders.iteritems() michael@0: if include] michael@0: declareIncludes = [ michael@0: 'mozilla/dom/BindingDeclarations.h', michael@0: 'mozilla/dom/Nullable.h', michael@0: 'mozilla/ErrorResult.h', michael@0: 'jspubtd.h', michael@0: 'js/RootingAPI.h', michael@0: ] michael@0: if jsImplemented: michael@0: declareIncludes.append('nsWeakReference.h') michael@0: michael@0: curr = CGHeaders(descriptors, michael@0: dictionaries, michael@0: mainCallbacks + workerCallbacks, michael@0: callbackDescriptors, michael@0: declareIncludes, michael@0: bindingHeaders, michael@0: prefix, michael@0: curr, michael@0: config, michael@0: jsImplemented) michael@0: michael@0: # Add include guards. michael@0: curr = CGIncludeGuard(prefix, curr) michael@0: michael@0: # Add the auto-generated comment. michael@0: curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) michael@0: michael@0: # Store the final result. michael@0: self.root = curr michael@0: michael@0: def declare(self): michael@0: return stripTrailingWhitespace(self.root.declare()) michael@0: michael@0: def define(self): michael@0: return stripTrailingWhitespace(self.root.define()) michael@0: michael@0: def deps(self): michael@0: return self.root.deps() michael@0: michael@0: michael@0: class CGNativeMember(ClassMethod): michael@0: def __init__(self, descriptorProvider, member, name, signature, extendedAttrs, michael@0: breakAfter=True, passJSBitsAsNeeded=True, visibility="public", michael@0: jsObjectsArePtr=False, variadicIsSequence=False): michael@0: """ michael@0: If jsObjectsArePtr is true, typed arrays and "object" will be michael@0: passed as JSObject*. michael@0: michael@0: If passJSBitsAsNeeded is false, we don't automatically pass in a michael@0: JSContext* or a JSObject* based on the return and argument types. We michael@0: can still pass it based on 'implicitJSContext' annotations. michael@0: """ michael@0: self.descriptorProvider = descriptorProvider michael@0: self.member = member michael@0: self.extendedAttrs = extendedAttrs michael@0: self.resultAlreadyAddRefed = isResultAlreadyAddRefed(self.extendedAttrs) michael@0: self.passJSBitsAsNeeded = passJSBitsAsNeeded michael@0: self.jsObjectsArePtr = jsObjectsArePtr michael@0: self.variadicIsSequence = variadicIsSequence michael@0: breakAfterSelf = "\n" if breakAfter else "" michael@0: ClassMethod.__init__(self, name, michael@0: self.getReturnType(signature[0], False), michael@0: self.getArgs(signature[0], signature[1]), michael@0: static=member.isStatic(), michael@0: # Mark our getters, which are attrs that michael@0: # have a non-void return type, as const. michael@0: const=(not member.isStatic() and member.isAttr() and michael@0: not signature[0].isVoid()), michael@0: breakAfterReturnDecl=" ", michael@0: breakAfterSelf=breakAfterSelf, michael@0: visibility=visibility) michael@0: michael@0: def getReturnType(self, type, isMember): michael@0: return self.getRetvalInfo(type, isMember)[0] michael@0: michael@0: def getRetvalInfo(self, type, isMember): michael@0: """ michael@0: Returns a tuple: michael@0: michael@0: The first element is the type declaration for the retval michael@0: michael@0: The second element is a default value that can be used on error returns. michael@0: For cases whose behavior depends on isMember, the second element will be michael@0: None if isMember is true. michael@0: michael@0: The third element is a template for actually returning a value stored in michael@0: "${declName}" and "${holderName}". This means actually returning it if michael@0: we're not outparam, else assigning to the "retval" outparam. If michael@0: isMember is true, this can be None, since in that case the caller will michael@0: never examine this value. michael@0: """ michael@0: if type.isVoid(): michael@0: return "void", "", "" michael@0: if type.isPrimitive() and type.tag() in builtinNames: michael@0: result = CGGeneric(builtinNames[type.tag()]) michael@0: defaultReturnArg = "0" michael@0: if type.nullable(): michael@0: result = CGTemplatedType("Nullable", result) michael@0: defaultReturnArg = "" michael@0: return (result.define(), michael@0: "%s(%s)" % (result.define(), defaultReturnArg), michael@0: "return ${declName};\n") michael@0: if type.isDOMString(): michael@0: if isMember: michael@0: # No need for a third element in the isMember case michael@0: return "nsString", None, None michael@0: # Outparam michael@0: return "void", "", "aRetVal = ${declName};\n" michael@0: if type.isByteString(): michael@0: if isMember: michael@0: # No need for a third element in the isMember case michael@0: return "nsCString", None, None michael@0: # Outparam michael@0: return "void", "", "aRetVal = ${declName};\n" michael@0: if type.isEnum(): michael@0: enumName = type.unroll().inner.identifier.name michael@0: if type.nullable(): michael@0: enumName = CGTemplatedType("Nullable", michael@0: CGGeneric(enumName)).define() michael@0: defaultValue = "%s()" % enumName michael@0: else: michael@0: defaultValue = "%s(0)" % enumName michael@0: return enumName, defaultValue, "return ${declName};\n" michael@0: if type.isGeckoInterface(): michael@0: iface = type.unroll().inner michael@0: result = CGGeneric(self.descriptorProvider.getDescriptor( michael@0: iface.identifier.name).prettyNativeType) michael@0: if self.resultAlreadyAddRefed: michael@0: if isMember: michael@0: holder = "nsRefPtr" michael@0: else: michael@0: holder = "already_AddRefed" michael@0: if memberReturnsNewObject(self.member): michael@0: warning = "" michael@0: else: michael@0: warning = "// Mark this as resultNotAddRefed to return raw pointers\n" michael@0: result = CGWrapper(result, michael@0: pre=("%s%s<" % (warning, holder)), michael@0: post=">") michael@0: else: michael@0: result = CGWrapper(result, post="*") michael@0: # Since we always force an owning type for callback return values, michael@0: # our ${declName} is an OwningNonNull or nsRefPtr. So we can just michael@0: # .forget() to get our already_AddRefed. michael@0: return result.define(), "nullptr", "return ${declName}.forget();\n" michael@0: if type.isCallback(): michael@0: return ("already_AddRefed<%s>" % type.unroll().identifier.name, michael@0: "nullptr", "return ${declName}.forget();\n") michael@0: if type.isAny(): michael@0: if isMember: michael@0: # No need for a third element in the isMember case michael@0: return "JS::Value", None, None michael@0: # Outparam michael@0: return "void", "", "aRetVal.set(${declName});\n" michael@0: michael@0: if type.isObject(): michael@0: if isMember: michael@0: # No need for a third element in the isMember case michael@0: return "JSObject*", None, None michael@0: return "void", "", "aRetVal.set(${declName});\n" michael@0: if type.isSpiderMonkeyInterface(): michael@0: if isMember: michael@0: # No need for a third element in the isMember case michael@0: return "JSObject*", None, None michael@0: if type.nullable(): michael@0: returnCode = "${declName}.IsNull() ? nullptr : ${declName}.Value().Obj();\n" michael@0: else: michael@0: returnCode = "${declName}.Obj();\n" michael@0: return "void", "", "aRetVal.set(%s);\n" % returnCode michael@0: if type.isSequence(): michael@0: # If we want to handle sequence-of-sequences return values, we're michael@0: # going to need to fix example codegen to not produce nsTArray michael@0: # for the relevant argument... michael@0: assert not isMember michael@0: # Outparam. michael@0: if type.nullable(): michael@0: returnCode = dedent(""" michael@0: if (${declName}.IsNull()) { michael@0: aRetVal.SetNull(); michael@0: } else { michael@0: aRetVal.SetValue().SwapElements(${declName}.Value()); michael@0: } michael@0: """) michael@0: else: michael@0: returnCode = "aRetVal.SwapElements(${declName});\n" michael@0: return "void", "", returnCode michael@0: if type.isMozMap(): michael@0: # If we want to handle MozMap-of-MozMap return values, we're michael@0: # going to need to fix example codegen to not produce MozMap michael@0: # for the relevant argument... michael@0: assert not isMember michael@0: # In this case we convert directly into our outparam to start with michael@0: return "void", "", "" michael@0: if type.isDate(): michael@0: result = CGGeneric("Date") michael@0: if type.nullable(): michael@0: result = CGTemplatedType("Nullable", result) michael@0: return (result.define(), "%s()" % result.define(), michael@0: "return ${declName};\n") michael@0: if type.isDictionary(): michael@0: if isMember: michael@0: # Only the first member of the tuple matters here, but return michael@0: # bogus values for the others in case someone decides to use michael@0: # them. michael@0: return CGDictionary.makeDictionaryName(type.inner), None, None michael@0: # In this case we convert directly into our outparam to start with michael@0: return "void", "", "" michael@0: if type.isUnion(): michael@0: if isMember: michael@0: # Only the first member of the tuple matters here, but return michael@0: # bogus values for the others in case someone decides to use michael@0: # them. michael@0: return CGUnionStruct.unionTypeDecl(type, True), None, None michael@0: # In this case we convert directly into our outparam to start with michael@0: return "void", "", "" michael@0: michael@0: raise TypeError("Don't know how to declare return value for %s" % michael@0: type) michael@0: michael@0: def getArgs(self, returnType, argList): michael@0: args = [self.getArg(arg) for arg in argList] michael@0: # Now the outparams michael@0: if returnType.isDOMString(): michael@0: args.append(Argument("nsString&", "aRetVal")) michael@0: elif returnType.isByteString(): michael@0: args.append(Argument("nsCString&", "aRetVal")) michael@0: elif returnType.isSequence(): michael@0: nullable = returnType.nullable() michael@0: if nullable: michael@0: returnType = returnType.inner michael@0: # And now the actual underlying type michael@0: elementDecl = self.getReturnType(returnType.inner, True) michael@0: type = CGTemplatedType("nsTArray", CGGeneric(elementDecl)) michael@0: if nullable: michael@0: type = CGTemplatedType("Nullable", type) michael@0: args.append(Argument("%s&" % type.define(), "aRetVal")) michael@0: elif returnType.isMozMap(): michael@0: nullable = returnType.nullable() michael@0: if nullable: michael@0: returnType = returnType.inner michael@0: # And now the actual underlying type michael@0: elementDecl = self.getReturnType(returnType.inner, True) michael@0: type = CGTemplatedType("MozMap", CGGeneric(elementDecl)) michael@0: if nullable: michael@0: type = CGTemplatedType("Nullable", type) michael@0: args.append(Argument("%s&" % type.define(), "aRetVal")) michael@0: elif returnType.isDictionary(): michael@0: nullable = returnType.nullable() michael@0: if nullable: michael@0: returnType = returnType.inner michael@0: dictType = CGGeneric(CGDictionary.makeDictionaryName(returnType.inner)) michael@0: if nullable: michael@0: dictType = CGTemplatedType("Nullable", dictType) michael@0: args.append(Argument("%s&" % dictType.define(), "aRetVal")) michael@0: elif returnType.isUnion(): michael@0: args.append(Argument("%s&" % michael@0: CGUnionStruct.unionTypeDecl(returnType, True), michael@0: "aRetVal")) michael@0: elif returnType.isAny(): michael@0: args.append(Argument("JS::MutableHandle", "aRetVal")) michael@0: elif returnType.isObject() or returnType.isSpiderMonkeyInterface(): michael@0: args.append(Argument("JS::MutableHandle", "aRetVal")) michael@0: michael@0: # And the ErrorResult michael@0: if 'infallible' not in self.extendedAttrs: michael@0: # Use aRv so it won't conflict with local vars named "rv" michael@0: args.append(Argument("ErrorResult&", "aRv")) michael@0: # The legacycaller thisval michael@0: if self.member.isMethod() and self.member.isLegacycaller(): michael@0: # If it has an identifier, we can't deal with it yet michael@0: assert self.member.isIdentifierLess() michael@0: args.insert(0, Argument("JS::Value", "aThisVal")) michael@0: # And jscontext bits. michael@0: if needCx(returnType, argList, self.extendedAttrs, michael@0: self.passJSBitsAsNeeded): michael@0: args.insert(0, Argument("JSContext*", "cx")) michael@0: if needScopeObject(returnType, argList, self.extendedAttrs, michael@0: self.descriptorProvider.wrapperCache, michael@0: self.passJSBitsAsNeeded, michael@0: self.member.getExtendedAttribute("StoreInSlot")): michael@0: args.insert(1, Argument("JS::Handle", "obj")) michael@0: # And if we're static, a global michael@0: if self.member.isStatic(): michael@0: args.insert(0, Argument("const GlobalObject&", "global")) michael@0: return args michael@0: michael@0: def doGetArgType(self, type, optional, isMember): michael@0: """ michael@0: The main work of getArgType. Returns a string type decl, whether this michael@0: is a const ref, as well as whether the type should be wrapped in michael@0: Nullable as needed. michael@0: michael@0: isMember can be false or one of the strings "Sequence", "Variadic", michael@0: "MozMap" michael@0: """ michael@0: if type.isArray(): michael@0: raise TypeError("Can't handle array arguments yet") michael@0: michael@0: if type.isSequence(): michael@0: nullable = type.nullable() michael@0: if nullable: michael@0: type = type.inner michael@0: elementType = type.inner michael@0: argType = self.getArgType(elementType, False, "Sequence")[0] michael@0: decl = CGTemplatedType("Sequence", argType) michael@0: return decl.define(), True, True michael@0: michael@0: if type.isMozMap(): michael@0: nullable = type.nullable() michael@0: if nullable: michael@0: type = type.inner michael@0: elementType = type.inner michael@0: argType = self.getArgType(elementType, False, "MozMap")[0] michael@0: decl = CGTemplatedType("MozMap", argType) michael@0: return decl.define(), True, True michael@0: michael@0: if type.isUnion(): michael@0: # unionTypeDecl will handle nullable types, so return False for michael@0: # auto-wrapping in Nullable michael@0: return CGUnionStruct.unionTypeDecl(type, isMember), True, False michael@0: michael@0: if type.isGeckoInterface() and not type.isCallbackInterface(): michael@0: iface = type.unroll().inner michael@0: argIsPointer = type.nullable() or iface.isExternal() michael@0: forceOwningType = iface.isCallback() or isMember michael@0: if argIsPointer: michael@0: if (optional or isMember) and forceOwningType: michael@0: typeDecl = "nsRefPtr<%s>" michael@0: else: michael@0: typeDecl = "%s*" michael@0: else: michael@0: if optional or isMember: michael@0: if forceOwningType: michael@0: typeDecl = "OwningNonNull<%s>" michael@0: else: michael@0: typeDecl = "NonNull<%s>" michael@0: else: michael@0: typeDecl = "%s&" michael@0: return ((typeDecl % michael@0: self.descriptorProvider.getDescriptor(iface.identifier.name).prettyNativeType), michael@0: False, False) michael@0: michael@0: if type.isSpiderMonkeyInterface(): michael@0: if self.jsObjectsArePtr: michael@0: return "JSObject*", False, False michael@0: michael@0: return type.name, True, True michael@0: michael@0: if type.isDOMString(): michael@0: if isMember: michael@0: declType = "nsString" michael@0: else: michael@0: declType = "nsAString" michael@0: return declType, True, False michael@0: michael@0: if type.isByteString(): michael@0: declType = "nsCString" michael@0: return declType, True, False michael@0: michael@0: if type.isEnum(): michael@0: return type.unroll().inner.identifier.name, False, True michael@0: michael@0: if type.isCallback() or type.isCallbackInterface(): michael@0: forceOwningType = optional or isMember michael@0: if type.nullable(): michael@0: if forceOwningType: michael@0: declType = "nsRefPtr<%s>" michael@0: else: michael@0: declType = "%s*" michael@0: else: michael@0: if forceOwningType: michael@0: declType = "OwningNonNull<%s>" michael@0: else: michael@0: declType = "%s&" michael@0: if type.isCallback(): michael@0: name = type.unroll().identifier.name michael@0: else: michael@0: name = type.unroll().inner.identifier.name michael@0: return declType % name, False, False michael@0: michael@0: if type.isAny(): michael@0: # Don't do the rooting stuff for variadics for now michael@0: if isMember: michael@0: declType = "JS::Value" michael@0: else: michael@0: declType = "JS::Handle" michael@0: return declType, False, False michael@0: michael@0: if type.isObject(): michael@0: if isMember: michael@0: declType = "JSObject*" michael@0: else: michael@0: declType = "JS::Handle" michael@0: return declType, False, False michael@0: michael@0: if type.isDictionary(): michael@0: typeName = CGDictionary.makeDictionaryName(type.inner) michael@0: return typeName, True, True michael@0: michael@0: if type.isDate(): michael@0: return "Date", False, True michael@0: michael@0: assert type.isPrimitive() michael@0: michael@0: return builtinNames[type.tag()], False, True michael@0: michael@0: def getArgType(self, type, optional, isMember): michael@0: """ michael@0: Get the type of an argument declaration. Returns the type CGThing, and michael@0: whether this should be a const ref. michael@0: michael@0: isMember can be False, "Sequence", or "Variadic" michael@0: """ michael@0: decl, ref, handleNullable = self.doGetArgType(type, optional, isMember) michael@0: decl = CGGeneric(decl) michael@0: if handleNullable and type.nullable(): michael@0: decl = CGTemplatedType("Nullable", decl) michael@0: ref = True michael@0: if isMember == "Variadic": michael@0: arrayType = "Sequence" if self.variadicIsSequence else "nsTArray" michael@0: decl = CGTemplatedType(arrayType, decl) michael@0: ref = True michael@0: elif optional: michael@0: # Note: All variadic args claim to be optional, but we can just use michael@0: # empty arrays to represent them not being present. michael@0: decl = CGTemplatedType("Optional", decl) michael@0: ref = True michael@0: return (decl, ref) michael@0: michael@0: def getArg(self, arg): michael@0: """ michael@0: Get the full argument declaration for an argument michael@0: """ michael@0: decl, ref = self.getArgType(arg.type, michael@0: arg.optional and not arg.defaultValue, michael@0: "Variadic" if arg.variadic else False) michael@0: if ref: michael@0: decl = CGWrapper(decl, pre="const ", post="&") michael@0: michael@0: return Argument(decl.define(), arg.identifier.name) michael@0: michael@0: michael@0: class CGExampleMethod(CGNativeMember): michael@0: def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True): michael@0: CGNativeMember.__init__(self, descriptor, method, michael@0: CGSpecializedMethod.makeNativeName(descriptor, michael@0: method), michael@0: signature, michael@0: descriptor.getExtendedAttributes(method), michael@0: breakAfter=breakAfter, michael@0: variadicIsSequence=True) michael@0: michael@0: def define(self, cgClass): michael@0: return '' michael@0: michael@0: michael@0: class CGExampleGetter(CGNativeMember): michael@0: def __init__(self, descriptor, attr): michael@0: CGNativeMember.__init__(self, descriptor, attr, michael@0: CGSpecializedGetter.makeNativeName(descriptor, michael@0: attr), michael@0: (attr.type, []), michael@0: descriptor.getExtendedAttributes(attr, michael@0: getter=True)) michael@0: michael@0: def define(self, cgClass): michael@0: return '' michael@0: michael@0: michael@0: class CGExampleSetter(CGNativeMember): michael@0: def __init__(self, descriptor, attr): michael@0: CGNativeMember.__init__(self, descriptor, attr, michael@0: CGSpecializedSetter.makeNativeName(descriptor, michael@0: attr), michael@0: (BuiltinTypes[IDLBuiltinType.Types.void], michael@0: [FakeArgument(attr.type, attr)]), michael@0: descriptor.getExtendedAttributes(attr, michael@0: setter=True)) michael@0: michael@0: def define(self, cgClass): michael@0: return '' michael@0: michael@0: michael@0: class CGBindingImplClass(CGClass): michael@0: """ michael@0: Common codegen for generating a C++ implementation of a WebIDL interface michael@0: """ michael@0: def __init__(self, descriptor, cgMethod, cgGetter, cgSetter, wantGetParent=True): michael@0: """ michael@0: cgMethod, cgGetter and cgSetter are classes used to codegen methods, michael@0: getters and setters. michael@0: """ michael@0: self.descriptor = descriptor michael@0: self._deps = descriptor.interface.getDeps() michael@0: michael@0: iface = descriptor.interface michael@0: michael@0: self.methodDecls = [] michael@0: michael@0: def appendMethod(m, isConstructor=False): michael@0: sigs = m.signatures() michael@0: for s in sigs[:-1]: michael@0: # Don't put a blank line after overloads, until we michael@0: # get to the last one. michael@0: self.methodDecls.append(cgMethod(descriptor, m, s, michael@0: isConstructor, michael@0: breakAfter=False)) michael@0: self.methodDecls.append(cgMethod(descriptor, m, sigs[-1], michael@0: isConstructor)) michael@0: michael@0: if iface.ctor(): michael@0: appendMethod(iface.ctor(), isConstructor=True) michael@0: for n in iface.namedConstructors: michael@0: appendMethod(n, isConstructor=True) michael@0: for m in iface.members: michael@0: if m.isMethod(): michael@0: if m.isIdentifierLess(): michael@0: continue michael@0: appendMethod(m) michael@0: elif m.isAttr(): michael@0: self.methodDecls.append(cgGetter(descriptor, m)) michael@0: if not m.readonly: michael@0: self.methodDecls.append(cgSetter(descriptor, m)) michael@0: michael@0: # Now do the special operations michael@0: def appendSpecialOperation(name, op): michael@0: if op is None: michael@0: return michael@0: if name == "IndexedCreator" or name == "NamedCreator": michael@0: # These are identical to the setters michael@0: return michael@0: assert len(op.signatures()) == 1 michael@0: returnType, args = op.signatures()[0] michael@0: # Make a copy of the args, since we plan to modify them. michael@0: args = list(args) michael@0: if op.isGetter() or op.isDeleter(): michael@0: # This is a total hack. The '&' belongs with the michael@0: # type, not the name! But it works, and is simpler michael@0: # than trying to somehow make this pretty. michael@0: args.append(FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean], michael@0: op, name="&found")) michael@0: if name == "Stringifier": michael@0: if op.isIdentifierLess(): michael@0: # XXXbz I wish we were consistent about our renaming here. michael@0: name = "Stringify" michael@0: else: michael@0: # We already added this method michael@0: return michael@0: if name == "LegacyCaller": michael@0: if op.isIdentifierLess(): michael@0: # XXXbz I wish we were consistent about our renaming here. michael@0: name = "LegacyCall" michael@0: else: michael@0: # We already added this method michael@0: return michael@0: if name == "Jsonifier": michael@0: # We already added this method michael@0: return michael@0: self.methodDecls.append( michael@0: CGNativeMember(descriptor, op, michael@0: name, michael@0: (returnType, args), michael@0: descriptor.getExtendedAttributes(op))) michael@0: # Sort things by name so we get stable ordering in the output. michael@0: ops = descriptor.operations.items() michael@0: ops.sort(key=lambda x: x[0]) michael@0: for name, op in ops: michael@0: appendSpecialOperation(name, op) michael@0: # If we support indexed properties, then we need a Length() michael@0: # method so we know which indices are supported. michael@0: if descriptor.supportsIndexedProperties(): michael@0: # But we don't need it if we already have an infallible michael@0: # "length" attribute, which we often do. michael@0: haveLengthAttr = any( michael@0: m for m in iface.members if m.isAttr() and michael@0: CGSpecializedGetter.makeNativeName(descriptor, m) == "Length") michael@0: if not haveLengthAttr: michael@0: self.methodDecls.append( michael@0: CGNativeMember(descriptor, FakeMember(), michael@0: "Length", michael@0: (BuiltinTypes[IDLBuiltinType.Types.unsigned_long], michael@0: []), michael@0: {"infallible": True})) michael@0: # And if we support named properties we need to be able to michael@0: # enumerate the supported names and test whether they're enumerable. michael@0: if descriptor.supportsNamedProperties(): michael@0: self.methodDecls.append( michael@0: CGNativeMember( michael@0: descriptor, FakeMember(), michael@0: "GetSupportedNames", michael@0: (IDLSequenceType(None, michael@0: BuiltinTypes[IDLBuiltinType.Types.domstring]), michael@0: # Let's use unsigned long for the type here, though really michael@0: # it's just a C++ "unsigned"... michael@0: [FakeArgument(BuiltinTypes[IDLBuiltinType.Types.unsigned_long], michael@0: FakeMember(), michael@0: name="aFlags")]), michael@0: {"infallible": True})) michael@0: self.methodDecls.append( michael@0: CGNativeMember( michael@0: descriptor, FakeMember(), michael@0: "NameIsEnumerable", michael@0: (BuiltinTypes[IDLBuiltinType.Types.boolean], michael@0: [FakeArgument(BuiltinTypes[IDLBuiltinType.Types.domstring], michael@0: FakeMember(), michael@0: name="aName")]), michael@0: { "infallible": True })) michael@0: michael@0: wrapArgs = [Argument('JSContext*', 'aCx')] michael@0: self.methodDecls.insert(0, michael@0: ClassMethod("WrapObject", "JSObject*", michael@0: wrapArgs, virtual=descriptor.wrapperCache, michael@0: breakAfterReturnDecl=" ", michael@0: override=descriptor.wrapperCache, michael@0: body=self.getWrapObjectBody())) michael@0: if wantGetParent: michael@0: self.methodDecls.insert(0, michael@0: ClassMethod("GetParentObject", michael@0: self.getGetParentObjectReturnType(), michael@0: [], const=True, michael@0: breakAfterReturnDecl=" ", michael@0: body=self.getGetParentObjectBody())) michael@0: michael@0: # Invoke CGClass.__init__ in any subclasses afterwards to do the actual codegen. michael@0: michael@0: def getWrapObjectBody(self): michael@0: return None michael@0: michael@0: def getGetParentObjectReturnType(self): michael@0: return ("// TODO: return something sensible here, and change the return type\n" michael@0: "%s*" % self.descriptor.nativeType.split('::')[-1]) michael@0: michael@0: def getGetParentObjectBody(self): michael@0: return None michael@0: michael@0: def deps(self): michael@0: return self._deps michael@0: michael@0: michael@0: class CGExampleClass(CGBindingImplClass): michael@0: """ michael@0: Codegen for the actual example class implementation for this descriptor michael@0: """ michael@0: def __init__(self, descriptor): michael@0: CGBindingImplClass.__init__(self, descriptor, michael@0: CGExampleMethod, CGExampleGetter, CGExampleSetter, michael@0: wantGetParent=descriptor.wrapperCache) michael@0: michael@0: self.refcounted = descriptor.nativeOwnership == "refcounted" michael@0: michael@0: self.parentIface = descriptor.interface.parent michael@0: if self.parentIface: michael@0: self.parentDesc = descriptor.getDescriptor( michael@0: self.parentIface.identifier.name) michael@0: bases = [ClassBase(self.nativeLeafName(self.parentDesc))] michael@0: else: michael@0: bases = [] michael@0: if self.refcounted: michael@0: bases.append(ClassBase("nsISupports /* Change nativeOwnership in the binding configuration if you don't want this */")) michael@0: if descriptor.wrapperCache: michael@0: bases.append(ClassBase("nsWrapperCache /* Change wrapperCache in the binding configuration if you don't want this */")) michael@0: else: michael@0: bases.append(ClassBase("NonRefcountedDOMObject")) michael@0: michael@0: if self.refcounted: michael@0: if self.parentIface: michael@0: extradeclarations = ( michael@0: "public:\n" michael@0: " NS_DECL_ISUPPORTS_INHERITED\n" michael@0: " NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(%s, %s)\n" michael@0: "\n" % (self.nativeLeafName(descriptor), michael@0: self.nativeLeafName(self.parentDesc))) michael@0: else: michael@0: extradeclarations = ( michael@0: "public:\n" michael@0: " NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n" michael@0: " NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(%s)\n" michael@0: "\n" % self.nativeLeafName(descriptor)) michael@0: else: michael@0: extradeclarations = "" michael@0: michael@0: if descriptor.interface.hasChildInterfaces(): michael@0: decorators = "" michael@0: else: michael@0: decorators = "MOZ_FINAL" michael@0: michael@0: CGClass.__init__(self, self.nativeLeafName(descriptor), michael@0: bases=bases, michael@0: constructors=[ClassConstructor([], michael@0: visibility="public")], michael@0: destructor=ClassDestructor(visibility="public"), michael@0: methods=self.methodDecls, michael@0: decorators=decorators, michael@0: extradeclarations=extradeclarations) michael@0: michael@0: def define(self): michael@0: # Just override CGClass and do our own thing michael@0: if self.descriptor.wrapperCache: michael@0: setDOMBinding = " SetIsDOMBinding();\n" michael@0: else: michael@0: setDOMBinding = "" michael@0: if self.refcounted: michael@0: ctordtor = dedent(""" michael@0: ${nativeType}::${nativeType}() michael@0: { michael@0: %s} michael@0: michael@0: ${nativeType}::~${nativeType}() michael@0: { michael@0: } michael@0: """) % setDOMBinding michael@0: else: michael@0: ctordtor = dedent(""" michael@0: ${nativeType}::${nativeType}() michael@0: { michael@0: MOZ_COUNT_CTOR(${nativeType}); michael@0: } michael@0: michael@0: ${nativeType}::~${nativeType}() michael@0: { michael@0: MOZ_COUNT_DTOR(${nativeType}); michael@0: } michael@0: """) michael@0: michael@0: if self.refcounted: michael@0: if self.parentIface: michael@0: ccImpl = dedent(""" michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_INHERITED_0(${nativeType}, ${parentType}) michael@0: NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType}) michael@0: NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType}) michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType}) michael@0: NS_INTERFACE_MAP_END_INHERITING(${parentType}) michael@0: michael@0: """) michael@0: else: michael@0: ccImpl = dedent(""" michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(${nativeType}) michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(${nativeType}) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(${nativeType}) michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType}) michael@0: NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY michael@0: NS_INTERFACE_MAP_ENTRY(nsISupports) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: """) michael@0: else: michael@0: ccImpl = "" michael@0: michael@0: classImpl = ccImpl + ctordtor + "\n" + dedent(""" michael@0: JSObject* michael@0: ${nativeType}::WrapObject(JSContext* aCx) michael@0: { michael@0: return ${ifaceName}Binding::Wrap(aCx, this); michael@0: } michael@0: michael@0: """) michael@0: return string.Template(classImpl).substitute( michael@0: ifaceName=self.descriptor.name, michael@0: nativeType=self.nativeLeafName(self.descriptor), michael@0: parentType=self.nativeLeafName(self.parentDesc) if self.parentIface else "") michael@0: michael@0: @staticmethod michael@0: def nativeLeafName(descriptor): michael@0: return descriptor.nativeType.split('::')[-1] michael@0: michael@0: michael@0: class CGExampleRoot(CGThing): michael@0: """ michael@0: Root codegen class for example implementation generation. Instantiate the michael@0: class and call declare or define to generate header or cpp code, michael@0: respectively. michael@0: """ michael@0: def __init__(self, config, interfaceName): michael@0: # Let's assume we're not doing workers stuff michael@0: descriptor = config.getDescriptor(interfaceName, False) michael@0: michael@0: self.root = CGWrapper(CGExampleClass(descriptor), michael@0: pre="\n", post="\n") michael@0: michael@0: self.root = CGNamespace.build(["mozilla", "dom"], self.root) michael@0: michael@0: self.root = CGList([CGClassForwardDeclare("JSContext", isStruct=True), michael@0: self.root], "\n") michael@0: michael@0: # Throw in our #includes michael@0: self.root = CGHeaders([], [], [], [], michael@0: ["nsWrapperCache.h", michael@0: "nsCycleCollectionParticipant.h", michael@0: "mozilla/Attributes.h", michael@0: "mozilla/ErrorResult.h"], michael@0: ["mozilla/dom/%s.h" % interfaceName, michael@0: ("mozilla/dom/%s" % michael@0: CGHeaders.getDeclarationFilename(descriptor.interface))], "", self.root) michael@0: michael@0: # And now some include guards michael@0: self.root = CGIncludeGuard(interfaceName, self.root) michael@0: michael@0: # And our license block comes before everything else michael@0: self.root = CGWrapper(self.root, pre=dedent(""" michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: """)) michael@0: michael@0: def declare(self): michael@0: return self.root.declare() michael@0: michael@0: def define(self): michael@0: return self.root.define() michael@0: michael@0: michael@0: def jsImplName(name): michael@0: return name + "JSImpl" michael@0: michael@0: michael@0: class CGJSImplMember(CGNativeMember): michael@0: """ michael@0: Base class for generating code for the members of the implementation class michael@0: for a JS-implemented WebIDL interface. michael@0: """ michael@0: def __init__(self, descriptorProvider, member, name, signature, michael@0: extendedAttrs, breakAfter=True, passJSBitsAsNeeded=True, michael@0: visibility="public", jsObjectsArePtr=False, michael@0: variadicIsSequence=False): michael@0: CGNativeMember.__init__(self, descriptorProvider, member, name, michael@0: signature, extendedAttrs, breakAfter=breakAfter, michael@0: passJSBitsAsNeeded=passJSBitsAsNeeded, michael@0: visibility=visibility, michael@0: jsObjectsArePtr=jsObjectsArePtr, michael@0: variadicIsSequence=variadicIsSequence) michael@0: self.body = self.getImpl() michael@0: michael@0: def getArgs(self, returnType, argList): michael@0: args = CGNativeMember.getArgs(self, returnType, argList) michael@0: args.append(Argument("JSCompartment*", "aCompartment", "nullptr")) michael@0: return args michael@0: michael@0: michael@0: class CGJSImplMethod(CGJSImplMember): michael@0: """ michael@0: Class for generating code for the methods for a JS-implemented WebIDL michael@0: interface. michael@0: """ michael@0: def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True): michael@0: self.signature = signature michael@0: self.descriptor = descriptor michael@0: self.isConstructor = isConstructor michael@0: CGJSImplMember.__init__(self, descriptor, method, michael@0: CGSpecializedMethod.makeNativeName(descriptor, michael@0: method), michael@0: signature, michael@0: descriptor.getExtendedAttributes(method), michael@0: breakAfter=breakAfter, michael@0: variadicIsSequence=True, michael@0: passJSBitsAsNeeded=False) michael@0: michael@0: def getArgs(self, returnType, argList): michael@0: if self.isConstructor: michael@0: # Skip the JSCompartment bits for constructors; it's handled michael@0: # manually in getImpl. michael@0: return CGNativeMember.getArgs(self, returnType, argList) michael@0: return CGJSImplMember.getArgs(self, returnType, argList) michael@0: michael@0: def getImpl(self): michael@0: args = self.getArgs(self.signature[0], self.signature[1]) michael@0: if not self.isConstructor: michael@0: return 'return mImpl->%s(%s);\n' % (self.name, ", ".join(arg.name for arg in args)) michael@0: michael@0: assert self.descriptor.interface.isJSImplemented() michael@0: if self.name != 'Constructor': michael@0: raise TypeError("Named constructors are not supported for JS implemented WebIDL. See bug 851287.") michael@0: if len(self.signature[1]) != 0: michael@0: # The first two arguments to the constructor implementation are not michael@0: # arguments to the WebIDL constructor, so don't pass them to __Init() michael@0: assert args[0].argType == 'const GlobalObject&' michael@0: assert args[1].argType == 'JSContext*' michael@0: constructorArgs = [arg.name for arg in args[2:]] michael@0: constructorArgs.append("js::GetObjectCompartment(scopeObj)") michael@0: initCall = fill( michael@0: """ michael@0: // Wrap the object before calling __Init so that __DOM_IMPL__ is available. michael@0: nsCOMPtr globalHolder = do_QueryInterface(window); michael@0: JS::Rooted scopeObj(cx, globalHolder->GetGlobalJSObject()); michael@0: MOZ_ASSERT(js::IsObjectInContextCompartment(scopeObj, cx)); michael@0: JS::Rooted wrappedVal(cx); michael@0: if (!WrapNewBindingObject(cx, impl, &wrappedVal)) { michael@0: //XXX Assertion disabled for now, see bug 991271. michael@0: MOZ_ASSERT(true || JS_IsExceptionPending(cx)); michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return nullptr; michael@0: } michael@0: // Initialize the object with the constructor arguments. michael@0: impl->mImpl->__Init(${args}); michael@0: if (aRv.Failed()) { michael@0: return nullptr; michael@0: } michael@0: """, michael@0: args=", ".join(constructorArgs)) michael@0: else: michael@0: initCall = "" michael@0: return genConstructorBody(self.descriptor, initCall) michael@0: michael@0: michael@0: def genConstructorBody(descriptor, initCall=""): michael@0: return fill( michael@0: """ michael@0: JS::Rooted jsImplObj(cx); michael@0: nsCOMPtr window = michael@0: ConstructJSImplementation(cx, "${contractId}", global, &jsImplObj, aRv); michael@0: if (aRv.Failed()) { michael@0: return nullptr; michael@0: } michael@0: // Build the C++ implementation. michael@0: nsRefPtr<${implClass}> impl = new ${implClass}(jsImplObj, window); michael@0: $*{initCall} michael@0: return impl.forget(); michael@0: """, michael@0: contractId=descriptor.interface.getJSImplementation(), michael@0: implClass=descriptor.name, michael@0: initCall=initCall) michael@0: michael@0: michael@0: # We're always fallible michael@0: def callbackGetterName(attr, descriptor): michael@0: return "Get" + MakeNativeName( michael@0: descriptor.binaryNames.get(attr.identifier.name, attr.identifier.name)) michael@0: michael@0: michael@0: def callbackSetterName(attr, descriptor): michael@0: return "Set" + MakeNativeName( michael@0: descriptor.binaryNames.get(attr.identifier.name, attr.identifier.name)) michael@0: michael@0: michael@0: class CGJSImplGetter(CGJSImplMember): michael@0: """ michael@0: Class for generating code for the getters of attributes for a JS-implemented michael@0: WebIDL interface. michael@0: """ michael@0: def __init__(self, descriptor, attr): michael@0: CGJSImplMember.__init__(self, descriptor, attr, michael@0: CGSpecializedGetter.makeNativeName(descriptor, michael@0: attr), michael@0: (attr.type, []), michael@0: descriptor.getExtendedAttributes(attr, michael@0: getter=True), michael@0: passJSBitsAsNeeded=False) michael@0: michael@0: def getImpl(self): michael@0: callbackArgs = [arg.name for arg in self.getArgs(self.member.type, [])] michael@0: return 'return mImpl->%s(%s);\n' % ( michael@0: callbackGetterName(self.member, self.descriptorProvider), michael@0: ", ".join(callbackArgs)) michael@0: michael@0: michael@0: class CGJSImplSetter(CGJSImplMember): michael@0: """ michael@0: Class for generating code for the setters of attributes for a JS-implemented michael@0: WebIDL interface. michael@0: """ michael@0: def __init__(self, descriptor, attr): michael@0: CGJSImplMember.__init__(self, descriptor, attr, michael@0: CGSpecializedSetter.makeNativeName(descriptor, michael@0: attr), michael@0: (BuiltinTypes[IDLBuiltinType.Types.void], michael@0: [FakeArgument(attr.type, attr)]), michael@0: descriptor.getExtendedAttributes(attr, michael@0: setter=True), michael@0: passJSBitsAsNeeded=False) michael@0: michael@0: def getImpl(self): michael@0: callbackArgs = [arg.name for arg in self.getArgs(BuiltinTypes[IDLBuiltinType.Types.void], michael@0: [FakeArgument(self.member.type, self.member)])] michael@0: return 'mImpl->%s(%s);\n' % ( michael@0: callbackSetterName(self.member, self.descriptorProvider), michael@0: ", ".join(callbackArgs)) michael@0: michael@0: michael@0: class CGJSImplClass(CGBindingImplClass): michael@0: def __init__(self, descriptor): michael@0: CGBindingImplClass.__init__(self, descriptor, CGJSImplMethod, CGJSImplGetter, CGJSImplSetter) michael@0: michael@0: if descriptor.interface.parent: michael@0: parentClass = descriptor.getDescriptor( michael@0: descriptor.interface.parent.identifier.name).jsImplParent michael@0: baseClasses = [ClassBase(parentClass)] michael@0: isupportsDecl = "NS_DECL_ISUPPORTS_INHERITED\n" michael@0: ccDecl = ("NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(%s, %s)\n" % michael@0: (descriptor.name, parentClass)) michael@0: constructorBody = dedent(""" michael@0: // Make sure we're an nsWrapperCache already michael@0: MOZ_ASSERT(static_cast(this)); michael@0: // And that our ancestor has called SetIsDOMBinding() michael@0: MOZ_ASSERT(IsDOMBinding()); michael@0: """) michael@0: extradefinitions = fill( michael@0: """ michael@0: NS_IMPL_CYCLE_COLLECTION_INHERITED(${ifaceName}, ${parentClass}, mImpl, mParent) michael@0: NS_IMPL_ADDREF_INHERITED(${ifaceName}, ${parentClass}) michael@0: NS_IMPL_RELEASE_INHERITED(${ifaceName}, ${parentClass}) michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(${ifaceName}) michael@0: NS_INTERFACE_MAP_END_INHERITING(${parentClass}) michael@0: """, michael@0: ifaceName=self.descriptor.name, michael@0: parentClass=parentClass) michael@0: else: michael@0: baseClasses = [ClassBase("nsSupportsWeakReference"), michael@0: ClassBase("nsWrapperCache")] michael@0: isupportsDecl = "NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n" michael@0: ccDecl = ("NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(%s)\n" % michael@0: descriptor.name) michael@0: constructorBody = "SetIsDOMBinding();\n" michael@0: extradefinitions = fill( michael@0: """ michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(${ifaceName}) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(${ifaceName}) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mImpl) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER michael@0: tmp->ClearWeakReferences(); michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(${ifaceName}) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImpl) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(${ifaceName}) michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(${ifaceName}) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(${ifaceName}) michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${ifaceName}) michael@0: NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY michael@0: NS_INTERFACE_MAP_ENTRY(nsISupports) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) michael@0: NS_INTERFACE_MAP_END michael@0: """, michael@0: ifaceName=self.descriptor.name) michael@0: michael@0: extradeclarations = fill( michael@0: """ michael@0: public: michael@0: $*{isupportsDecl} michael@0: $*{ccDecl} michael@0: michael@0: private: michael@0: nsRefPtr<${jsImplName}> mImpl; michael@0: nsCOMPtr mParent; michael@0: michael@0: """, michael@0: isupportsDecl=isupportsDecl, michael@0: ccDecl=ccDecl, michael@0: jsImplName=jsImplName(descriptor.name)) michael@0: michael@0: if descriptor.interface.hasChildInterfaces(): michael@0: decorators = "" michael@0: # We need a public virtual destructor our subclasses can use michael@0: destructor = ClassDestructor(virtual=True, visibility="public") michael@0: else: michael@0: decorators = "MOZ_FINAL" michael@0: destructor = None michael@0: michael@0: baseConstructors = [ michael@0: ("mImpl(new %s(aJSImplObject, /* aIncumbentGlobal = */ nullptr))" % michael@0: jsImplName(descriptor.name)), michael@0: "mParent(aParent)"] michael@0: parentInterface = descriptor.interface.parent michael@0: while parentInterface: michael@0: if parentInterface.isJSImplemented(): michael@0: baseConstructors.insert( michael@0: 0, "%s(aJSImplObject, aParent)" % parentClass) michael@0: break michael@0: parentInterface = parentInterface.parent michael@0: if not parentInterface and descriptor.interface.parent: michael@0: # We only have C++ ancestors, so only pass along the window michael@0: baseConstructors.insert(0, michael@0: "%s(aParent)" % parentClass) michael@0: michael@0: constructor = ClassConstructor( michael@0: [Argument("JS::Handle", "aJSImplObject"), michael@0: Argument("nsPIDOMWindow*", "aParent")], michael@0: visibility="public", michael@0: baseConstructors=baseConstructors, michael@0: body=constructorBody) michael@0: michael@0: self.methodDecls.append( michael@0: ClassMethod("_Create", michael@0: "bool", michael@0: [Argument("JSContext*", "cx"), michael@0: Argument("unsigned", "argc"), michael@0: Argument("JS::Value*", "vp")], michael@0: static=True, michael@0: body=self.getCreateFromExistingBody())) michael@0: michael@0: CGClass.__init__(self, descriptor.name, michael@0: bases=baseClasses, michael@0: constructors=[constructor], michael@0: destructor=destructor, michael@0: methods=self.methodDecls, michael@0: decorators=decorators, michael@0: extradeclarations=extradeclarations, michael@0: extradefinitions=extradefinitions) michael@0: michael@0: def getWrapObjectBody(self): michael@0: return fill( michael@0: """ michael@0: JS::Rooted obj(aCx, ${name}Binding::Wrap(aCx, this)); michael@0: if (!obj) { michael@0: return nullptr; michael@0: } michael@0: michael@0: // Now define it on our chrome object michael@0: JSAutoCompartment ac(aCx, mImpl->Callback()); michael@0: if (!JS_WrapObject(aCx, &obj)) { michael@0: return nullptr; michael@0: } michael@0: if (!JS_DefineProperty(aCx, mImpl->Callback(), "__DOM_IMPL__", obj, 0)) { michael@0: return nullptr; michael@0: } michael@0: return obj; michael@0: """, michael@0: name=self.descriptor.name) michael@0: michael@0: def getGetParentObjectReturnType(self): michael@0: return "nsISupports*" michael@0: michael@0: def getGetParentObjectBody(self): michael@0: return "return mParent;\n" michael@0: michael@0: def getCreateFromExistingBody(self): michael@0: # XXXbz we could try to get parts of this (e.g. the argument michael@0: # conversions) auto-generated by somehow creating an IDLMethod and michael@0: # adding it to our interface, but we'd still need to special-case the michael@0: # implementation slightly to have it not try to forward to the JS michael@0: # object... michael@0: return fill( michael@0: """ michael@0: JS::CallArgs args = JS::CallArgsFromVp(argc, vp); michael@0: if (args.length() < 2) { michael@0: return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "${ifaceName}._create"); michael@0: } michael@0: if (!args[0].isObject()) { michael@0: return ThrowErrorMessage(cx, MSG_NOT_OBJECT, "Argument 1 of ${ifaceName}._create"); michael@0: } michael@0: if (!args[1].isObject()) { michael@0: return ThrowErrorMessage(cx, MSG_NOT_OBJECT, "Argument 2 of ${ifaceName}._create"); michael@0: } michael@0: michael@0: // GlobalObject will go through wrappers as needed for us, and michael@0: // is simpler than the right UnwrapArg incantation. michael@0: GlobalObject global(cx, &args[0].toObject()); michael@0: if (global.Failed()) { michael@0: return false; michael@0: } michael@0: nsCOMPtr window = do_QueryInterface(global.GetAsSupports()); michael@0: if (!window) { michael@0: return ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "Argument 1 of ${ifaceName}._create", "Window"); michael@0: } michael@0: JS::Rooted arg(cx, &args[1].toObject()); michael@0: nsRefPtr<${implName}> impl = new ${implName}(arg, window); michael@0: MOZ_ASSERT(js::IsObjectInContextCompartment(arg, cx)); michael@0: return WrapNewBindingObject(cx, impl, args.rval()); michael@0: """, michael@0: ifaceName=self.descriptor.interface.identifier.name, michael@0: implName=self.descriptor.name) michael@0: michael@0: michael@0: def isJSImplementedDescriptor(descriptorProvider): michael@0: return (isinstance(descriptorProvider, Descriptor) and michael@0: descriptorProvider.interface.isJSImplemented()) michael@0: michael@0: michael@0: class CGCallback(CGClass): michael@0: def __init__(self, idlObject, descriptorProvider, baseName, methods, michael@0: getters=[], setters=[]): michael@0: self.baseName = baseName michael@0: self._deps = idlObject.getDeps() michael@0: self.idlObject = idlObject michael@0: name = idlObject.identifier.name michael@0: if isJSImplementedDescriptor(descriptorProvider): michael@0: name = jsImplName(name) michael@0: # For our public methods that needThisHandling we want most of the michael@0: # same args and the same return type as what CallbackMember michael@0: # generates. So we want to take advantage of all its michael@0: # CGNativeMember infrastructure, but that infrastructure can't deal michael@0: # with templates and most especially template arguments. So just michael@0: # cheat and have CallbackMember compute all those things for us. michael@0: realMethods = [] michael@0: for method in methods: michael@0: if not method.needThisHandling: michael@0: realMethods.append(method) michael@0: else: michael@0: realMethods.extend(self.getMethodImpls(method)) michael@0: realMethods.append( michael@0: ClassMethod("operator==", "bool", michael@0: [Argument("const %s&" % name, "aOther")], michael@0: inline=True, bodyInHeader=True, michael@0: const=True, michael@0: body=("return %s::operator==(aOther);\n" % baseName))) michael@0: CGClass.__init__(self, name, michael@0: bases=[ClassBase(baseName)], michael@0: constructors=self.getConstructors(), michael@0: methods=realMethods+getters+setters) michael@0: michael@0: def getConstructors(self): michael@0: if (not self.idlObject.isInterface() and michael@0: not self.idlObject._treatNonObjectAsNull): michael@0: body = "MOZ_ASSERT(JS_ObjectIsCallable(nullptr, mCallback));\n" michael@0: else: michael@0: # Not much we can assert about it, other than not being null, and michael@0: # CallbackObject does that already. michael@0: body = "" michael@0: return [ClassConstructor( michael@0: [Argument("JS::Handle", "aCallback"), michael@0: Argument("nsIGlobalObject*", "aIncumbentGlobal")], michael@0: bodyInHeader=True, michael@0: visibility="public", michael@0: explicit=True, michael@0: baseConstructors=[ michael@0: "%s(aCallback, aIncumbentGlobal)" % self.baseName, michael@0: ], michael@0: body=body)] michael@0: michael@0: def getMethodImpls(self, method): michael@0: assert method.needThisHandling michael@0: args = list(method.args) michael@0: # Strip out the JSContext*/JSObject* args michael@0: # that got added. michael@0: assert args[0].name == "cx" and args[0].argType == "JSContext*" michael@0: assert args[1].name == "aThisVal" and args[1].argType == "JS::Handle" michael@0: args = args[2:] michael@0: # Record the names of all the arguments, so we can use them when we call michael@0: # the private method. michael@0: argnames = [arg.name for arg in args] michael@0: argnamesWithThis = ["s.GetContext()", "thisValJS"] + argnames michael@0: argnamesWithoutThis = ["s.GetContext()", "JS::UndefinedHandleValue"] + argnames michael@0: # Now that we've recorded the argnames for our call to our private michael@0: # method, insert our optional argument for deciding whether the michael@0: # CallSetup should re-throw exceptions on aRv. michael@0: args.append(Argument("ExceptionHandling", "aExceptionHandling", michael@0: "eReportExceptions")) michael@0: # And now insert our template argument. michael@0: argsWithoutThis = list(args) michael@0: args.insert(0, Argument("const T&", "thisObjPtr")) michael@0: errorReturn = method.getDefaultRetval() michael@0: michael@0: setupCall = fill( michael@0: """ michael@0: CallSetup s(this, aRv, aExceptionHandling); michael@0: if (!s.GetContext()) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return${errorReturn}; michael@0: } michael@0: """, michael@0: errorReturn=errorReturn) michael@0: michael@0: bodyWithThis = fill( michael@0: """ michael@0: $*{setupCall} michael@0: JS::Rooted thisObjJS(s.GetContext(), michael@0: WrapCallThisObject(s.GetContext(), thisObjPtr)); michael@0: if (!thisObjJS) { michael@0: aRv.Throw(NS_ERROR_FAILURE); michael@0: return${errorReturn}; michael@0: } michael@0: JS::Rooted thisValJS(s.GetContext(), michael@0: JS::ObjectValue(*thisObjJS)); michael@0: return ${methodName}(${callArgs}); michael@0: """, michael@0: setupCall=setupCall, michael@0: errorReturn=errorReturn, michael@0: methodName=method.name, michael@0: callArgs=", ".join(argnamesWithThis)) michael@0: bodyWithoutThis = fill( michael@0: """ michael@0: $*{setupCall} michael@0: return ${methodName}(${callArgs}); michael@0: """, michael@0: setupCall=setupCall, michael@0: errorReturn=errorReturn, michael@0: methodName=method.name, michael@0: callArgs=", ".join(argnamesWithoutThis)) michael@0: michael@0: return [ClassMethod(method.name, method.returnType, args, michael@0: bodyInHeader=True, michael@0: templateArgs=["typename T"], michael@0: body=bodyWithThis), michael@0: ClassMethod(method.name, method.returnType, argsWithoutThis, michael@0: bodyInHeader=True, michael@0: body=bodyWithoutThis), michael@0: method] michael@0: michael@0: def deps(self): michael@0: return self._deps michael@0: michael@0: michael@0: class CGCallbackFunction(CGCallback): michael@0: def __init__(self, callback, descriptorProvider): michael@0: self.callback = callback michael@0: CGCallback.__init__(self, callback, descriptorProvider, michael@0: "CallbackFunction", michael@0: methods=[CallCallback(callback, descriptorProvider)]) michael@0: michael@0: def getConstructors(self): michael@0: return CGCallback.getConstructors(self) + [ michael@0: ClassConstructor( michael@0: [Argument("CallbackFunction*", "aOther")], michael@0: bodyInHeader=True, michael@0: visibility="public", michael@0: explicit=True, michael@0: baseConstructors=["CallbackFunction(aOther)"])] michael@0: michael@0: michael@0: class CGCallbackInterface(CGCallback): michael@0: def __init__(self, descriptor): michael@0: iface = descriptor.interface michael@0: attrs = [m for m in iface.members if m.isAttr() and not m.isStatic()] michael@0: getters = [CallbackGetter(a, descriptor) for a in attrs] michael@0: setters = [CallbackSetter(a, descriptor) for a in attrs michael@0: if not a.readonly] michael@0: methods = [m for m in iface.members michael@0: if m.isMethod() and not m.isStatic() and not m.isIdentifierLess()] michael@0: methods = [CallbackOperation(m, sig, descriptor) for m in methods michael@0: for sig in m.signatures()] michael@0: if iface.isJSImplemented() and iface.ctor(): michael@0: sigs = descriptor.interface.ctor().signatures() michael@0: if len(sigs) != 1: michael@0: raise TypeError("We only handle one constructor. See bug 869268.") michael@0: methods.append(CGJSImplInitOperation(sigs[0], descriptor)) michael@0: CGCallback.__init__(self, iface, descriptor, "CallbackInterface", michael@0: methods, getters=getters, setters=setters) michael@0: michael@0: michael@0: class FakeMember(): michael@0: def __init__(self): michael@0: self.treatNullAs = "Default" michael@0: michael@0: def isStatic(self): michael@0: return False michael@0: michael@0: def isAttr(self): michael@0: return False michael@0: michael@0: def isMethod(self): michael@0: return False michael@0: michael@0: def getExtendedAttribute(self, name): michael@0: # Claim to be a [NewObject] so we can avoid the "mark this michael@0: # resultNotAddRefed" comments CGNativeMember codegen would michael@0: # otherwise stick in. michael@0: if name == "NewObject": michael@0: return True michael@0: return None michael@0: michael@0: michael@0: class CallbackMember(CGNativeMember): michael@0: def __init__(self, sig, name, descriptorProvider, needThisHandling, rethrowContentException=False): michael@0: """ michael@0: needThisHandling is True if we need to be able to accept a specified michael@0: thisObj, False otherwise. michael@0: """ michael@0: assert not rethrowContentException or not needThisHandling michael@0: michael@0: self.retvalType = sig[0] michael@0: self.originalSig = sig michael@0: args = sig[1] michael@0: self.argCount = len(args) michael@0: if self.argCount > 0: michael@0: # Check for variadic arguments michael@0: lastArg = args[self.argCount-1] michael@0: if lastArg.variadic: michael@0: self.argCountStr = ("(%d - 1) + %s.Length()" % michael@0: (self.argCount, lastArg.identifier.name)) michael@0: else: michael@0: self.argCountStr = "%d" % self.argCount michael@0: self.needThisHandling = needThisHandling michael@0: # If needThisHandling, we generate ourselves as private and the caller michael@0: # will handle generating public versions that handle the "this" stuff. michael@0: visibility = "private" if needThisHandling else "public" michael@0: self.rethrowContentException = rethrowContentException michael@0: # We don't care, for callback codegen, whether our original member was michael@0: # a method or attribute or whatnot. Just always pass FakeMember() michael@0: # here. michael@0: CGNativeMember.__init__(self, descriptorProvider, FakeMember(), michael@0: name, (self.retvalType, args), michael@0: extendedAttrs={}, michael@0: passJSBitsAsNeeded=False, michael@0: visibility=visibility, michael@0: jsObjectsArePtr=True) michael@0: # We have to do all the generation of our body now, because michael@0: # the caller relies on us throwing if we can't manage it. michael@0: self.exceptionCode = ("aRv.Throw(NS_ERROR_UNEXPECTED);\n" michael@0: "return%s;\n" % self.getDefaultRetval()) michael@0: self.body = self.getImpl() michael@0: michael@0: def getImpl(self): michael@0: setupCall = self.getCallSetup() michael@0: declRval = self.getRvalDecl() michael@0: if self.argCount > 0: michael@0: argvDecl = fill( michael@0: """ michael@0: JS::AutoValueVector argv(cx); michael@0: if (!argv.resize(${argCount})) { michael@0: aRv.Throw(NS_ERROR_OUT_OF_MEMORY); michael@0: return${errorReturn}; michael@0: } michael@0: """, michael@0: argCount=self.argCountStr, michael@0: errorReturn=self.getDefaultRetval()) michael@0: else: michael@0: # Avoid weird 0-sized arrays michael@0: argvDecl = "" michael@0: convertArgs = self.getArgConversions() michael@0: doCall = self.getCall() michael@0: returnResult = self.getResultConversion() michael@0: michael@0: return setupCall + declRval + argvDecl + convertArgs + doCall + returnResult michael@0: michael@0: def getResultConversion(self): michael@0: replacements = { michael@0: "val": "rval", michael@0: "mutableVal": "&rval", michael@0: "holderName": "rvalHolder", michael@0: "declName": "rvalDecl", michael@0: # We actually want to pass in a null scope object here, because michael@0: # wrapping things into our current compartment (that of mCallback) michael@0: # is what we want. michael@0: "obj": "nullptr" michael@0: } michael@0: michael@0: if isJSImplementedDescriptor(self.descriptorProvider): michael@0: isCallbackReturnValue = "JSImpl" michael@0: else: michael@0: isCallbackReturnValue = "Callback" michael@0: sourceDescription = "return value of %s" % self.getPrettyName() michael@0: convertType = instantiateJSToNativeConversion( michael@0: getJSToNativeConversionInfo(self.retvalType, michael@0: self.descriptorProvider, michael@0: exceptionCode=self.exceptionCode, michael@0: isCallbackReturnValue=isCallbackReturnValue, michael@0: sourceDescription=sourceDescription), michael@0: replacements) michael@0: assignRetval = string.Template( michael@0: self.getRetvalInfo(self.retvalType, michael@0: False)[2]).substitute(replacements) michael@0: type = convertType.define() michael@0: if type == "": michael@0: type = "\n" # BOGUS extra blank line michael@0: if assignRetval == "": michael@0: assignRetval = "\n" # BOGUS extra blank line michael@0: return type + assignRetval michael@0: michael@0: def getArgConversions(self): michael@0: # Just reget the arglist from self.originalSig, because our superclasses michael@0: # just have way to many members they like to clobber, so I can't find a michael@0: # safe member name to store it in. michael@0: argConversions = [self.getArgConversion(i, arg) michael@0: for i, arg in enumerate(self.originalSig[1])] michael@0: if not argConversions: michael@0: return "\n\n" # BOGUS extra blank line michael@0: michael@0: # Do them back to front, so our argc modifications will work michael@0: # correctly, because we examine trailing arguments first. michael@0: argConversions.reverse() michael@0: # Wrap each one in a scope so that any locals it has don't leak out, and michael@0: # also so that we can just "break;" for our successCode. michael@0: argConversions = [CGWrapper(CGIndenter(CGGeneric(c)), michael@0: pre="do {\n", michael@0: post="} while (0);\n") michael@0: for c in argConversions] michael@0: if self.argCount > 0: michael@0: argConversions.insert(0, self.getArgcDecl()) michael@0: # And slap them together. michael@0: return CGList(argConversions, "\n").define() + "\n" michael@0: michael@0: def getArgConversion(self, i, arg): michael@0: argval = arg.identifier.name michael@0: michael@0: if arg.variadic: michael@0: argval = argval + "[idx]" michael@0: jsvalIndex = "%d + idx" % i michael@0: else: michael@0: jsvalIndex = "%d" % i michael@0: if arg.optional and not arg.defaultValue: michael@0: argval += ".Value()" michael@0: if arg.type.isDOMString(): michael@0: # XPConnect string-to-JS conversion wants to mutate the string. So michael@0: # let's give it a string it can mutate michael@0: # XXXbz if we try to do a sequence of strings, this will kinda fail. michael@0: result = "mutableStr" michael@0: prepend = "nsString mutableStr(%s);\n" % argval michael@0: else: michael@0: result = argval michael@0: prepend = "" michael@0: michael@0: try: michael@0: conversion = prepend + wrapForType( michael@0: arg.type, self.descriptorProvider, michael@0: { michael@0: 'result': result, michael@0: 'successCode': "continue;\n" if arg.variadic else "break;\n", michael@0: 'jsvalRef': "argv.handleAt(%s)" % jsvalIndex, michael@0: 'jsvalHandle': "argv.handleAt(%s)" % jsvalIndex, michael@0: # XXXbz we don't have anything better to use for 'obj', michael@0: # really... It's OK to use CallbackPreserveColor because michael@0: # CallSetup already handled the unmark-gray bits for us. michael@0: 'obj': 'CallbackPreserveColor()', michael@0: 'returnsNewObject': False, michael@0: 'exceptionCode': self.exceptionCode michael@0: }) michael@0: except MethodNotNewObjectError as err: michael@0: raise TypeError("%s being passed as an argument to %s but is not " michael@0: "wrapper cached, so can't be reliably converted to " michael@0: "a JS object." % michael@0: (err.typename, self.getPrettyName())) michael@0: if arg.variadic: michael@0: conversion = fill( michael@0: """ michael@0: for (uint32_t idx = 0; idx < ${arg}.Length(); ++idx) { michael@0: $*{conversion} michael@0: } michael@0: break; michael@0: """, michael@0: arg=arg.identifier.name, michael@0: conversion=conversion) michael@0: elif arg.optional and not arg.defaultValue: michael@0: conversion = fill( michael@0: """ michael@0: if (${argName}.WasPassed()) { michael@0: $*{conversion} michael@0: } else if (argc == ${iPlus1}) { michael@0: // This is our current trailing argument; reduce argc michael@0: --argc; michael@0: } else { michael@0: argv[${i}] = JS::UndefinedValue(); michael@0: } michael@0: """, michael@0: argName=arg.identifier.name, michael@0: conversion=conversion, michael@0: iPlus1=i + 1, michael@0: i=i) michael@0: return conversion michael@0: michael@0: def getDefaultRetval(self): michael@0: default = self.getRetvalInfo(self.retvalType, False)[1] michael@0: if len(default) != 0: michael@0: default = " " + default michael@0: return default michael@0: michael@0: def getArgs(self, returnType, argList): michael@0: args = CGNativeMember.getArgs(self, returnType, argList) michael@0: if not self.needThisHandling: michael@0: # Since we don't need this handling, we're the actual method that michael@0: # will be called, so we need an aRethrowExceptions argument. michael@0: if self.rethrowContentException: michael@0: args.append(Argument("JSCompartment*", "aCompartment", "nullptr")) michael@0: else: michael@0: args.append(Argument("ExceptionHandling", "aExceptionHandling", michael@0: "eReportExceptions")) michael@0: return args michael@0: # We want to allow the caller to pass in a "this" value, as michael@0: # well as a JSContext. michael@0: return [Argument("JSContext*", "cx"), michael@0: Argument("JS::Handle", "aThisVal")] + args michael@0: michael@0: def getCallSetup(self): michael@0: if self.needThisHandling: michael@0: # It's been done for us already michael@0: return "" michael@0: callSetup = "CallSetup s(this, aRv" michael@0: if self.rethrowContentException: michael@0: # getArgs doesn't add the aExceptionHandling argument but does add michael@0: # aCompartment for us. michael@0: callSetup += ", eRethrowContentExceptions, aCompartment, /* aIsJSImplementedWebIDL = */ " michael@0: callSetup += toStringBool(isJSImplementedDescriptor(self.descriptorProvider)) michael@0: else: michael@0: callSetup += ", aExceptionHandling" michael@0: callSetup += ");\n" michael@0: return fill( michael@0: """ michael@0: $*{callSetup} michael@0: JSContext* cx = s.GetContext(); michael@0: if (!cx) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return${errorReturn}; michael@0: } michael@0: """, michael@0: callSetup=callSetup, michael@0: errorReturn=self.getDefaultRetval()) michael@0: michael@0: def getArgcDecl(self): michael@0: return CGGeneric("unsigned argc = %s;\n" % self.argCountStr) michael@0: michael@0: @staticmethod michael@0: def ensureASCIIName(idlObject): michael@0: type = "attribute" if idlObject.isAttr() else "operation" michael@0: if re.match("[^\x20-\x7E]", idlObject.identifier.name): michael@0: raise SyntaxError('Callback %s name "%s" contains non-ASCII ' michael@0: "characters. We can't handle that. %s" % michael@0: (type, idlObject.identifier.name, michael@0: idlObject.location)) michael@0: if re.match('"', idlObject.identifier.name): michael@0: raise SyntaxError("Callback %s name '%s' contains " michael@0: "double-quote character. We can't handle " michael@0: "that. %s" % michael@0: (type, idlObject.identifier.name, michael@0: idlObject.location)) michael@0: michael@0: michael@0: class CallbackMethod(CallbackMember): michael@0: def __init__(self, sig, name, descriptorProvider, needThisHandling, michael@0: rethrowContentException=False): michael@0: CallbackMember.__init__(self, sig, name, descriptorProvider, michael@0: needThisHandling, rethrowContentException) michael@0: michael@0: def getRvalDecl(self): michael@0: return "JS::Rooted rval(cx, JS::UndefinedValue());\n" michael@0: michael@0: def getCall(self): michael@0: if self.argCount > 0: michael@0: args = "JS::HandleValueArray::subarray(argv, 0, argc)" michael@0: else: michael@0: args = "JS::HandleValueArray::empty()" michael@0: michael@0: return fill( michael@0: """ michael@0: $*{declCallable} michael@0: $*{declThis} michael@0: if (${callGuard}!JS::Call(cx, ${thisVal}, callable, michael@0: ${args}, &rval)) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return${errorReturn}; michael@0: } michael@0: """, michael@0: declCallable=self.getCallableDecl(), michael@0: declThis=self.getThisDecl(), michael@0: callGuard=self.getCallGuard(), michael@0: thisVal=self.getThisVal(), michael@0: args=args, michael@0: errorReturn=self.getDefaultRetval()) michael@0: michael@0: michael@0: class CallCallback(CallbackMethod): michael@0: def __init__(self, callback, descriptorProvider): michael@0: self.callback = callback michael@0: CallbackMethod.__init__(self, callback.signatures()[0], "Call", michael@0: descriptorProvider, needThisHandling=True) michael@0: michael@0: def getThisDecl(self): michael@0: return "" michael@0: michael@0: def getThisVal(self): michael@0: return "aThisVal" michael@0: michael@0: def getCallableDecl(self): michael@0: return "JS::Rooted callable(cx, JS::ObjectValue(*mCallback));\n" michael@0: michael@0: def getPrettyName(self): michael@0: return self.callback.identifier.name michael@0: michael@0: def getCallGuard(self): michael@0: if self.callback._treatNonObjectAsNull: michael@0: return "JS_ObjectIsCallable(cx, mCallback) && " michael@0: return "" michael@0: michael@0: michael@0: class CallbackOperationBase(CallbackMethod): michael@0: """ michael@0: Common class for implementing various callback operations. michael@0: """ michael@0: def __init__(self, signature, jsName, nativeName, descriptor, singleOperation, rethrowContentException=False): michael@0: self.singleOperation = singleOperation michael@0: self.methodName = descriptor.binaryNames.get(jsName, jsName) michael@0: CallbackMethod.__init__(self, signature, nativeName, descriptor, singleOperation, rethrowContentException) michael@0: michael@0: def getThisDecl(self): michael@0: if not self.singleOperation: michael@0: return "JS::Rooted thisValue(cx, JS::ObjectValue(*mCallback));\n" michael@0: # This relies on getCallableDecl declaring a boolean michael@0: # isCallable in the case when we're a single-operation michael@0: # interface. michael@0: return dedent(""" michael@0: JS::Rooted thisValue(cx, isCallable ? aThisVal.get() michael@0: : JS::ObjectValue(*mCallback)); michael@0: """) michael@0: michael@0: def getThisVal(self): michael@0: return "thisValue" michael@0: michael@0: def getCallableDecl(self): michael@0: getCallableFromProp = fill( michael@0: """ michael@0: if (!GetCallableProperty(cx, "${methodName}", &callable)) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return${errorReturn}; michael@0: } michael@0: """, michael@0: methodName=self.methodName, michael@0: errorReturn=self.getDefaultRetval()) michael@0: if not self.singleOperation: michael@0: return 'JS::Rooted callable(cx);\n' + getCallableFromProp michael@0: return fill( michael@0: """ michael@0: bool isCallable = JS_ObjectIsCallable(cx, mCallback); michael@0: JS::Rooted callable(cx); michael@0: if (isCallable) { michael@0: callable = JS::ObjectValue(*mCallback); michael@0: } else { michael@0: $*{getCallableFromProp} michael@0: } michael@0: """, michael@0: getCallableFromProp=getCallableFromProp) michael@0: michael@0: def getCallGuard(self): michael@0: return "" michael@0: michael@0: michael@0: class CallbackOperation(CallbackOperationBase): michael@0: """ michael@0: Codegen actual WebIDL operations on callback interfaces. michael@0: """ michael@0: def __init__(self, method, signature, descriptor): michael@0: self.ensureASCIIName(method) michael@0: self.method = method michael@0: jsName = method.identifier.name michael@0: CallbackOperationBase.__init__(self, signature, michael@0: jsName, michael@0: MakeNativeName(descriptor.binaryNames.get(jsName, jsName)), michael@0: descriptor, descriptor.interface.isSingleOperationInterface(), michael@0: rethrowContentException=descriptor.interface.isJSImplemented()) michael@0: michael@0: def getPrettyName(self): michael@0: return "%s.%s" % (self.descriptorProvider.interface.identifier.name, michael@0: self.method.identifier.name) michael@0: michael@0: michael@0: class CallbackAccessor(CallbackMember): michael@0: """ michael@0: Shared superclass for CallbackGetter and CallbackSetter. michael@0: """ michael@0: def __init__(self, attr, sig, name, descriptor): michael@0: self.ensureASCIIName(attr) michael@0: self.attrName = attr.identifier.name michael@0: CallbackMember.__init__(self, sig, name, descriptor, michael@0: needThisHandling=False, michael@0: rethrowContentException=descriptor.interface.isJSImplemented()) michael@0: michael@0: def getPrettyName(self): michael@0: return "%s.%s" % (self.descriptorProvider.interface.identifier.name, michael@0: self.attrName) michael@0: michael@0: michael@0: class CallbackGetter(CallbackAccessor): michael@0: def __init__(self, attr, descriptor): michael@0: CallbackAccessor.__init__(self, attr, michael@0: (attr.type, []), michael@0: callbackGetterName(attr, descriptor), michael@0: descriptor) michael@0: michael@0: def getRvalDecl(self): michael@0: return "JS::Rooted rval(cx, JS::UndefinedValue());\n" michael@0: michael@0: def getCall(self): michael@0: return fill( michael@0: """ michael@0: JS::Rooted callback(cx, mCallback); michael@0: if (!JS_GetProperty(cx, callback, "${attrName}", &rval)) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return${errorReturn}; michael@0: } michael@0: """, michael@0: attrName=self.descriptorProvider.binaryNames.get(self.attrName, michael@0: self.attrName), michael@0: errorReturn=self.getDefaultRetval()) michael@0: michael@0: michael@0: class CallbackSetter(CallbackAccessor): michael@0: def __init__(self, attr, descriptor): michael@0: CallbackAccessor.__init__(self, attr, michael@0: (BuiltinTypes[IDLBuiltinType.Types.void], michael@0: [FakeArgument(attr.type, attr)]), michael@0: callbackSetterName(attr, descriptor), michael@0: descriptor) michael@0: michael@0: def getRvalDecl(self): michael@0: # We don't need an rval michael@0: return "" michael@0: michael@0: def getCall(self): michael@0: return fill( michael@0: """ michael@0: MOZ_ASSERT(argv.length() == 1); michael@0: if (!JS_SetProperty(cx, CallbackPreserveColor(), "${attrName}", argv.handleAt(0))) { michael@0: aRv.Throw(NS_ERROR_UNEXPECTED); michael@0: return${errorReturn}; michael@0: } michael@0: """, michael@0: attrName=self.descriptorProvider.binaryNames.get(self.attrName, michael@0: self.attrName), michael@0: errorReturn=self.getDefaultRetval()) michael@0: michael@0: def getArgcDecl(self): michael@0: return None michael@0: michael@0: michael@0: class CGJSImplInitOperation(CallbackOperationBase): michael@0: """ michael@0: Codegen the __Init() method used to pass along constructor arguments for JS-implemented WebIDL. michael@0: """ michael@0: def __init__(self, sig, descriptor): michael@0: assert sig in descriptor.interface.ctor().signatures() michael@0: CallbackOperationBase.__init__(self, (BuiltinTypes[IDLBuiltinType.Types.void], sig[1]), michael@0: "__init", "__Init", descriptor, False, True) michael@0: michael@0: def getPrettyName(self): michael@0: return "__init" michael@0: michael@0: michael@0: class GlobalGenRoots(): michael@0: """ michael@0: Roots for global codegen. michael@0: michael@0: To generate code, call the method associated with the target, and then michael@0: call the appropriate define/declare method. michael@0: """ michael@0: michael@0: @staticmethod michael@0: def GeneratedAtomList(config): michael@0: # Atom enum michael@0: dictionaries = config.dictionaries michael@0: michael@0: structs = [] michael@0: michael@0: for dict in dictionaries: michael@0: dictMembers = dict.members michael@0: if len(dictMembers) == 0: michael@0: continue michael@0: michael@0: classMembers = [ClassMember(CGDictionary.makeIdName(m.identifier.name), michael@0: "InternedStringId", michael@0: visibility="public") for m in dictMembers] michael@0: michael@0: structName = dict.identifier.name + "Atoms" michael@0: structs.append((structName, michael@0: CGWrapper(CGClass(structName, michael@0: bases=None, michael@0: isStruct=True, michael@0: members=classMembers), post='\n'))) michael@0: michael@0: structs.sort() michael@0: generatedStructs = [struct for structName, struct in structs] michael@0: structNames = [structName for structName, struct in structs] michael@0: michael@0: mainStruct = CGWrapper(CGClass("PerThreadAtomCache", michael@0: bases=[ClassBase(structName) for structName in structNames], michael@0: isStruct=True), michael@0: post='\n') michael@0: michael@0: structs = CGList(generatedStructs + [mainStruct]) michael@0: michael@0: # Wrap all of that in our namespaces. michael@0: curr = CGNamespace.build(['mozilla', 'dom'], michael@0: CGWrapper(structs, pre='\n')) michael@0: curr = CGWrapper(curr, post='\n') michael@0: michael@0: # Add include statement for InternedStringId. michael@0: declareIncludes = ['mozilla/dom/BindingUtils.h'] michael@0: curr = CGHeaders([], [], [], [], declareIncludes, [], 'GeneratedAtomList', michael@0: curr) michael@0: michael@0: # Add include guards. michael@0: curr = CGIncludeGuard('GeneratedAtomList', curr) michael@0: michael@0: # Add the auto-generated comment. michael@0: curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) michael@0: michael@0: # Done. michael@0: return curr michael@0: michael@0: @staticmethod michael@0: def PrototypeList(config): michael@0: michael@0: # Prototype ID enum. michael@0: descriptorsWithPrototype = config.getDescriptors(hasInterfacePrototypeObject=True) michael@0: protos = [d.name for d in descriptorsWithPrototype] michael@0: idEnum = CGNamespacedEnum('id', 'ID', ['_ID_Start'] + protos, michael@0: [0, '_ID_Start']) michael@0: idEnum = CGList([idEnum]) michael@0: michael@0: # This is only used by DOM worker code, once there are no more consumers michael@0: # of INTERFACE_CHAIN_* this code should be removed. michael@0: def ifaceChainMacro(ifaceCount): michael@0: supplied = [CGGeneric(declare="_iface_" + str(i + 1)) for i in range(ifaceCount)] michael@0: remaining = [CGGeneric(declare="prototypes::id::_ID_Count")] * (config.maxProtoChainLength - ifaceCount) michael@0: macro = CGWrapper(CGList(supplied, ", "), michael@0: pre="#define INTERFACE_CHAIN_" + str(ifaceCount) + "(", michael@0: post=") \\\n", michael@0: declareOnly=True) michael@0: macroContent = CGIndenter(CGList(supplied + remaining, ", \\\n")) michael@0: macroContent = CGIndenter(CGWrapper(macroContent, pre="{ \\\n", michael@0: post=" \\\n}", michael@0: declareOnly=True)) michael@0: return CGWrapper(CGList([macro, macroContent]), post="\n\n", michael@0: declareOnly=True) michael@0: michael@0: idEnum.append(ifaceChainMacro(1)) michael@0: michael@0: def fieldSizeAssert(amount, jitInfoField, message): michael@0: maxFieldValue = "(uint64_t(1) << (sizeof(((JSJitInfo*)nullptr)->%s) * 8))" % jitInfoField michael@0: return CGGeneric(declare="static_assert(%s < %s, \"%s\");\n\n" michael@0: % (amount, maxFieldValue, message)) michael@0: michael@0: idEnum.append(fieldSizeAssert("id::_ID_Count", "protoID", michael@0: "Too many prototypes!")) michael@0: michael@0: # Wrap all of that in our namespaces. michael@0: idEnum = CGNamespace.build(['mozilla', 'dom', 'prototypes'], michael@0: CGWrapper(idEnum, pre='\n')) michael@0: idEnum = CGWrapper(idEnum, post='\n') michael@0: michael@0: curr = CGList([CGGeneric(define="#include \n\n"), michael@0: idEnum]) michael@0: michael@0: # Let things know the maximum length of the prototype chain. michael@0: maxMacroName = "MAX_PROTOTYPE_CHAIN_LENGTH" michael@0: maxMacro = CGGeneric(declare="#define " + maxMacroName + " " + str(config.maxProtoChainLength)) michael@0: curr.append(CGWrapper(maxMacro, post='\n\n')) michael@0: curr.append(fieldSizeAssert(maxMacroName, "depth", michael@0: "Some inheritance chain is too long!")) michael@0: michael@0: # Constructor ID enum. michael@0: constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True)] michael@0: idEnum = CGNamespacedEnum('id', 'ID', ['_ID_Start'] + constructors, michael@0: ['prototypes::id::_ID_Count', '_ID_Start']) michael@0: michael@0: # Wrap all of that in our namespaces. michael@0: idEnum = CGNamespace.build(['mozilla', 'dom', 'constructors'], michael@0: CGWrapper(idEnum, pre='\n')) michael@0: idEnum = CGWrapper(idEnum, post='\n') michael@0: michael@0: curr.append(idEnum) michael@0: michael@0: traitsDecls = [CGGeneric(declare=dedent(""" michael@0: template michael@0: struct PrototypeTraits; michael@0: """))] michael@0: traitsDecls.extend(CGPrototypeTraitsClass(d) for d in descriptorsWithPrototype) michael@0: michael@0: ifaceNamesWithProto = [d.interface.identifier.name michael@0: for d in descriptorsWithPrototype] michael@0: traitsDecls.append(CGStringTable("NamesOfInterfacesWithProtos", michael@0: ifaceNamesWithProto)) michael@0: michael@0: traitsDecl = CGNamespace.build(['mozilla', 'dom'], michael@0: CGList(traitsDecls)) michael@0: michael@0: curr.append(traitsDecl) michael@0: michael@0: # Add include guards. michael@0: curr = CGIncludeGuard('PrototypeList', curr) michael@0: michael@0: # Add the auto-generated comment. michael@0: curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) michael@0: michael@0: # Done. michael@0: return curr michael@0: michael@0: @staticmethod michael@0: def RegisterBindings(config): michael@0: michael@0: # TODO - Generate the methods we want michael@0: curr = CGRegisterProtos(config) michael@0: michael@0: # Wrap all of that in our namespaces. michael@0: curr = CGNamespace.build(['mozilla', 'dom'], michael@0: CGWrapper(curr, post='\n')) michael@0: curr = CGWrapper(curr, post='\n') michael@0: michael@0: # Add the includes michael@0: defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface) michael@0: for desc in config.getDescriptors(hasInterfaceObject=True, michael@0: workers=False, michael@0: register=True)] michael@0: defineIncludes.append('nsScriptNameSpaceManager.h') michael@0: defineIncludes.extend([CGHeaders.getDeclarationFilename(desc.interface) michael@0: for desc in config.getDescriptors(isNavigatorProperty=True, michael@0: workers=False, michael@0: register=True)]) michael@0: curr = CGHeaders([], [], [], [], [], defineIncludes, 'RegisterBindings', michael@0: curr) michael@0: michael@0: # Add include guards. michael@0: curr = CGIncludeGuard('RegisterBindings', curr) michael@0: michael@0: # Done. michael@0: return curr michael@0: michael@0: @staticmethod michael@0: def UnionTypes(config): michael@0: michael@0: (includes, implincludes, michael@0: declarations, unions) = UnionTypes(config.getDescriptors(), michael@0: config.getDictionaries(), michael@0: config.getCallbacks(), michael@0: config) michael@0: includes.add("mozilla/dom/OwningNonNull.h") michael@0: includes.add("mozilla/dom/UnionMember.h") michael@0: includes.add("mozilla/dom/BindingDeclarations.h") michael@0: # Need BindingUtils.h for FakeDependentString michael@0: includes.add("mozilla/dom/BindingUtils.h") michael@0: implincludes.add("mozilla/dom/PrimitiveConversions.h") michael@0: michael@0: # Wrap all of that in our namespaces. michael@0: curr = CGNamespace.build(['mozilla', 'dom'], unions) michael@0: michael@0: curr = CGWrapper(curr, post='\n') michael@0: michael@0: namespaces = [] michael@0: stack = [CGList([])] michael@0: for clazz, isStruct in sorted(declarations): michael@0: elements = clazz.split("::") michael@0: clazz = CGClassForwardDeclare(elements.pop(), isStruct=isStruct) michael@0: i = 0 michael@0: if len(elements) > 0: michael@0: common = min(len(namespaces), len(elements)) michael@0: while i < common and namespaces[i] == elements[i]: michael@0: i += 1 michael@0: michael@0: # pop all the namespaces that should be closed michael@0: namespaces = namespaces[:i] michael@0: michael@0: # add all the namespaces that should be opened michael@0: for j, namespace in enumerate(elements[i:]): michael@0: namespaces.append(namespace) michael@0: # every CGNamespace that we add holds a CGList michael@0: list = CGList([]) michael@0: # add the new namespace to the list on top of the stack michael@0: stack[i + j].append(CGNamespace(namespace, list)) michael@0: # set the top of the namespace stack to the list of the new michael@0: # namespace michael@0: stack[i + j + 1:] = [list] michael@0: michael@0: stack[len(elements)].append(clazz) michael@0: michael@0: curr = CGList([stack[0], curr], "\n") michael@0: michael@0: curr = CGHeaders([], [], [], [], includes, implincludes, 'UnionTypes', michael@0: curr) michael@0: michael@0: # Add include guards. michael@0: curr = CGIncludeGuard('UnionTypes', curr) michael@0: michael@0: # Done. michael@0: return curr michael@0: michael@0: @staticmethod michael@0: def UnionConversions(config): michael@0: michael@0: headers, unions = UnionConversions(config.getDescriptors(), michael@0: config.getDictionaries(), michael@0: config.getCallbacks(), michael@0: config) michael@0: michael@0: # Wrap all of that in our namespaces. michael@0: curr = CGNamespace.build(['mozilla', 'dom'], unions) michael@0: michael@0: curr = CGWrapper(curr, post='\n') michael@0: michael@0: headers.update(["nsDebug.h", "mozilla/dom/UnionTypes.h"]) michael@0: curr = CGHeaders([], [], [], [], headers, [], 'UnionConversions', curr) michael@0: michael@0: # Add include guards. michael@0: curr = CGIncludeGuard('UnionConversions', curr) michael@0: michael@0: # Done. michael@0: return curr michael@0: michael@0: michael@0: # Code generator for simple events michael@0: class CGEventGetter(CGNativeMember): michael@0: def __init__(self, descriptor, attr): michael@0: ea = descriptor.getExtendedAttributes(attr, getter=True) michael@0: ea.append('resultNotAddRefed') michael@0: CGNativeMember.__init__(self, descriptor, attr, michael@0: CGSpecializedGetter.makeNativeName(descriptor, michael@0: attr), michael@0: (attr.type, []), michael@0: ea) michael@0: self.body = self.getMethodBody() michael@0: michael@0: def getArgs(self, returnType, argList): michael@0: if 'infallible' not in self.extendedAttrs: michael@0: raise TypeError("Event code generator does not support [Throws]!") michael@0: if not self.member.isAttr(): michael@0: raise TypeError("Event code generator does not support methods") michael@0: if self.member.isStatic(): michael@0: raise TypeError("Event code generators does not support static attributes") michael@0: return CGNativeMember.getArgs(self, returnType, argList) michael@0: michael@0: def getMethodBody(self): michael@0: type = self.member.type michael@0: memberName = CGDictionary.makeMemberName(self.member.identifier.name) michael@0: if (type.isPrimitive() and type.tag() in builtinNames) or type.isEnum() or type.isGeckoInterface(): michael@0: return "return " + memberName + ";\n" michael@0: if type.isDOMString() or type.isByteString(): michael@0: return "aRetVal = " + memberName + ";\n" michael@0: if type.isSpiderMonkeyInterface() or type.isObject(): michael@0: return fill( michael@0: """ michael@0: if (${memberName}) { michael@0: JS::ExposeObjectToActiveJS(${memberName}); michael@0: } michael@0: aRetVal.set(${memberName}); michael@0: return; michael@0: """, michael@0: memberName=memberName) michael@0: if type.isAny(): michael@0: return fill( michael@0: """ michael@0: JS::ExposeValueToActiveJS(${memberName}); michael@0: aRetVal.set(${memberName}); michael@0: return; michael@0: """, michael@0: memberName=memberName) michael@0: if type.isUnion(): michael@0: return "aRetVal = " + memberName + ";\n" michael@0: raise TypeError("Event code generator does not support this type!") michael@0: michael@0: def declare(self, cgClass): michael@0: if getattr(self.member, "originatingInterface", michael@0: cgClass.descriptor.interface) != cgClass.descriptor.interface: michael@0: return "" michael@0: return CGNativeMember.declare(self, cgClass) michael@0: michael@0: def define(self, cgClass): michael@0: if getattr(self.member, "originatingInterface", michael@0: cgClass.descriptor.interface) != cgClass.descriptor.interface: michael@0: return "" michael@0: return CGNativeMember.define(self, cgClass) michael@0: michael@0: michael@0: class CGEventSetter(CGNativeMember): michael@0: def __init__(self): michael@0: raise TypeError("Event code generator does not support setters!") michael@0: michael@0: michael@0: class CGEventMethod(CGNativeMember): michael@0: def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True): michael@0: if not isConstructor: michael@0: raise TypeError("Event code generator does not support methods!") michael@0: self.wantsConstructorForNativeCaller = True michael@0: CGNativeMember.__init__(self, descriptor, method, michael@0: CGSpecializedMethod.makeNativeName(descriptor, michael@0: method), michael@0: signature, michael@0: descriptor.getExtendedAttributes(method), michael@0: breakAfter=breakAfter, michael@0: variadicIsSequence=True) michael@0: self.originalArgs = list(self.args) michael@0: michael@0: def getArgs(self, returnType, argList): michael@0: args = [self.getArg(arg) for arg in argList] michael@0: return args michael@0: michael@0: def getArg(self, arg): michael@0: decl, ref = self.getArgType(arg.type, michael@0: arg.optional and not arg.defaultValue, michael@0: "Variadic" if arg.variadic else False) michael@0: if ref: michael@0: decl = CGWrapper(decl, pre="const ", post="&") michael@0: michael@0: name = arg.identifier.name michael@0: name = "a" + name[0].upper() + name[1:] michael@0: return Argument(decl.define(), name) michael@0: michael@0: def declare(self, cgClass): michael@0: self.args = list(self.originalArgs) michael@0: self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner")) michael@0: constructorForNativeCaller = CGNativeMember.declare(self, cgClass) + "\n" michael@0: self.args = list(self.originalArgs) michael@0: if needCx(None, self.descriptorProvider.interface.members, [], True): michael@0: self.args.insert(0, Argument("JSContext*", "aCx")) michael@0: self.args.insert(0, Argument("const GlobalObject&", "aGlobal")) michael@0: self.args.append(Argument('ErrorResult&', 'aRv')) michael@0: return constructorForNativeCaller + CGNativeMember.declare(self, cgClass) michael@0: michael@0: def define(self, cgClass): michael@0: self.args = list(self.originalArgs) michael@0: members = "" michael@0: holdJS = "" michael@0: iface = self.descriptorProvider.interface michael@0: while iface.identifier.name != "Event": michael@0: for m in self.descriptorProvider.getDescriptor(iface.identifier.name).interface.members: michael@0: if m.isAttr(): michael@0: # We initialize all the other member variables in the michael@0: # Constructor except those ones coming from the Event. michael@0: if getattr(m, "originatingInterface", michael@0: cgClass.descriptor.interface).identifier.name == "Event": michael@0: continue michael@0: name = CGDictionary.makeMemberName(m.identifier.name) michael@0: members += "e->%s = %s.%s;\n" % (name, self.args[1].name, name) michael@0: if m.type.isAny() or m.type.isObject() or m.type.isSpiderMonkeyInterface(): michael@0: holdJS = "mozilla::HoldJSObjects(e.get());\n" michael@0: iface = iface.parent michael@0: michael@0: self.body = fill( michael@0: """ michael@0: nsRefPtr<${nativeType}> e = new ${nativeType}(aOwner); michael@0: bool trusted = e->Init(aOwner); michael@0: e->InitEvent(${eventType}, ${eventInit}.mBubbles, ${eventInit}.mCancelable); michael@0: $*{members} michael@0: e->SetTrusted(trusted); michael@0: $*{holdJS} michael@0: return e.forget(); michael@0: """, michael@0: nativeType=self.descriptorProvider.nativeType.split('::')[-1], michael@0: eventType=self.args[0].name, michael@0: eventInit=self.args[1].name, michael@0: members=members, michael@0: holdJS=holdJS) michael@0: michael@0: self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner")) michael@0: constructorForNativeCaller = CGNativeMember.define(self, cgClass) + "\n" michael@0: self.args = list(self.originalArgs) michael@0: self.body = fill( michael@0: """ michael@0: nsCOMPtr owner = do_QueryInterface(aGlobal.GetAsSupports()); michael@0: return Constructor(owner, ${arg0}, ${arg1}); michael@0: """, michael@0: arg0=self.args[0].name, michael@0: arg1=self.args[1].name) michael@0: if needCx(None, self.descriptorProvider.interface.members, [], True): michael@0: self.args.insert(0, Argument("JSContext*", "aCx")) michael@0: self.args.insert(0, Argument("const GlobalObject&", "aGlobal")) michael@0: self.args.append(Argument('ErrorResult&', 'aRv')) michael@0: return constructorForNativeCaller + CGNativeMember.define(self, cgClass) michael@0: michael@0: michael@0: class CGEventClass(CGBindingImplClass): michael@0: """ michael@0: Codegen for the actual Event class implementation for this descriptor michael@0: """ michael@0: def __init__(self, descriptor): michael@0: CGBindingImplClass.__init__(self, descriptor, CGEventMethod, CGEventGetter, CGEventSetter, False) michael@0: members = [] michael@0: for m in descriptor.interface.members: michael@0: if m.isAttr(): michael@0: if getattr(m, "originatingInterface", michael@0: descriptor.interface) != descriptor.interface: michael@0: continue michael@0: if m.type.isPrimitive() and m.type.tag() in builtinNames: michael@0: nativeType = CGGeneric(builtinNames[m.type.tag()]) michael@0: if m.type.nullable(): michael@0: nativeType = CGTemplatedType("Nullable", nativeType) michael@0: nativeType = nativeType.define() michael@0: elif m.type.isEnum(): michael@0: nativeType = m.type.unroll().inner.identifier.name michael@0: if m.type.nullable(): michael@0: nativeType = CGTemplatedType("Nullable", michael@0: CGGeneric(nativeType)).define() michael@0: elif m.type.isDOMString(): michael@0: nativeType = "nsString" michael@0: elif m.type.isByteString(): michael@0: nativeType = "nsCString" michael@0: elif m.type.isGeckoInterface(): michael@0: iface = m.type.unroll().inner michael@0: nativeType = self.descriptor.getDescriptor( michael@0: iface.identifier.name).nativeType michael@0: # Now trim off unnecessary namespaces michael@0: nativeType = nativeType.split("::") michael@0: if nativeType[0] == "mozilla": michael@0: nativeType.pop(0) michael@0: if nativeType[0] == "dom": michael@0: nativeType.pop(0) michael@0: nativeType = CGWrapper(CGGeneric("::".join(nativeType)), pre="nsRefPtr<", post=">").define() michael@0: elif m.type.isAny(): michael@0: nativeType = "JS::Heap" michael@0: elif m.type.isObject() or m.type.isSpiderMonkeyInterface(): michael@0: nativeType = "JS::Heap" michael@0: elif m.type.isUnion(): michael@0: nativeType = CGUnionStruct.unionTypeDecl(m.type, True) michael@0: else: michael@0: raise TypeError("Don't know how to declare member of type %s" % michael@0: m.type) michael@0: members.append(ClassMember(CGDictionary.makeMemberName(m.identifier.name), michael@0: nativeType, michael@0: visibility="private", michael@0: body="body")) michael@0: michael@0: parent = self.descriptor.interface.parent michael@0: self.parentType = self.descriptor.getDescriptor(parent.identifier.name).nativeType.split('::')[-1] michael@0: baseDeclarations = fill( michael@0: """ michael@0: public: michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(${nativeType}, ${parentType}) michael@0: virtual ~${nativeType}(); michael@0: protected: michael@0: ${nativeType}(mozilla::dom::EventTarget* aOwner); michael@0: michael@0: """, michael@0: nativeType=self.descriptor.nativeType.split('::')[-1], michael@0: parentType=self.parentType) michael@0: michael@0: className = descriptor.nativeType.split('::')[-1] michael@0: asConcreteTypeMethod = ClassMethod("As%s" % className, michael@0: "%s*" % className, michael@0: [], michael@0: virtual=True, michael@0: body="return this;\n", michael@0: breakAfterReturnDecl=" ") michael@0: michael@0: CGClass.__init__(self, className, michael@0: bases=[ClassBase(self.parentType)], michael@0: methods=[asConcreteTypeMethod]+self.methodDecls, michael@0: members=members, michael@0: extradeclarations=baseDeclarations) michael@0: michael@0: def getWrapObjectBody(self): michael@0: return "return %sBinding::Wrap(aCx, this);\n" % self.descriptor.name michael@0: michael@0: def implTraverse(self): michael@0: retVal = "" michael@0: for m in self.descriptor.interface.members: michael@0: if m.isAttr() and m.type.isGeckoInterface(): michael@0: retVal += (" NS_IMPL_CYCLE_COLLECTION_TRAVERSE(" + michael@0: CGDictionary.makeMemberName(m.identifier.name) + michael@0: ")\n") michael@0: return retVal michael@0: michael@0: def implUnlink(self): michael@0: retVal = "" michael@0: for m in self.descriptor.interface.members: michael@0: if m.isAttr(): michael@0: name = CGDictionary.makeMemberName(m.identifier.name) michael@0: if m.type.isGeckoInterface(): michael@0: retVal += " NS_IMPL_CYCLE_COLLECTION_UNLINK(" + name + ")\n" michael@0: elif m.type.isAny(): michael@0: retVal += " tmp->" + name + ".setUndefined();\n" michael@0: elif m.type.isObject() or m.type.isSpiderMonkeyInterface(): michael@0: retVal += " tmp->" + name + " = nullptr;\n" michael@0: return retVal michael@0: michael@0: def implTrace(self): michael@0: retVal = "" michael@0: for m in self.descriptor.interface.members: michael@0: if m.isAttr(): michael@0: name = CGDictionary.makeMemberName(m.identifier.name) michael@0: if m.type.isAny(): michael@0: retVal += " NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(" + name + ")\n" michael@0: elif m.type.isObject() or m.type.isSpiderMonkeyInterface(): michael@0: retVal += " NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(" + name + ")\n" michael@0: elif typeNeedsRooting(m.type): michael@0: raise TypeError("Need to implement tracing for event " michael@0: "member of type %s" % m.type) michael@0: return retVal michael@0: michael@0: def define(self): michael@0: dropJS = "" michael@0: for m in self.descriptor.interface.members: michael@0: if m.isAttr(): michael@0: member = CGDictionary.makeMemberName(m.identifier.name) michael@0: if m.type.isAny(): michael@0: dropJS += member + " = JS::UndefinedValue();\n" michael@0: elif m.type.isObject() or m.type.isSpiderMonkeyInterface(): michael@0: dropJS += member + " = nullptr;\n" michael@0: if dropJS != "": michael@0: dropJS += "mozilla::DropJSObjects(this);\n" michael@0: # Just override CGClass and do our own thing michael@0: nativeType = self.descriptor.nativeType.split('::')[-1] michael@0: ctorParams = ("aOwner, nullptr, nullptr" if self.parentType == "Event" michael@0: else "aOwner") michael@0: michael@0: classImpl = fill( michael@0: """ michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(${nativeType}) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(${nativeType}, ${parentType}) michael@0: NS_IMPL_RELEASE_INHERITED(${nativeType}, ${parentType}) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(${nativeType}, ${parentType}) michael@0: $*{traverse} michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(${nativeType}, ${parentType}) michael@0: $*{trace} michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(${nativeType}, ${parentType}) michael@0: $*{unlink} michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(${nativeType}) michael@0: NS_INTERFACE_MAP_END_INHERITING(${parentType}) michael@0: michael@0: ${nativeType}::${nativeType}(mozilla::dom::EventTarget* aOwner) michael@0: : ${parentType}(${ctorParams}) michael@0: { michael@0: } michael@0: michael@0: ${nativeType}::~${nativeType}() michael@0: { michael@0: $*{dropJS} michael@0: } michael@0: michael@0: """, michael@0: ifaceName=self.descriptor.name, michael@0: nativeType=nativeType, michael@0: ctorParams=ctorParams, michael@0: parentType=self.parentType, michael@0: traverse=self.implTraverse(), michael@0: unlink=self.implUnlink(), michael@0: trace=self.implTrace(), michael@0: dropJS=dropJS) michael@0: return classImpl + CGBindingImplClass.define(self) michael@0: michael@0: michael@0: class CGEventRoot(CGThing): michael@0: def __init__(self, config, interfaceName): michael@0: # Let's assume we're not doing workers stuff, for now michael@0: descriptor = config.getDescriptor(interfaceName, False) michael@0: michael@0: self.root = CGWrapper(CGEventClass(descriptor), michael@0: pre="\n", post="\n") michael@0: michael@0: self.root = CGNamespace.build(["mozilla", "dom"], self.root) michael@0: michael@0: self.root = CGList([CGClassForwardDeclare("JSContext", isStruct=True), michael@0: self.root]) michael@0: michael@0: parent = descriptor.interface.parent.identifier.name michael@0: michael@0: # Throw in our #includes michael@0: self.root = CGHeaders([descriptor], [], [], [], michael@0: [ michael@0: config.getDescriptor(parent, False).headerFile, michael@0: "mozilla/Attributes.h", michael@0: "mozilla/ErrorResult.h", michael@0: "mozilla/dom/%sBinding.h" % interfaceName, michael@0: 'mozilla/dom/BindingUtils.h', michael@0: ], michael@0: [ michael@0: "%s.h" % interfaceName, michael@0: "js/GCAPI.h", michael@0: 'mozilla/dom/Nullable.h', michael@0: 'nsDOMQS.h' michael@0: ], michael@0: "", self.root) michael@0: michael@0: # And now some include guards michael@0: self.root = CGIncludeGuard(interfaceName, self.root) michael@0: michael@0: self.root = CGWrapper(self.root, pre=AUTOGENERATED_WARNING_COMMENT) michael@0: michael@0: self.root = CGWrapper(self.root, pre=dedent(""" michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: """)) michael@0: michael@0: def declare(self): michael@0: return self.root.declare() michael@0: michael@0: def define(self): michael@0: return self.root.define()