aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/bindings/codegen/CodegenRust.py
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom/bindings/codegen/CodegenRust.py')
-rw-r--r--components/script/dom/bindings/codegen/CodegenRust.py5534
1 files changed, 5534 insertions, 0 deletions
diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py
new file mode 100644
index 00000000000..1666589940e
--- /dev/null
+++ b/components/script/dom/bindings/codegen/CodegenRust.py
@@ -0,0 +1,5534 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# Common codegen classes.
+
+import operator
+import os
+import re
+import string
+
+from WebIDL import (
+ BuiltinTypes,
+ IDLBuiltinType,
+ IDLNullValue,
+ IDLType,
+ IDLUndefinedValue,
+)
+
+from Configuration import getTypesFromDescriptor, getTypesFromDictionary, getTypesFromCallback
+
+AUTOGENERATED_WARNING_COMMENT = \
+ "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n"
+ADDPROPERTY_HOOK_NAME = '_addProperty'
+FINALIZE_HOOK_NAME = '_finalize'
+TRACE_HOOK_NAME = '_trace'
+CONSTRUCT_HOOK_NAME = '_constructor'
+HASINSTANCE_HOOK_NAME = '_hasInstance'
+
+def replaceFileIfChanged(filename, newContents):
+ """
+ Read a copy of the old file, so that we don't touch it if it hasn't changed.
+ Returns True if the file was updated, false otherwise.
+ """
+ #XXXjdm This doesn't play well with make right now.
+ # Force the file to always be updated, or else changing CodegenRust.py
+ # will cause many autogenerated bindings to be regenerated perpetually
+ # until the result is actually different.
+
+ #oldFileContents = ""
+ #try:
+ # oldFile = open(filename, 'rb')
+ # oldFileContents = ''.join(oldFile.readlines())
+ # oldFile.close()
+ #except:
+ # pass
+
+ #if newContents == oldFileContents:
+ # return False
+
+ f = open(filename, 'wb')
+ f.write(newContents)
+ f.close()
+
+ return True
+
+def toStringBool(arg):
+ return str(not not arg).lower()
+
+def toBindingNamespace(arg):
+ return re.sub("((_workers)?$)", "Binding\\1", arg);
+
+def stripTrailingWhitespace(text):
+ tail = '\n' if text.endswith('\n') else ''
+ lines = text.splitlines()
+ for i in range(len(lines)):
+ lines[i] = lines[i].rstrip()
+ return '\n'.join(lines) + tail
+
+def MakeNativeName(name):
+ return name[0].upper() + name[1:]
+
+builtinNames = {
+ IDLType.Tags.bool: 'bool',
+ IDLType.Tags.int8: 'i8',
+ IDLType.Tags.int16: 'i16',
+ IDLType.Tags.int32: 'i32',
+ IDLType.Tags.int64: 'i64',
+ IDLType.Tags.uint8: 'u8',
+ IDLType.Tags.uint16: 'u16',
+ IDLType.Tags.uint32: 'u32',
+ IDLType.Tags.uint64: 'u64',
+ IDLType.Tags.float: 'f32',
+ IDLType.Tags.double: 'f64'
+}
+
+numericTags = [
+ IDLType.Tags.int8, IDLType.Tags.uint8,
+ IDLType.Tags.int16, IDLType.Tags.uint16,
+ IDLType.Tags.int32, IDLType.Tags.uint32,
+ IDLType.Tags.int64, IDLType.Tags.uint64,
+ IDLType.Tags.float, IDLType.Tags.double
+ ]
+
+class CastableObjectUnwrapper():
+ """
+ A class for unwrapping an object named by the "source" argument
+ based on the passed-in descriptor. Stringifies to a Rust expression of
+ the appropriate type.
+
+ codeOnFailure is the code to run if unwrapping fails.
+ """
+ def __init__(self, descriptor, source, codeOnFailure):
+ self.substitution = {
+ "type": descriptor.nativeType,
+ "depth": descriptor.interface.inheritanceDepth(),
+ "prototype": "PrototypeList::id::" + descriptor.name,
+ "protoID": "PrototypeList::id::" + descriptor.name + " as uint",
+ "source": source,
+ "codeOnFailure": CGIndenter(CGGeneric(codeOnFailure), 4).define(),
+ }
+
+ def __str__(self):
+ return string.Template(
+"""match unwrap_jsmanaged(${source}, ${prototype}, ${depth}) {
+ Ok(val) => val,
+ Err(()) => {
+${codeOnFailure}
+ }
+}""").substitute(self.substitution)
+
+
+class CGThing():
+ """
+ Abstract base class for things that spit out code.
+ """
+ def __init__(self):
+ pass # Nothing for now
+
+ def define(self):
+ """Produce code for a Rust file."""
+ assert(False) # Override me!
+
+
+class CGNativePropertyHooks(CGThing):
+ """
+ Generate a NativePropertyHooks for a given descriptor
+ """
+ def __init__(self, descriptor, properties):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ self.properties = properties
+
+ def define(self):
+ parent = self.descriptor.interface.parent
+ if parent:
+ parentHooks = "Some(&::dom::bindings::codegen::Bindings::%sBinding::sNativePropertyHooks)" % parent.identifier.name
+ else:
+ parentHooks = "None"
+
+ substitutions = {
+ "parentHooks": parentHooks
+ }
+
+ return string.Template(
+ "pub static sNativePropertyHooks: NativePropertyHooks = NativePropertyHooks {\n"
+ " native_properties: &sNativeProperties,\n"
+ " proto_hooks: ${parentHooks},\n"
+ "};\n").substitute(substitutions)
+
+
+class CGMethodCall(CGThing):
+ """
+ A class to generate selection of a method signature from a set of
+ signatures and generation of a call to that signature.
+ """
+ def __init__(self, argsPre, nativeMethodName, static, descriptor, method):
+ CGThing.__init__(self)
+
+ methodName = '\\"%s.%s\\"' % (descriptor.interface.identifier.name, method.identifier.name)
+
+ def requiredArgCount(signature):
+ arguments = signature[1]
+ if len(arguments) == 0:
+ return 0
+ requiredArgs = len(arguments)
+ while requiredArgs and arguments[requiredArgs-1].optional:
+ requiredArgs -= 1
+ return requiredArgs
+
+ def getPerSignatureCall(signature, argConversionStartsAt=0, signatureIndex=0):
+ return CGPerSignatureCall(signature[0], argsPre, signature[1],
+ nativeMethodName + '_'*signatureIndex,
+ static, descriptor,
+ method, argConversionStartsAt)
+
+
+ signatures = method.signatures()
+ if len(signatures) == 1:
+ # Special case: we can just do a per-signature method call
+ # here for our one signature and not worry about switching
+ # on anything.
+ signature = signatures[0]
+ self.cgRoot = CGList([getPerSignatureCall(signature)])
+ requiredArgs = requiredArgCount(signature)
+
+
+ if requiredArgs > 0:
+ code = (
+ "if argc < %d {\n"
+ " throw_type_error(cx, \"Not enough arguments to %s.\");\n"
+ " return 0;\n"
+ "}" % (requiredArgs, methodName))
+ self.cgRoot.prepend(
+ CGWrapper(CGGeneric(code), pre="\n", post="\n"))
+
+ return
+
+ # Need to find the right overload
+ maxArgCount = method.maxArgCount
+ allowedArgCounts = method.allowedArgCounts
+
+ argCountCases = []
+ for argCount in allowedArgCounts:
+ possibleSignatures = method.signaturesForArgCount(argCount)
+ if len(possibleSignatures) == 1:
+ # easy case!
+ signature = possibleSignatures[0]
+
+
+ sigIndex = signatures.index(signature)
+ argCountCases.append(
+ CGCase(str(argCount), getPerSignatureCall(signature,
+ signatureIndex=sigIndex)))
+ continue
+
+ distinguishingIndex = method.distinguishingIndexForArgCount(argCount)
+
+ # We can't handle unions at the distinguishing index.
+ for (returnType, args) in possibleSignatures:
+ if args[distinguishingIndex].type.isUnion():
+ raise TypeError("No support for unions as distinguishing "
+ "arguments yet: %s",
+ args[distinguishingIndex].location)
+
+ # Convert all our arguments up to the distinguishing index.
+ # Doesn't matter which of the possible signatures we use, since
+ # they all have the same types up to that point; just use
+ # possibleSignatures[0]
+ caseBody = [CGGeneric("let argv_start = JS_ARGV(cx, vp);")]
+ caseBody.extend([ CGArgumentConverter(possibleSignatures[0][1][i],
+ i, "argv_start", "argc",
+ descriptor) for i in
+ range(0, distinguishingIndex) ])
+
+ # Select the right overload from our set.
+ distinguishingArg = "(*argv_start.offset(%d))" % distinguishingIndex
+
+ def pickFirstSignature(condition, filterLambda):
+ sigs = filter(filterLambda, possibleSignatures)
+ assert len(sigs) < 2
+ if len(sigs) > 0:
+ if condition is None:
+ caseBody.append(
+ getPerSignatureCall(sigs[0], distinguishingIndex,
+ possibleSignatures.index(sigs[0])))
+ else:
+ caseBody.append(CGGeneric("if " + condition + " {"))
+ caseBody.append(CGIndenter(
+ getPerSignatureCall(sigs[0], distinguishingIndex,
+ possibleSignatures.index(sigs[0]))))
+ caseBody.append(CGGeneric("}"))
+ return True
+ return False
+
+ # First check for null or undefined
+ pickFirstSignature("%s.isNullOrUndefined()" % distinguishingArg,
+ lambda s: (s[1][distinguishingIndex].type.nullable() or
+ s[1][distinguishingIndex].type.isDictionary()))
+
+ # Now check for distinguishingArg being an object that implements a
+ # non-callback interface. That includes typed arrays and
+ # arraybuffers.
+ interfacesSigs = [
+ s for s in possibleSignatures
+ if (s[1][distinguishingIndex].type.isObject() or
+ s[1][distinguishingIndex].type.isNonCallbackInterface()) ]
+ # There might be more than one of these; we need to check
+ # which ones we unwrap to.
+
+ if len(interfacesSigs) > 0:
+ # The spec says that we should check for "platform objects
+ # implementing an interface", but it's enough to guard on these
+ # being an object. The code for unwrapping non-callback
+ # interfaces and typed arrays will just bail out and move on to
+ # the next overload if the object fails to unwrap correctly. We
+ # could even not do the isObject() check up front here, but in
+ # cases where we have multiple object overloads it makes sense
+ # to do it only once instead of for each overload. That will
+ # also allow the unwrapping test to skip having to do codegen
+ # for the null-or-undefined case, which we already handled
+ # above.
+ caseBody.append(CGGeneric("if (%s).is_object() {" %
+ (distinguishingArg)))
+ for idx, sig in enumerate(interfacesSigs):
+ caseBody.append(CGIndenter(CGGeneric("loop {")));
+ type = sig[1][distinguishingIndex].type
+
+ # The argument at index distinguishingIndex can't possibly
+ # be unset here, because we've already checked that argc is
+ # large enough that we can examine this argument.
+ template, _, declType, needsRooting = getJSToNativeConversionTemplate(
+ type, descriptor, failureCode="break;", isDefinitelyObject=True)
+
+ testCode = instantiateJSToNativeConversionTemplate(
+ template,
+ {"val": distinguishingArg},
+ declType,
+ "arg%d" % distinguishingIndex,
+ needsRooting)
+
+ # Indent by 4, since we need to indent further than our "do" statement
+ caseBody.append(CGIndenter(testCode, 4));
+ # If we got this far, we know we unwrapped to the right
+ # interface, so just do the call. Start conversion with
+ # distinguishingIndex + 1, since we already converted
+ # distinguishingIndex.
+ caseBody.append(CGIndenter(
+ getPerSignatureCall(sig, distinguishingIndex + 1, idx), 4))
+ caseBody.append(CGIndenter(CGGeneric("}")))
+
+ caseBody.append(CGGeneric("}"))
+
+ # XXXbz Now we're supposed to check for distinguishingArg being
+ # an array or a platform object that supports indexed
+ # properties... skip that last for now. It's a bit of a pain.
+ pickFirstSignature("%s.isObject() && IsArrayLike(cx, &%s.toObject())" %
+ (distinguishingArg, distinguishingArg),
+ lambda s:
+ (s[1][distinguishingIndex].type.isArray() or
+ s[1][distinguishingIndex].type.isSequence() or
+ s[1][distinguishingIndex].type.isObject()))
+
+ # Check for Date objects
+ # XXXbz Do we need to worry about security wrappers around the Date?
+ pickFirstSignature("%s.isObject() && JS_ObjectIsDate(cx, &%s.toObject())" %
+ (distinguishingArg, distinguishingArg),
+ lambda s: (s[1][distinguishingIndex].type.isDate() or
+ s[1][distinguishingIndex].type.isObject()))
+
+ # Check for vanilla JS objects
+ # XXXbz Do we need to worry about security wrappers?
+ pickFirstSignature("%s.isObject() && !IsPlatformObject(cx, &%s.toObject())" %
+ (distinguishingArg, distinguishingArg),
+ lambda s: (s[1][distinguishingIndex].type.isCallback() or
+ s[1][distinguishingIndex].type.isCallbackInterface() or
+ s[1][distinguishingIndex].type.isDictionary() or
+ s[1][distinguishingIndex].type.isObject()))
+
+ # The remaining cases are mutually exclusive. The
+ # pickFirstSignature calls are what change caseBody
+ # Check for strings or enums
+ if pickFirstSignature(None,
+ lambda s: (s[1][distinguishingIndex].type.isString() or
+ s[1][distinguishingIndex].type.isEnum())):
+ pass
+ # Check for primitives
+ elif pickFirstSignature(None,
+ lambda s: s[1][distinguishingIndex].type.isPrimitive()):
+ pass
+ # Check for "any"
+ elif pickFirstSignature(None,
+ lambda s: s[1][distinguishingIndex].type.isAny()):
+ pass
+ else:
+ # Just throw; we have no idea what we're supposed to
+ # do with this.
+ caseBody.append(CGGeneric("return Throw(cx, NS_ERROR_XPC_BAD_CONVERT_JS);"))
+
+ argCountCases.append(CGCase(str(argCount),
+ CGList(caseBody, "\n")))
+
+ overloadCGThings = []
+ overloadCGThings.append(
+ CGGeneric("let argcount = cmp::min(argc, %d);" %
+ maxArgCount))
+ overloadCGThings.append(
+ CGSwitch("argcount",
+ argCountCases,
+ CGGeneric("throw_type_error(cx, \"Not enough arguments to %s.\");\n"
+ "return 0;\n" % methodName)))
+ #XXXjdm Avoid unreachable statement warnings
+ #overloadCGThings.append(
+ # CGGeneric('fail!("We have an always-returning default case");\n'
+ # 'return 0;'))
+ self.cgRoot = CGWrapper(CGList(overloadCGThings, "\n"),
+ pre="\n")
+
+ def define(self):
+ return self.cgRoot.define()
+
+class FakeCastableDescriptor():
+ def __init__(self, descriptor):
+ self.nativeType = "*const %s" % descriptor.concreteType
+ self.name = descriptor.name
+ class FakeInterface:
+ def inheritanceDepth(self):
+ return descriptor.interface.inheritanceDepth()
+ self.interface = FakeInterface()
+
+def dictionaryHasSequenceMember(dictionary):
+ return (any(typeIsSequenceOrHasSequenceMember(m.type) for m in
+ dictionary.members) or
+ (dictionary.parent and
+ dictionaryHasSequenceMember(dictionary.parent)))
+
+def typeIsSequenceOrHasSequenceMember(type):
+ if type.nullable():
+ type = type.inner
+ if type.isSequence():
+ return True
+ if type.isArray():
+ elementType = type.inner
+ return typeIsSequenceOrHasSequenceMember(elementType)
+ if type.isDictionary():
+ return dictionaryHasSequenceMember(type.inner)
+ if type.isUnion():
+ return any(typeIsSequenceOrHasSequenceMember(m.type) for m in
+ type.flatMemberTypes)
+ return False
+
+def typeNeedsRooting(type, descriptorProvider):
+ return type.isGeckoInterface() and descriptorProvider.getDescriptor(type.name).needsRooting
+
+def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
+ isDefinitelyObject=False,
+ isMember=False,
+ isArgument=False,
+ invalidEnumValueFatal=True,
+ defaultValue=None,
+ treatNullAs="Default",
+ isEnforceRange=False,
+ isClamp=False,
+ exceptionCode=None,
+ allowTreatNonObjectAsNull=False,
+ isCallbackReturnValue=False,
+ sourceDescription="value"):
+ """
+ Get a template for converting a JS value to a native object based on the
+ given type and descriptor. If failureCode is given, then we're actually
+ testing whether we can convert the argument to the desired type. That
+ means that failures to convert due to the JS value being the wrong type of
+ value need to use failureCode instead of throwing exceptions. Failures to
+ convert that are due to JS exceptions (from toString or valueOf methods) or
+ out of memory conditions need to throw exceptions no matter what
+ failureCode is.
+
+ If isDefinitelyObject is True, that means we know the value
+ isObject() and we have no need to recheck that.
+
+ if isMember is True, we're being converted from a property of some
+ JS object, not from an actual method argument, so we can't rely on
+ our jsval being rooted or outliving us in any way. Any caller
+ passing true needs to ensure that it is handled correctly in
+ typeIsSequenceOrHasSequenceMember.
+
+ invalidEnumValueFatal controls whether an invalid enum value conversion
+ attempt will throw (if true) or simply return without doing anything (if
+ false).
+
+ If defaultValue is not None, it's the IDL default value for this conversion
+
+ If isEnforceRange is true, we're converting an integer and throwing if the
+ value is out of range.
+
+ If isClamp is true, we're converting an integer and clamping if the
+ value is out of range.
+
+ If allowTreatNonObjectAsNull is true, then [TreatNonObjectAsNull]
+ extended attributes on nullable callback functions will be honored.
+
+ The return value from this function is a tuple consisting of four things:
+
+ 1) A string representing the conversion code. This will have template
+ substitution performed on it as follows:
+
+ ${val} replaced by an expression for the JS::Value in question
+
+ 2) A string or None representing Rust code for the default value (if any).
+
+ 3) A CGThing representing the native C++ type we're converting to
+ (declType). This is allowed to be None if the conversion code is
+ supposed to be used as-is.
+
+ 4) A boolean indicating whether the caller has to root the result.
+
+ """
+ # We should not have a defaultValue if we know we're an object
+ assert(not isDefinitelyObject or defaultValue is None)
+
+ # If exceptionCode is not set, we'll just rethrow the exception we got.
+ # Note that we can't just set failureCode to exceptionCode, because setting
+ # failureCode will prevent pending exceptions from being set in cases when
+ # they really should be!
+ if exceptionCode is None:
+ exceptionCode = "return 0;"
+
+ needsRooting = typeNeedsRooting(type, descriptorProvider)
+
+ def handleOptional(template, declType, default):
+ assert (defaultValue is None) == (default is None)
+ return (template, default, declType, needsRooting)
+
+ # Unfortunately, .capitalize() on a string will lowercase things inside the
+ # string, which we do not want.
+ def firstCap(string):
+ return string[0].upper() + string[1:]
+
+ # Helper functions for dealing with failures due to the JS value being the
+ # wrong type of value
+ # Helper functions for dealing with failures due to the JS value being the
+ # wrong type of value
+ def onFailureNotAnObject(failureCode):
+ return CGWrapper(
+ CGGeneric(
+ failureCode or
+ ('throw_type_error(cx, "%s is not an object.");\n'
+ '%s' % (firstCap(sourceDescription), exceptionCode))),
+ post="\n")
+ def onFailureBadType(failureCode, typeName):
+ return CGWrapper(
+ CGGeneric(
+ failureCode or
+ ('throw_type_error(cx, \"%s does not implement interface %s.\");\n'
+ '%s' % (firstCap(sourceDescription), typeName,
+ exceptionCode))),
+ post="\n")
+ def onFailureNotCallable(failureCode):
+ return CGWrapper(
+ CGGeneric(
+ failureCode or
+ ('throw_type_error(cx, \"%s is not callable.\");\n'
+ '%s' % (firstCap(sourceDescription), exceptionCode))),
+ post="\n")
+
+
+ # A helper function for handling null default values. Checks that the
+ # default value, if it exists, is null.
+ def handleDefaultNull(nullValue):
+ if defaultValue is None:
+ return None
+
+ if not isinstance(defaultValue, IDLNullValue):
+ raise TypeError("Can't handle non-null default value here")
+
+ assert type.nullable() or type.isDictionary()
+ return nullValue
+
+ # A helper function for wrapping up the template body for
+ # possibly-nullable objecty stuff
+ def wrapObjectTemplate(templateBody, isDefinitelyObject, type,
+ failureCode=None):
+ if not isDefinitelyObject:
+ # Handle the non-object cases by wrapping up the whole
+ # thing in an if cascade.
+ templateBody = (
+ "if (${val}).is_object() {\n" +
+ CGIndenter(CGGeneric(templateBody)).define() + "\n")
+ if type.nullable():
+ templateBody += (
+ "} else if (${val}).is_null_or_undefined() {\n"
+ " None\n")
+ templateBody += (
+ "} else {\n" +
+ CGIndenter(onFailureNotAnObject(failureCode)).define() +
+ "}\n")
+
+ return templateBody
+
+ assert not (isEnforceRange and isClamp) # These are mutually exclusive
+
+ if type.isArray():
+ raise TypeError("Can't handle array arguments yet")
+
+ if type.isSequence():
+ raise TypeError("Can't handle sequence arguments yet")
+
+ if type.isUnion():
+ declType = CGGeneric(type.name + "::" + type.name)
+ if type.nullable():
+ declType = CGWrapper(declType, pre="Option<", post=" >")
+
+ templateBody = ("match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n"
+ " Ok(value) => value,\n"
+ " Err(()) => { %s },\n"
+ "}" % exceptionCode)
+
+ return handleOptional(templateBody, declType, handleDefaultNull("None"))
+
+ if type.isGeckoInterface():
+ assert not isEnforceRange and not isClamp
+
+ descriptor = descriptorProvider.getDescriptor(
+ type.unroll().inner.identifier.name)
+
+ if descriptor.interface.isCallback():
+ name = descriptor.nativeType
+ declType = CGGeneric("Option<%s>" % name);
+ conversion = ("Some(%s::new((${val}).to_object()))" % name)
+
+ template = wrapObjectTemplate(conversion, isDefinitelyObject, type,
+ failureCode)
+ return handleOptional(template, declType, handleDefaultNull("None"))
+
+ if isMember:
+ descriptorType = descriptor.memberType
+ elif isArgument:
+ descriptorType = descriptor.argumentType
+ else:
+ descriptorType = descriptor.nativeType
+
+ templateBody = ""
+ if descriptor.interface.isConsequential():
+ raise TypeError("Consequential interface %s being used as an "
+ "argument" % descriptor.interface.identifier.name)
+
+ if failureCode is None:
+ substitutions = {
+ "sourceDescription": sourceDescription,
+ "interface": descriptor.interface.identifier.name,
+ "exceptionCode": exceptionCode,
+ }
+ unwrapFailureCode = string.Template(
+ 'throw_type_error(cx, "${sourceDescription} does not '
+ 'implement interface ${interface}.");\n'
+ '${exceptionCode}').substitute(substitutions)
+ else:
+ unwrapFailureCode = failureCode
+
+ templateBody = str(CastableObjectUnwrapper(
+ descriptor,
+ "(${val}).to_object()",
+ unwrapFailureCode))
+
+ declType = CGGeneric(descriptorType)
+ if type.nullable():
+ templateBody = "Some(%s)" % templateBody
+ declType = CGWrapper(declType, pre="Option<", post=">")
+
+ if isMember:
+ templateBody += ".root()"
+
+ templateBody = wrapObjectTemplate(templateBody, isDefinitelyObject,
+ type, failureCode)
+
+ return handleOptional(templateBody, declType, handleDefaultNull("None"))
+
+ if type.isSpiderMonkeyInterface():
+ raise TypeError("Can't handle SpiderMonkey interface arguments yet")
+
+ if type.isDOMString():
+ assert not isEnforceRange and not isClamp
+
+ treatAs = {
+ "Default": "Default",
+ "EmptyString": "Empty",
+ }
+ if treatNullAs not in treatAs:
+ raise TypeError("We don't support [TreatNullAs=%s]" % treatNullAs)
+ if type.nullable():
+ nullBehavior = "()"
+ else:
+ nullBehavior = treatAs[treatNullAs]
+
+ conversionCode = (
+ "match FromJSValConvertible::from_jsval(cx, ${val}, %s) {\n"
+ " Ok(strval) => strval,\n"
+ " Err(_) => { %s },\n"
+ "}" % (nullBehavior, exceptionCode))
+
+ if defaultValue is None:
+ default = None
+ elif isinstance(defaultValue, IDLNullValue):
+ assert type.nullable()
+ default = "None"
+ else:
+ assert defaultValue.type.tag() == IDLType.Tags.domstring
+ value = "str::from_utf8(data).unwrap().to_string()"
+ if type.nullable():
+ value = "Some(%s)" % value
+
+ default = (
+ "static data: [u8, ..%s] = [ %s ];\n"
+ "%s" %
+ (len(defaultValue.value) + 1,
+ ", ".join(["'" + char + "' as u8" for char in defaultValue.value] + ["0"]),
+ value))
+
+ declType = "DOMString"
+ if type.nullable():
+ declType = "Option<%s>" % declType
+
+ return handleOptional(conversionCode, CGGeneric(declType), default)
+
+ if type.isByteString():
+ assert not isEnforceRange and not isClamp
+
+ conversionCode = (
+ "match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n"
+ " Ok(strval) => strval,\n"
+ " Err(_) => { %s },\n"
+ "}" % exceptionCode)
+
+ declType = CGGeneric("ByteString")
+ if type.nullable():
+ declType = CGWrapper(declType, pre="Option<", post=">")
+
+ return handleOptional(conversionCode, declType, handleDefaultNull("None"))
+
+ if type.isEnum():
+ assert not isEnforceRange and not isClamp
+
+ if type.nullable():
+ raise TypeError("We don't support nullable enumerated arguments "
+ "yet")
+ enum = type.inner.identifier.name
+ if invalidEnumValueFatal:
+ handleInvalidEnumValueCode = exceptionCode
+ else:
+ handleInvalidEnumValueCode = "return 1;"
+
+ template = (
+ "match FindEnumStringIndex(cx, ${val}, %(values)s) {\n"
+ " Err(_) => { %(exceptionCode)s },\n"
+ " Ok(None) => { %(handleInvalidEnumValueCode)s },\n"
+ " Ok(Some(index)) => {\n"
+ " //XXXjdm need some range checks up in here.\n"
+ " unsafe { mem::transmute(index) }\n"
+ " },\n"
+ "}" % { "values" : enum + "Values::strings",
+ "exceptionCode" : exceptionCode,
+"handleInvalidEnumValueCode" : handleInvalidEnumValueCode })
+
+ if defaultValue is not None:
+ assert(defaultValue.type.tag() == IDLType.Tags.domstring)
+ default = "%sValues::%s" % (enum, getEnumValueName(defaultValue.value))
+ else:
+ default = None
+
+ return handleOptional(template, CGGeneric(enum), default)
+
+ if type.isCallback():
+ assert not isEnforceRange and not isClamp
+ assert not type.treatNonCallableAsNull()
+ assert not type.treatNonObjectAsNull() or type.nullable()
+ assert not type.treatNonObjectAsNull() or not type.treatNonCallableAsNull()
+
+ declType = CGGeneric('%s::%s' % (type.unroll().module(), type.unroll().identifier.name))
+
+ conversion = CGCallbackTempRoot(declType.define())
+
+ if type.nullable():
+ declType = CGTemplatedType("Option", declType)
+ conversion = CGWrapper(conversion, pre="Some(", post=")")
+
+ if allowTreatNonObjectAsNull and type.treatNonObjectAsNull():
+ if not isDefinitelyObject:
+ haveObject = "${val}.is_object()"
+ template = CGIfElseWrapper(haveObject,
+ conversion,
+ CGGeneric("None")).define()
+ else:
+ template = conversion
+ else:
+ template = CGIfElseWrapper("JS_ObjectIsCallable(cx, ${val}.to_object()) != 0",
+ conversion,
+ onFailureNotCallable(failureCode)).define()
+ template = wrapObjectTemplate(
+ template,
+ isDefinitelyObject,
+ type,
+ failureCode)
+
+ if defaultValue is not None:
+ assert allowTreatNonObjectAsNull
+ assert type.treatNonObjectAsNull()
+ assert type.nullable()
+ assert isinstance(defaultValue, IDLNullValue)
+ default = "None"
+ else:
+ default = None
+
+ return (template, default, declType, needsRooting)
+
+ if type.isAny():
+ assert not isEnforceRange and not isClamp
+
+ declType = CGGeneric("JSVal")
+
+ if defaultValue is None:
+ default = None
+ elif isinstance(defaultValue, IDLNullValue):
+ default = "NullValue()"
+ elif isinstance(defaultValue, IDLUndefinedValue):
+ default = "UndefinedValue()"
+ else:
+ raise TypeError("Can't handle non-null, non-undefined default value here")
+
+ return handleOptional("${val}", declType, default)
+
+ if type.isObject():
+ raise TypeError("Can't handle object arguments yet")
+
+ if type.isDictionary():
+ if failureCode is not None:
+ raise TypeError("Can't handle dictionaries when failureCode is not None")
+ # There are no nullable dictionaries
+ assert not type.nullable()
+
+ typeName = CGDictionary.makeDictionaryName(type.inner)
+ declType = CGGeneric(typeName)
+ template = ("match %s::new(cx, ${val}) {\n"
+ " Ok(dictionary) => dictionary,\n"
+ " Err(_) => return 0,\n"
+ "}" % typeName)
+
+ return handleOptional(template, declType, handleDefaultNull("%s::empty()" % typeName))
+
+ if type.isVoid():
+ # This one only happens for return values, and its easy: Just
+ # ignore the jsval.
+ return ("", None, None, False)
+
+ if not type.isPrimitive():
+ raise TypeError("Need conversion for argument type '%s'" % str(type))
+
+ assert not isEnforceRange and not isClamp
+
+ if failureCode is None:
+ failureCode = 'return 0'
+
+ declType = CGGeneric(builtinNames[type.tag()])
+ if type.nullable():
+ declType = CGWrapper(declType, pre="Option<", post=">")
+
+ #XXXjdm support conversionBehavior here
+ template = (
+ "match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n"
+ " Ok(v) => v,\n"
+ " Err(_) => { %s }\n"
+ "}" % exceptionCode)
+
+ if defaultValue is not None:
+ if isinstance(defaultValue, IDLNullValue):
+ assert type.nullable()
+ defaultStr = "None"
+ else:
+ tag = defaultValue.type.tag()
+ if tag in numericTags:
+ defaultStr = str(defaultValue.value)
+ else:
+ assert(tag == IDLType.Tags.bool)
+ defaultStr = toStringBool(defaultValue.value)
+
+ if type.nullable():
+ defaultStr = "Some(%s)" % defaultStr
+ else:
+ defaultStr = None
+
+ return handleOptional(template, declType, defaultStr)
+
+def instantiateJSToNativeConversionTemplate(templateBody, replacements,
+ declType, declName, needsRooting):
+ """
+ Take the templateBody and declType as returned by
+ getJSToNativeConversionTemplate, a set of replacements as required by the
+ strings in such a templateBody, and a declName, and generate code to
+ convert into a stack Rust binding with that name.
+ """
+ result = CGList([], "\n")
+
+ conversion = CGGeneric(
+ string.Template(templateBody).substitute(replacements)
+ )
+
+ if declType is not None:
+ newDecl = [
+ CGGeneric("let "),
+ CGGeneric(declName),
+ CGGeneric(": "),
+ declType,
+ CGGeneric(" = "),
+ conversion,
+ CGGeneric(";"),
+ ]
+ result.append(CGList(newDecl))
+ else:
+ result.append(conversion)
+
+ # Add an empty CGGeneric to get an extra newline after the argument
+ # conversion.
+ result.append(CGGeneric(""))
+
+ if needsRooting:
+ rootBody = "let %s = %s.root();" % (declName, declName)
+ result.append(CGGeneric(rootBody))
+ result.append(CGGeneric(""))
+
+ return result;
+
+def convertConstIDLValueToJSVal(value):
+ if isinstance(value, IDLNullValue):
+ return "NullVal"
+ tag = value.type.tag()
+ if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
+ IDLType.Tags.uint16, IDLType.Tags.int32]:
+ return "IntVal(%s)" % (value.value)
+ if tag == IDLType.Tags.uint32:
+ return "UintVal(%s)" % (value.value)
+ if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]:
+ return "DoubleVal(%s)" % (value.value)
+ if tag == IDLType.Tags.bool:
+ return "BoolVal(true)" if value.value else "BoolVal(false)"
+ if tag in [IDLType.Tags.float, IDLType.Tags.double]:
+ return "DoubleVal(%s)" % (value.value)
+ raise TypeError("Const value of unhandled type: " + value.type)
+
+class CGArgumentConverter(CGThing):
+ """
+ A class that takes an IDL argument object, its index in the
+ argument list, and the argv and argc strings and generates code to
+ unwrap the argument to the right native type.
+ """
+ def __init__(self, argument, index, argv, argc, descriptorProvider,
+ invalidEnumValueFatal=True):
+ CGThing.__init__(self)
+ assert(not argument.defaultValue or argument.optional)
+
+ replacer = {
+ "index": index,
+ "argc": argc,
+ "argv": argv
+ }
+ condition = string.Template("${index} < ${argc}").substitute(replacer)
+
+ replacementVariables = {
+ "val": string.Template("(*${argv}.offset(${index}))").substitute(replacer),
+ }
+
+ template, default, declType, needsRooting = getJSToNativeConversionTemplate(
+ argument.type,
+ descriptorProvider,
+ invalidEnumValueFatal=invalidEnumValueFatal,
+ defaultValue=argument.defaultValue,
+ treatNullAs=argument.treatNullAs,
+ isEnforceRange=argument.enforceRange,
+ isClamp=argument.clamp,
+ isMember="Variadic" if argument.variadic else False,
+ allowTreatNonObjectAsNull=argument.allowTreatNonCallableAsNull())
+
+ if not argument.variadic:
+ if argument.optional:
+ if argument.defaultValue:
+ assert default
+ template = CGIfElseWrapper(condition,
+ CGGeneric(template),
+ CGGeneric(default)).define()
+ else:
+ assert not default
+ declType = CGWrapper(declType, pre="Option<", post=">")
+ template = CGIfElseWrapper(condition,
+ CGGeneric("Some(%s)" % template),
+ CGGeneric("None")).define()
+ else:
+ assert not default
+
+ self.converter = instantiateJSToNativeConversionTemplate(
+ template, replacementVariables, declType, "arg%d" % index,
+ needsRooting)
+ else:
+ assert argument.optional
+ variadicConversion = {
+ "val": string.Template("(*${argv}.offset(variadicArg as int))").substitute(replacer),
+ }
+ innerConverter = instantiateJSToNativeConversionTemplate(
+ template, variadicConversion, declType, "slot",
+ needsRooting)
+
+ seqType = CGTemplatedType("Vec", declType)
+ variadicConversion = string.Template(
+ "{\n"
+ " let mut vector: ${seqType} = Vec::with_capacity((${argc} - ${index}) as uint);\n"
+ " for variadicArg in range(${index}, ${argc}) {\n"
+ "${inner}\n"
+ " vector.push(slot);\n"
+ " }\n"
+ " vector\n"
+ "}"
+ ).substitute({
+ "index": index,
+ "argc": argc,
+ "seqType": seqType.define(),
+ "inner": CGIndenter(innerConverter, 4).define(),
+ })
+
+ self.converter = instantiateJSToNativeConversionTemplate(
+ variadicConversion, replacementVariables, seqType, "arg%d" % index,
+ False)
+
+ def define(self):
+ return self.converter.define()
+
+
+def wrapForType(jsvalRef, result='result', successCode='return 1;'):
+ """
+ Reflect a Rust value into JS.
+
+ * 'jsvalRef': a Rust reference to the JSVal in which to store the result
+ of the conversion;
+ * 'result': the name of the variable in which the Rust value is stored;
+ * 'successCode': the code to run once we have done the conversion.
+ """
+ return "%s = (%s).to_jsval(cx);\n%s" % (jsvalRef, result, successCode)
+
+
+def typeNeedsCx(type, retVal=False):
+ if type is None:
+ return False
+ if type.nullable():
+ type = type.inner
+ if type.isSequence() or type.isArray():
+ type = type.inner
+ if type.isUnion():
+ return any(typeNeedsCx(t) for t in type.unroll().flatMemberTypes)
+ if retVal and type.isSpiderMonkeyInterface():
+ return True
+ return type.isAny() or type.isObject()
+
+def typeRetValNeedsRooting(type):
+ if type is None:
+ return False
+ if type.nullable():
+ type = type.inner
+ return type.isGeckoInterface() and not type.isCallback() and not type.isCallbackInterface()
+
+def memberIsCreator(member):
+ return member.getExtendedAttribute("Creator") is not None
+
+# Returns a CGThing containing the type of the return value.
+def getRetvalDeclarationForType(returnType, descriptorProvider):
+ if returnType is None or returnType.isVoid():
+ # Nothing to declare
+ return CGGeneric("()")
+ if returnType.isPrimitive() and returnType.tag() in builtinNames:
+ result = CGGeneric(builtinNames[returnType.tag()])
+ if returnType.nullable():
+ result = CGWrapper(result, pre="Option<", post=">")
+ return result
+ if returnType.isDOMString():
+ result = CGGeneric("DOMString")
+ if returnType.nullable():
+ result = CGWrapper(result, pre="Option<", post=">")
+ return result
+ if returnType.isByteString():
+ result = CGGeneric("ByteString")
+ if returnType.nullable():
+ result = CGWrapper(result, pre="Option<", post=">")
+ return result
+ if returnType.isEnum():
+ result = CGGeneric(returnType.unroll().inner.identifier.name)
+ if returnType.nullable():
+ result = CGWrapper(result, pre="Option<", post=">")
+ return result
+ if returnType.isGeckoInterface():
+ descriptor = descriptorProvider.getDescriptor(
+ returnType.unroll().inner.identifier.name)
+ result = CGGeneric(descriptor.returnType)
+ if returnType.nullable():
+ result = CGWrapper(result, pre="Option<", post=">")
+ return result
+ if returnType.isCallback():
+ result = CGGeneric('%s::%s' % (returnType.unroll().module(),
+ returnType.unroll().identifier.name))
+ if returnType.nullable():
+ result = CGWrapper(result, pre="Option<", post=">")
+ return result
+ if returnType.isUnion():
+ result = CGGeneric('%s::%s' % (returnType.unroll().name, returnType.unroll().name))
+ if returnType.nullable():
+ result = CGWrapper(result, pre="Option<", post=">")
+ return result
+ if returnType.isAny():
+ return CGGeneric("JSVal")
+ if returnType.isObject() or returnType.isSpiderMonkeyInterface():
+ return CGGeneric("*mut JSObject")
+ if returnType.isSequence():
+ raise TypeError("We don't support sequence return values")
+
+ raise TypeError("Don't know how to declare return value for %s" %
+ returnType)
+
+class PropertyDefiner:
+ """
+ A common superclass for defining things on prototype objects.
+
+ Subclasses should implement generateArray to generate the actual arrays of
+ things we're defining. They should also set self.regular to the list of
+ things exposed to web pages.
+ """
+ def __init__(self, descriptor, name):
+ self.descriptor = descriptor
+ self.name = name
+
+ def variableName(self):
+ return "s" + self.name
+
+ def length(self):
+ return len(self.regular)
+
+ def __str__(self):
+ # We only need to generate id arrays for things that will end
+ # up used via ResolveProperty or EnumerateProperties.
+ return self.generateArray(self.regular, self.variableName())
+
+ def generatePrefableArray(self, array, name, specTemplate, specTerminator,
+ specType, getDataTuple):
+ """
+ This method generates our various arrays.
+
+ array is an array of interface members as passed to generateArray
+
+ name is the name as passed to generateArray
+
+ specTemplate is a template for each entry of the spec array
+
+ specTerminator is a terminator for the spec array (inserted at the end
+ of the array), or None
+
+ specType is the actual typename of our spec
+
+ getDataTuple is a callback function that takes an array entry and
+ returns a tuple suitable for substitution into specTemplate.
+ """
+
+ assert(len(array) is not 0)
+ specs = []
+
+ for member in array:
+ specs.append(specTemplate % getDataTuple(member))
+ if specTerminator:
+ specs.append(specTerminator)
+
+ return (("static %s: &'static [%s] = &[\n" +
+ ",\n".join(specs) + "\n" +
+ "];\n\n") % (name, specType))
+
+# The length of a method is the maximum of the lengths of the
+# argument lists of all its overloads.
+def methodLength(method):
+ signatures = method.signatures()
+ return max([len(arguments) for (retType, arguments) in signatures])
+
+class MethodDefiner(PropertyDefiner):
+ """
+ A class for defining methods on a prototype object.
+ """
+ def __init__(self, descriptor, name, static):
+ PropertyDefiner.__init__(self, descriptor, name)
+
+ # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822
+ # We should be able to check for special operations without an
+ # identifier. For now we check if the name starts with __
+ methods = [m for m in descriptor.interface.members if
+ m.isMethod() and m.isStatic() == static and
+ not m.isIdentifierLess()]
+ self.regular = [{"name": m.identifier.name,
+ "methodInfo": not m.isStatic(),
+ "length": methodLength(m),
+ "flags": "JSPROP_ENUMERATE" }
+ for m in methods]
+
+ # FIXME Check for an existing iterator on the interface first.
+ if any(m.isGetter() and m.isIndexed() for m in methods):
+ self.regular.append({"name": 'iterator',
+ "methodInfo": False,
+ "nativeName": "JS_ArrayIterator",
+ "length": 0,
+ "flags": "JSPROP_ENUMERATE" })
+
+ def generateArray(self, array, name):
+ if len(array) == 0:
+ return ""
+
+ def specData(m):
+ if m.get("methodInfo", True):
+ jitinfo = ("&%s_methodinfo" % m["name"])
+ accessor = "genericMethod"
+ else:
+ jitinfo = "0 as *const JSJitInfo"
+ accessor = m.get("nativeName", m["name"])
+ return (m["name"], accessor, jitinfo, m["length"], m["flags"])
+
+ def stringDecl(m):
+ return "static %s_name: [u8, ..%i] = %s;\n" % (m["name"], len(m["name"]) + 1,
+ str_to_const_array(m["name"]))
+
+ decls = ''.join([stringDecl(m) for m in array])
+ return decls + self.generatePrefableArray(
+ array, name,
+ ' JSFunctionSpec {name: &%s_name as *const u8 as *const libc::c_char, call: JSNativeWrapper {op: Some(%s), info: %s}, nargs: %s, flags: %s as u16, selfHostedName: 0 as *const libc::c_char }',
+ ' JSFunctionSpec {name: 0 as *const libc::c_char, call: JSNativeWrapper {op: None, info: 0 as *const JSJitInfo}, nargs: 0, flags: 0, selfHostedName: 0 as *const libc::c_char }',
+ 'JSFunctionSpec',
+ specData)
+
+class AttrDefiner(PropertyDefiner):
+ def __init__(self, descriptor, name, static):
+ PropertyDefiner.__init__(self, descriptor, name)
+ self.name = name
+ self.regular = [
+ m
+ for m in descriptor.interface.members
+ if m.isAttr() and m.isStatic() == static
+ ]
+ self.static = static
+
+ def generateArray(self, array, name):
+ if len(array) == 0:
+ return ""
+
+ def flags(attr):
+ return "JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS"
+
+ def getter(attr):
+ if self.static:
+ accessor = 'get_' + attr.identifier.name
+ jitinfo = "0"
+ else:
+ if attr.hasLenientThis():
+ accessor = "genericLenientGetter"
+ else:
+ accessor = "genericGetter"
+ jitinfo = "&%s_getterinfo" % attr.identifier.name
+
+ return ("JSPropertyOpWrapper {op: Some(%(native)s), info: %(info)s as *const JSJitInfo}"
+ % {"info" : jitinfo,
+ "native" : accessor})
+
+ def setter(attr):
+ if attr.readonly:
+ return "JSStrictPropertyOpWrapper {op: None, info: 0 as *const JSJitInfo}"
+
+ if self.static:
+ accessor = 'set_' + attr.identifier.name
+ jitinfo = "0"
+ else:
+ if attr.hasLenientThis():
+ accessor = "genericLenientSetter"
+ else:
+ accessor = "genericSetter"
+ jitinfo = "&%s_setterinfo" % attr.identifier.name
+
+ return ("JSStrictPropertyOpWrapper {op: Some(%(native)s), info: %(info)s as *const JSJitInfo}"
+ % {"info" : jitinfo,
+ "native" : accessor})
+
+ def specData(attr):
+ return (attr.identifier.name, flags(attr), getter(attr),
+ setter(attr))
+
+ def stringDecl(attr):
+ name = attr.identifier.name
+ return "static %s_name: [u8, ..%i] = %s;\n" % (name, len(name) + 1,
+ str_to_const_array(name))
+
+ decls = ''.join([stringDecl(m) for m in array])
+
+ return decls + self.generatePrefableArray(
+ array, name,
+ ' JSPropertySpec { name: &%s_name as *const u8 as *const libc::c_char, tinyid: 0, flags: ((%s) & 0xFF) as u8, getter: %s, setter: %s }',
+ ' JSPropertySpec { name: 0 as *const libc::c_char, tinyid: 0, flags: 0, getter: JSPropertyOpWrapper {op: None, info: 0 as *const JSJitInfo}, setter: JSStrictPropertyOpWrapper {op: None, info: 0 as *const JSJitInfo} }',
+ 'JSPropertySpec',
+ specData)
+
+class ConstDefiner(PropertyDefiner):
+ """
+ A class for definining constants on the interface object
+ """
+ def __init__(self, descriptor, name):
+ PropertyDefiner.__init__(self, descriptor, name)
+ self.name = name
+ self.regular = [m for m in descriptor.interface.members if m.isConst()]
+
+ def generateArray(self, array, name):
+ if len(array) == 0:
+ return ""
+
+ def specData(const):
+ return (const.identifier.name,
+ convertConstIDLValueToJSVal(const.value))
+
+ def stringDecl(const):
+ name = const.identifier.name
+ return "static %s_name: &'static [u8] = &%s;\n" % (name, str_to_const_array(name))
+
+ decls = ''.join([stringDecl(m) for m in array])
+
+ return decls + self.generatePrefableArray(
+ array, name,
+ ' ConstantSpec { name: %s_name, value: %s }',
+ None,
+ 'ConstantSpec',
+ specData)
+
+# We'll want to insert the indent at the beginnings of lines, but we
+# don't want to indent empty lines. So only indent lines that have a
+# non-newline character on them.
+lineStartDetector = re.compile("^(?=[^\n])", re.MULTILINE)
+class CGIndenter(CGThing):
+ """
+ A class that takes another CGThing and generates code that indents that
+ CGThing by some number of spaces. The default indent is two spaces.
+ """
+ def __init__(self, child, indentLevel=2):
+ CGThing.__init__(self)
+ self.child = child
+ self.indent = " " * indentLevel
+
+ def define(self):
+ defn = self.child.define()
+ if defn is not "":
+ return re.sub(lineStartDetector, self.indent, defn)
+ else:
+ return defn
+
+class CGWrapper(CGThing):
+ """
+ Generic CGThing that wraps other CGThings with pre and post text.
+ """
+ def __init__(self, child, pre="", post="", reindent=False):
+ CGThing.__init__(self)
+ self.child = child
+ self.pre = pre
+ self.post = post
+ self.reindent = reindent
+
+ def define(self):
+ defn = self.child.define()
+ if self.reindent:
+ # We don't use lineStartDetector because we don't want to
+ # insert whitespace at the beginning of our _first_ line.
+ defn = stripTrailingWhitespace(
+ defn.replace("\n", "\n" + (" " * len(self.pre))))
+ return self.pre + defn + self.post
+
+class CGImports(CGWrapper):
+ """
+ Generates the appropriate import/use statements.
+ """
+ def __init__(self, child, descriptors, imports):
+ """
+ Adds a set of imports.
+ """
+ ignored_warnings = [
+ # Allow unreachable_code because we use 'break' in a way that
+ # sometimes produces two 'break's in a row. See for example
+ # CallbackMember.getArgConversions.
+ 'unreachable_code',
+ 'non_camel_case_types',
+ 'non_uppercase_statics',
+ 'unnecessary_parens',
+ 'unused_imports',
+ 'unused_variable',
+ 'unused_unsafe',
+ 'unused_mut',
+ 'dead_assignment',
+ 'dead_code',
+ ]
+
+ statements = ['#![allow(%s)]' % ','.join(ignored_warnings)]
+ statements.extend('use %s;' % i for i in sorted(imports))
+
+ CGWrapper.__init__(self, child,
+ pre='\n'.join(statements) + '\n\n')
+
+ @staticmethod
+ def getDeclarationFilename(decl):
+ # Use our local version of the header, not the exported one, so that
+ # test bindings, which don't export, will work correctly.
+ basename = os.path.basename(decl.filename())
+ return basename.replace('.webidl', 'Binding.rs')
+
+class CGIfWrapper(CGWrapper):
+ def __init__(self, child, condition):
+ pre = CGWrapper(CGGeneric(condition), pre="if ", post=" {\n",
+ reindent=True)
+ CGWrapper.__init__(self, CGIndenter(child), pre=pre.define(),
+ post="\n}")
+
+class CGTemplatedType(CGWrapper):
+ def __init__(self, templateName, child):
+ CGWrapper.__init__(self, child, pre=templateName + "<", post=">")
+
+class CGNamespace(CGWrapper):
+ def __init__(self, namespace, child, public=False):
+ pre = "%smod %s {\n" % ("pub " if public else "", namespace)
+ post = "} // mod %s\n" % namespace
+ CGWrapper.__init__(self, child, pre=pre, post=post)
+
+ @staticmethod
+ def build(namespaces, child, public=False):
+ """
+ Static helper method to build multiple wrapped namespaces.
+ """
+ if not namespaces:
+ return child
+ inner = CGNamespace.build(namespaces[1:], child, public=public)
+ return CGNamespace(namespaces[0], inner, public=public)
+
+def DOMClass(descriptor):
+ protoList = ['PrototypeList::id::' + proto for proto in descriptor.prototypeChain]
+ # Pad out the list to the right length with IDCount so we
+ # guarantee that all the lists are the same length. IDCount
+ # is never the ID of any prototype, so it's safe to use as
+ # padding.
+ protoList.extend(['PrototypeList::id::IDCount'] * (descriptor.config.maxProtoChainLength - len(protoList)))
+ prototypeChainString = ', '.join(protoList)
+ return """DOMClass {
+ interface_chain: [ %s ],
+ native_hooks: &sNativePropertyHooks,
+}""" % prototypeChainString
+
+class CGDOMJSClass(CGThing):
+ """
+ Generate a DOMJSClass for a given descriptor
+ """
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+
+ def define(self):
+ traceHook = "Some(%s)" % TRACE_HOOK_NAME
+ if self.descriptor.isGlobal():
+ flags = "JSCLASS_IS_GLOBAL | JSCLASS_DOM_GLOBAL"
+ slots = "JSCLASS_GLOBAL_SLOT_COUNT + 1"
+ else:
+ flags = "0"
+ slots = "1"
+ return """
+static Class_name: [u8, ..%i] = %s;
+static Class: DOMJSClass = DOMJSClass {
+ base: js::Class {
+ name: &Class_name as *const u8 as *const libc::c_char,
+ flags: JSCLASS_IS_DOMJSCLASS | %s | (((%s) & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT as uint), //JSCLASS_HAS_RESERVED_SLOTS(%s),
+ addProperty: Some(JS_PropertyStub),
+ delProperty: Some(JS_PropertyStub),
+ getProperty: Some(JS_PropertyStub),
+ setProperty: Some(JS_StrictPropertyStub),
+ enumerate: Some(JS_EnumerateStub),
+ resolve: Some(JS_ResolveStub),
+ convert: Some(JS_ConvertStub),
+ finalize: Some(%s),
+ checkAccess: None,
+ call: None,
+ hasInstance: None,
+ construct: None,
+ trace: %s,
+
+ ext: js::ClassExtension {
+ equality: 0 as *const u8,
+ outerObject: %s,
+ innerObject: None,
+ iteratorObject: 0 as *const u8,
+ unused: 0 as *const u8,
+ isWrappedNative: 0 as *const u8,
+ },
+
+ ops: js::ObjectOps {
+ lookupGeneric: 0 as *const u8,
+ lookupProperty: 0 as *const u8,
+ lookupElement: 0 as *const u8,
+ lookupSpecial: 0 as *const u8,
+ defineGeneric: 0 as *const u8,
+ defineProperty: 0 as *const u8,
+ defineElement: 0 as *const u8,
+ defineSpecial: 0 as *const u8,
+ getGeneric: 0 as *const u8,
+ getProperty: 0 as *const u8,
+ getElement: 0 as *const u8,
+ getElementIfPresent: 0 as *const u8,
+ getSpecial: 0 as *const u8,
+ setGeneric: 0 as *const u8,
+ setProperty: 0 as *const u8,
+ setElement: 0 as *const u8,
+ setSpecial: 0 as *const u8,
+ getGenericAttributes: 0 as *const u8,
+ getPropertyAttributes: 0 as *const u8,
+ getElementAttributes: 0 as *const u8,
+ getSpecialAttributes: 0 as *const u8,
+ setGenericAttributes: 0 as *const u8,
+ setPropertyAttributes: 0 as *const u8,
+ setElementAttributes: 0 as *const u8,
+ setSpecialAttributes: 0 as *const u8,
+ deleteProperty: 0 as *const u8,
+ deleteElement: 0 as *const u8,
+ deleteSpecial: 0 as *const u8,
+
+ enumerate: 0 as *const u8,
+ typeOf: 0 as *const u8,
+ thisObject: %s,
+ clear: 0 as *const u8,
+ },
+ },
+ dom_class: %s
+};
+""" % (len(self.descriptor.interface.identifier.name) + 1,
+ str_to_const_array(self.descriptor.interface.identifier.name),
+ flags, slots, slots,
+ FINALIZE_HOOK_NAME, traceHook,
+ self.descriptor.outerObjectHook,
+ self.descriptor.outerObjectHook,
+ CGIndenter(CGGeneric(DOMClass(self.descriptor))).define())
+
+def str_to_const_array(s):
+ return "[" + (", ".join(map(lambda x: "'" + x + "' as u8", list(s)) + ['0 as u8'])) + "]"
+
+class CGPrototypeJSClass(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+
+ def define(self):
+ return """
+static PrototypeClassName__: [u8, ..%s] = %s;
+static PrototypeClass: JSClass = JSClass {
+ name: &PrototypeClassName__ as *const u8 as *const libc::c_char,
+ flags: (1 & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT as uint, //JSCLASS_HAS_RESERVED_SLOTS(1)
+ addProperty: Some(JS_PropertyStub),
+ delProperty: Some(JS_PropertyStub),
+ getProperty: Some(JS_PropertyStub),
+ setProperty: Some(JS_StrictPropertyStub),
+ enumerate: Some(JS_EnumerateStub),
+ resolve: Some(JS_ResolveStub),
+ convert: Some(JS_ConvertStub),
+ finalize: None,
+ checkAccess: None,
+ call: None,
+ hasInstance: None,
+ construct: None,
+ trace: None,
+ reserved: [0 as *mut libc::c_void, ..40]
+};
+""" % (len(self.descriptor.interface.identifier.name + "Prototype") + 1,
+ str_to_const_array(self.descriptor.interface.identifier.name + "Prototype"))
+
+class CGInterfaceObjectJSClass(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+
+ def define(self):
+ if True:
+ return ""
+ ctorname = "0 as *const u8" if not self.descriptor.interface.ctor() else CONSTRUCT_HOOK_NAME
+ hasinstance = HASINSTANCE_HOOK_NAME
+ return """
+static InterfaceObjectClass: JSClass = {
+ %s, 0,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_PropertyStub,
+ JS_StrictPropertyStub,
+ JS_EnumerateStub,
+ JS_ResolveStub,
+ JS_ConvertStub,
+ 0 as *const u8,
+ 0 as *const u8,
+ %s,
+ %s,
+ %s,
+ 0 as *const u8,
+ JSCLASS_NO_INTERNAL_MEMBERS
+};
+""" % (str_to_const_array("Function"), ctorname, hasinstance, ctorname)
+
+class CGList(CGThing):
+ """
+ Generate code for a list of GCThings. Just concatenates them together, with
+ an optional joiner string. "\n" is a common joiner.
+ """
+ def __init__(self, children, joiner=""):
+ CGThing.__init__(self)
+ self.children = children
+ self.joiner = joiner
+ def append(self, child):
+ self.children.append(child)
+ def prepend(self, child):
+ self.children.insert(0, child)
+ def join(self, generator):
+ return self.joiner.join(filter(lambda s: len(s) > 0, (child for child in generator)))
+
+ def define(self):
+ return self.join(child.define() for child in self.children if child is not None)
+
+
+class CGIfElseWrapper(CGList):
+ def __init__(self, condition, ifTrue, ifFalse):
+ kids = [ CGIfWrapper(ifTrue, condition),
+ CGWrapper(CGIndenter(ifFalse), pre=" else {\n", post="\n}") ]
+ CGList.__init__(self, kids)
+
+
+class CGGeneric(CGThing):
+ """
+ A class that spits out a fixed string into the codegen. Can spit out a
+ separate string for the declaration too.
+ """
+ def __init__(self, text):
+ self.text = text
+
+ def define(self):
+ return self.text
+
+class CGCallbackTempRoot(CGGeneric):
+ def __init__(self, name):
+ val = "%s::new(tempRoot)" % name
+ define = """{
+ let tempRoot = ${val}.to_object();
+ %s
+}""" % val
+ CGGeneric.__init__(self, define)
+
+
+def getAllTypes(descriptors, dictionaries, callbacks):
+ """
+ Generate all the types we're dealing with. For each type, a tuple
+ containing type, descriptor, dictionary is yielded. The
+ descriptor and dictionary can be None if the type does not come
+ from a descriptor or dictionary; they will never both be non-None.
+ """
+ for d in descriptors:
+ for t in getTypesFromDescriptor(d):
+ yield (t, d, None)
+ for dictionary in dictionaries:
+ for t in getTypesFromDictionary(dictionary):
+ yield (t, None, dictionary)
+ for callback in callbacks:
+ for t in getTypesFromCallback(callback):
+ yield (t, None, None)
+
+def SortedTuples(l):
+ """
+ Sort a list of tuples based on the first item in the tuple
+ """
+ return sorted(l, key=operator.itemgetter(0))
+
+def SortedDictValues(d):
+ """
+ Returns a list of values from the dict sorted by key.
+ """
+ # Create a list of tuples containing key and value, sorted on key.
+ d = SortedTuples(d.items())
+ # We're only interested in the values.
+ return (i[1] for i in d)
+
+def UnionTypes(descriptors, dictionaries, callbacks, config):
+ """
+ Returns a CGList containing CGUnionStructs for every union.
+ """
+
+ imports = [
+ 'dom::bindings::utils::unwrap_jsmanaged',
+ 'dom::bindings::codegen::PrototypeList',
+ 'dom::bindings::conversions::FromJSValConvertible',
+ 'dom::bindings::conversions::ToJSValConvertible',
+ 'dom::bindings::conversions::Default',
+ 'dom::bindings::error::throw_not_in_union',
+ 'dom::bindings::js::JS',
+ 'dom::types::*',
+ 'js::jsapi::JSContext',
+ 'js::jsval::JSVal',
+ 'servo_util::str::DOMString',
+ ]
+
+ # Now find all the things we'll need as arguments and return values because
+ # we need to wrap or unwrap them.
+ unionStructs = dict()
+ for (t, descriptor, dictionary) in getAllTypes(descriptors, dictionaries, callbacks):
+ assert not descriptor or not dictionary
+ t = t.unroll()
+ if not t.isUnion():
+ continue
+ name = str(t)
+ if not name in unionStructs:
+ provider = descriptor or config.getDescriptorProvider()
+ unionStructs[name] = CGNamespace(name,
+ CGImports(CGList([
+ CGUnionStruct(t, provider),
+ CGUnionConversionStruct(t, provider)
+ ]), [], imports),
+ public=True)
+
+ return CGList(SortedDictValues(unionStructs), "\n\n")
+
+
+class Argument():
+ """
+ A class for outputting the type and name of an argument
+ """
+ def __init__(self, argType, name, default=None, mutable=False):
+ self.argType = argType
+ self.name = name
+ self.default = default
+ self.mutable = mutable
+ def declare(self):
+ string = ('mut ' if self.mutable else '') + self.name + ((': ' + self.argType) if self.argType else '')
+ #XXXjdm Support default arguments somehow :/
+ #if self.default is not None:
+ # string += " = " + self.default
+ return string
+ def define(self):
+ return self.argType + ' ' + self.name
+
+class CGAbstractMethod(CGThing):
+ """
+ An abstract class for generating code for a method. Subclasses
+ should override definition_body to create the actual code.
+
+ descriptor is the descriptor for the interface the method is associated with
+
+ name is the name of the method as a string
+
+ returnType is the IDLType of the return value
+
+ args is a list of Argument objects
+
+ inline should be True to generate an inline method, whose body is
+ part of the declaration.
+
+ alwaysInline should be True to generate an inline method annotated with
+ MOZ_ALWAYS_INLINE.
+
+ If templateArgs is not None it should be a list of strings containing
+ template arguments, and the function will be templatized using those
+ arguments.
+ """
+ def __init__(self, descriptor, name, returnType, args, inline=False, alwaysInline=False, extern=False, pub=False, templateArgs=None, unsafe=True):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ self.name = name
+ self.returnType = returnType
+ self.args = args
+ self.alwaysInline = alwaysInline
+ self.extern = extern
+ self.templateArgs = templateArgs
+ self.pub = pub;
+ self.unsafe = unsafe
+ def _argstring(self):
+ return ', '.join([a.declare() for a in self.args])
+ def _template(self):
+ if self.templateArgs is None:
+ return ''
+ return '<%s>\n' % ', '.join(self.templateArgs)
+
+ def _decorators(self):
+ decorators = []
+ if self.alwaysInline:
+ decorators.append('#[inline(always)]')
+
+ if self.extern:
+ decorators.append('extern')
+
+ if self.pub:
+ decorators.append('pub')
+
+ if not decorators:
+ return ''
+ return ' '.join(decorators) + ' '
+
+ def _returnType(self):
+ return (" -> %s" % self.returnType) if self.returnType != "void" else ""
+
+ def define(self):
+ body = self.definition_body()
+ if self.unsafe:
+ body = CGWrapper(body, pre="unsafe {\n", post="\n}")
+
+ return CGWrapper(CGIndenter(body),
+ pre=self.definition_prologue(),
+ post=self.definition_epilogue()).define()
+
+ def definition_prologue(self):
+ return "%sfn %s%s(%s)%s {\n" % (self._decorators(), self.name, self._template(),
+ self._argstring(), self._returnType())
+ def definition_epilogue(self):
+ return "\n}\n"
+ def definition_body(self):
+ assert(False) # Override me!
+
+def CreateBindingJSObject(descriptor, parent=None):
+ create = "let mut raw: JS<%s> = JS::from_raw(&*aObject);\n" % descriptor.concreteType
+ if descriptor.proxy:
+ assert not descriptor.isGlobal()
+ create += """
+let handler = RegisterBindings::proxy_handlers[PrototypeList::proxies::%s as uint];
+let mut private = PrivateValue(squirrel_away_unique(aObject) as *const libc::c_void);
+let obj = with_compartment(aCx, proto, || {
+ NewProxyObject(aCx, handler,
+ &private,
+ proto, %s,
+ ptr::mut_null(), ptr::mut_null())
+});
+assert!(obj.is_not_null());
+
+""" % (descriptor.name, parent)
+ else:
+ if descriptor.isGlobal():
+ create += "let obj = CreateDOMGlobal(aCx, &Class.base as *const js::Class as *const JSClass);\n"
+ else:
+ create += ("let obj = with_compartment(aCx, proto, || {\n"
+ " JS_NewObject(aCx, &Class.base as *const js::Class as *const JSClass, &*proto, &*%s)\n"
+ "});\n" % parent)
+ create += """assert!(obj.is_not_null());
+
+JS_SetReservedSlot(obj, DOM_OBJECT_SLOT as u32,
+ PrivateValue(squirrel_away_unique(aObject) as *const libc::c_void));
+"""
+ return create
+
+class CGWrapMethod(CGAbstractMethod):
+ """
+ Class that generates the FooBinding::Wrap function for non-callback
+ interfaces.
+ """
+ def __init__(self, descriptor):
+ assert not descriptor.interface.isCallback()
+ if not descriptor.isGlobal():
+ args = [Argument('*mut JSContext', 'aCx'), Argument('&GlobalRef', 'aScope'),
+ Argument("Box<%s>" % descriptor.concreteType, 'aObject', mutable=True)]
+ else:
+ args = [Argument('*mut JSContext', 'aCx'),
+ Argument("Box<%s>" % descriptor.concreteType, 'aObject', mutable=True)]
+ retval = 'Temporary<%s>' % descriptor.concreteType
+ CGAbstractMethod.__init__(self, descriptor, 'Wrap', retval, args, pub=True)
+
+ def definition_body(self):
+ if not self.descriptor.isGlobal():
+ return CGGeneric("""\
+let scope = aScope.reflector().get_jsobject();
+assert!(scope.is_not_null());
+assert!(((*JS_GetClass(scope)).flags & JSCLASS_IS_GLOBAL) != 0);
+
+let proto = with_compartment(aCx, scope, || GetProtoObject(aCx, scope, scope));
+assert!(proto.is_not_null());
+
+%s
+
+raw.reflector().set_jsobject(obj);
+
+Temporary::new(raw)""" % CreateBindingJSObject(self.descriptor, "scope"))
+ else:
+ return CGGeneric("""\
+%s
+with_compartment(aCx, obj, || {
+ let proto = GetProtoObject(aCx, obj, obj);
+ JS_SetPrototype(aCx, obj, proto);
+
+ raw.reflector().set_jsobject(obj);
+
+ RegisterBindings::Register(aCx, obj);
+});
+
+Temporary::new(raw)""" % CreateBindingJSObject(self.descriptor))
+
+
+class CGIDLInterface(CGThing):
+ """
+ Class for codegen of an implementation of the IDLInterface trait.
+ """
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+
+ def define(self):
+ replacer = {
+ 'type': self.descriptor.name,
+ 'depth': self.descriptor.interface.inheritanceDepth(),
+ }
+ return string.Template("""
+impl IDLInterface for ${type} {
+ fn get_prototype_id(_: Option<${type}>) -> PrototypeList::id::ID {
+ PrototypeList::id::${type}
+ }
+ fn get_prototype_depth(_: Option<${type}>) -> uint {
+ ${depth}
+ }
+}
+""").substitute(replacer)
+
+
+class CGAbstractExternMethod(CGAbstractMethod):
+ """
+ Abstract base class for codegen of implementation-only (no
+ declaration) static methods.
+ """
+ def __init__(self, descriptor, name, returnType, args):
+ CGAbstractMethod.__init__(self, descriptor, name, returnType, args,
+ inline=False, extern=True)
+
+class PropertyArrays():
+ def __init__(self, descriptor):
+ self.staticMethods = MethodDefiner(descriptor, "StaticMethods",
+ static=True)
+ self.staticAttrs = AttrDefiner(descriptor, "StaticAttributes",
+ static=True)
+ self.methods = MethodDefiner(descriptor, "Methods", static=False)
+ self.attrs = AttrDefiner(descriptor, "Attributes", static=False)
+ self.consts = ConstDefiner(descriptor, "Constants")
+ pass
+
+ @staticmethod
+ def arrayNames():
+ return [ "staticMethods", "staticAttrs", "methods", "attrs", "consts" ]
+
+ def variableNames(self):
+ names = {}
+ for array in self.arrayNames():
+ names[array] = getattr(self, array).variableName()
+ return names
+ def __str__(self):
+ define = ""
+ for array in self.arrayNames():
+ define += str(getattr(self, array))
+ return define
+
+
+class CGNativeProperties(CGThing):
+ def __init__(self, descriptor, properties):
+ CGThing.__init__(self)
+ self.properties = properties
+
+ def define(self):
+ def getField(array):
+ propertyArray = getattr(self.properties, array)
+ if propertyArray.length() > 0:
+ value = "Some(%s)" % propertyArray.variableName()
+ else:
+ value = "None"
+
+ return CGGeneric(string.Template('${name}: ${value},').substitute({
+ 'name': array,
+ 'value': value,
+ }))
+
+ nativeProps = CGList([getField(array) for array in self.properties.arrayNames()], '\n')
+ return CGWrapper(CGIndenter(nativeProps),
+ pre="static sNativeProperties: NativeProperties = NativeProperties {\n",
+ post="\n};\n").define()
+
+
+class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
+ """
+ Generate the CreateInterfaceObjects method for an interface descriptor.
+
+ properties should be a PropertyArrays instance.
+ """
+ def __init__(self, descriptor, properties):
+ assert not descriptor.interface.isCallback()
+ args = [Argument('*mut JSContext', 'aCx'), Argument('*mut JSObject', 'aGlobal'),
+ Argument('*mut JSObject', 'aReceiver')]
+ CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', '*mut JSObject', args)
+ self.properties = properties
+ def definition_body(self):
+ protoChain = self.descriptor.prototypeChain
+ if len(protoChain) == 1:
+ getParentProto = "JS_GetObjectPrototype(aCx, aGlobal)"
+ else:
+ parentProtoName = self.descriptor.prototypeChain[-2]
+ getParentProto = ("%s::GetProtoObject(aCx, aGlobal, aReceiver)" %
+ toBindingNamespace(parentProtoName))
+
+ getParentProto = ("let parentProto: *mut JSObject = %s;\n"
+ "assert!(parentProto.is_not_null());\n") % getParentProto
+
+ if self.descriptor.concrete:
+ if self.descriptor.proxy:
+ domClass = "&Class"
+ else:
+ domClass = "&Class.dom_class"
+ else:
+ domClass = "ptr::null()"
+
+ if self.descriptor.interface.hasInterfaceObject():
+ if self.descriptor.interface.ctor():
+ constructHook = CONSTRUCT_HOOK_NAME
+ constructArgs = methodLength(self.descriptor.interface.ctor())
+ else:
+ constructHook = "ThrowingConstructor"
+ constructArgs = 0
+
+ constructor = 'Some((%s, "%s", %d))' % (
+ constructHook, self.descriptor.interface.identifier.name,
+ constructArgs)
+ else:
+ constructor = 'None'
+
+ call = """return CreateInterfaceObjects2(aCx, aGlobal, aReceiver, parentProto,
+ &PrototypeClass, %s,
+ %s,
+ &sNativeProperties);""" % (constructor, domClass)
+
+ return CGList([
+ CGGeneric(getParentProto),
+ CGGeneric(call % self.properties.variableNames())
+ ], "\n")
+
+class CGGetPerInterfaceObject(CGAbstractMethod):
+ """
+ A method for getting a per-interface object (a prototype object or interface
+ constructor object).
+ """
+ def __init__(self, descriptor, name, idPrefix="", pub=False):
+ args = [Argument('*mut JSContext', 'aCx'), Argument('*mut JSObject', 'aGlobal'),
+ Argument('*mut JSObject', 'aReceiver')]
+ CGAbstractMethod.__init__(self, descriptor, name,
+ '*mut JSObject', args, pub=pub)
+ self.id = idPrefix + "id::" + self.descriptor.name
+ def definition_body(self):
+ return CGGeneric("""
+
+/* aGlobal and aReceiver are usually the same, but they can be different
+ too. For example a sandbox often has an xray wrapper for a window as the
+ prototype of the sandbox's global. In that case aReceiver is the xray
+ wrapper and aGlobal is the sandbox's global.
+ */
+
+assert!(((*JS_GetClass(aGlobal)).flags & JSCLASS_DOM_GLOBAL) != 0);
+
+/* Check to see whether the interface objects are already installed */
+let protoOrIfaceArray = GetProtoOrIfaceArray(aGlobal);
+let cachedObject: *mut JSObject = *protoOrIfaceArray.offset(%s as int);
+if cachedObject.is_null() {
+ let tmp: *mut JSObject = CreateInterfaceObjects(aCx, aGlobal, aReceiver);
+ assert!(tmp.is_not_null());
+ *protoOrIfaceArray.offset(%s as int) = tmp;
+ tmp
+} else {
+ cachedObject
+}""" % (self.id, self.id))
+
+class CGGetProtoObjectMethod(CGGetPerInterfaceObject):
+ """
+ A method for getting the interface prototype object.
+ """
+ def __init__(self, descriptor):
+ CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject",
+ "PrototypeList::", pub=True)
+ def definition_body(self):
+ return CGList([
+ CGGeneric("""\
+/* Get the interface prototype object for this class. This will create the
+ object as needed. */"""),
+ CGGetPerInterfaceObject.definition_body(self),
+ ])
+
+class CGGetConstructorObjectMethod(CGGetPerInterfaceObject):
+ """
+ A method for getting the interface constructor object.
+ """
+ def __init__(self, descriptor):
+ CGGetPerInterfaceObject.__init__(self, descriptor, "GetConstructorObject",
+ "constructors::")
+ def definition_body(self):
+ return CGList([
+ CGGeneric("""\
+/* Get the interface object for this class. This will create the object as
+ needed. */"""),
+ CGGetPerInterfaceObject.definition_body(self),
+ ])
+
+
+class CGDefineProxyHandler(CGAbstractMethod):
+ """
+ A method to create and cache the proxy trap for a given interface.
+ """
+ def __init__(self, descriptor):
+ assert descriptor.proxy
+ CGAbstractMethod.__init__(self, descriptor, 'DefineProxyHandler', '*const libc::c_void', [], pub=True)
+
+ def define(self):
+ return CGAbstractMethod.define(self)
+
+ def definition_body(self):
+ body = """\
+let traps = ProxyTraps {
+ getPropertyDescriptor: Some(getPropertyDescriptor),
+ getOwnPropertyDescriptor: Some(getOwnPropertyDescriptor),
+ defineProperty: Some(defineProperty),
+ getOwnPropertyNames: ptr::null(),
+ delete_: Some(delete_),
+ enumerate: ptr::null(),
+
+ has: None,
+ hasOwn: Some(hasOwn),
+ get: Some(get),
+ set: None,
+ keys: ptr::null(),
+ iterate: None,
+
+ call: None,
+ construct: None,
+ nativeCall: ptr::null(),
+ hasInstance: None,
+ typeOf: None,
+ objectClassIs: None,
+ obj_toString: Some(obj_toString),
+ fun_toString: None,
+ //regexp_toShared: ptr::null(),
+ defaultValue: None,
+ iteratorNext: None,
+ finalize: Some(%s),
+ getElementIfPresent: None,
+ getPrototypeOf: None,
+ trace: Some(%s)
+};
+
+CreateProxyHandler(&traps, &Class as *const _ as *const _)
+""" % (FINALIZE_HOOK_NAME,
+ TRACE_HOOK_NAME)
+ return CGGeneric(body)
+
+
+
+class CGDefineDOMInterfaceMethod(CGAbstractMethod):
+ """
+ A method for resolve hooks to try to lazily define the interface object for
+ a given interface.
+ """
+ def __init__(self, descriptor):
+ assert descriptor.interface.hasInterfaceObject()
+ args = [
+ Argument('*mut JSContext', 'cx'),
+ Argument('*mut JSObject', 'global'),
+ ]
+ CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface', 'void', args, pub=True)
+
+ def define(self):
+ return CGAbstractMethod.define(self)
+
+ def definition_body(self):
+ return CGGeneric("""\
+assert!(global.is_not_null());
+assert!(GetProtoObject(cx, global, global).is_not_null());""")
+
+def needCx(returnType, arguments, considerTypes):
+ return (considerTypes and
+ (typeNeedsCx(returnType, True) or
+ any(typeNeedsCx(a.type) for a in arguments)))
+
+class CGCallGenerator(CGThing):
+ """
+ A class to generate an actual call to a C++ object. Assumes that the C++
+ object is stored in a variable whose name is given by the |object| argument.
+
+ errorResult should be a string for the value to return in case of an
+ exception from the native code, or None if no error reporting is needed.
+ """
+ def __init__(self, errorResult, arguments, argsPre, returnType,
+ extendedAttributes, descriptorProvider, nativeMethodName,
+ static, object="this"):
+ CGThing.__init__(self)
+
+ assert errorResult is None or isinstance(errorResult, str)
+
+ isFallible = errorResult is not None
+
+ result = getRetvalDeclarationForType(returnType, descriptorProvider)
+ if isFallible:
+ result = CGWrapper(result, pre="Result<", post=", Error>")
+
+ args = CGList([CGGeneric(arg) for arg in argsPre], ", ")
+ for (a, name) in arguments:
+ #XXXjdm Perhaps we should pass all nontrivial types by borrowed pointer
+ if a.type.isGeckoInterface():
+ if not (a.type.nullable() or a.optional):
+ name = "&" + name
+ elif a.type.isDictionary():
+ name = "&" + name
+ args.append(CGGeneric(name))
+
+ needsCx = needCx(returnType, (a for (a, _) in arguments), True)
+
+ if not "cx" in argsPre and needsCx:
+ args.prepend(CGGeneric("cx"))
+
+ # Build up our actual call
+ self.cgRoot = CGList([], "\n")
+
+ call = CGGeneric(nativeMethodName)
+ if static:
+ call = CGWrapper(call, pre="%s::" % descriptorProvider.interface.identifier.name)
+ else:
+ call = CGWrapper(call, pre="(*%s)." % object)
+ call = CGList([call, CGWrapper(args, pre="(", post=")")])
+
+ self.cgRoot.append(CGList([
+ CGGeneric("let result: "),
+ result,
+ CGGeneric(" = "),
+ call,
+ CGGeneric(";"),
+ ]))
+
+ if isFallible:
+ if static:
+ glob = ""
+ else:
+ glob = " let global = global_object_for_js_object(this.reflector().get_jsobject());\n"\
+ " let global = global.root();\n"
+
+ self.cgRoot.append(CGGeneric(
+ "let result = match result {\n"
+ " Ok(result) => result,\n"
+ " Err(e) => {\n"
+ "%s"
+ " throw_dom_exception(cx, &global.root_ref(), e);\n"
+ " return%s;\n"
+ " },\n"
+ "};\n" % (glob, errorResult)))
+
+ if typeRetValNeedsRooting(returnType):
+ self.cgRoot.append(CGGeneric("let result = result.root();"))
+
+ def define(self):
+ return self.cgRoot.define()
+
+class MethodNotCreatorError(Exception):
+ def __init__(self, typename):
+ self.typename = typename
+
+class CGPerSignatureCall(CGThing):
+ """
+ This class handles the guts of generating code for a particular
+ call signature. A call signature consists of four things:
+
+ 1) A return type, which can be None to indicate that there is no
+ actual return value (e.g. this is an attribute setter) or an
+ IDLType if there's an IDL type involved (including |void|).
+ 2) An argument list, which is allowed to be empty.
+ 3) A name of a native method to call.
+ 4) Whether or not this method is static.
+
+ We also need to know whether this is a method or a getter/setter
+ to do error reporting correctly.
+
+ The idlNode parameter can be either a method or an attr. We can query
+ |idlNode.identifier| in both cases, so we can be agnostic between the two.
+ """
+ # XXXbz For now each entry in the argument list is either an
+ # IDLArgument or a FakeArgument, but longer-term we may want to
+ # have ways of flagging things like JSContext* or optional_argc in
+ # there.
+
+ def __init__(self, returnType, argsPre, arguments, nativeMethodName, static,
+ descriptor, idlNode, argConversionStartsAt=0,
+ getter=False, setter=False):
+ CGThing.__init__(self)
+ self.returnType = returnType
+ self.descriptor = descriptor
+ self.idlNode = idlNode
+ self.extendedAttributes = descriptor.getExtendedAttributes(idlNode,
+ getter=getter,
+ setter=setter)
+ self.argsPre = argsPre
+ self.arguments = arguments
+ self.argCount = len(arguments)
+ if self.argCount > argConversionStartsAt:
+ # Insert our argv in there
+ cgThings = [CGGeneric(self.getArgvDecl())]
+ else:
+ cgThings = []
+ cgThings.extend([CGArgumentConverter(arguments[i], i, self.getArgv(),
+ self.getArgc(), self.descriptor,
+ invalidEnumValueFatal=not setter) for
+ i in range(argConversionStartsAt, self.argCount)])
+
+ cgThings.append(CGCallGenerator(
+ ' false as JSBool' if self.isFallible() else None,
+ self.getArguments(), self.argsPre, returnType,
+ self.extendedAttributes, descriptor, nativeMethodName,
+ static))
+ self.cgRoot = CGList(cgThings, "\n")
+
+ def getArgv(self):
+ return "argv" if self.argCount > 0 else ""
+ def getArgvDecl(self):
+ return "\nlet argv = JS_ARGV(cx, vp);\n"
+ def getArgc(self):
+ return "argc"
+ def getArguments(self):
+ def process(arg, i):
+ argVal = "arg" + str(i)
+ if arg.type.isGeckoInterface() and not arg.type.unroll().inner.isCallback():
+ argVal += ".root_ref()"
+ return argVal
+ return [(a, process(a, i)) for (i, a) in enumerate(self.arguments)]
+
+ def isFallible(self):
+ return not 'infallible' in self.extendedAttributes
+
+ def wrap_return_value(self):
+ return wrapForType('*vp')
+
+ def define(self):
+ return (self.cgRoot.define() + "\n" + self.wrap_return_value())
+
+class CGSwitch(CGList):
+ """
+ A class to generate code for a switch statement.
+
+ Takes three constructor arguments: an expression, a list of cases,
+ and an optional default.
+
+ Each case is a CGCase. The default is a CGThing for the body of
+ the default case, if any.
+ """
+ def __init__(self, expression, cases, default=None):
+ CGList.__init__(self, [CGIndenter(c) for c in cases], "\n")
+ self.prepend(CGWrapper(CGGeneric(expression),
+ pre="match ", post=" {"));
+ if default is not None:
+ self.append(
+ CGIndenter(
+ CGWrapper(
+ CGIndenter(default),
+ pre="_ => {\n",
+ post="\n}"
+ )
+ )
+ )
+
+ self.append(CGGeneric("}"))
+
+class CGCase(CGList):
+ """
+ A class to generate code for a case statement.
+
+ Takes three constructor arguments: an expression, a CGThing for
+ the body (allowed to be None if there is no body), and an optional
+ argument (defaulting to False) for whether to fall through.
+ """
+ def __init__(self, expression, body, fallThrough=False):
+ CGList.__init__(self, [], "\n")
+ self.append(CGWrapper(CGGeneric(expression), post=" => {"))
+ bodyList = CGList([body], "\n")
+ if fallThrough:
+ raise TypeError("fall through required but unsupported")
+ #bodyList.append(CGGeneric('fail!("fall through unsupported"); /* Fall through */'))
+ self.append(CGIndenter(bodyList));
+ self.append(CGGeneric("}"))
+
+class CGGetterCall(CGPerSignatureCall):
+ """
+ A class to generate a native object getter call for a particular IDL
+ getter.
+ """
+ def __init__(self, argsPre, returnType, nativeMethodName, descriptor, attr):
+ CGPerSignatureCall.__init__(self, returnType, argsPre, [],
+ nativeMethodName, attr.isStatic(), descriptor,
+ attr, getter=True)
+
+class FakeArgument():
+ """
+ A class that quacks like an IDLArgument. This is used to make
+ setters look like method calls or for special operations.
+ """
+ def __init__(self, type, interfaceMember, allowTreatNonObjectAsNull=False):
+ self.type = type
+ self.optional = False
+ self.variadic = False
+ self.defaultValue = None
+ self._allowTreatNonObjectAsNull = allowTreatNonObjectAsNull
+ self.treatNullAs = interfaceMember.treatNullAs
+ self.enforceRange = False
+ self.clamp = False
+
+ def allowTreatNonCallableAsNull(self):
+ return self._allowTreatNonObjectAsNull
+
+class CGSetterCall(CGPerSignatureCall):
+ """
+ A class to generate a native object setter call for a particular IDL
+ setter.
+ """
+ def __init__(self, argsPre, argType, nativeMethodName, descriptor, attr):
+ CGPerSignatureCall.__init__(self, None, argsPre,
+ [FakeArgument(argType, attr, allowTreatNonObjectAsNull=True)],
+ nativeMethodName, attr.isStatic(), descriptor, attr,
+ setter=True)
+ def wrap_return_value(self):
+ # We have no return value
+ return "\nreturn 1;"
+ def getArgc(self):
+ return "1"
+ def getArgvDecl(self):
+ # We just get our stuff from our last arg no matter what
+ return ""
+
+class CGAbstractBindingMethod(CGAbstractExternMethod):
+ """
+ Common class to generate the JSNatives for all our methods, getters, and
+ setters. This will generate the function declaration and unwrap the
+ |this| object. Subclasses are expected to override the generate_code
+ function to do the rest of the work. This function should return a
+ CGThing which is already properly indented.
+ """
+ def __init__(self, descriptor, name, args, unwrapFailureCode=None):
+ CGAbstractExternMethod.__init__(self, descriptor, name, "JSBool", args)
+
+ if unwrapFailureCode is None:
+ self.unwrapFailureCode = (
+ 'throw_type_error(cx, "\\"this\\" object does not '
+ 'implement interface %s.");\n'
+ 'return 0;' % descriptor.interface.identifier.name)
+ else:
+ self.unwrapFailureCode = unwrapFailureCode
+
+ def definition_body(self):
+ # Our descriptor might claim that we're not castable, simply because
+ # we're someone's consequential interface. But for this-unwrapping, we
+ # know that we're the real deal. So fake a descriptor here for
+ # consumption by FailureFatalCastableObjectUnwrapper.
+ unwrapThis = str(CastableObjectUnwrapper(
+ FakeCastableDescriptor(self.descriptor),
+ "obj", self.unwrapFailureCode))
+ unwrapThis = CGGeneric(
+ "let obj: *mut JSObject = JS_THIS_OBJECT(cx, vp as *mut JSVal);\n"
+ "if obj.is_null() {\n"
+ " return false as JSBool;\n"
+ "}\n"
+ "\n"
+ "let this: JS<%s> = %s;\n" % (self.descriptor.concreteType, unwrapThis))
+ return CGList([ unwrapThis, self.generate_code() ], "\n")
+
+ def generate_code(self):
+ assert(False) # Override me
+
+
+class CGAbstractStaticBindingMethod(CGAbstractMethod):
+ """
+ Common class to generate the JSNatives for all our static methods, getters
+ and setters. This will generate the function declaration and unwrap the
+ global object. Subclasses are expected to override the generate_code
+ function to do the rest of the work. This function should return a
+ CGThing which is already properly indented.
+ """
+ def __init__(self, descriptor, name):
+ args = [
+ Argument('*mut JSContext', 'cx'),
+ Argument('libc::c_uint', 'argc'),
+ Argument('*mut JSVal', 'vp'),
+ ]
+ CGAbstractMethod.__init__(self, descriptor, name, "JSBool", args, extern=True)
+
+ def definition_body(self):
+ return self.generate_code()
+
+ def generate_code(self):
+ assert False # Override me
+
+
+class CGGenericMethod(CGAbstractBindingMethod):
+ """
+ A class for generating the C++ code for an IDL method..
+ """
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSContext', 'cx'), Argument('libc::c_uint', 'argc'),
+ Argument('*mut JSVal', 'vp')]
+ CGAbstractBindingMethod.__init__(self, descriptor, 'genericMethod', args)
+
+ def generate_code(self):
+ return CGGeneric(
+ "let _info: *const JSJitInfo = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
+ "return CallJitMethodOp(_info, cx, obj, this.unsafe_get() as *mut libc::c_void, argc, vp);")
+
+class CGSpecializedMethod(CGAbstractExternMethod):
+ """
+ A class for generating the C++ code for a specialized method that the JIT
+ can call with lower overhead.
+ """
+ def __init__(self, descriptor, method):
+ self.method = method
+ name = method.identifier.name
+ args = [Argument('*mut JSContext', 'cx'), Argument('JSHandleObject', '_obj'),
+ Argument('*const %s' % descriptor.concreteType, 'this'),
+ Argument('libc::c_uint', 'argc'), Argument('*mut JSVal', 'vp')]
+ CGAbstractExternMethod.__init__(self, descriptor, name, 'JSBool', args)
+
+ def definition_body(self):
+ nativeName = CGSpecializedMethod.makeNativeName(self.descriptor,
+ self.method)
+ return CGWrapper(CGMethodCall([], nativeName, self.method.isStatic(),
+ self.descriptor, self.method),
+ pre="let this = JS::from_raw(this);\n"
+ "let this = this.root();\n")
+
+ @staticmethod
+ def makeNativeName(descriptor, method):
+ return MakeNativeName(method.identifier.name)
+
+class CGStaticMethod(CGAbstractStaticBindingMethod):
+ """
+ A class for generating the Rust code for an IDL static method.
+ """
+ def __init__(self, descriptor, method):
+ self.method = method
+ name = method.identifier.name
+ CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
+
+ def generate_code(self):
+ nativeName = CGSpecializedMethod.makeNativeName(self.descriptor,
+ self.method)
+ return CGMethodCall([], nativeName, True, self.descriptor, self.method)
+
+
+class CGGenericGetter(CGAbstractBindingMethod):
+ """
+ A class for generating the C++ code for an IDL attribute getter.
+ """
+ def __init__(self, descriptor, lenientThis=False):
+ args = [Argument('*mut JSContext', 'cx'), Argument('libc::c_uint', 'argc'),
+ Argument('*mut JSVal', 'vp')]
+ if lenientThis:
+ name = "genericLenientGetter"
+ unwrapFailureCode = (
+ "MOZ_ASSERT(!JS_IsExceptionPending(cx));\n"
+ "JS_SET_RVAL(cx, vp, JS::UndefinedValue());\n"
+ "return true;")
+ else:
+ name = "genericGetter"
+ unwrapFailureCode = None
+ CGAbstractBindingMethod.__init__(self, descriptor, name, args,
+ unwrapFailureCode)
+
+ def generate_code(self):
+ return CGGeneric(
+ "let info: *const JSJitInfo = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
+ "return CallJitPropertyOp(info, cx, obj, this.unsafe_get() as *mut libc::c_void, vp);\n")
+
+class CGSpecializedGetter(CGAbstractExternMethod):
+ """
+ A class for generating the code for a specialized attribute getter
+ that the JIT can call with lower overhead.
+ """
+ def __init__(self, descriptor, attr):
+ self.attr = attr
+ name = 'get_' + attr.identifier.name
+ args = [ Argument('*mut JSContext', 'cx'),
+ Argument('JSHandleObject', '_obj'),
+ Argument('*const %s' % descriptor.concreteType, 'this'),
+ Argument('*mut JSVal', 'vp') ]
+ CGAbstractExternMethod.__init__(self, descriptor, name, "JSBool", args)
+
+ def definition_body(self):
+ nativeName = CGSpecializedGetter.makeNativeName(self.descriptor,
+ self.attr)
+
+ return CGWrapper(CGGetterCall([], self.attr.type, nativeName,
+ self.descriptor, self.attr),
+ pre="let this = JS::from_raw(this);\n"
+ "let this = this.root();\n")
+
+ @staticmethod
+ def makeNativeName(descriptor, attr):
+ nativeName = MakeNativeName(attr.identifier.name)
+ infallible = ('infallible' in
+ descriptor.getExtendedAttributes(attr, getter=True))
+ if attr.type.nullable() or not infallible:
+ return "Get" + nativeName
+
+ return nativeName
+
+
+class CGStaticGetter(CGAbstractStaticBindingMethod):
+ """
+ A class for generating the C++ code for an IDL static attribute getter.
+ """
+ def __init__(self, descriptor, attr):
+ self.attr = attr
+ name = 'get_' + attr.identifier.name
+ CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
+
+ def generate_code(self):
+ nativeName = CGSpecializedGetter.makeNativeName(self.descriptor,
+ self.attr)
+ return CGGetterCall([], self.attr.type, nativeName, self.descriptor,
+ self.attr)
+
+
+class CGGenericSetter(CGAbstractBindingMethod):
+ """
+ A class for generating the Rust code for an IDL attribute setter.
+ """
+ def __init__(self, descriptor, lenientThis=False):
+ args = [Argument('*mut JSContext', 'cx'), Argument('libc::c_uint', 'argc'),
+ Argument('*mut JSVal', 'vp')]
+ if lenientThis:
+ name = "genericLenientSetter"
+ unwrapFailureCode = (
+ "MOZ_ASSERT(!JS_IsExceptionPending(cx));\n"
+ "return true;")
+ else:
+ name = "genericSetter"
+ unwrapFailureCode = None
+ CGAbstractBindingMethod.__init__(self, descriptor, name, args,
+ unwrapFailureCode)
+
+ def generate_code(self):
+ return CGGeneric(
+ "let mut undef = UndefinedValue();\n"
+ "let argv: *mut JSVal = if argc != 0 { JS_ARGV(cx, vp) } else { &mut undef as *mut JSVal };\n"
+ "let info: *const JSJitInfo = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
+ "if CallJitPropertyOp(info, cx, obj, this.unsafe_get() as *mut libc::c_void, argv) == 0 {\n"
+ " return 0;\n"
+ "}\n"
+ "*vp = UndefinedValue();\n"
+ "return 1;")
+
+class CGSpecializedSetter(CGAbstractExternMethod):
+ """
+ A class for generating the code for a specialized attribute setter
+ that the JIT can call with lower overhead.
+ """
+ def __init__(self, descriptor, attr):
+ self.attr = attr
+ name = 'set_' + attr.identifier.name
+ args = [ Argument('*mut JSContext', 'cx'),
+ Argument('JSHandleObject', '_obj'),
+ Argument('*const %s' % descriptor.concreteType, 'this'),
+ Argument('*mut JSVal', 'argv')]
+ CGAbstractExternMethod.__init__(self, descriptor, name, "JSBool", args)
+
+ def definition_body(self):
+ nativeName = CGSpecializedSetter.makeNativeName(self.descriptor,
+ self.attr)
+ return CGWrapper(CGSetterCall([], self.attr.type, nativeName,
+ self.descriptor, self.attr),
+ pre="let this = JS::from_raw(this);\n"
+ "let this = this.root();\n")
+
+ @staticmethod
+ def makeNativeName(descriptor, attr):
+ return "Set" + MakeNativeName(attr.identifier.name)
+
+
+class CGStaticSetter(CGAbstractStaticBindingMethod):
+ """
+ A class for generating the C++ code for an IDL static attribute setter.
+ """
+ def __init__(self, descriptor, attr):
+ self.attr = attr
+ name = 'set_' + attr.identifier.name
+ CGAbstractStaticBindingMethod.__init__(self, descriptor, name)
+
+ def generate_code(self):
+ nativeName = CGSpecializedSetter.makeNativeName(self.descriptor,
+ self.attr)
+ checkForArg = CGGeneric(
+ "let argv = JS_ARGV(cx, vp);\n"
+ "if (argc == 0) {\n"
+ " throw_type_error(cx, \"Not enough arguments to %s setter.\");\n"
+ " return 0;\n"
+ "}\n" % self.attr.identifier.name)
+ call = CGSetterCall([], self.attr.type, nativeName, self.descriptor,
+ self.attr)
+ return CGList([checkForArg, call])
+
+
+class CGMemberJITInfo(CGThing):
+ """
+ A class for generating the JITInfo for a property that points to
+ our specialized getter and setter.
+ """
+ def __init__(self, descriptor, member):
+ self.member = member
+ self.descriptor = descriptor
+
+ def defineJitInfo(self, infoName, opName, infallible):
+ protoID = "PrototypeList::id::%s as u32" % self.descriptor.name
+ depth = self.descriptor.interface.inheritanceDepth()
+ failstr = "true" if infallible else "false"
+ return ("\n"
+ "static %s: JSJitInfo = JSJitInfo {\n"
+ " op: %s as *const u8,\n"
+ " protoID: %s,\n"
+ " depth: %s,\n"
+ " isInfallible: %s, /* False in setters. */\n"
+ " isConstant: false /* Only relevant for getters. */\n"
+ "};\n" % (infoName, opName, protoID, depth, failstr))
+
+ def define(self):
+ if self.member.isAttr():
+ getterinfo = ("%s_getterinfo" % self.member.identifier.name)
+ getter = ("get_%s" % self.member.identifier.name)
+ getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True)
+ result = self.defineJitInfo(getterinfo, getter, getterinfal)
+ if not self.member.readonly:
+ setterinfo = ("%s_setterinfo" % self.member.identifier.name)
+ setter = ("set_%s" % self.member.identifier.name)
+ # Setters are always fallible, since they have to do a typed unwrap.
+ result += self.defineJitInfo(setterinfo, setter, False)
+ return result
+ if self.member.isMethod():
+ methodinfo = ("%s_methodinfo" % self.member.identifier.name)
+ # Actually a JSJitMethodOp, but JSJitPropertyOp by struct definition.
+ method = ("%s" % self.member.identifier.name)
+
+ # Methods are infallible if they are infallible, have no arguments
+ # to unwrap, and have a return type that's infallible to wrap up for
+ # return.
+ methodInfal = False
+ sigs = self.member.signatures()
+ if len(sigs) == 1:
+ # Don't handle overloading. If there's more than one signature,
+ # one of them must take arguments.
+ sig = sigs[0]
+ if len(sig[1]) == 0:
+ # No arguments and infallible return boxing
+ methodInfal = True
+
+ result = self.defineJitInfo(methodinfo, method, methodInfal)
+ return result
+ raise TypeError("Illegal member type to CGPropertyJITInfo")
+
+def getEnumValueName(value):
+ # Some enum values can be empty strings. Others might have weird
+ # characters in them. Deal with the former by returning "_empty",
+ # deal with possible name collisions from that by throwing if the
+ # enum value is actually "_empty", and throw on any value
+ # containing non-ASCII chars for now. Replace all chars other than
+ # [0-9A-Za-z_] with '_'.
+ if re.match("[^\x20-\x7E]", value):
+ raise SyntaxError('Enum value "' + value + '" contains non-ASCII characters')
+ if re.match("^[0-9]", value):
+ raise SyntaxError('Enum value "' + value + '" starts with a digit')
+ value = re.sub(r'[^0-9A-Za-z_]', '_', value)
+ if re.match("^_[A-Z]|__", value):
+ raise SyntaxError('Enum value "' + value + '" is reserved by the C++ spec')
+ if value == "_empty":
+ raise SyntaxError('"_empty" is not an IDL enum value we support yet')
+ if value == "":
+ return "_empty"
+ return MakeNativeName(value)
+
+class CGEnum(CGThing):
+ def __init__(self, enum):
+ CGThing.__init__(self)
+ inner = """
+use dom::bindings::conversions::ToJSValConvertible;
+use js::jsapi::JSContext;
+use js::jsval::JSVal;
+
+#[repr(uint)]
+#[deriving(Encodable, PartialEq)]
+pub enum valuelist {
+ %s
+}
+
+pub static strings: &'static [&'static str] = &[
+ %s,
+];
+
+impl ToJSValConvertible for valuelist {
+ fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
+ strings[*self as uint].to_string().to_jsval(cx)
+ }
+}
+""" % (",\n ".join(map(getEnumValueName, enum.values())),
+ ",\n ".join(['"%s"' % val for val in enum.values()]))
+
+ self.cgRoot = CGList([
+ CGNamespace.build([enum.identifier.name + "Values"],
+ CGIndenter(CGGeneric(inner)), public=True),
+ CGGeneric("pub type %s = self::%sValues::valuelist;\n" %
+ (enum.identifier.name, enum.identifier.name)),
+ ])
+
+ def define(self):
+ return self.cgRoot.define()
+
+
+def convertConstIDLValueToRust(value):
+ tag = value.type.tag()
+ if tag in [IDLType.Tags.int8, IDLType.Tags.uint8,
+ IDLType.Tags.int16, IDLType.Tags.uint16,
+ IDLType.Tags.int32, IDLType.Tags.uint32,
+ IDLType.Tags.int64, IDLType.Tags.uint64,
+ IDLType.Tags.float, IDLType.Tags.double]:
+ return str(value.value)
+
+ if tag == IDLType.Tags.bool:
+ return toStringBool(value.value)
+
+ raise TypeError("Const value of unhandled type: " + value.type)
+
+class CGConstant(CGThing):
+ def __init__(self, constants):
+ CGThing.__init__(self)
+ self.constants = constants
+
+ def define(self):
+ def stringDecl(const):
+ name = const.identifier.name
+ value = convertConstIDLValueToRust(const.value)
+ return CGGeneric("pub static %s: %s = %s;\n" % (name, builtinNames[const.value.type.tag()], value))
+
+ return CGIndenter(CGList(stringDecl(m) for m in self.constants)).define()
+
+def getUnionTypeTemplateVars(type, descriptorProvider):
+ # For dictionaries and sequences we need to pass None as the failureCode
+ # for getJSToNativeConversionTemplate.
+ # Also, for dictionaries we would need to handle conversion of
+ # null/undefined to the dictionary correctly.
+ if type.isDictionary() or type.isSequence():
+ raise TypeError("Can't handle dictionaries or sequences in unions")
+
+ if type.isGeckoInterface():
+ name = type.inner.identifier.name
+ typeName = descriptorProvider.getDescriptor(name).nativeType
+ elif type.isEnum():
+ name = type.inner.identifier.name
+ typeName = name
+ elif type.isArray() or type.isSequence():
+ name = str(type)
+ #XXXjdm dunno about typeName here
+ typeName = "/*" + type.name + "*/"
+ elif type.isDOMString():
+ name = type.name
+ typeName = "DOMString"
+ elif type.isPrimitive():
+ name = type.name
+ typeName = builtinNames[type.tag()]
+ else:
+ name = type.name
+ typeName = "/*" + type.name + "*/"
+
+ template, _, _, _ = getJSToNativeConversionTemplate(
+ type, descriptorProvider, failureCode="return Ok(None);",
+ exceptionCode='return Err(());',
+ isDefinitelyObject=True)
+
+ assert not type.isObject()
+ jsConversion = string.Template(template).substitute({
+ "val": "value",
+ })
+ jsConversion = CGWrapper(CGGeneric(jsConversion), pre="Ok(Some(", post="))")
+
+ return {
+ "name": name,
+ "typeName": typeName,
+ "jsConversion": jsConversion,
+ }
+
+class CGUnionStruct(CGThing):
+ def __init__(self, type, descriptorProvider):
+ assert not type.nullable()
+ assert not type.hasNullableType
+
+ CGThing.__init__(self)
+ self.type = type
+ self.descriptorProvider = descriptorProvider
+
+ def define(self):
+ templateVars = map(lambda t: getUnionTypeTemplateVars(t, self.descriptorProvider),
+ self.type.flatMemberTypes)
+ enumValues = [
+ " e%s(%s)," % (v["name"], v["typeName"]) for v in templateVars
+ ]
+ enumConversions = [
+ " e%s(ref inner) => inner.to_jsval(cx)," % v["name"] for v in templateVars
+ ]
+ return ("""pub enum %s {
+%s
+}
+
+impl ToJSValConvertible for %s {
+ fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
+ match *self {
+%s
+ }
+ }
+}
+""") % (self.type, "\n".join(enumValues),
+ self.type, "\n".join(enumConversions))
+
+
+class CGUnionConversionStruct(CGThing):
+ def __init__(self, type, descriptorProvider):
+ assert not type.nullable()
+ assert not type.hasNullableType
+
+ CGThing.__init__(self)
+ self.type = type
+ self.descriptorProvider = descriptorProvider
+
+ def from_jsval(self):
+ memberTypes = self.type.flatMemberTypes
+ names = []
+ conversions = []
+
+ interfaceMemberTypes = filter(lambda t: t.isNonCallbackInterface(), memberTypes)
+ if len(interfaceMemberTypes) > 0:
+ def get_name(memberType):
+ if self.type.isGeckoInterface():
+ return memberType.inner.identifier.name
+
+ return memberType.name
+
+ def get_match(name):
+ return (
+ "match %s::TryConvertTo%s(cx, value) {\n"
+ " Err(_) => return Err(()),\n"
+ " Ok(Some(value)) => return Ok(e%s(value)),\n"
+ " Ok(None) => (),\n"
+ "}\n") % (self.type, name, name)
+
+ typeNames = [get_name(memberType) for memberType in interfaceMemberTypes]
+ interfaceObject = CGList(CGGeneric(get_match(typeName)) for typeName in typeNames)
+ names.extend(typeNames)
+ else:
+ interfaceObject = None
+
+ arrayObjectMemberTypes = filter(lambda t: t.isArray() or t.isSequence(), memberTypes)
+ if len(arrayObjectMemberTypes) > 0:
+ assert len(arrayObjectMemberTypes) == 1
+ raise TypeError("Can't handle arrays or sequences in unions.")
+ else:
+ arrayObject = None
+
+ dateObjectMemberTypes = filter(lambda t: t.isDate(), memberTypes)
+ if len(dateObjectMemberTypes) > 0:
+ assert len(dateObjectMemberTypes) == 1
+ raise TypeError("Can't handle dates in unions.")
+ else:
+ dateObject = None
+
+ callbackMemberTypes = filter(lambda t: t.isCallback() or t.isCallbackInterface(), memberTypes)
+ if len(callbackMemberTypes) > 0:
+ assert len(callbackMemberTypes) == 1
+ raise TypeError("Can't handle callbacks in unions.")
+ else:
+ callbackObject = None
+
+ dictionaryMemberTypes = filter(lambda t: t.isDictionary(), memberTypes)
+ if len(dictionaryMemberTypes) > 0:
+ raise TypeError("No support for unwrapping dictionaries as member "
+ "of a union")
+ else:
+ dictionaryObject = None
+
+ if callbackObject or dictionaryObject:
+ assert False, "Not currently supported"
+ else:
+ nonPlatformObject = None
+
+ objectMemberTypes = filter(lambda t: t.isObject(), memberTypes)
+ if len(objectMemberTypes) > 0:
+ raise TypeError("Can't handle objects in unions.")
+ else:
+ object = None
+
+ hasObjectTypes = interfaceObject or arrayObject or dateObject or nonPlatformObject or object
+ if hasObjectTypes:
+ assert interfaceObject
+ templateBody = CGList([interfaceObject], "\n")
+ conversions.append(CGIfWrapper(templateBody, "value.is_object()"))
+
+ otherMemberTypes = [
+ t for t in memberTypes if t.isPrimitive() or t.isString() or t.isEnum()
+ ]
+ if len(otherMemberTypes) > 0:
+ assert len(otherMemberTypes) == 1
+ memberType = otherMemberTypes[0]
+ if memberType.isEnum():
+ name = memberType.inner.identifier.name
+ else:
+ name = memberType.name
+ match = (
+ "match %s::TryConvertTo%s(cx, value) {\n"
+ " Err(_) => return Err(()),\n"
+ " Ok(Some(value)) => return Ok(e%s(value)),\n"
+ " Ok(None) => (),\n"
+ "}\n") % (self.type, name, name)
+ conversions.append(CGGeneric(match))
+ names.append(name)
+
+ conversions.append(CGGeneric(
+ "throw_not_in_union(cx, \"%s\");\n"
+ "Err(())" % ", ".join(names)))
+ method = CGWrapper(
+ CGIndenter(CGList(conversions, "\n\n")),
+ pre="fn from_jsval(cx: *mut JSContext, value: JSVal, _option: ()) -> Result<%s, ()> {\n" % self.type,
+ post="\n}")
+ return CGWrapper(
+ CGIndenter(method),
+ pre="impl FromJSValConvertible<()> for %s {\n" % self.type,
+ post="\n}")
+
+ def try_method(self, t):
+ templateVars = getUnionTypeTemplateVars(t, self.descriptorProvider)
+ returnType = "Result<Option<%s>, ()>" % templateVars["typeName"]
+ jsConversion = templateVars["jsConversion"]
+
+ return CGWrapper(
+ CGIndenter(jsConversion, 4),
+ pre="fn TryConvertTo%s(cx: *mut JSContext, value: JSVal) -> %s {\n" % (t.name, returnType),
+ post="\n}")
+
+ def define(self):
+ from_jsval = self.from_jsval()
+ methods = CGIndenter(CGList([
+ self.try_method(t) for t in self.type.flatMemberTypes
+ ], "\n\n"))
+ return """
+%s
+
+impl %s {
+%s
+}
+""" % (from_jsval.define(), self.type, methods.define())
+
+
+class ClassItem:
+ """ Use with CGClass """
+ def __init__(self, name, visibility):
+ self.name = name
+ self.visibility = visibility
+ def declare(self, cgClass):
+ assert False
+ def define(self, cgClass):
+ assert False
+
+class ClassBase(ClassItem):
+ def __init__(self, name, visibility='pub'):
+ ClassItem.__init__(self, name, visibility)
+ def declare(self, cgClass):
+ return '%s %s' % (self.visibility, self.name)
+ def define(self, cgClass):
+ # Only in the header
+ return ''
+
+class ClassMethod(ClassItem):
+ def __init__(self, name, returnType, args, inline=False, static=False,
+ virtual=False, const=False, bodyInHeader=False,
+ templateArgs=None, visibility='public', body=None,
+ breakAfterReturnDecl="\n",
+ breakAfterSelf="\n", override=False):
+ """
+ override indicates whether to flag the method as MOZ_OVERRIDE
+ """
+ assert not override or virtual
+ self.returnType = returnType
+ self.args = args
+ self.inline = False
+ self.static = static
+ self.virtual = virtual
+ self.const = const
+ self.bodyInHeader = True
+ self.templateArgs = templateArgs
+ self.body = body
+ self.breakAfterReturnDecl = breakAfterReturnDecl
+ self.breakAfterSelf = breakAfterSelf
+ self.override = override
+ ClassItem.__init__(self, name, visibility)
+
+ def getDecorators(self, declaring):
+ decorators = []
+ if self.inline:
+ decorators.append('inline')
+ if declaring:
+ if self.static:
+ decorators.append('static')
+ if self.virtual:
+ decorators.append('virtual')
+ if decorators:
+ return ' '.join(decorators) + ' '
+ return ''
+
+ def getBody(self):
+ # Override me or pass a string to constructor
+ assert self.body is not None
+ return self.body
+
+ def declare(self, cgClass):
+ templateClause = '<%s>' % ', '.join(self.templateArgs) \
+ if self.bodyInHeader and self.templateArgs else ''
+ args = ', '.join([a.declare() for a in self.args])
+ if self.bodyInHeader:
+ body = CGIndenter(CGGeneric(self.getBody())).define()
+ body = ' {\n' + body + '\n}'
+ else:
+ body = ';'
+
+ return string.Template("${decorators}%s"
+ "${visibility}fn ${name}${templateClause}(${args})${returnType}${const}${override}${body}%s" %
+ (self.breakAfterReturnDecl, self.breakAfterSelf)
+ ).substitute({
+ 'templateClause': templateClause,
+ 'decorators': self.getDecorators(True),
+ 'returnType': (" -> %s" % self.returnType) if self.returnType else "",
+ 'name': self.name,
+ 'const': ' const' if self.const else '',
+ 'override': ' MOZ_OVERRIDE' if self.override else '',
+ 'args': args,
+ 'body': body,
+ 'visibility': self.visibility + ' ' if self.visibility is not 'priv' else ''
+ })
+
+ def define(self, cgClass):
+ pass
+
+class ClassUsingDeclaration(ClassItem):
+ """"
+ Used for importing a name from a base class into a CGClass
+
+ baseClass is the name of the base class to import the name from
+
+ name is the name to import
+
+ visibility determines the visibility of the name (public,
+ protected, private), defaults to public.
+ """
+ def __init__(self, baseClass, name, visibility='public'):
+ self.baseClass = baseClass
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ return string.Template("""using ${baseClass}::${name};
+""").substitute({ 'baseClass': self.baseClass,
+ 'name': self.name })
+
+ def define(self, cgClass):
+ return ''
+
+class ClassConstructor(ClassItem):
+ """
+ Used for adding a constructor to a CGClass.
+
+ args is a list of Argument objects that are the arguments taken by the
+ constructor.
+
+ inline should be True if the constructor should be marked inline.
+
+ bodyInHeader should be True if the body should be placed in the class
+ declaration in the header.
+
+ visibility determines the visibility of the constructor (public,
+ protected, private), defaults to private.
+
+ explicit should be True if the constructor should be marked explicit.
+
+ baseConstructors is a list of strings containing calls to base constructors,
+ defaults to None.
+
+ body contains a string with the code for the constructor, defaults to empty.
+ """
+ def __init__(self, args, inline=False, bodyInHeader=False,
+ visibility="priv", explicit=False, baseConstructors=None,
+ body=""):
+ self.args = args
+ self.inline = False
+ self.bodyInHeader = bodyInHeader
+ self.explicit = explicit
+ self.baseConstructors = baseConstructors or []
+ self.body = body
+ ClassItem.__init__(self, None, visibility)
+
+ def getDecorators(self, declaring):
+ decorators = []
+ if self.explicit:
+ decorators.append('explicit')
+ if self.inline and declaring:
+ decorators.append('inline')
+ if decorators:
+ return ' '.join(decorators) + ' '
+ return ''
+
+ def getInitializationList(self, cgClass):
+ items = [str(c) for c in self.baseConstructors]
+ for m in cgClass.members:
+ if not m.static:
+ initialize = m.body
+ if initialize:
+ items.append(m.name + "(" + initialize + ")")
+
+ if len(items) > 0:
+ return '\n : ' + ',\n '.join(items)
+ return ''
+
+ def getBody(self, cgClass):
+ initializers = [" parent: %s" % str(self.baseConstructors[0])]
+ return (self.body + (
+ "%s {\n"
+ "%s\n"
+ "}") % (cgClass.name, '\n'.join(initializers)))
+
+ def declare(self, cgClass):
+ args = ', '.join([a.declare() for a in self.args])
+ body = ' ' + self.getBody(cgClass);
+ body = stripTrailingWhitespace(body.replace('\n', '\n '))
+ if len(body) > 0:
+ body += '\n'
+ body = ' {\n' + body + '}'
+
+ return string.Template("""pub fn ${decorators}new(${args}) -> ${className}${body}
+""").substitute({ 'decorators': self.getDecorators(True),
+ 'className': cgClass.getNameString(),
+ 'args': args,
+ 'body': body })
+
+ def define(self, cgClass):
+ if self.bodyInHeader:
+ return ''
+
+ args = ', '.join([a.define() for a in self.args])
+
+ body = ' ' + self.getBody()
+ body = '\n' + stripTrailingWhitespace(body.replace('\n', '\n '))
+ if len(body) > 0:
+ body += '\n'
+
+ return string.Template("""${decorators}
+${className}::${className}(${args})${initializationList}
+{${body}}
+""").substitute({ 'decorators': self.getDecorators(False),
+ 'className': cgClass.getNameString(),
+ 'args': args,
+ 'initializationList': self.getInitializationList(cgClass),
+ 'body': body })
+
+class ClassDestructor(ClassItem):
+ """
+ Used for adding a destructor to a CGClass.
+
+ inline should be True if the destructor should be marked inline.
+
+ bodyInHeader should be True if the body should be placed in the class
+ declaration in the header.
+
+ visibility determines the visibility of the destructor (public,
+ protected, private), defaults to private.
+
+ body contains a string with the code for the destructor, defaults to empty.
+
+ virtual determines whether the destructor is virtual, defaults to False.
+ """
+ def __init__(self, inline=False, bodyInHeader=False,
+ visibility="private", body='', virtual=False):
+ self.inline = inline or bodyInHeader
+ self.bodyInHeader = bodyInHeader
+ self.body = body
+ self.virtual = virtual
+ ClassItem.__init__(self, None, visibility)
+
+ def getDecorators(self, declaring):
+ decorators = []
+ if self.virtual and declaring:
+ decorators.append('virtual')
+ if self.inline and declaring:
+ decorators.append('inline')
+ if decorators:
+ return ' '.join(decorators) + ' '
+ return ''
+
+ def getBody(self):
+ return self.body
+
+ def declare(self, cgClass):
+ if self.bodyInHeader:
+ body = ' ' + self.getBody();
+ body = stripTrailingWhitespace(body.replace('\n', '\n '))
+ if len(body) > 0:
+ body += '\n'
+ body = '\n{\n' + body + '}'
+ else:
+ body = ';'
+
+ return string.Template("""${decorators}~${className}()${body}
+""").substitute({ 'decorators': self.getDecorators(True),
+ 'className': cgClass.getNameString(),
+ 'body': body })
+
+ def define(self, cgClass):
+ if self.bodyInHeader:
+ return ''
+
+ body = ' ' + self.getBody()
+ body = '\n' + stripTrailingWhitespace(body.replace('\n', '\n '))
+ if len(body) > 0:
+ body += '\n'
+
+ return string.Template("""${decorators}
+${className}::~${className}()
+{${body}}
+""").substitute({ 'decorators': self.getDecorators(False),
+ 'className': cgClass.getNameString(),
+ 'body': body })
+
+class ClassMember(ClassItem):
+ def __init__(self, name, type, visibility="priv", static=False,
+ body=None):
+ self.type = type;
+ self.static = static
+ self.body = body
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ return '%s %s: %s,\n' % (self.visibility, self.name, self.type)
+
+ def define(self, cgClass):
+ if not self.static:
+ return ''
+ if self.body:
+ body = " = " + self.body
+ else:
+ body = ""
+ return '%s %s::%s%s;\n' % (self.type, cgClass.getNameString(),
+ self.name, body)
+
+class ClassTypedef(ClassItem):
+ def __init__(self, name, type, visibility="public"):
+ self.type = type
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ return 'typedef %s %s;\n' % (self.type, self.name)
+
+ def define(self, cgClass):
+ # Only goes in the header
+ return ''
+
+class ClassEnum(ClassItem):
+ def __init__(self, name, entries, values=None, visibility="public"):
+ self.entries = entries
+ self.values = values
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ entries = []
+ for i in range(0, len(self.entries)):
+ if not self.values or i >= len(self.values):
+ entry = '%s' % self.entries[i]
+ else:
+ entry = '%s = %s' % (self.entries[i], self.values[i])
+ entries.append(entry)
+ name = '' if not self.name else ' ' + self.name
+ return 'enum%s\n{\n %s\n};\n' % (name, ',\n '.join(entries))
+
+ def define(self, cgClass):
+ # Only goes in the header
+ return ''
+
+class ClassUnion(ClassItem):
+ def __init__(self, name, entries, visibility="public"):
+ self.entries = [entry + ";" for entry in entries]
+ ClassItem.__init__(self, name, visibility)
+
+ def declare(self, cgClass):
+ return 'union %s\n{\n %s\n};\n' % (self.name, '\n '.join(self.entries))
+
+ def define(self, cgClass):
+ # Only goes in the header
+ return ''
+
+class CGClass(CGThing):
+ def __init__(self, name, bases=[], members=[], constructors=[],
+ destructor=None, methods=[],
+ typedefs = [], enums=[], unions=[], templateArgs=[],
+ templateSpecialization=[], isStruct=False,
+ disallowCopyConstruction=False, indent='',
+ decorators='',
+ extradeclarations='',
+ extradefinitions=''):
+ CGThing.__init__(self)
+ self.name = name
+ self.bases = bases
+ self.members = members
+ self.constructors = constructors
+ # We store our single destructor in a list, since all of our
+ # code wants lists of members.
+ self.destructors = [destructor] if destructor else []
+ self.methods = methods
+ self.typedefs = typedefs
+ self.enums = enums
+ self.unions = unions
+ self.templateArgs = templateArgs
+ self.templateSpecialization = templateSpecialization
+ self.isStruct = isStruct
+ self.disallowCopyConstruction = disallowCopyConstruction
+ self.indent = indent
+ self.decorators = decorators
+ self.extradeclarations = extradeclarations
+ self.extradefinitions = extradefinitions
+
+ def getNameString(self):
+ className = self.name
+ if self.templateSpecialization:
+ className = className + \
+ '<%s>' % ', '.join([str(a) for a
+ in self.templateSpecialization])
+ return className
+
+ def define(self):
+ result = ''
+ if self.templateArgs:
+ templateArgs = [a.declare() for a in self.templateArgs]
+ templateArgs = templateArgs[len(self.templateSpecialization):]
+ result = result + self.indent + 'template <%s>\n' \
+ % ','.join([str(a) for a in templateArgs])
+
+ if self.templateSpecialization:
+ specialization = \
+ '<%s>' % ', '.join([str(a) for a in self.templateSpecialization])
+ else:
+ specialization = ''
+
+ myself = ''
+ if self.decorators != '':
+ myself += self.decorators + '\n'
+ myself += '%spub struct %s%s' % (self.indent, self.name, specialization)
+ result += myself
+
+ assert len(self.bases) == 1 #XXjdm Can we support multiple inheritance?
+
+ result += '{\n%s\n' % self.indent
+
+ if self.bases:
+ self.members = [ClassMember("parent", self.bases[0].name, "pub")] + self.members
+
+ result += CGIndenter(CGGeneric(self.extradeclarations),
+ len(self.indent)).define()
+
+ def declareMembers(cgClass, memberList):
+ result = ''
+
+ for member in memberList:
+ declaration = member.declare(cgClass)
+ declaration = CGIndenter(CGGeneric(declaration)).define()
+ result = result + declaration
+ return result
+
+ if self.disallowCopyConstruction:
+ class DisallowedCopyConstructor(object):
+ def __init__(self):
+ self.visibility = "private"
+ def declare(self, cgClass):
+ name = cgClass.getNameString()
+ return ("%s(const %s&) MOZ_DELETE;\n"
+ "void operator=(const %s) MOZ_DELETE;\n" % (name, name, name))
+ disallowedCopyConstructors = [DisallowedCopyConstructor()]
+ else:
+ disallowedCopyConstructors = []
+
+ order = [(self.enums, ''), (self.unions, ''),
+ (self.typedefs, ''), (self.members, '')]
+
+ for (memberList, separator) in order:
+ memberString = declareMembers(self, memberList)
+ if self.indent:
+ memberString = CGIndenter(CGGeneric(memberString),
+ len(self.indent)).define()
+ result = result + memberString
+
+ result += self.indent + '}\n\n'
+ result += 'impl %s {\n' % self.name
+
+ order = [(self.constructors + disallowedCopyConstructors, '\n'),
+ (self.destructors, '\n'), (self.methods, '\n)')]
+ for (memberList, separator) in order:
+ memberString = declareMembers(self, memberList)
+ if self.indent:
+ memberString = CGIndenter(CGGeneric(memberString),
+ len(self.indent)).define()
+ result = result + memberString
+
+ result += "}"
+ return result
+
+class CGProxySpecialOperation(CGPerSignatureCall):
+ """
+ Base class for classes for calling an indexed or named special operation
+ (don't use this directly, use the derived classes below).
+ """
+ def __init__(self, descriptor, operation):
+ nativeName = MakeNativeName(operation)
+ operation = descriptor.operations[operation]
+ assert len(operation.signatures()) == 1
+ signature = operation.signatures()[0]
+
+ (returnType, arguments) = signature
+
+ # We pass len(arguments) as the final argument so that the
+ # CGPerSignatureCall won't do any argument conversion of its own.
+ CGPerSignatureCall.__init__(self, returnType, "", arguments, nativeName,
+ False, descriptor, operation,
+ len(arguments))
+
+ if operation.isSetter() or operation.isCreator():
+ # arguments[0] is the index or name of the item that we're setting.
+ argument = arguments[1]
+ template, _, declType, needsRooting = getJSToNativeConversionTemplate(
+ argument.type, descriptor, treatNullAs=argument.treatNullAs)
+ templateValues = {
+ "val": "(*desc).value",
+ }
+ self.cgRoot.prepend(instantiateJSToNativeConversionTemplate(
+ template, templateValues, declType, argument.identifier.name,
+ needsRooting))
+ elif operation.isGetter():
+ self.cgRoot.prepend(CGGeneric("let mut found = false;"))
+
+ def getArguments(self):
+ def process(arg):
+ argVal = arg.identifier.name
+ if arg.type.isGeckoInterface() and not arg.type.unroll().inner.isCallback():
+ argVal += ".root_ref()"
+ return argVal
+ args = [(a, process(a)) for a in self.arguments]
+ if self.idlNode.isGetter():
+ args.append((FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean],
+ self.idlNode),
+ "&mut found"))
+ return args
+
+ def wrap_return_value(self):
+ if not self.idlNode.isGetter() or self.templateValues is None:
+ return ""
+
+ wrap = CGGeneric(wrapForType(**self.templateValues))
+ wrap = CGIfWrapper(wrap, "found")
+ return "\n" + wrap.define()
+
+class CGProxyIndexedGetter(CGProxySpecialOperation):
+ """
+ Class to generate a call to an indexed getter. If templateValues is not None
+ the returned value will be wrapped with wrapForType using templateValues.
+ """
+ def __init__(self, descriptor, templateValues=None):
+ self.templateValues = templateValues
+ CGProxySpecialOperation.__init__(self, descriptor, 'IndexedGetter')
+
+class CGProxyIndexedSetter(CGProxySpecialOperation):
+ """
+ Class to generate a call to an indexed setter.
+ """
+ def __init__(self, descriptor):
+ CGProxySpecialOperation.__init__(self, descriptor, 'IndexedSetter')
+
+class CGProxyNamedGetter(CGProxySpecialOperation):
+ """
+ Class to generate a call to an named getter. If templateValues is not None
+ the returned value will be wrapped with wrapForType using templateValues.
+ """
+ def __init__(self, descriptor, templateValues=None):
+ self.templateValues = templateValues
+ CGProxySpecialOperation.__init__(self, descriptor, 'NamedGetter')
+
+class CGProxyNamedSetter(CGProxySpecialOperation):
+ """
+ Class to generate a call to a named setter.
+ """
+ def __init__(self, descriptor):
+ CGProxySpecialOperation.__init__(self, descriptor, 'NamedSetter')
+
+class CGProxyUnwrap(CGAbstractMethod):
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSObject', 'obj')]
+ CGAbstractMethod.__init__(self, descriptor, "UnwrapProxy", '*const ' + descriptor.concreteType, args, alwaysInline=True)
+
+ def definition_body(self):
+ return CGGeneric("""/*if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
+ obj = js::UnwrapObject(obj);
+}*/
+//MOZ_ASSERT(IsProxy(obj));
+let box_ = GetProxyPrivate(obj).to_private() as *const %s;
+return box_;""" % self.descriptor.concreteType)
+
+class CGDOMJSProxyHandler_getOwnPropertyDescriptor(CGAbstractExternMethod):
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'proxy'),
+ Argument('jsid', 'id'), Argument('bool', 'set'),
+ Argument('*mut JSPropertyDescriptor', 'desc')]
+ CGAbstractExternMethod.__init__(self, descriptor, "getOwnPropertyDescriptor",
+ "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ indexedSetter = self.descriptor.operations['IndexedSetter']
+
+ setOrIndexedGet = ""
+ if indexedGetter or indexedSetter:
+ setOrIndexedGet += "let index = GetArrayIndexFromId(cx, id);\n"
+
+ if indexedGetter:
+ readonly = toStringBool(self.descriptor.operations['IndexedSetter'] is None)
+ fillDescriptor = "FillPropertyDescriptor(&mut *desc, proxy, %s);\nreturn true;" % readonly
+ templateValues = {'jsvalRef': '(*desc).value', 'successCode': fillDescriptor}
+ get = ("if index.is_some() {\n" +
+ " let index = index.unwrap();\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define() + "\n" +
+ "}\n")
+
+ if indexedSetter or self.descriptor.operations['NamedSetter']:
+ setOrIndexedGet += "if set != 0 {\n"
+ if indexedSetter:
+ setOrIndexedGet += (" if index.is_some() {\n" +
+ " let index = index.unwrap();\n")
+ if not 'IndexedCreator' in self.descriptor.operations:
+ # FIXME need to check that this is a 'supported property index'
+ assert False
+ setOrIndexedGet += (" FillPropertyDescriptor(&mut *desc, proxy, false);\n" +
+ " return true;\n" +
+ " }\n")
+ if self.descriptor.operations['NamedSetter']:
+ setOrIndexedGet += " if RUST_JSID_IS_STRING(id) {\n"
+ if not 'NamedCreator' in self.descriptor.operations:
+ # FIXME need to check that this is a 'supported property name'
+ assert False
+ setOrIndexedGet += (" FillPropertyDescriptor(&mut *desc, proxy, false);\n" +
+ " return true;\n" +
+ " }\n")
+ setOrIndexedGet += "}"
+ if indexedGetter:
+ setOrIndexedGet += (" else {\n" +
+ CGIndenter(CGGeneric(get)).define() +
+ "}")
+ setOrIndexedGet += "\n\n"
+ elif indexedGetter:
+ setOrIndexedGet += ("if !set {\n" +
+ CGIndenter(CGGeneric(get)).define() +
+ "}\n\n")
+
+ namedGetter = self.descriptor.operations['NamedGetter']
+ if namedGetter:
+ readonly = toStringBool(self.descriptor.operations['NamedSetter'] is None)
+ fillDescriptor = "FillPropertyDescriptor(&mut *desc, proxy, %s);\nreturn true;" % readonly
+ templateValues = {'jsvalRef': '(*desc).value', 'successCode': fillDescriptor}
+ # Once we start supporting OverrideBuiltins we need to make
+ # ResolveOwnProperty or EnumerateOwnProperties filter out named
+ # properties that shadow prototype properties.
+ namedGet = ("\n" +
+ "if !set && RUST_JSID_IS_STRING(id) != 0 && !HasPropertyOnPrototype(cx, proxy, id) {\n" +
+ " let name = jsid_to_str(cx, id);\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() + "\n" +
+ "}\n")
+ else:
+ namedGet = ""
+
+ return setOrIndexedGet + """let expando: *mut JSObject = GetExpandoObject(proxy);
+//if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) {
+if expando.is_not_null() {
+ let flags = if set { JSRESOLVE_ASSIGNING } else { 0 } | JSRESOLVE_QUALIFIED;
+ if JS_GetPropertyDescriptorById(cx, expando, id, flags, desc) == 0 {
+ return false;
+ }
+ if (*desc).obj.is_not_null() {
+ // Pretend the property lives on the wrapper.
+ (*desc).obj = proxy;
+ return true;
+ }
+}
+""" + namedGet + """
+(*desc).obj = ptr::mut_null();
+return true;"""
+
+ def definition_body(self):
+ return CGGeneric(self.getBody())
+
+class CGDOMJSProxyHandler_defineProperty(CGAbstractExternMethod):
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'proxy'),
+ Argument('jsid', 'id'),
+ Argument('*const JSPropertyDescriptor', 'desc')]
+ CGAbstractExternMethod.__init__(self, descriptor, "defineProperty", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ set = ""
+
+ indexedSetter = self.descriptor.operations['IndexedSetter']
+ if indexedSetter:
+ if not (self.descriptor.operations['IndexedCreator'] is indexedSetter):
+ raise TypeError("Can't handle creator that's different from the setter")
+ set += ("let index = GetArrayIndexFromId(cx, id);\n" +
+ "if index.is_some() {\n" +
+ " let index = index.unwrap();\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyIndexedSetter(self.descriptor)).define() +
+ " return true;\n" +
+ "}\n")
+ elif self.descriptor.operations['IndexedGetter']:
+ set += ("if GetArrayIndexFromId(cx, id).is_some() {\n" +
+ " return false;\n" +
+ " //return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" +
+ "}\n") % self.descriptor.name
+
+ namedSetter = self.descriptor.operations['NamedSetter']
+ if namedSetter:
+ if not self.descriptor.operations['NamedCreator'] is namedSetter:
+ raise TypeError("Can't handle creator that's different from the setter")
+ set += ("if RUST_JSID_IS_STRING(id) != 0 {\n" +
+ " let name = jsid_to_str(cx, id);\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyNamedSetter(self.descriptor)).define() + "\n" +
+ "}\n")
+ elif self.descriptor.operations['NamedGetter']:
+ set += ("if RUST_JSID_IS_STRING(id) {\n" +
+ " let name = jsid_to_str(cx, id);\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyNamedGetter(self.descriptor)).define() +
+ " if (found) {\n"
+ " return false;\n" +
+ " //return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" +
+ " }\n" +
+ " return true;\n"
+ "}\n") % (self.descriptor.name)
+ return set + """return proxyhandler::defineProperty_(%s);""" % ", ".join(a.name for a in self.args)
+
+ def definition_body(self):
+ return CGGeneric(self.getBody())
+
+class CGDOMJSProxyHandler_hasOwn(CGAbstractExternMethod):
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'proxy'),
+ Argument('jsid', 'id'), Argument('*mut bool', 'bp')]
+ CGAbstractExternMethod.__init__(self, descriptor, "hasOwn", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ if indexedGetter:
+ indexed = ("let index = GetArrayIndexFromId(cx, id);\n" +
+ "if index.is_some() {\n" +
+ " let index = index.unwrap();\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyIndexedGetter(self.descriptor)).define() + "\n" +
+ " *bp = found;\n" +
+ " return true;\n" +
+ "}\n\n")
+ else:
+ indexed = ""
+
+ namedGetter = self.descriptor.operations['NamedGetter']
+ if namedGetter:
+ named = ("if RUST_JSID_IS_STRING(id) != 0 && !HasPropertyOnPrototype(cx, proxy, id) {\n" +
+ " let name = jsid_to_str(cx, id);\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyNamedGetter(self.descriptor)).define() + "\n" +
+ " *bp = found;\n"
+ " return true;\n"
+ "}\n" +
+ "\n")
+ else:
+ named = ""
+
+ return indexed + """let expando: *mut JSObject = GetExpandoObject(proxy);
+if expando.is_not_null() {
+ let mut b: JSBool = 1;
+ let ok = JS_HasPropertyById(cx, expando, id, &mut b) != 0;
+ *bp = b != 0;
+ if !ok || *bp {
+ return ok;
+ }
+}
+
+""" + named + """*bp = false;
+return true;"""
+
+ def definition_body(self):
+ return CGGeneric(self.getBody())
+
+class CGDOMJSProxyHandler_get(CGAbstractExternMethod):
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'proxy'),
+ Argument('*mut JSObject', 'receiver'), Argument('jsid', 'id'),
+ Argument('*mut JSVal', 'vp')]
+ CGAbstractExternMethod.__init__(self, descriptor, "get", "bool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ getFromExpando = """let expando = GetExpandoObject(proxy);
+if expando.is_not_null() {
+ let mut hasProp = 0;
+ if JS_HasPropertyById(cx, expando, id, &mut hasProp) == 0 {
+ return false;
+ }
+
+ if hasProp != 0 {
+ return JS_GetPropertyById(cx, expando, id, vp) != 0;
+ }
+}"""
+
+ templateValues = {
+ 'jsvalRef': '*vp',
+ 'successCode': 'return true;',
+ }
+
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ if indexedGetter:
+ getIndexedOrExpando = ("let index = GetArrayIndexFromId(cx, id);\n" +
+ "if index.is_some() {\n" +
+ " let index = index.unwrap();\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define())
+ getIndexedOrExpando += """
+ // Even if we don't have this index, we don't forward the
+ // get on to our expando object.
+} else {
+ %s
+}
+""" % (stripTrailingWhitespace(getFromExpando.replace('\n', '\n ')))
+ else:
+ getIndexedOrExpando = getFromExpando + "\n"
+
+ namedGetter = self.descriptor.operations['NamedGetter']
+ if namedGetter and False: #XXXjdm unfinished
+ getNamed = ("if (JSID_IS_STRING(id)) {\n" +
+ " let name = jsid_to_str(cx, id);\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let this = this.root();\n" +
+ CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() +
+ "}\n") % (self.descriptor.concreteType)
+ else:
+ getNamed = ""
+
+ return """//MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
+ //"Should not have a XrayWrapper here");
+
+%s
+let mut found = false;
+if !GetPropertyOnPrototype(cx, proxy, id, &mut found, vp) {
+ return false;
+}
+
+if found {
+ return true;
+}
+%s
+*vp = UndefinedValue();
+return true;""" % (getIndexedOrExpando, getNamed)
+
+ def definition_body(self):
+ return CGGeneric(self.getBody())
+
+class CGDOMJSProxyHandler_obj_toString(CGAbstractExternMethod):
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'proxy')]
+ CGAbstractExternMethod.__init__(self, descriptor, "obj_toString", "*mut JSString", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ stringifier = self.descriptor.operations['Stringifier']
+ if stringifier:
+ nativeName = MakeNativeName(stringifier.identifier.name)
+ signature = stringifier.signatures()[0]
+ returnType = signature[0]
+ extendedAttributes = self.descriptor.getExtendedAttributes(stringifier)
+ infallible = 'infallible' in extendedAttributes
+ if not infallible:
+ error = CGGeneric(
+ ('ThrowMethodFailedWithDetails(cx, rv, "%s", "toString");\n' +
+ "return NULL;") % self.descriptor.interface.identifier.name)
+ else:
+ error = None
+ call = CGCallGenerator(error, [], "", returnType, extendedAttributes, self.descriptor, nativeName, False, object="UnwrapProxy(proxy)")
+ return call.define() + """
+
+JSString* jsresult;
+return xpc_qsStringToJsstring(cx, result, &jsresult) ? jsresult : NULL;"""
+
+ return """let s = "%s".to_c_str();
+ _obj_toString(cx, s.as_ptr())""" % self.descriptor.name
+
+ def definition_body(self):
+ return CGGeneric(self.getBody())
+
+class CGAbstractClassHook(CGAbstractExternMethod):
+ """
+ Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw
+ 'this' unwrapping as it assumes that the unwrapped type is always known.
+ """
+ def __init__(self, descriptor, name, returnType, args):
+ CGAbstractExternMethod.__init__(self, descriptor, name, returnType,
+ args)
+
+ def definition_body_prologue(self):
+ return CGGeneric("""\
+let this: *const %s = unwrap::<%s>(obj);
+""" % (self.descriptor.concreteType, self.descriptor.concreteType))
+
+ def definition_body(self):
+ return CGList([
+ self.definition_body_prologue(),
+ self.generate_code(),
+ ])
+
+ def generate_code(self):
+ # Override me
+ assert(False)
+
+def finalizeHook(descriptor, hookName, context):
+ release = """let val = JS_GetReservedSlot(obj, dom_object_slot(obj));
+let _: Box<%s> = mem::transmute(val.to_private());
+debug!("%s finalize: {:p}", this);
+""" % (descriptor.concreteType, descriptor.concreteType)
+ return release
+
+class CGClassTraceHook(CGAbstractClassHook):
+ """
+ A hook to trace through our native object; used for GC and CC
+ """
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSTracer', 'trc'), Argument('*mut JSObject', 'obj')]
+ CGAbstractClassHook.__init__(self, descriptor, TRACE_HOOK_NAME, 'void',
+ args)
+
+ def generate_code(self):
+ return CGGeneric("(*this).trace(%s);" % self.args[0].name)
+
+class CGClassConstructHook(CGAbstractExternMethod):
+ """
+ JS-visible constructor for our objects
+ """
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSContext', 'cx'), Argument('u32', 'argc'), Argument('*mut JSVal', 'vp')]
+ CGAbstractExternMethod.__init__(self, descriptor, CONSTRUCT_HOOK_NAME,
+ 'JSBool', args)
+ self._ctor = self.descriptor.interface.ctor()
+
+ def define(self):
+ if not self._ctor:
+ return ""
+ return CGAbstractExternMethod.define(self)
+
+ def definition_body(self):
+ preamble = CGGeneric("""\
+let global = global_object_for_js_object(JS_CALLEE(cx, vp).to_object());
+let global = global.root();
+""")
+ nativeName = MakeNativeName(self._ctor.identifier.name)
+ callGenerator = CGMethodCall(["&global.root_ref()"], nativeName, True,
+ self.descriptor, self._ctor)
+ return CGList([preamble, callGenerator])
+
+class CGClassFinalizeHook(CGAbstractClassHook):
+ """
+ A hook for finalize, used to release our native object.
+ """
+ def __init__(self, descriptor):
+ args = [Argument('*mut JSFreeOp', 'fop'), Argument('*mut JSObject', 'obj')]
+ CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME,
+ 'void', args)
+
+ def generate_code(self):
+ return CGGeneric(finalizeHook(self.descriptor, self.name, self.args[0].name))
+
+class CGDOMJSProxyHandlerDOMClass(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+
+ def define(self):
+ return """
+static Class: DOMClass = """ + DOMClass(self.descriptor) + """;
+
+"""
+
+
+class CGInterfaceTrait(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+
+ def argument_type(ty, optional=False, defaultValue=None, variadic=False):
+ _, _, declType, _ = getJSToNativeConversionTemplate(
+ ty, descriptor, isArgument=True)
+
+ if variadic:
+ declType = CGWrapper(declType, pre="Vec<", post=">")
+ elif optional and not defaultValue:
+ declType = CGWrapper(declType, pre="Option<", post=">")
+
+ if ty.isGeckoInterface() and not (ty.nullable() or optional):
+ declType = CGWrapper(declType, pre="&")
+ elif ty.isDictionary():
+ declType = CGWrapper(declType, pre="&")
+
+ return declType.define()
+
+ def attribute_arguments(needCx, argument=None):
+ if needCx:
+ yield "cx", "*mut JSContext"
+
+ if argument:
+ yield "value", argument_type(argument)
+
+ def method_arguments(returnType, arguments, trailing=None):
+ if needCx(returnType, arguments, True):
+ yield "cx", "*mut JSContext"
+
+ for argument in arguments:
+ ty = argument_type(argument.type, argument.optional,
+ argument.defaultValue, argument.variadic)
+ yield CGDictionary.makeMemberName(argument.identifier.name), ty
+
+ if trailing:
+ yield trailing
+
+ def return_type(rettype, infallible):
+ result = getRetvalDeclarationForType(rettype, descriptor)
+ if not infallible:
+ result = CGWrapper(result, pre="Fallible<", post=">")
+ return result.define()
+
+ def members():
+ for m in descriptor.interface.members:
+ if m.isMethod() and not m.isStatic():
+ name = CGSpecializedMethod.makeNativeName(descriptor, m)
+ infallible = 'infallible' in descriptor.getExtendedAttributes(m)
+ for idx, (rettype, arguments) in enumerate(m.signatures()):
+ arguments = method_arguments(rettype, arguments)
+ rettype = return_type(rettype, infallible)
+ yield name + ('_' * idx), arguments, rettype
+ elif m.isAttr() and not m.isStatic():
+ name = CGSpecializedGetter.makeNativeName(descriptor, m)
+ infallible = 'infallible' in descriptor.getExtendedAttributes(m, getter=True)
+ needCx = typeNeedsCx(m.type)
+ yield name, attribute_arguments(needCx), return_type(m.type, infallible)
+
+ if not m.readonly:
+ name = CGSpecializedSetter.makeNativeName(descriptor, m)
+ infallible = 'infallible' in descriptor.getExtendedAttributes(m, setter=True)
+ if infallible:
+ rettype = "()"
+ else:
+ rettype = "ErrorResult"
+ yield name, attribute_arguments(needCx, m.type), rettype
+
+ if descriptor.proxy:
+ for name, operation in descriptor.operations.iteritems():
+ if not operation:
+ continue
+
+ assert len(operation.signatures()) == 1
+ rettype, arguments = operation.signatures()[0]
+
+ infallible = 'infallible' in descriptor.getExtendedAttributes(operation)
+ arguments = method_arguments(rettype, arguments, ("found", "&mut bool"))
+ rettype = return_type(rettype, infallible)
+ yield name, arguments, rettype
+
+ def fmt(arguments):
+ return "".join(", %s: %s" % argument for argument in arguments)
+
+ methods = CGList([
+ CGGeneric("fn %s(&self%s) -> %s;\n" % (name, fmt(arguments), rettype))
+ for name, arguments, rettype in members()
+ ], "")
+ self.cgRoot = CGWrapper(CGIndenter(methods),
+ pre="pub trait %sMethods {\n" % descriptor.interface.identifier.name,
+ post="}")
+
+ def define(self):
+ return self.cgRoot.define()
+
+
+class CGDescriptor(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+
+ assert not descriptor.interface.isCallback()
+
+ cgThings = []
+ cgThings.append(CGGetProtoObjectMethod(descriptor))
+ if descriptor.interface.hasInterfaceObject():
+ # https://github.com/mozilla/servo/issues/2665
+ # cgThings.append(CGGetConstructorObjectMethod(descriptor))
+ pass
+
+ (hasMethod, hasGetter, hasLenientGetter,
+ hasSetter, hasLenientSetter) = False, False, False, False, False
+ for m in descriptor.interface.members:
+ if m.isMethod() and not m.isIdentifierLess():
+ if m.isStatic():
+ assert descriptor.interface.hasInterfaceObject()
+ cgThings.append(CGStaticMethod(descriptor, m))
+ else:
+ cgThings.append(CGSpecializedMethod(descriptor, m))
+ cgThings.append(CGMemberJITInfo(descriptor, m))
+ hasMethod = True
+ elif m.isAttr():
+ if m.isStatic():
+ assert descriptor.interface.hasInterfaceObject()
+ cgThings.append(CGStaticGetter(descriptor, m))
+ else:
+ cgThings.append(CGSpecializedGetter(descriptor, m))
+ if m.hasLenientThis():
+ hasLenientGetter = True
+ else:
+ hasGetter = True
+
+ if not m.readonly:
+ if m.isStatic():
+ assert descriptor.interface.hasInterfaceObject()
+ cgThings.append(CGStaticSetter(descriptor, m))
+ else:
+ cgThings.append(CGSpecializedSetter(descriptor, m))
+ if m.hasLenientThis():
+ hasLenientSetter = True
+ else:
+ hasSetter = True
+
+ if not m.isStatic():
+ cgThings.append(CGMemberJITInfo(descriptor, m))
+ if hasMethod:
+ cgThings.append(CGGenericMethod(descriptor))
+ if hasGetter:
+ cgThings.append(CGGenericGetter(descriptor))
+ if hasLenientGetter:
+ pass
+ if hasSetter:
+ cgThings.append(CGGenericSetter(descriptor))
+ if hasLenientSetter:
+ pass
+
+ if descriptor.concrete:
+ cgThings.append(CGClassFinalizeHook(descriptor))
+ cgThings.append(CGClassTraceHook(descriptor))
+
+ if descriptor.interface.hasInterfaceObject():
+ cgThings.append(CGClassConstructHook(descriptor))
+ cgThings.append(CGInterfaceObjectJSClass(descriptor))
+
+ cgThings.append(CGPrototypeJSClass(descriptor))
+
+ properties = PropertyArrays(descriptor)
+ cgThings.append(CGGeneric(str(properties)))
+ cgThings.append(CGNativeProperties(descriptor, properties))
+ cgThings.append(CGNativePropertyHooks(descriptor, properties))
+ cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties))
+
+ cgThings.append(CGNamespace.build([descriptor.name + "Constants"],
+ CGConstant(m for m in descriptor.interface.members if m.isConst()),
+ public=True))
+
+ if descriptor.interface.hasInterfaceObject():
+ cgThings.append(CGDefineDOMInterfaceMethod(descriptor))
+
+ if descriptor.proxy:
+ cgThings.append(CGDefineProxyHandler(descriptor))
+
+ if descriptor.concrete:
+ if descriptor.proxy:
+ #cgThings.append(CGProxyIsProxy(descriptor))
+ cgThings.append(CGProxyUnwrap(descriptor))
+ cgThings.append(CGDOMJSProxyHandlerDOMClass(descriptor))
+ cgThings.append(CGDOMJSProxyHandler_getOwnPropertyDescriptor(descriptor))
+ cgThings.append(CGDOMJSProxyHandler_obj_toString(descriptor))
+ cgThings.append(CGDOMJSProxyHandler_get(descriptor))
+ cgThings.append(CGDOMJSProxyHandler_hasOwn(descriptor))
+ if descriptor.operations['IndexedSetter'] or descriptor.operations['NamedSetter']:
+ cgThings.append(CGDOMJSProxyHandler_defineProperty(descriptor))
+
+ #cgThings.append(CGDOMJSProxyHandler(descriptor))
+ #cgThings.append(CGIsMethod(descriptor))
+ pass
+ else:
+ cgThings.append(CGDOMJSClass(descriptor))
+ pass
+
+ cgThings.append(CGWrapMethod(descriptor))
+
+ cgThings.append(CGIDLInterface(descriptor))
+ cgThings.append(CGInterfaceTrait(descriptor))
+
+ cgThings = CGList(cgThings, "\n")
+ cgThings = CGWrapper(cgThings, pre='\n', post='\n')
+ #self.cgRoot = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name),
+ # cgThings),
+ # post='\n')
+ self.cgRoot = cgThings
+
+ def define(self):
+ return self.cgRoot.define()
+
+class CGNamespacedEnum(CGThing):
+ def __init__(self, namespace, enumName, names, values, comment="", deriving=""):
+
+ if not values:
+ values = []
+
+ # Account for explicit enum values.
+ entries = []
+ for i in range(0, len(names)):
+ if len(values) > i and values[i] is not None:
+ entry = "%s = %s" % (names[i], values[i])
+ else:
+ entry = names[i]
+ entries.append(entry)
+
+ # Append a Count.
+ entries.append(enumName + 'Count = ' + str(len(entries)))
+
+ # Indent.
+ entries = [' ' + e for e in entries]
+
+ # Build the enum body.
+ enumstr = comment + 'pub enum %s {\n%s\n}\n' % (enumName, ',\n'.join(entries))
+ if deriving:
+ enumstr = ('#[deriving(%s)]\n' % deriving) + enumstr
+ curr = CGGeneric(enumstr)
+
+ # Add some whitespace padding.
+ curr = CGWrapper(curr, pre='\n',post='\n')
+
+ # Add the namespace.
+ curr = CGNamespace(namespace, curr, public=True)
+
+ # Add the typedef
+ #typedef = '\ntypedef %s::%s %s;\n\n' % (namespace, enumName, enumName)
+ #curr = CGList([curr, CGGeneric(typedef)])
+
+ # Save the result.
+ self.node = curr
+
+ def define(self):
+ return self.node.define()
+
+class CGDictionary(CGThing):
+ def __init__(self, dictionary, descriptorProvider):
+ self.dictionary = dictionary;
+ if all(CGDictionary(d, descriptorProvider).generatable for
+ d in CGDictionary.getDictionaryDependencies(dictionary)):
+ self.generatable = True
+ else:
+ self.generatable = False
+ # Nothing else to do here
+ return
+ self.memberInfo = [
+ (member,
+ getJSToNativeConversionTemplate(member.type,
+ descriptorProvider,
+ isMember="Dictionary",
+ defaultValue=member.defaultValue,
+ failureCode="return Err(());",
+ exceptionCode="return Err(());"))
+ for member in dictionary.members ]
+
+ def define(self):
+ if not self.generatable:
+ return ""
+ return self.struct() + "\n" + self.impl()
+
+ def struct(self):
+ d = self.dictionary
+ if d.parent:
+ inheritance = " pub parent: %s::%s<'a, 'b>,\n" % (self.makeModuleName(d.parent),
+ self.makeClassName(d.parent))
+ else:
+ inheritance = ""
+ memberDecls = [" pub %s: %s," %
+ (self.makeMemberName(m[0].identifier.name), self.getMemberType(m))
+ for m in self.memberInfo]
+
+ return (string.Template(
+ "pub struct ${selfName}<'a, 'b> {\n" +
+ "${inheritance}" +
+ "\n".join(memberDecls) + "\n" +
+ "}").substitute( { "selfName": self.makeClassName(d),
+ "inheritance": inheritance }))
+
+ def impl(self):
+ d = self.dictionary
+ if d.parent:
+ initParent = ("parent: match %s::%s::new(cx, val) {\n"
+ " Ok(parent) => parent,\n"
+ " Err(_) => return Err(()),\n"
+ "},\n") % (self.makeModuleName(d.parent),
+ self.makeClassName(d.parent))
+ else:
+ initParent = ""
+
+ def memberInit(memberInfo):
+ member, _ = memberInfo
+ name = self.makeMemberName(member.identifier.name)
+ conversion = self.getMemberConversion(memberInfo)
+ return CGGeneric("%s: %s,\n" % (name, conversion.define()))
+
+ memberInits = CGList([memberInit(m) for m in self.memberInfo])
+
+ return string.Template(
+ "impl<'a, 'b> ${selfName}<'a, 'b> {\n"
+ " pub fn empty() -> ${selfName}<'a, 'b> {\n"
+ " ${selfName}::new(ptr::mut_null(), NullValue()).unwrap()\n"
+ " }\n"
+ " pub fn new(cx: *mut JSContext, val: JSVal) -> Result<${selfName}<'a, 'b>, ()> {\n"
+ " let object = if val.is_null_or_undefined() {\n"
+ " ptr::mut_null()\n"
+ " } else if val.is_object() {\n"
+ " val.to_object()\n"
+ " } else {\n"
+ " throw_type_error(cx, \"Value not an object.\");\n"
+ " return Err(());\n"
+ " };\n"
+ " Ok(${selfName} {\n"
+ "${initParent}"
+ "${initMembers}"
+ " })\n"
+ " }\n"
+ "}").substitute({
+ "selfName": self.makeClassName(d),
+ "initParent": CGIndenter(CGGeneric(initParent), indentLevel=6).define(),
+ "initMembers": CGIndenter(memberInits, indentLevel=6).define(),
+ })
+
+ @staticmethod
+ def makeDictionaryName(dictionary):
+ return dictionary.identifier.name
+
+ def makeClassName(self, dictionary):
+ return self.makeDictionaryName(dictionary)
+
+ @staticmethod
+ def makeModuleName(dictionary):
+ name = dictionary.identifier.name
+ if name.endswith('Init'):
+ return toBindingNamespace(name.replace('Init', ''))
+ #XXXjdm This breaks on the test webidl files, sigh.
+ #raise TypeError("No idea how to find this dictionary's definition: " + name)
+ return "/* uh oh */ %s" % name
+
+ def getMemberType(self, memberInfo):
+ member, (_, _, declType, _) = memberInfo
+ if not member.defaultValue:
+ declType = CGWrapper(declType, pre="Option<", post=">")
+ return declType.define()
+
+ def getMemberConversion(self, memberInfo):
+ def indent(s):
+ return CGIndenter(CGGeneric(s), 8).define()
+
+ member, (templateBody, default, declType, _) = memberInfo
+ replacements = { "val": "value" }
+ conversion = string.Template(templateBody).substitute(replacements)
+
+ assert (member.defaultValue is None) == (default is None)
+ if not default:
+ default = "None"
+ conversion = "Some(%s)" % conversion
+
+ conversion = (
+ "match get_dictionary_property(cx, object, \"%s\") {\n"
+ " Err(()) => return Err(()),\n"
+ " Ok(Some(value)) => {\n"
+ "%s\n"
+ " },\n"
+ " Ok(None) => {\n"
+ "%s\n"
+ " },\n"
+ "}") % (member.identifier.name, indent(conversion), indent(default))
+
+ return CGGeneric(conversion)
+
+ @staticmethod
+ def makeIdName(name):
+ return name + "_id"
+
+ @staticmethod
+ def makeMemberName(name):
+ # Can't use Rust keywords as member names.
+ if name == "type":
+ return name + "_"
+ return name
+
+ @staticmethod
+ def getDictionaryDependencies(dictionary):
+ deps = set();
+ if dictionary.parent:
+ deps.add(dictionary.parent)
+ for member in dictionary.members:
+ if member.type.isDictionary():
+ deps.add(member.type.unroll().inner)
+ return deps
+
+class CGRegisterProtos(CGAbstractMethod):
+ def __init__(self, config):
+ arguments = [
+ Argument('*mut JSContext', 'cx'),
+ Argument('*mut JSObject', 'global'),
+ ]
+ CGAbstractMethod.__init__(self, None, 'Register', 'void', arguments,
+ unsafe=False, pub=True)
+ self.config = config
+
+ def definition_body(self):
+ return CGList([
+ CGGeneric("codegen::Bindings::%sBinding::DefineDOMInterface(cx, global);" % desc.name)
+ for desc in self.config.getDescriptors(hasInterfaceObject=True, register=True)
+ ], "\n")
+
+
+class CGRegisterProxyHandlersMethod(CGAbstractMethod):
+ def __init__(self, descriptors):
+ CGAbstractMethod.__init__(self, None, 'RegisterProxyHandlers', 'void', [],
+ unsafe=True, pub=True)
+ self.descriptors = descriptors
+
+ def definition_body(self):
+ return CGList([
+ CGGeneric("proxy_handlers[proxies::%s as uint] = codegen::Bindings::%sBinding::DefineProxyHandler();" % (desc.name, desc.name))
+ for desc in self.descriptors
+ ], "\n")
+
+
+class CGRegisterProxyHandlers(CGThing):
+ def __init__(self, config):
+ descriptors = config.getDescriptors(proxy=True)
+ length = len(descriptors)
+ self.root = CGList([
+ CGGeneric("pub static mut proxy_handlers: [*const libc::c_void, ..%d] = [0 as *const libc::c_void, ..%d];" % (length, length)),
+ CGRegisterProxyHandlersMethod(descriptors),
+ ], "\n")
+
+ def define(self):
+ return self.root.define()
+
+
+class CGBindingRoot(CGThing):
+ """
+ Root codegen class for binding generation. Instantiate the class, and call
+ declare or define to generate header or cpp code (respectively).
+ """
+ def __init__(self, config, prefix, webIDLFile):
+ descriptors = config.getDescriptors(webIDLFile=webIDLFile,
+ isCallback=False)
+ dictionaries = config.getDictionaries(webIDLFile=webIDLFile)
+
+ cgthings = []
+
+ mainCallbacks = config.getCallbacks(webIDLFile=webIDLFile)
+ callbackDescriptors = config.getDescriptors(webIDLFile=webIDLFile,
+ isCallback=True)
+
+ # Do codegen for all the enums
+ cgthings = [CGEnum(e) for e in config.getEnums(webIDLFile)]
+
+ cgthings.extend([CGDictionary(d, config.getDescriptorProvider())
+ for d in dictionaries])
+
+ # Do codegen for all the callbacks.
+ cgthings.extend(CGList([CGCallbackFunction(c, config.getDescriptorProvider()),
+ CGCallbackFunctionImpl(c)], "\n")
+ for c in mainCallbacks)
+
+ # Do codegen for all the descriptors
+ cgthings.extend([CGDescriptor(x) for x in descriptors])
+
+ # Do codegen for all the callback interfaces.
+ cgthings.extend(CGList([CGCallbackInterface(x),
+ CGCallbackFunctionImpl(x)], "\n")
+ for x in callbackDescriptors)
+
+ # And make sure we have the right number of newlines at the end
+ curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n")
+
+ # Wrap all of that in our namespaces.
+ #curr = CGNamespace.build(['dom'],
+ # CGWrapper(curr, pre="\n"))
+
+ # Add imports
+ #XXXjdm This should only import the namespace for the current binding,
+ # not every binding ever.
+ curr = CGImports(curr, descriptors, [
+ 'js',
+ 'js::{JS_ARGV, JS_CALLEE, JS_THIS_OBJECT}',
+ 'js::{JSCLASS_GLOBAL_SLOT_COUNT, JSCLASS_IS_DOMJSCLASS}',
+ 'js::{JSCLASS_IS_GLOBAL, JSCLASS_RESERVED_SLOTS_SHIFT}',
+ 'js::{JSCLASS_RESERVED_SLOTS_MASK, JSID_VOID, JSJitInfo}',
+ 'js::{JSPROP_ENUMERATE, JSPROP_NATIVE_ACCESSORS, JSPROP_SHARED}',
+ 'js::{JSRESOLVE_ASSIGNING, JSRESOLVE_QUALIFIED}',
+ 'js::jsapi::{JS_CallFunctionValue, JS_GetClass, JS_GetGlobalForObject}',
+ 'js::jsapi::{JS_GetObjectPrototype, JS_GetProperty, JS_GetPropertyById}',
+ 'js::jsapi::{JS_GetPropertyDescriptorById, JS_GetReservedSlot}',
+ 'js::jsapi::{JS_HasProperty, JS_HasPropertyById, JS_IsExceptionPending}',
+ 'js::jsapi::{JS_NewObject, JS_ObjectIsCallable, JS_SetPrototype}',
+ 'js::jsapi::{JS_SetReservedSlot, JS_WrapValue, JSBool, JSContext}',
+ 'js::jsapi::{JSClass, JSFreeOp, JSFunctionSpec, JSHandleObject, jsid}',
+ 'js::jsapi::{JSNativeWrapper, JSObject, JSPropertyDescriptor, JS_ArrayIterator}',
+ 'js::jsapi::{JSPropertyOpWrapper, JSPropertySpec, JS_PropertyStub}',
+ 'js::jsapi::{JSStrictPropertyOpWrapper, JSString, JSTracer, JS_ConvertStub}',
+ 'js::jsapi::{JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub}',
+ 'js::jsval::JSVal',
+ 'js::jsval::{ObjectValue, ObjectOrNullValue, PrivateValue}',
+ 'js::jsval::{NullValue, UndefinedValue}',
+ 'js::glue::{CallJitMethodOp, CallJitPropertyOp, CreateProxyHandler}',
+ 'js::glue::{GetProxyPrivate, NewProxyObject, ProxyTraps}',
+ 'js::glue::{RUST_FUNCTION_VALUE_TO_JITINFO}',
+ 'js::glue::{RUST_JS_NumberValue, RUST_JSID_IS_STRING}',
+ 'js::rust::with_compartment',
+ 'dom::types::*',
+ 'dom::bindings',
+ 'dom::bindings::global::GlobalRef',
+ 'dom::bindings::js::{JS, JSRef, Root, RootedReference, Temporary}',
+ 'dom::bindings::js::{OptionalRootable, OptionalRootedRootable, ResultRootable}',
+ 'dom::bindings::js::{OptionalRootedReference, OptionalOptionalRootedRootable}',
+ 'dom::bindings::utils::{CreateDOMGlobal, CreateInterfaceObjects2}',
+ 'dom::bindings::utils::{ConstantSpec, cx_for_dom_object}',
+ 'dom::bindings::utils::{dom_object_slot, DOM_OBJECT_SLOT, DOMClass}',
+ 'dom::bindings::utils::{DOMJSClass, JSCLASS_DOM_GLOBAL}',
+ 'dom::bindings::utils::{FindEnumStringIndex, GetArrayIndexFromId}',
+ 'dom::bindings::utils::{GetPropertyOnPrototype, GetProtoOrIfaceArray}',
+ 'dom::bindings::utils::{HasPropertyOnPrototype, IntVal}',
+ 'dom::bindings::utils::{jsid_to_str}',
+ 'dom::bindings::utils::global_object_for_js_object',
+ 'dom::bindings::utils::{Reflectable}',
+ 'dom::bindings::utils::{squirrel_away_unique}',
+ 'dom::bindings::utils::{ThrowingConstructor, unwrap, unwrap_jsmanaged}',
+ 'dom::bindings::utils::VoidVal',
+ 'dom::bindings::utils::get_dictionary_property',
+ 'dom::bindings::utils::{NativeProperties, NativePropertyHooks}',
+ 'dom::bindings::trace::JSTraceable',
+ 'dom::bindings::callback::{CallbackContainer,CallbackInterface,CallbackFunction}',
+ 'dom::bindings::callback::{CallSetup,ExceptionHandling}',
+ 'dom::bindings::callback::{WrapCallThisObject}',
+ 'dom::bindings::conversions::{FromJSValConvertible, ToJSValConvertible}',
+ 'dom::bindings::conversions::IDLInterface',
+ 'dom::bindings::conversions::{Default, Empty}',
+ 'dom::bindings::codegen::*',
+ 'dom::bindings::codegen::Bindings::*',
+ 'dom::bindings::codegen::RegisterBindings',
+ 'dom::bindings::codegen::UnionTypes::*',
+ 'dom::bindings::error::{FailureUnknown, Fallible, Error, ErrorResult}',
+ 'dom::bindings::error::throw_dom_exception',
+ 'dom::bindings::error::throw_type_error',
+ 'dom::bindings::proxyhandler',
+ 'dom::bindings::proxyhandler::{_obj_toString, defineProperty}',
+ 'dom::bindings::proxyhandler::{FillPropertyDescriptor, GetExpandoObject}',
+ 'dom::bindings::proxyhandler::{delete_, getPropertyDescriptor}',
+ 'dom::bindings::str::ByteString',
+ 'page::JSPageInfo',
+ 'libc',
+ 'servo_util::str::DOMString',
+ 'std::mem',
+ 'std::cmp',
+ 'std::ptr',
+ 'std::str',
+ 'std::num',
+ ])
+
+ # Add the auto-generated comment.
+ curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
+
+ # Store the final result.
+ self.root = curr
+
+ def define(self):
+ return stripTrailingWhitespace(self.root.define())
+
+class CGNativeMember(ClassMethod):
+ def __init__(self, descriptorProvider, member, name, signature, extendedAttrs,
+ breakAfter=True, passJSBitsAsNeeded=True, visibility="public",
+ jsObjectsArePtr=False, variadicIsSequence=False):
+ """
+ If jsObjectsArePtr is true, typed arrays and "object" will be
+ passed as JSObject*.
+
+ If passJSBitsAsNeeded is false, we don't automatically pass in a
+ JSContext* or a JSObject* based on the return and argument types.
+ """
+ self.descriptorProvider = descriptorProvider
+ self.member = member
+ self.extendedAttrs = extendedAttrs
+ self.passJSBitsAsNeeded = passJSBitsAsNeeded
+ self.jsObjectsArePtr = jsObjectsArePtr
+ self.variadicIsSequence = variadicIsSequence
+ breakAfterSelf = "\n" if breakAfter else ""
+ ClassMethod.__init__(self, name,
+ self.getReturnType(signature[0], False),
+ self.getArgs(signature[0], signature[1]),
+ static=member.isStatic(),
+ # Mark our getters, which are attrs that
+ # have a non-void return type, as const.
+ const=(not member.isStatic() and member.isAttr() and
+ not signature[0].isVoid()),
+ breakAfterReturnDecl=" ",
+ breakAfterSelf=breakAfterSelf,
+ visibility=visibility)
+
+ def getReturnType(self, type, isMember):
+ return self.getRetvalInfo(type, isMember)[0]
+
+ def getRetvalInfo(self, type, isMember):
+ """
+ Returns a tuple:
+
+ The first element is the type declaration for the retval
+
+ The second element is a template for actually returning a value stored in
+ "${declName}". This means actually returning it if
+ we're not outparam, else assigning to the "retval" outparam. If
+ isMember is true, this can be None, since in that case the caller will
+ never examine this value.
+ """
+ if type.isVoid():
+ typeDecl, template = "", ""
+ elif type.isPrimitive() and type.tag() in builtinNames:
+ result = CGGeneric(builtinNames[type.tag()])
+ if type.nullable():
+ raise TypeError("Nullable primitives are not supported here.")
+
+ typeDecl, template = result.define(), "return Ok(${declName});"
+ elif type.isDOMString():
+ if isMember:
+ # No need for a third element in the isMember case
+ typeDecl, template = "nsString", None
+ # Outparam
+ else:
+ typeDecl, template = "void", "retval = ${declName};"
+ elif type.isByteString():
+ if isMember:
+ # No need for a third element in the isMember case
+ typeDecl, template = "nsCString", None
+ # Outparam
+ typeDecl, template = "void", "retval = ${declName};"
+ elif type.isEnum():
+ enumName = type.unroll().inner.identifier.name
+ if type.nullable():
+ enumName = CGTemplatedType("Nullable",
+ CGGeneric(enumName)).define()
+ typeDecl, template = enumName, "return ${declName};"
+ elif type.isGeckoInterface():
+ iface = type.unroll().inner;
+ nativeType = self.descriptorProvider.getDescriptor(
+ iface.identifier.name).nativeType
+ # Now trim off unnecessary namespaces
+ nativeType = nativeType.split("::")
+ if nativeType[0] == "mozilla":
+ nativeType.pop(0)
+ if nativeType[0] == "dom":
+ nativeType.pop(0)
+ result = CGWrapper(CGGeneric("::".join(nativeType)), post="*")
+ # Since we always force an owning type for callback return values,
+ # our ${declName} is an OwningNonNull or nsRefPtr. So we can just
+ # .forget() to get our already_AddRefed.
+ typeDecl, template = result.define(), "return ${declName}.forget();"
+ elif type.isCallback():
+ typeDecl, template = \
+ ("already_AddRefed<%s>" % type.unroll().identifier.name,
+ "return ${declName}.forget();")
+ elif type.isAny():
+ typeDecl, template = "JSVal", "return Ok(${declName});"
+ elif type.isObject():
+ typeDecl, template = "JSObject*", "return ${declName};"
+ elif type.isSpiderMonkeyInterface():
+ if type.nullable():
+ returnCode = "return ${declName}.IsNull() ? nullptr : ${declName}.Value().Obj();"
+ else:
+ returnCode = "return ${declName}.Obj();"
+ typeDecl, template = "JSObject*", returnCode
+ elif type.isSequence():
+ # If we want to handle sequence-of-sequences return values, we're
+ # going to need to fix example codegen to not produce nsTArray<void>
+ # for the relevant argument...
+ assert not isMember
+ # Outparam.
+ if type.nullable():
+ returnCode = ("if (${declName}.IsNull()) {\n"
+ " retval.SetNull();\n"
+ "} else {\n"
+ " retval.SetValue().SwapElements(${declName}.Value());\n"
+ "}")
+ else:
+ returnCode = "retval.SwapElements(${declName});"
+ typeDecl, template = "void", returnCode
+ elif type.isDate():
+ result = CGGeneric("Date")
+ if type.nullable():
+ result = CGTemplatedType("Nullable", result)
+ typeDecl, template = result.define(), "return ${declName};"
+ else:
+ raise TypeError("Don't know how to declare return value for %s" % type)
+
+ if not 'infallible' in self.extendedAttrs:
+ if typeDecl:
+ typeDecl = "Fallible<%s>" % typeDecl
+ else:
+ typeDecl = "ErrorResult"
+ if not template:
+ template = "return Ok(());"
+ return typeDecl, template
+
+ def getArgs(self, returnType, argList):
+ args = [self.getArg(arg) for arg in argList]
+ # Now the outparams
+ if returnType.isDOMString():
+ args.append(Argument("nsString&", "retval"))
+ if returnType.isByteString():
+ args.append(Argument("nsCString&", "retval"))
+ elif returnType.isSequence():
+ nullable = returnType.nullable()
+ if nullable:
+ returnType = returnType.inner
+ # And now the actual underlying type
+ elementDecl = self.getReturnType(returnType.inner, True)
+ type = CGTemplatedType("nsTArray", CGGeneric(elementDecl))
+ if nullable:
+ type = CGTemplatedType("Nullable", type)
+ args.append(Argument("%s&" % type.define(), "retval"))
+ # The legacycaller thisval
+ if self.member.isMethod() and self.member.isLegacycaller():
+ # If it has an identifier, we can't deal with it yet
+ assert self.member.isIdentifierLess()
+ args.insert(0, Argument("JS::Value", "aThisVal"))
+ # And jscontext bits.
+ if needCx(returnType, argList, self.passJSBitsAsNeeded):
+ args.insert(0, Argument("JSContext*", "cx"))
+ # And if we're static, a global
+ if self.member.isStatic():
+ args.insert(0, Argument("const GlobalObject&", "global"))
+ return args
+
+ def doGetArgType(self, type, optional, isMember):
+ """
+ The main work of getArgType. Returns a string type decl, whether this
+ is a const ref, as well as whether the type should be wrapped in
+ Nullable as needed.
+
+ isMember can be false or one of the strings "Sequence" or "Variadic"
+ """
+ if type.isArray():
+ raise TypeError("Can't handle array arguments yet")
+
+ if type.isSequence():
+ nullable = type.nullable()
+ if nullable:
+ type = type.inner
+ elementType = type.inner
+ argType = self.getArgType(elementType, False, "Sequence")[0]
+ decl = CGTemplatedType("Sequence", argType)
+ return decl.define(), True, True
+
+ if type.isUnion():
+ if type.nullable():
+ type = type.inner
+ return str(type) + "::" + str(type), False, True
+
+ if type.isGeckoInterface() and not type.isCallbackInterface():
+ iface = type.unroll().inner
+ argIsPointer = type.nullable()
+ forceOwningType = iface.isCallback() or isMember
+ if argIsPointer:
+ if (optional or isMember) and forceOwningType:
+ typeDecl = "nsRefPtr<%s>"
+ else:
+ typeDecl = "*%s"
+ else:
+ if optional or isMember:
+ if forceOwningType:
+ typeDecl = "OwningNonNull<%s>"
+ else:
+ typeDecl = "NonNull<%s>"
+ else:
+ typeDecl = "%s"
+ descriptor = self.descriptorProvider.getDescriptor(iface.identifier.name)
+ return (typeDecl % descriptor.argumentType,
+ False, False)
+
+ if type.isSpiderMonkeyInterface():
+ if self.jsObjectsArePtr:
+ return "JSObject*", False, False
+
+ return type.name, True, True
+
+ if type.isDOMString():
+ declType = "DOMString"
+ return declType, True, False
+
+ if type.isByteString():
+ declType = "nsCString"
+ return declType, True, False
+
+ if type.isEnum():
+ return type.unroll().inner.identifier.name, False, True
+
+ if type.isCallback() or type.isCallbackInterface():
+ forceOwningType = optional or isMember
+ if type.nullable():
+ if forceOwningType:
+ declType = "nsRefPtr<%s>"
+ else:
+ declType = "%s*"
+ else:
+ if forceOwningType:
+ declType = "OwningNonNull<%s>"
+ else:
+ declType = "%s&"
+ if type.isCallback():
+ name = type.unroll().identifier.name
+ else:
+ name = type.unroll().inner.identifier.name
+ return declType % name, False, False
+
+ if type.isAny():
+ # Don't do the rooting stuff for variadics for now
+ if isMember:
+ declType = "JS::Value"
+ else:
+ declType = "JSVal"
+ return declType, False, False
+
+ if type.isObject():
+ if isMember:
+ declType = "JSObject*"
+ else:
+ declType = "JS::Handle<JSObject*>"
+ return declType, False, False
+
+ if type.isDictionary():
+ typeName = CGDictionary.makeDictionaryName(type.inner)
+ return typeName, True, True
+
+ if type.isDate():
+ return "Date", False, True
+
+ assert type.isPrimitive()
+
+ return builtinNames[type.tag()], False, True
+
+ def getArgType(self, type, optional, isMember):
+ """
+ Get the type of an argument declaration. Returns the type CGThing, and
+ whether this should be a const ref.
+
+ isMember can be False, "Sequence", or "Variadic"
+ """
+ (decl, ref, handleNullable) = self.doGetArgType(type, optional,
+ isMember)
+ decl = CGGeneric(decl)
+ if handleNullable and type.nullable():
+ decl = CGTemplatedType("Nullable", decl)
+ ref = True
+ if isMember == "Variadic":
+ arrayType = "Sequence" if self.variadicIsSequence else "nsTArray"
+ decl = CGTemplatedType(arrayType, decl)
+ ref = True
+ elif optional:
+ # Note: All variadic args claim to be optional, but we can just use
+ # empty arrays to represent them not being present.
+ decl = CGTemplatedType("Option", decl)
+ ref = False
+ return (decl, ref)
+
+ def getArg(self, arg):
+ """
+ Get the full argument declaration for an argument
+ """
+ (decl, ref) = self.getArgType(arg.type,
+ arg.optional and not arg.defaultValue,
+ "Variadic" if arg.variadic else False)
+ if ref:
+ decl = CGWrapper(decl, pre="&")
+
+ return Argument(decl.define(), arg.identifier.name)
+
+class CGCallback(CGClass):
+ def __init__(self, idlObject, descriptorProvider, baseName, methods,
+ getters=[], setters=[]):
+ self.baseName = baseName
+ self._deps = idlObject.getDeps()
+ name = idlObject.identifier.name
+ # For our public methods that needThisHandling we want most of the
+ # same args and the same return type as what CallbackMember
+ # generates. So we want to take advantage of all its
+ # CGNativeMember infrastructure, but that infrastructure can't deal
+ # with templates and most especially template arguments. So just
+ # cheat and have CallbackMember compute all those things for us.
+ realMethods = []
+ for method in methods:
+ if not method.needThisHandling:
+ realMethods.append(method)
+ else:
+ realMethods.extend(self.getMethodImpls(method))
+ CGClass.__init__(self, name,
+ bases=[ClassBase(baseName)],
+ constructors=self.getConstructors(),
+ methods=realMethods+getters+setters,
+ decorators="#[deriving(PartialEq,Clone,Encodable)]")
+
+ def getConstructors(self):
+ return [ClassConstructor(
+ [Argument("*mut JSObject", "aCallback")],
+ bodyInHeader=True,
+ visibility="pub",
+ explicit=False,
+ baseConstructors=[
+ "%s::new(aCallback)" % self.baseName
+ ])]
+
+ def getMethodImpls(self, method):
+ assert method.needThisHandling
+ args = list(method.args)
+ # Strip out the JSContext*/JSObject* args
+ # that got added.
+ assert args[0].name == "cx" and args[0].argType == "*mut JSContext"
+ assert args[1].name == "aThisObj" and args[1].argType == "*mut JSObject"
+ args = args[2:]
+ # Record the names of all the arguments, so we can use them when we call
+ # the private method.
+ argnames = [arg.name for arg in args]
+ argnamesWithThis = ["s.GetContext()", "thisObjJS"] + argnames
+ argnamesWithoutThis = ["s.GetContext()", "ptr::mut_null()"] + argnames
+ # Now that we've recorded the argnames for our call to our private
+ # method, insert our optional argument for deciding whether the
+ # CallSetup should re-throw exceptions on aRv.
+ args.append(Argument("ExceptionHandling", "aExceptionHandling",
+ "ReportExceptions"))
+
+ args[0] = Argument('&' + args[0].argType, args[0].name, args[0].default)
+ method.args[2] = args[0]
+
+ # And now insert our template argument.
+ argsWithoutThis = list(args)
+ args.insert(0, Argument("&JSRef<T>", "thisObj"))
+
+ # And the self argument
+ method.args.insert(0, Argument(None, "&self"))
+ args.insert(0, Argument(None, "&self"))
+ argsWithoutThis.insert(0, Argument(None, "&self"))
+
+ setupCall = ("let s = CallSetup::new(self, aExceptionHandling);\n"
+ "if s.GetContext().is_null() {\n"
+ " return Err(FailureUnknown);\n"
+ "}\n")
+
+ bodyWithThis = string.Template(
+ setupCall+
+ "let thisObjJS = WrapCallThisObject(s.GetContext(), thisObj);\n"
+ "if thisObjJS.is_null() {\n"
+ " return Err(FailureUnknown);\n"
+ "}\n"
+ "return ${methodName}(${callArgs});").substitute({
+ "callArgs" : ", ".join(argnamesWithThis),
+ "methodName": 'self.' + method.name,
+ })
+ bodyWithoutThis = string.Template(
+ setupCall +
+ "return ${methodName}(${callArgs});").substitute({
+ "callArgs" : ", ".join(argnamesWithoutThis),
+ "methodName": 'self.' + method.name,
+ })
+ return [ClassMethod(method.name+'_', method.returnType, args,
+ bodyInHeader=True,
+ templateArgs=["T: Reflectable"],
+ body=bodyWithThis,
+ visibility='pub'),
+ ClassMethod(method.name+'__', method.returnType, argsWithoutThis,
+ bodyInHeader=True,
+ body=bodyWithoutThis,
+ visibility='pub'),
+ method]
+
+ def deps(self):
+ return self._deps
+
+# We're always fallible
+def callbackGetterName(attr):
+ return "Get" + MakeNativeName(attr.identifier.name)
+
+def callbackSetterName(attr):
+ return "Set" + MakeNativeName(attr.identifier.name)
+
+class CGCallbackFunction(CGCallback):
+ def __init__(self, callback, descriptorProvider):
+ CGCallback.__init__(self, callback, descriptorProvider,
+ "CallbackFunction",
+ methods=[CallCallback(callback, descriptorProvider)])
+
+ def getConstructors(self):
+ return CGCallback.getConstructors(self)
+
+class CGCallbackFunctionImpl(CGGeneric):
+ def __init__(self, callback):
+ impl = string.Template("""impl CallbackContainer for ${type} {
+ fn new(callback: *mut JSObject) -> ${type} {
+ ${type}::new(callback)
+ }
+
+ fn callback(&self) -> *mut JSObject {
+ self.parent.callback()
+ }
+}
+
+impl ToJSValConvertible for ${type} {
+ fn to_jsval(&self, cx: *mut JSContext) -> JSVal {
+ self.callback().to_jsval(cx)
+ }
+}
+""").substitute({"type": callback.name})
+ CGGeneric.__init__(self, impl)
+
+class CGCallbackInterface(CGCallback):
+ def __init__(self, descriptor):
+ iface = descriptor.interface
+ attrs = [m for m in iface.members if m.isAttr() and not m.isStatic()]
+ getters = [CallbackGetter(a, descriptor) for a in attrs]
+ setters = [CallbackSetter(a, descriptor) for a in attrs
+ if not a.readonly]
+ methods = [m for m in iface.members
+ if m.isMethod() and not m.isStatic() and not m.isIdentifierLess()]
+ methods = [CallbackOperation(m, sig, descriptor) for m in methods
+ for sig in m.signatures()]
+ assert not iface.isJSImplemented() or not iface.ctor()
+ CGCallback.__init__(self, iface, descriptor, "CallbackInterface",
+ methods, getters=getters, setters=setters)
+
+class FakeMember():
+ def __init__(self):
+ self.treatNullAs = "Default"
+ def isStatic(self):
+ return False
+ def isAttr(self):
+ return False
+ def isMethod(self):
+ return False
+ def getExtendedAttribute(self, name):
+ return None
+
+class CallbackMember(CGNativeMember):
+ def __init__(self, sig, name, descriptorProvider, needThisHandling, rethrowContentException=False):
+ """
+ needThisHandling is True if we need to be able to accept a specified
+ thisObj, False otherwise.
+ """
+ assert not rethrowContentException or not needThisHandling
+
+ self.retvalType = sig[0]
+ self.originalSig = sig
+ args = sig[1]
+ self.argCount = len(args)
+ if self.argCount > 0:
+ # Check for variadic arguments
+ lastArg = args[self.argCount-1]
+ if lastArg.variadic:
+ self.argCountStr = (
+ "(%d - 1) + %s.Length()" % (self.argCount,
+ lastArg.identifier.name))
+ else:
+ self.argCountStr = "%d" % self.argCount
+ self.needThisHandling = needThisHandling
+ # If needThisHandling, we generate ourselves as private and the caller
+ # will handle generating public versions that handle the "this" stuff.
+ visibility = "priv" if needThisHandling else "pub"
+ self.rethrowContentException = rethrowContentException
+ # We don't care, for callback codegen, whether our original member was
+ # a method or attribute or whatnot. Just always pass FakeMember()
+ # here.
+ CGNativeMember.__init__(self, descriptorProvider, FakeMember(),
+ name, (self.retvalType, args),
+ extendedAttrs={},
+ passJSBitsAsNeeded=False,
+ visibility=visibility,
+ jsObjectsArePtr=True)
+ # We have to do all the generation of our body now, because
+ # the caller relies on us throwing if we can't manage it.
+ self.exceptionCode= "return Err(FailureUnknown);\n"
+ self.body = self.getImpl()
+
+ def getImpl(self):
+ replacements = {
+ "declRval": self.getRvalDecl(),
+ "returnResult": self.getResultConversion(),
+ "convertArgs": self.getArgConversions(),
+ "doCall": self.getCall(),
+ "setupCall": self.getCallSetup(),
+ }
+ if self.argCount > 0:
+ replacements["argCount"] = self.argCountStr
+ replacements["argvDecl"] = string.Template(
+ "let mut argv = Vec::from_elem(${argCount}, UndefinedValue());\n"
+ ).substitute(replacements)
+ else:
+ # Avoid weird 0-sized arrays
+ replacements["argvDecl"] = ""
+
+ # Newlines and semicolons are in the values
+ pre = string.Template(
+ "${setupCall}"
+ "${declRval}"
+ "${argvDecl}").substitute(replacements)
+ body = string.Template(
+ "${convertArgs}"
+ "${doCall}"
+ "${returnResult}").substitute(replacements)
+ return CGList([
+ CGGeneric(pre),
+ CGWrapper(CGIndenter(CGGeneric(body)),
+ pre="with_compartment(cx, self.parent.callback(), || {\n",
+ post="})")
+ ], "\n").define()
+
+ def getResultConversion(self):
+ replacements = {
+ "val": "rval",
+ "declName": "rvalDecl",
+ }
+
+ template, _, declType, needsRooting = getJSToNativeConversionTemplate(
+ self.retvalType,
+ self.descriptorProvider,
+ exceptionCode=self.exceptionCode,
+ isCallbackReturnValue="Callback",
+ # XXXbz we should try to do better here
+ sourceDescription="return value")
+
+ convertType = instantiateJSToNativeConversionTemplate(
+ template, replacements, declType, "rvalDecl", needsRooting)
+
+ assignRetval = string.Template(
+ self.getRetvalInfo(self.retvalType,
+ False)[1]).substitute(replacements)
+ return convertType.define() + "\n" + assignRetval + "\n"
+
+ def getArgConversions(self):
+ # Just reget the arglist from self.originalSig, because our superclasses
+ # just have way to many members they like to clobber, so I can't find a
+ # safe member name to store it in.
+ argConversions = [self.getArgConversion(i, arg) for (i, arg)
+ in enumerate(self.originalSig[1])]
+ # Do them back to front, so our argc modifications will work
+ # correctly, because we examine trailing arguments first.
+ argConversions.reverse();
+ # Wrap each one in a scope so that any locals it has don't leak out, and
+ # also so that we can just "break;" for our successCode.
+ argConversions = [CGWrapper(CGIndenter(CGGeneric(c)),
+ pre="loop {\n",
+ post="\nbreak;}\n")
+ for c in argConversions]
+ if self.argCount > 0:
+ argConversions.insert(0, self.getArgcDecl())
+ # And slap them together.
+ return CGList(argConversions, "\n\n").define() + "\n\n"
+
+ def getArgConversion(self, i, arg):
+ argval = arg.identifier.name
+
+ if arg.variadic:
+ argval = argval + "[idx]"
+ jsvalIndex = "%d + idx" % i
+ else:
+ jsvalIndex = "%d" % i
+ if arg.optional and not arg.defaultValue:
+ argval += ".clone().unwrap()"
+
+ conversion = wrapForType("*argv.get_mut(%s)" % jsvalIndex,
+ result=argval,
+ successCode="continue;" if arg.variadic else "break;")
+ if arg.variadic:
+ conversion = string.Template(
+ "for (uint32_t idx = 0; idx < ${arg}.Length(); ++idx) {\n" +
+ CGIndenter(CGGeneric(conversion)).define() + "\n"
+ "}\n"
+ "break;").substitute({ "arg": arg.identifier.name })
+ elif arg.optional and not arg.defaultValue:
+ conversion = (
+ CGIfWrapper(CGGeneric(conversion),
+ "%s.is_some()" % arg.identifier.name).define() +
+ " else if (argc == %d) {\n"
+ " // This is our current trailing argument; reduce argc\n"
+ " argc -= 1;\n"
+ "} else {\n"
+ " *argv.get_mut(%d) = UndefinedValue();\n"
+ "}" % (i+1, i))
+ return conversion
+
+ def getArgs(self, returnType, argList):
+ args = CGNativeMember.getArgs(self, returnType, argList)
+ if not self.needThisHandling:
+ # Since we don't need this handling, we're the actual method that
+ # will be called, so we need an aRethrowExceptions argument.
+ if self.rethrowContentException:
+ args.append(Argument("JSCompartment*", "aCompartment", "nullptr"))
+ else:
+ args.append(Argument("ExceptionHandling", "aExceptionHandling",
+ "ReportExceptions"))
+ return args
+ # We want to allow the caller to pass in a "this" object, as
+ # well as a JSContext.
+ return [Argument("*mut JSContext", "cx"),
+ Argument("*mut JSObject", "aThisObj")] + args
+
+ def getCallSetup(self):
+ if self.needThisHandling:
+ # It's been done for us already
+ return ""
+ callSetup = "CallSetup s(CallbackPreserveColor(), aRv"
+ if self.rethrowContentException:
+ # getArgs doesn't add the aExceptionHandling argument but does add
+ # aCompartment for us.
+ callSetup += ", RethrowContentExceptions, aCompartment"
+ else:
+ callSetup += ", aExceptionHandling"
+ callSetup += ");"
+ return string.Template(
+ "${callSetup}\n"
+ "JSContext* cx = s.GetContext();\n"
+ "if (!cx) {\n"
+ " return Err(FailureUnknown);\n"
+ "}\n").substitute({
+ "callSetup": callSetup,
+ })
+
+ def getArgcDecl(self):
+ return CGGeneric("let mut argc = %su32;" % self.argCountStr);
+
+ @staticmethod
+ def ensureASCIIName(idlObject):
+ type = "attribute" if idlObject.isAttr() else "operation"
+ if re.match("[^\x20-\x7E]", idlObject.identifier.name):
+ raise SyntaxError('Callback %s name "%s" contains non-ASCII '
+ "characters. We can't handle that. %s" %
+ (type, idlObject.identifier.name,
+ idlObject.location))
+ if re.match('"', idlObject.identifier.name):
+ raise SyntaxError("Callback %s name '%s' contains "
+ "double-quote character. We can't handle "
+ "that. %s" %
+ (type, idlObject.identifier.name,
+ idlObject.location))
+
+class CallbackMethod(CallbackMember):
+ def __init__(self, sig, name, descriptorProvider, needThisHandling, rethrowContentException=False):
+ CallbackMember.__init__(self, sig, name, descriptorProvider,
+ needThisHandling, rethrowContentException)
+ def getRvalDecl(self):
+ return "let mut rval = UndefinedValue();\n"
+
+ def getCall(self):
+ replacements = {
+ "thisObj": self.getThisObj(),
+ "getCallable": self.getCallableDecl()
+ }
+ if self.argCount > 0:
+ replacements["argv"] = "argv.as_mut_ptr()"
+ replacements["argc"] = "argc"
+ else:
+ replacements["argv"] = "nullptr"
+ replacements["argc"] = "0"
+ return string.Template("${getCallable}"
+ "let ok = unsafe {\n"
+ " JS_CallFunctionValue(cx, ${thisObj}, callable,\n"
+ " ${argc}, ${argv}, &mut rval)\n"
+ "};\n"
+ "if ok == 0 {\n"
+ " return Err(FailureUnknown);\n"
+ "}\n").substitute(replacements)
+
+class CallCallback(CallbackMethod):
+ def __init__(self, callback, descriptorProvider):
+ CallbackMethod.__init__(self, callback.signatures()[0], "Call",
+ descriptorProvider, needThisHandling=True)
+
+ def getThisObj(self):
+ return "aThisObj"
+
+ def getCallableDecl(self):
+ return "let callable = ObjectValue(unsafe {&*self.parent.callback()});\n";
+
+class CallbackOperationBase(CallbackMethod):
+ """
+ Common class for implementing various callback operations.
+ """
+ def __init__(self, signature, jsName, nativeName, descriptor, singleOperation, rethrowContentException=False):
+ self.singleOperation = singleOperation
+ self.methodName = jsName
+ CallbackMethod.__init__(self, signature, nativeName, descriptor, singleOperation, rethrowContentException)
+
+ def getThisObj(self):
+ if not self.singleOperation:
+ return "self.parent.callback()"
+ # This relies on getCallableDecl declaring a boolean
+ # isCallable in the case when we're a single-operation
+ # interface.
+ return "if isCallable { aThisObj } else { self.parent.callback() }"
+
+ def getCallableDecl(self):
+ replacements = {
+ "methodName": self.methodName
+ }
+ getCallableFromProp = string.Template(
+ 'match self.parent.GetCallableProperty(cx, "${methodName}") {\n'
+ ' Err(_) => return Err(FailureUnknown),\n'
+ ' Ok(callable) => callable,\n'
+ '}').substitute(replacements)
+ if not self.singleOperation:
+ return 'JS::Rooted<JS::Value> callable(cx);\n' + getCallableFromProp
+ return (
+ 'let isCallable = unsafe { JS_ObjectIsCallable(cx, self.parent.callback()) != 0 };\n'
+ 'let callable =\n' +
+ CGIndenter(
+ CGIfElseWrapper('isCallable',
+ CGGeneric('unsafe { ObjectValue(&*self.parent.callback()) }'),
+ CGGeneric(getCallableFromProp))).define() + ';\n')
+
+class CallbackOperation(CallbackOperationBase):
+ """
+ Codegen actual WebIDL operations on callback interfaces.
+ """
+ def __init__(self, method, signature, descriptor):
+ self.ensureASCIIName(method)
+ jsName = method.identifier.name
+ CallbackOperationBase.__init__(self, signature,
+ jsName, MakeNativeName(jsName),
+ descriptor, descriptor.interface.isSingleOperationInterface(),
+ rethrowContentException=descriptor.interface.isJSImplemented())
+
+class CallbackGetter(CallbackMember):
+ def __init__(self, attr, descriptor):
+ self.ensureASCIIName(attr)
+ self.attrName = attr.identifier.name
+ CallbackMember.__init__(self,
+ (attr.type, []),
+ callbackGetterName(attr),
+ descriptor,
+ needThisHandling=False,
+ rethrowContentException=descriptor.interface.isJSImplemented())
+
+ def getRvalDecl(self):
+ return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
+
+ def getCall(self):
+ replacements = {
+ "attrName": self.attrName
+ }
+ return string.Template(
+ 'if (!JS_GetProperty(cx, mCallback, "${attrName}", &rval)) {\n'
+ ' return Err(FailureUnknown);\n'
+ '}\n').substitute(replacements);
+
+class CallbackSetter(CallbackMember):
+ def __init__(self, attr, descriptor):
+ self.ensureASCIIName(attr)
+ self.attrName = attr.identifier.name
+ CallbackMember.__init__(self,
+ (BuiltinTypes[IDLBuiltinType.Types.void],
+ [FakeArgument(attr.type, attr)]),
+ callbackSetterName(attr),
+ descriptor,
+ needThisHandling=False,
+ rethrowContentException=descriptor.interface.isJSImplemented())
+
+ def getRvalDecl(self):
+ # We don't need an rval
+ return ""
+
+ def getCall(self):
+ replacements = {
+ "attrName": self.attrName,
+ "argv": "argv.handleAt(0)",
+ }
+ return string.Template(
+ 'MOZ_ASSERT(argv.length() == 1);\n'
+ 'if (!JS_SetProperty(cx, mCallback, "${attrName}", ${argv})) {\n'
+ ' return Err(FailureUnknown);\n'
+ '}\n').substitute(replacements)
+
+ def getArgcDecl(self):
+ return None
+
+class GlobalGenRoots():
+ """
+ Roots for global codegen.
+
+ To generate code, call the method associated with the target, and then
+ call the appropriate define/declare method.
+ """
+
+ @staticmethod
+ def PrototypeList(config):
+ # Prototype ID enum.
+ protos = [d.name for d in config.getDescriptors(isCallback=False)]
+ proxies = [d.name for d in config.getDescriptors(proxy=True)]
+
+ return CGList([
+ CGGeneric(AUTOGENERATED_WARNING_COMMENT),
+ CGGeneric("pub static MAX_PROTO_CHAIN_LENGTH: uint = %d;\n\n" % config.maxProtoChainLength),
+ CGNamespacedEnum('id', 'ID', protos, [0], deriving="PartialEq"),
+ CGNamespacedEnum('proxies', 'Proxy', proxies, [0], deriving="PartialEq"),
+ ])
+
+
+ @staticmethod
+ def RegisterBindings(config):
+ # TODO - Generate the methods we want
+ code = CGList([
+ CGRegisterProtos(config),
+ CGRegisterProxyHandlers(config),
+ ], "\n")
+
+ return CGImports(code, [], [
+ 'dom::bindings::codegen',
+ 'dom::bindings::codegen::PrototypeList::proxies',
+ 'js::jsapi::JSContext',
+ 'js::jsapi::JSObject',
+ 'libc',
+ ])
+
+ @staticmethod
+ def InterfaceTypes(config):
+ descriptors = [d.name for d in config.getDescriptors(register=True, isCallback=False)]
+ curr = CGList([CGGeneric("pub use dom::%s::%s;\n" % (name.lower(), name)) for name in descriptors])
+ curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
+ return curr
+
+ @staticmethod
+ def Bindings(config):
+
+ descriptors = (set(d.name + "Binding" for d in config.getDescriptors(register=True)) |
+ set(d.unroll().module() for d in config.callbacks))
+ curr = CGList([CGGeneric("pub mod %s;\n" % name) for name in sorted(descriptors)])
+ curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
+ return curr
+
+ @staticmethod
+ def InheritTypes(config):
+
+ descriptors = config.getDescriptors(register=True, isCallback=False)
+ allprotos = [CGGeneric("#![allow(unused_imports)]\n"),
+ CGGeneric("use dom::types::*;\n"),
+ CGGeneric("use dom::bindings::js::{JS, JSRef, Temporary};\n"),
+ CGGeneric("use dom::bindings::trace::JSTraceable;\n"),
+ CGGeneric("use dom::bindings::utils::Reflectable;\n"),
+ CGGeneric("use serialize::{Encodable, Encoder};\n"),
+ CGGeneric("use js::jsapi::JSTracer;\n\n")]
+ for descriptor in descriptors:
+ name = descriptor.name
+ protos = [CGGeneric('pub trait %s {}\n' % (name + 'Base'))]
+ for proto in descriptor.prototypeChain:
+ protos += [CGGeneric('impl %s for %s {}\n' % (proto + 'Base',
+ descriptor.concreteType))]
+ derived = [CGGeneric('pub trait %s { fn %s(&self) -> bool; }\n' %
+ (name + 'Derived', 'is_' + name.lower()))]
+ for protoName in descriptor.prototypeChain[1:-1]:
+ protoDescriptor = config.getDescriptor(protoName)
+ delegate = string.Template('''impl ${selfName} for ${baseName} {
+ fn ${fname}(&self) -> bool {
+ self.${parentName}.${fname}()
+ }
+}
+''').substitute({'fname': 'is_' + name.lower(),
+ 'selfName': name + 'Derived',
+ 'baseName': protoDescriptor.concreteType,
+ 'parentName': protoDescriptor.prototypeChain[-2].lower()})
+ derived += [CGGeneric(delegate)]
+ derived += [CGGeneric('\n')]
+
+ cast = [CGGeneric(string.Template('''pub trait ${castTraitName} {
+ #[inline(always)]
+ fn to_ref<'a, 'b, T: ${toBound}+Reflectable>(base: &'a JSRef<'b, T>) -> Option<&'a JSRef<'b, Self>> {
+ match base.deref().${checkFn}() {
+ true => unsafe { Some(base.transmute()) },
+ false => None
+ }
+ }
+
+ #[inline(always)]
+ fn to_mut_ref<'a, 'b, T: ${toBound}+Reflectable>(base: &'a mut JSRef<'b, T>) -> Option<&'a mut JSRef<'b, Self>> {
+ match base.deref().${checkFn}() {
+ true => unsafe { Some(base.transmute_mut()) },
+ false => None
+ }
+ }
+
+ #[inline(always)]
+ fn from_ref<'a, 'b, T: ${fromBound}>(derived: &'a JSRef<'b, T>) -> &'a JSRef<'b, Self> {
+ unsafe { derived.transmute() }
+ }
+
+ #[inline(always)]
+ fn from_mut_ref<'a, 'b, T: ${fromBound}>(derived: &'a mut JSRef<'b, T>) -> &'a mut JSRef<'b, Self> {
+ unsafe { derived.transmute_mut() }
+ }
+
+ #[inline(always)]
+ fn from_temporary<T: ${fromBound}+Reflectable>(derived: Temporary<T>) -> Temporary<Self> {
+ unsafe { derived.transmute() }
+ }
+}
+''').substitute({'checkFn': 'is_' + name.lower(),
+ 'castTraitName': name + 'Cast',
+ 'fromBound': name + 'Base',
+ 'toBound': name + 'Derived'})),
+ CGGeneric("impl %s for %s {}\n\n" % (name + 'Cast', name))]
+
+ trace = [CGGeneric(string.Template('''impl JSTraceable for ${name} {
+ fn trace(&self, tracer: *mut JSTracer) {
+ unsafe {
+ self.encode(&mut *tracer).ok().expect("failed to encode");
+ }
+ }
+}
+''').substitute({'name': name}))]
+
+ allprotos += protos + derived + cast + trace
+
+ curr = CGList(allprotos)
+ curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
+ return curr
+
+ @staticmethod
+ def UnionTypes(config):
+
+ curr = UnionTypes(config.getDescriptors(),
+ config.getDictionaries(),
+ config.getCallbacks(),
+ config)
+
+ # Add the auto-generated comment.
+ curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
+
+ # Done.
+ return curr