aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/script/dom/bindings/codegen/CodegenRust.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/script/dom/bindings/codegen/CodegenRust.py')
-rw-r--r--src/components/script/dom/bindings/codegen/CodegenRust.py4165
1 files changed, 4165 insertions, 0 deletions
diff --git a/src/components/script/dom/bindings/codegen/CodegenRust.py b/src/components/script/dom/bindings/codegen/CodegenRust.py
new file mode 100644
index 00000000000..3c0c07d5ab5
--- /dev/null
+++ b/src/components/script/dom/bindings/codegen/CodegenRust.py
@@ -0,0 +1,4165 @@
+# 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 os
+import string
+import operator
+
+from WebIDL import *
+from Configuration import NoSuchDescriptorError
+
+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.
+ """
+ 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()
+
+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 and storing it in a variable
+ called by the name in the "target" argument.
+
+ codeOnFailure is the code to run if unwrapping fails.
+ """
+ def __init__(self, descriptor, source, target, codeOnFailure):
+ assert descriptor.castable
+
+ self.substitution = { "type" : descriptor.nativeType,
+ "protoID" : "prototypes::id::" + descriptor.name + " as uint",
+ "source" : source,
+ "target" : target,
+ "codeOnFailure" : CGIndenter(CGGeneric(codeOnFailure), 4).define() }
+ if descriptor.hasXPConnectImpls:
+ # We don't use xpc_qsUnwrapThis because it will always throw on
+ # unwrap failure, whereas we want to control whether we throw or
+ # not.
+ self.substitution["codeOnFailure"] = CGIndenter(CGGeneric(string.Template(
+ "${type} *objPtr;\n"
+ "xpc_qsSelfRef objRef;\n"
+ "JS::Value val = JS::ObjectValue(*${source});\n"
+ "nsresult rv = xpc_qsUnwrapArg<${type}>(cx, val, &objPtr, &objRef.ptr, &val);\n"
+ "if (NS_FAILED(rv)) {\n"
+ "${codeOnFailure}\n"
+ "}\n"
+ "// We should be castable!\n"
+ "MOZ_ASSERT(!objRef.ptr);\n"
+ "// We should have an object, too!\n"
+ "MOZ_ASSERT(objPtr);\n"
+ "${target} = objPtr;").substitute(self.substitution)), 4).define()
+
+ def __str__(self):
+ return string.Template(
+"""${target} = unwrap(${source});
+""").substitute(self.substitution)
+#"""{
+# nsresult rv = UnwrapObject<${protoID}, ${type}>(cx, ${source}, ${target});
+# if (NS_FAILED(rv)) {
+#${codeOnFailure}
+# }
+#}""").substitute(self.substitution)
+
+class CGThing():
+ """
+ Abstract base class for things that spit out code.
+ """
+ def __init__(self):
+ pass # Nothing for now
+ def declare(self):
+ """Produce code for a header file."""
+ assert(False) # Override me!
+ def define(self):
+ """Produce code for a cpp file."""
+ assert(False) # Override me!
+
+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):
+ return CGPerSignatureCall(signature[0], argsPre, signature[1],
+ nativeMethodName, 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([ CGIndenter(getPerSignatureCall(signature)) ])
+ requiredArgs = requiredArgCount(signature)
+
+
+ if requiredArgs > 0:
+ code = (
+ "if (argc < %d) {\n"
+ " return 0; //XXXjdm throw exception\n"
+ " //return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, %s);\n"
+ "}" % (requiredArgs, methodName))
+ self.cgRoot.prepend(
+ CGWrapper(CGIndenter(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]
+
+ # (possibly) important optimization: if signature[1] has >
+ # argCount arguments and signature[1][argCount] is optional and
+ # there is only one signature for argCount+1, then the
+ # signature for argCount+1 is just ourselves and we can fall
+ # through.
+ if (len(signature[1]) > argCount and
+ signature[1][argCount].optional and
+ (argCount+1) in allowedArgCounts and
+ len(method.signaturesForArgCount(argCount+1)) == 1):
+ #XXXjdm unfinished
+ pass
+ #argCountCases.append(
+ # CGCase(str(argCount), None, True))
+ else:
+ pass
+ #XXXjdm unfinished
+ #argCountCases.append(
+ # CGCase(str(argCount), getPerSignatureCall(signature)))
+ 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("JS::Value* 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[%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))
+ else:
+ caseBody.append(CGGeneric("if (" + condition + ") {"))
+ caseBody.append(CGIndenter(
+ getPerSignatureCall(sigs[0], distinguishingIndex)))
+ 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.isObject()) {" %
+ (distinguishingArg)))
+ for sig in interfacesSigs:
+ caseBody.append(CGIndenter(CGGeneric("do {")));
+ 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.
+ testCode = instantiateJSToNativeConversionTemplate(
+ getJSToNativeConversionTemplate(type, descriptor,
+ failureCode="break;",
+ isDefinitelyObject=True),
+ {
+ "declName" : "arg%d" % distinguishingIndex,
+ "holderName" : ("arg%d" % distinguishingIndex) + "_holder",
+ "val" : distinguishingArg
+ })
+
+ # 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), 4))
+ caseBody.append(CGIndenter(CGGeneric("} while (0);")))
+
+ 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<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);" %
+ toStringBool(not descriptor.workers)))
+
+ #XXXjdm unfinished
+ #argCountCases.append(CGCase(str(argCount),
+ # CGList(caseBody, "\n")))
+
+ overloadCGThings = []
+ overloadCGThings.append(
+ CGGeneric("unsigned argcount = NS_MIN(argc, %du);" %
+ maxArgCount))
+ #XXXjdm unfinished
+ #overloadCGThings.append(
+ # CGSwitch("argcount",
+ # argCountCases,
+ # CGGeneric("return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, %s);\n" % methodName)))
+ overloadCGThings.append(
+ CGGeneric('MOZ_NOT_REACHED("We have an always-returning default case");\n'
+ 'return false;'))
+ self.cgRoot = CGWrapper(CGIndenter(CGList(overloadCGThings, "\n")),
+ pre="\n")
+
+ def define(self):
+ return self.cgRoot.define()
+
+class FakeCastableDescriptor():
+ def __init__(self, descriptor):
+ self.castable = True
+ self.workers = descriptor.workers
+ self.nativeType = descriptor.nativeType
+ self.pointerType = descriptor.pointerType
+ self.name = descriptor.name
+ self.hasXPConnectImpls = descriptor.hasXPConnectImpls
+
+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 getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
+ isDefinitelyObject=False,
+ isMember=False,
+ isOptional=False,
+ invalidEnumValueFatal=True,
+ defaultValue=None,
+ treatNullAs="Default",
+ treatUndefinedAs="Default",
+ isEnforceRange=False,
+ isClamp=False):
+ """
+ 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.
+
+ If isOptional is true, then we are doing conversion of an optional
+ argument with no default value.
+
+ 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.
+
+ 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
+ ${valPtr} is a pointer to the JS::Value in question
+ ${holderName} replaced by the holder's name, if any
+ ${declName} replaced by the declaration's name
+ ${haveValue} replaced by an expression that evaluates to a boolean
+ for whether we have a JS::Value. Only used when
+ defaultValue is not None.
+
+ 2) 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.
+ 3) A CGThing representing the type of a "holder" (holderType) which will
+ hold a possible reference to the C++ thing whose type we returned in #1,
+ or None if no such holder is needed.
+ 4) A boolean indicating whether the caller has to do optional-argument handling.
+ This will only be true if isOptional is true and if the returned template
+ expects both declType and holderType to be wrapped in Optional<>, with
+ ${declName} and ${holderName} adjusted to point to the Value() of the
+ Optional, and Construct() calls to be made on the Optional<>s as needed.
+
+ ${declName} must be in scope before the generated code is entered.
+
+ If holderType is not None then ${holderName} must be in scope
+ before the generated code is entered.
+ """
+ # If we have a defaultValue then we're not actually optional for
+ # purposes of what we need to be declared as.
+ assert(defaultValue is None or not isOptional)
+
+ # Also, we should not have a defaultValue if we know we're an object
+ assert(not isDefinitelyObject or defaultValue is None)
+
+ # 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
+ 'return ThrowErrorMessage(cx, MSG_NOT_OBJECT);'), post="\n")
+ def onFailureBadType(failureCode, typeName):
+ return CGWrapper(CGGeneric(
+ failureCode or
+ 'return ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s");' % typeName), post="\n")
+
+ # A helper function for handling default values. Takes a template
+ # body and the C++ code to set the default value and wraps the
+ # given template body in handling for the default value.
+ def handleDefault(template, setDefault):
+ if defaultValue is None:
+ return template
+ return CGWrapper(
+ CGIndenter(CGGeneric(template)),
+ pre="if ${haveValue} != 0 {\n",
+ post=("\n"
+ "} else {\n"
+ "%s;\n"
+ "}" %
+ CGIndenter(CGGeneric(setDefault)).define())).define()
+
+ # A helper function for handling null default values. Much like
+ # handleDefault, but checks that the default value, if it exists, is null.
+ def handleDefaultNull(template, codeToSetNull):
+ if (defaultValue is not None and
+ not isinstance(defaultValue, IDLNullValue)):
+ raise TypeError("Can't handle non-null default value here")
+ return handleDefault(template, codeToSetNull)
+
+ # A helper function for wrapping up the template body for
+ # possibly-nullable objecty stuff
+ def wrapObjectTemplate(templateBody, isDefinitelyObject, type,
+ codeToSetNull, failureCode=None):
+ if not isDefinitelyObject:
+ # Handle the non-object cases by wrapping up the whole
+ # thing in an if cascade.
+ templateBody = (
+ "if (${val}.isObject()) {\n" +
+ CGIndenter(CGGeneric(templateBody)).define() + "\n")
+ if type.nullable():
+ templateBody += (
+ "} else if (${val}.isNullOrUndefined()) {\n"
+ " %s;\n" % codeToSetNull)
+ templateBody += (
+ "} else {\n" +
+ CGIndenter(onFailureNotAnObject(failureCode)).define() +
+ "}")
+ if type.nullable():
+ templateBody = handleDefaultNull(templateBody, codeToSetNull)
+ else:
+ assert(defaultValue is None)
+
+ 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():
+ assert not isEnforceRange and not isClamp
+
+ if failureCode is not None:
+ raise TypeError("Can't handle sequences when failureCode is not None")
+ nullable = type.nullable();
+ # Be very careful not to change "type": we need it later
+ if nullable:
+ elementType = type.inner.inner
+ else:
+ elementType = type.inner
+
+ # We have to be careful with reallocation behavior for arrays. In
+ # particular, if we have a sequence of elements which are themselves
+ # sequences (so nsAutoTArrays) or have sequences as members, we have a
+ # problem. In that case, resizing the outermost nsAutoTarray to the
+ # right size will memmove its elements, but nsAutoTArrays are not
+ # memmovable and hence will end up with pointers to bogus memory, which
+ # is bad. To deal with this, we disallow sequences, arrays,
+ # dictionaries, and unions which contain sequences as sequence item
+ # types. If WebIDL ever adds another container type, we'd have to
+ # disallow it as well.
+ if typeIsSequenceOrHasSequenceMember(elementType):
+ raise TypeError("Can't handle a sequence containing another "
+ "sequence as an element or member of an element. "
+ "See the big comment explaining why.\n%s" %
+ str(type.location))
+
+ (elementTemplate, elementDeclType,
+ elementHolderType, dealWithOptional) = getJSToNativeConversionTemplate(
+ elementType, descriptorProvider, isMember=True)
+ if dealWithOptional:
+ raise TypeError("Shouldn't have optional things in sequences")
+ if elementHolderType is not None:
+ raise TypeError("Shouldn't need holders for sequences")
+
+ typeName = CGWrapper(elementDeclType, pre="Sequence< ", post=" >")
+ if nullable:
+ typeName = CGWrapper(typeName, pre="Nullable< ", post=" >")
+ arrayRef = "${declName}.Value()"
+ else:
+ arrayRef = "${declName}"
+ # If we're optional, the const will come from the Optional
+ mutableTypeName = typeName
+ if not isOptional:
+ typeName = CGWrapper(typeName, pre="const ")
+
+ templateBody = ("""JSObject* seq = &${val}.toObject();\n
+if (!IsArrayLike(cx, seq)) {
+ return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
+}
+uint32_t length;
+// JS_GetArrayLength actually works on all objects
+if (!JS_GetArrayLength(cx, seq, &length)) {
+ return false;
+}
+Sequence< %s > &arr = const_cast< Sequence< %s >& >(%s);
+if (!arr.SetCapacity(length)) {
+ return Throw<%s>(cx, NS_ERROR_OUT_OF_MEMORY);
+}
+for (uint32_t i = 0; i < length; ++i) {
+ jsval temp;
+ if (!JS_GetElement(cx, seq, i, &temp)) {
+ return false;
+ }
+""" % (toStringBool(descriptorProvider.workers),
+ elementDeclType.define(),
+ elementDeclType.define(),
+ arrayRef,
+ toStringBool(descriptorProvider.workers)))
+
+ templateBody += CGIndenter(CGGeneric(
+ string.Template(elementTemplate).substitute(
+ {
+ "val" : "temp",
+ "valPtr": "&temp",
+ "declName" : "(*arr.AppendElement())"
+ }
+ ))).define()
+
+ templateBody += "\n}"
+ templateBody = wrapObjectTemplate(templateBody, isDefinitelyObject,
+ type,
+ "const_cast< %s & >(${declName}).SetNull()" % mutableTypeName.define())
+ return (templateBody, typeName, None, isOptional)
+
+ if type.isUnion():
+ if isMember:
+ raise TypeError("Can't handle unions as members, we have a "
+ "holderType")
+ nullable = type.nullable();
+ if nullable:
+ type = type.inner
+
+ assert(defaultValue is None or
+ (isinstance(defaultValue, IDLNullValue) and nullable))
+
+ unionArgumentObj = "${holderName}"
+ if isOptional or nullable:
+ unionArgumentObj += ".ref()"
+
+ memberTypes = type.flatMemberTypes
+ names = []
+
+ interfaceMemberTypes = filter(lambda t: t.isNonCallbackInterface(), memberTypes)
+ if len(interfaceMemberTypes) > 0:
+ interfaceObject = []
+ for memberType in interfaceMemberTypes:
+ if type.isGeckoInterface():
+ name = memberType.inner.identifier.name
+ else:
+ name = memberType.name
+ interfaceObject.append(CGGeneric("(failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext" % (unionArgumentObj, name)))
+ names.append(name)
+ interfaceObject = CGWrapper(CGList(interfaceObject, " ||\n"), pre="done = ", post=";\n", reindent=True)
+ else:
+ interfaceObject = None
+
+ arrayObjectMemberTypes = filter(lambda t: t.isArray() or t.isSequence(), memberTypes)
+ if len(arrayObjectMemberTypes) > 0:
+ assert len(arrayObjectMemberTypes) == 1
+ memberType = arrayObjectMemberTypes[0]
+ name = memberType.name
+ arrayObject = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
+ # XXX Now we're supposed to check for an array or a platform object
+ # that supports indexed properties... skip that last for now. It's a
+ # bit of a pain.
+ arrayObject = CGWrapper(CGIndenter(arrayObject),
+ pre="if (IsArrayLike(cx, &argObj)) {\n",
+ post="}")
+ names.append(name)
+ else:
+ arrayObject = None
+
+ dateObjectMemberTypes = filter(lambda t: t.isDate(), memberTypes)
+ if len(dateObjectMemberTypes) > 0:
+ assert len(dateObjectMemberTypes) == 1
+ memberType = dateObjectMemberTypes[0]
+ name = memberType.name
+ dateObject = CGGeneric("%s.SetTo%s(cx, ${val}, ${valPtr});\n"
+ "done = true;" % (unionArgumentObj, name))
+ dateObject = CGWrapper(CGIndenter(dateObject),
+ pre="if (JS_ObjectIsDate(cx, &argObj)) {\n",
+ post="\n}")
+ names.append(name)
+ else:
+ dateObject = None
+
+ callbackMemberTypes = filter(lambda t: t.isCallback() or t.isCallbackInterface(), memberTypes)
+ if len(callbackMemberTypes) > 0:
+ assert len(callbackMemberTypes) == 1
+ memberType = callbackMemberTypes[0]
+ name = memberType.name
+ callbackObject = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
+ names.append(name)
+ 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:
+ nonPlatformObject = CGList([callbackObject, dictionaryObject], "\n")
+ nonPlatformObject = CGWrapper(CGIndenter(nonPlatformObject),
+ pre="if (!IsPlatformObject(cx, &argObj)) {\n",
+ post="\n}")
+ else:
+ nonPlatformObject = None
+
+ objectMemberTypes = filter(lambda t: t.isObject(), memberTypes)
+ if len(objectMemberTypes) > 0:
+ object = CGGeneric("%s.SetToObject(&argObj);\n"
+ "done = true;" % unionArgumentObj)
+ else:
+ object = None
+
+ hasObjectTypes = interfaceObject or arrayObject or dateObject or nonPlatformObject or object
+ if hasObjectTypes:
+ # If we try more specific object types first then we need to check
+ # whether that succeeded before converting to object.
+ if object and (interfaceObject or arrayObject or dateObject or nonPlatformObject):
+ object = CGWrapper(CGIndenter(object), pre="if (!done) {\n",
+ post=("\n}"))
+
+ if arrayObject or dateObject or nonPlatformObject:
+ # An object can be both an array object and not a platform
+ # object, but we shouldn't have both in the union's members
+ # because they are not distinguishable.
+ assert not (arrayObject and nonPlatformObject)
+ templateBody = CGList([arrayObject, dateObject, nonPlatformObject], " else ")
+ else:
+ templateBody = None
+ if interfaceObject:
+ if templateBody:
+ templateBody = CGList([templateBody, object], "\n")
+ templateBody = CGWrapper(CGIndenter(templateBody),
+ pre="if (!done) {\n", post=("\n}"))
+ templateBody = CGList([interfaceObject, templateBody], "\n")
+ else:
+ templateBody = CGList([templateBody, object], "\n")
+
+ if any([arrayObject, dateObject, nonPlatformObject, object]):
+ templateBody.prepend(CGGeneric("JSObject& argObj = ${val}.toObject();"))
+ templateBody = CGWrapper(CGIndenter(templateBody),
+ pre="if (${val}.isObject()) {\n",
+ post="\n}")
+ else:
+ templateBody = CGGeneric()
+
+ otherMemberTypes = filter(lambda t: t.isString() or t.isEnum(),
+ memberTypes)
+ otherMemberTypes.extend(t for t in memberTypes if t.isPrimitive())
+ if len(otherMemberTypes) > 0:
+ assert len(otherMemberTypes) == 1
+ memberType = otherMemberTypes[0]
+ if memberType.isEnum():
+ name = memberType.inner.identifier.name
+ else:
+ name = memberType.name
+ other = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
+ names.append(name)
+ if hasObjectTypes:
+ other = CGWrapper(CGIndenter(other), "{\n", post="\n}")
+ if object:
+ join = " else "
+ else:
+ other = CGWrapper(other, pre="if (!done) ")
+ join = "\n"
+ templateBody = CGList([templateBody, other], join)
+ else:
+ other = None
+
+ templateBody = CGWrapper(templateBody, pre="bool done = false, failed = false, tryNext;\n")
+ throw = CGGeneric("if (failed) {\n"
+ " return false;\n"
+ "}\n"
+ "if (!done) {\n"
+ " return ThrowErrorMessage(cx, MSG_NOT_IN_UNION, \"%s\");\n"
+ "}" % ", ".join(names))
+ templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw], "\n")), pre="{\n", post="\n}")
+
+ typeName = type.name
+ argumentTypeName = typeName + "Argument"
+ if nullable:
+ typeName = "Nullable<" + typeName + " >"
+ if isOptional:
+ nonConstDecl = "const_cast<Optional<" + typeName + " >& >(${declName})"
+ else:
+ nonConstDecl = "const_cast<" + typeName + "& >(${declName})"
+ typeName = "const " + typeName
+
+ def handleNull(templateBody, setToNullVar, extraConditionForNull=""):
+ null = CGGeneric("if (%s${val}.isNullOrUndefined()) {\n"
+ " %s.SetNull();\n"
+ "}" % (extraConditionForNull, setToNullVar))
+ templateBody = CGWrapper(CGIndenter(templateBody), pre="{\n", post="\n}")
+ return CGList([null, templateBody], " else ")
+
+ if type.hasNullableType:
+ templateBody = handleNull(templateBody, unionArgumentObj)
+
+ declType = CGGeneric(typeName)
+ holderType = CGGeneric(argumentTypeName)
+ if isOptional:
+ mutableDecl = nonConstDecl + ".Value()"
+ declType = CGWrapper(declType, pre="const Optional<", post=" >")
+ holderType = CGWrapper(holderType, pre="Maybe<", post=" >")
+ constructDecl = CGGeneric(nonConstDecl + ".Construct();")
+ if nullable:
+ constructHolder = CGGeneric("${holderName}.construct(%s.SetValue());" % mutableDecl)
+ else:
+ constructHolder = CGGeneric("${holderName}.construct(${declName}.Value());")
+ else:
+ mutableDecl = nonConstDecl
+ constructDecl = None
+ if nullable:
+ holderType = CGWrapper(holderType, pre="Maybe<", post=" >")
+ constructHolder = CGGeneric("${holderName}.construct(%s.SetValue());" % mutableDecl)
+ else:
+ constructHolder = CGWrapper(holderType, post=" ${holderName}(${declName});")
+ holderType = None
+
+ templateBody = CGList([constructHolder, templateBody], "\n")
+ if nullable:
+ if defaultValue:
+ assert(isinstance(defaultValue, IDLNullValue))
+ valueMissing = "!(${haveValue}) || "
+ else:
+ valueMissing = ""
+ templateBody = handleNull(templateBody, mutableDecl,
+ extraConditionForNull=valueMissing)
+ templateBody = CGList([constructDecl, templateBody], "\n")
+
+ return templateBody.define(), declType, holderType, False
+
+ if type.isGeckoInterface():
+ assert not isEnforceRange and not isClamp
+
+ descriptor = descriptorProvider.getDescriptor(
+ type.unroll().inner.identifier.name)
+ # This is an interface that we implement as a concrete class
+ # or an XPCOM interface.
+
+ # Allow null pointers for nullable types and old-binding classes
+ argIsPointer = type.nullable() or type.unroll().inner.isExternal()
+
+ # Sequences and non-worker callbacks have to hold a strong ref to the
+ # thing being passed down.
+ forceOwningType = (descriptor.interface.isCallback() and
+ not descriptor.workers) or isMember
+
+ typeName = descriptor.nativeType
+ typePtr = typeName + "*"
+
+ # Compute a few things:
+ # - declType is the type we want to return as the first element of our
+ # tuple.
+ # - holderType is the type we want to return as the third element
+ # of our tuple.
+
+ # Set up some sensible defaults for these things insofar as we can.
+ holderType = None
+ if argIsPointer:
+ if forceOwningType:
+ declType = "nsRefPtr<" + typeName + ">"
+ else:
+ declType = typePtr
+ else:
+ if forceOwningType:
+ declType = "OwningNonNull<" + typeName + ">"
+ else:
+ declType = "NonNull<" + typeName + ">"
+
+ templateBody = ""
+ if descriptor.castable:
+ if descriptor.prefable:
+ raise TypeError("We don't support prefable castable object "
+ "arguments (like %s), because we don't know "
+ "how to handle them being preffed off" %
+ descriptor.interface.identifier.name)
+ if descriptor.interface.isConsequential():
+ raise TypeError("Consequential interface %s being used as an "
+ "argument but flagged as castable" %
+ descriptor.interface.identifier.name)
+ if failureCode is not None:
+ templateBody += str(CastableObjectUnwrapper(
+ descriptor,
+ "&${val}.toObject()",
+ "${declName}",
+ failureCode))
+ else:
+ pass
+ #XXXjdm unfinished
+ #templateBody += str(FailureFatalCastableObjectUnwrapper(
+ # descriptor,
+ # "&${val}.toObject()",
+ # "${declName}"))
+ elif descriptor.interface.isCallback() and False:
+ #XXXjdm unfinished
+ templateBody += str(CallbackObjectUnwrapper(
+ descriptor,
+ "&${val}.toObject()",
+ "${declName}",
+ codeOnFailure=failureCode))
+ elif descriptor.workers:
+ templateBody += "${declName} = &${val}.toObject();"
+ else:
+ # Either external, or new-binding non-castable. We always have a
+ # holder for these, because we don't actually know whether we have
+ # to addref when unwrapping or not. So we just pass an
+ # getter_AddRefs(nsRefPtr) to XPConnect and if we'll need a release
+ # it'll put a non-null pointer in there.
+ if forceOwningType:
+ # Don't return a holderType in this case; our declName
+ # will just own stuff.
+ templateBody += "nsRefPtr<" + typeName + "> ${holderName};\n"
+ else:
+ holderType = "nsRefPtr<" + typeName + ">"
+ templateBody += (
+ "jsval tmpVal = ${val};\n" +
+ typePtr + " tmp;\n"
+ "if (NS_FAILED(xpc_qsUnwrapArg<" + typeName + ">(cx, ${val}, &tmp, static_cast<" + typeName + "**>(getter_AddRefs(${holderName})), &tmpVal))) {\n")
+ templateBody += CGIndenter(onFailureBadType(failureCode,
+ descriptor.interface.identifier.name)).define()
+ templateBody += ("}\n"
+ "MOZ_ASSERT(tmp);\n")
+
+ if not isDefinitelyObject:
+ # Our tmpVal will go out of scope, so we can't rely on it
+ # for rooting
+ templateBody += (
+ "if (tmpVal != ${val} && !${holderName}) {\n"
+ " // We have to have a strong ref, because we got this off\n"
+ " // some random object that might get GCed\n"
+ " ${holderName} = tmp;\n"
+ "}\n")
+
+ # And store our tmp, before it goes out of scope.
+ templateBody += "${declName} = tmp;"
+
+ templateBody = wrapObjectTemplate(templateBody, isDefinitelyObject,
+ type, "${declName} = NULL",
+ failureCode)
+
+ declType = CGGeneric(declType)
+ if holderType is not None:
+ holderType = CGGeneric(holderType)
+ return (templateBody, declType, holderType, isOptional)
+
+ if type.isSpiderMonkeyInterface():
+ assert not isEnforceRange and not isClamp
+ if isMember:
+ raise TypeError("Can't handle member arraybuffers or "
+ "arraybuffer views because making sure all the "
+ "objects are properly rooted is hard")
+ name = type.name
+ # By default, we use a Maybe<> to hold our typed array. And in the optional
+ # non-nullable case we want to pass Optional<TypedArray> to consumers, not
+ # Optional<NonNull<TypedArray> >, so jump though some hoops to do that.
+ holderType = "Maybe<%s>" % name
+ constructLoc = "${holderName}"
+ constructMethod = "construct"
+ constructInternal = "ref"
+ if type.nullable():
+ if isOptional:
+ declType = "const Optional<" + name + "*>"
+ else:
+ declType = name + "*"
+ else:
+ if isOptional:
+ declType = "const Optional<" + name + ">"
+ # We don't need a holder in this case
+ holderType = None
+ constructLoc = "(const_cast<Optional<" + name + ">& >(${declName}))"
+ constructMethod = "Construct"
+ constructInternal = "Value"
+ else:
+ declType = "NonNull<" + name + ">"
+ template = (
+ "%s.%s(cx, &${val}.toObject());\n"
+ "if (!%s.%s().inited()) {\n"
+ "%s" # No newline here because onFailureBadType() handles that
+ "}\n" %
+ (constructLoc, constructMethod, constructLoc, constructInternal,
+ CGIndenter(onFailureBadType(failureCode, type.name)).define()))
+ nullableTarget = ""
+ if type.nullable():
+ if isOptional:
+ mutableDecl = "(const_cast<Optional<" + name + "*>& >(${declName}))"
+ template += "%s.Construct();\n" % mutableDecl
+ nullableTarget = "%s.Value()" % mutableDecl
+ else:
+ nullableTarget = "${declName}"
+ template += "%s = ${holderName}.addr();" % nullableTarget
+ elif not isOptional:
+ template += "${declName} = ${holderName}.addr();"
+ template = wrapObjectTemplate(template, isDefinitelyObject, type,
+ "%s = NULL" % nullableTarget,
+ failureCode)
+
+ if holderType is not None:
+ holderType = CGGeneric(holderType)
+ # We handle all the optional stuff ourselves; no need for caller to do it.
+ return (template, CGGeneric(declType), holderType, False)
+
+ if type.isString():
+ assert not isEnforceRange and not isClamp
+
+ treatAs = {
+ "Default": "eStringify",
+ "EmptyString": "eEmpty",
+ "Null": "eNull"
+ }
+ if type.nullable():
+ # For nullable strings null becomes a null string.
+ treatNullAs = "Null"
+ # For nullable strings undefined becomes a null string unless
+ # specified otherwise.
+ if treatUndefinedAs == "Default":
+ treatUndefinedAs = "Null"
+ nullBehavior = treatAs[treatNullAs]
+ if treatUndefinedAs == "Missing":
+ raise TypeError("We don't support [TreatUndefinedAs=Missing]")
+ undefinedBehavior = treatAs[treatUndefinedAs]
+
+ def getConversionCode(varName):
+ #conversionCode = (
+ # "if (!ConvertJSValueToString(cx, ${val}, ${valPtr}, %s, %s, %s)) {\n"
+ # " return false;\n"
+ # "}" % (nullBehavior, undefinedBehavior, varName))
+ conversionCode = (
+ "let strval = jsval_to_str(cx, ${val});\n"
+ "if strval.is_err() {\n"
+ " return 0;\n"
+ "}\n"
+ "%s = str(strval.get());" % varName)
+ if defaultValue is None:
+ return conversionCode
+
+ if isinstance(defaultValue, IDLNullValue):
+ assert(type.nullable())
+ return handleDefault(conversionCode,
+ "%s.SetNull()" % varName)
+ return handleDefault(
+ conversionCode,
+ ("static const PRUnichar data[] = { %s };\n"
+ "%s.SetData(data, ArrayLength(data) - 1)" %
+ (", ".join(["'" + char + "'" for char in defaultValue.value] + ["0"]),
+ varName)))
+
+ if isMember:
+ # We have to make a copy, because our jsval may well not
+ # live as long as our string needs to.
+ declType = CGGeneric("nsString")
+ return (
+ "{\n"
+ " FakeDependentString str;\n"
+ "%s\n"
+ " ${declName} = str;\n"
+ "}\n" % CGIndenter(CGGeneric(getConversionCode("str"))).define(),
+ declType, None, isOptional)
+
+ if isOptional:
+ declType = "Option<DOMString>"
+ else:
+ declType = "DOMString"
+
+ return (
+ "%s\n" %
+ #"const_cast<%s&>(${declName}) = &${holderName};" %
+ (getConversionCode("${declName}")),
+ CGGeneric(declType), None, #CGGeneric("FakeDependentString"),
+ # No need to deal with Optional here; we have handled it already
+ False)
+
+ 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 = " return 0;\n"
+ else:
+ handleInvalidEnumValueCode = " return 1;\n"
+
+ template = (
+ "{\n"
+ #" int index = FindEnumStringIndex<%(invalidEnumValueFatal)s>(cx, ${val}, %(values)s, \"%(enumtype)s\", &ok);\n"
+ " let result = FindEnumStringIndex(cx, ${val}, %(values)s);\n"
+ " if result.is_err() {\n"
+ "%(handleInvalidEnumValueCode)s"
+ " }\n"
+ " let index = result.get();\n"
+ " ${declName} = cast::transmute(index); //XXXjdm need some range checks up in here\n"
+ "}" % { "enumtype" : enum,
+ "values" : enum + "Values::strings",
+ "invalidEnumValueFatal" : toStringBool(invalidEnumValueFatal),
+ "handleInvalidEnumValueCode" : handleInvalidEnumValueCode })
+
+ if defaultValue is not None:
+ assert(defaultValue.type.tag() == IDLType.Tags.domstring)
+ template = "" #XXXjdm unfinished
+ #template = handleDefault(template,
+ # ("${declName} = %sValues::%s" %
+ # (enum,
+ # getEnumValueName(defaultValue.value))))
+ return (template, CGGeneric(enum), None, isOptional)
+
+ if type.isCallback():
+ assert not isEnforceRange and not isClamp
+
+ if isMember:
+ raise TypeError("Can't handle member callbacks; need to sort out "
+ "rooting issues")
+ # XXXbz we're going to assume that callback types are always
+ # nullable and always have [TreatNonCallableAsNull] for now.
+ haveCallable = "${val}.isObject() && JS_ObjectIsCallable(cx, &${val}.toObject())"
+ if defaultValue is not None:
+ assert(isinstance(defaultValue, IDLNullValue))
+ haveCallable = "${haveValue} && " + haveCallable
+ return (
+ "if (%s) {\n"
+ " ${declName} = &${val}.toObject();\n"
+ "} else {\n"
+ " ${declName} = NULL;\n"
+ "}" % haveCallable,
+ CGGeneric("JSObject*"), None, isOptional)
+
+ if type.isAny():
+ assert not isEnforceRange and not isClamp
+
+ if isMember:
+ raise TypeError("Can't handle member 'any'; need to sort out "
+ "rooting issues")
+ templateBody = "${declName} = ${val};"
+ templateBody = handleDefaultNull(templateBody,
+ "${declName} = JS::NullValue()")
+ return (templateBody, CGGeneric("JS::Value"), None, isOptional)
+
+ if type.isObject():
+ assert not isEnforceRange and not isClamp
+
+ if isMember:
+ raise TypeError("Can't handle member 'object'; need to sort out "
+ "rooting issues")
+ template = wrapObjectTemplate("${declName} = &${val}.toObject();",
+ isDefinitelyObject, type,
+ "${declName} = NULL",
+ failureCode)
+ if type.nullable():
+ declType = CGGeneric("JSObject*")
+ else:
+ declType = CGGeneric("NonNull<JSObject>")
+ return (template, declType, None, isOptional)
+
+ 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()
+ # All optional dictionaries always have default values, so we
+ # should be able to assume not isOptional here.
+ assert not isOptional
+
+ typeName = CGDictionary.makeDictionaryName(type.inner,
+ descriptorProvider.workers)
+ actualTypeName = typeName
+ selfRef = "${declName}"
+
+ declType = CGGeneric(actualTypeName)
+
+ # If we're a member of something else, the const
+ # will come from the Optional or our container.
+ if not isMember:
+ selfRef = "%s" % selfRef
+
+ # We do manual default value handling here, because we
+ # actually do want a jsval, and we only handle null anyway
+ if defaultValue is not None:
+ assert(isinstance(defaultValue, IDLNullValue))
+ val = "if ${haveValue} { ${val} } else { JSVAL_NULL }"
+ else:
+ val = "${val}"
+
+ template = ("%s = %s::new();\n"
+ "if %s.Init(cx, %s) == 0 {\n"
+ " return 0;\n"
+ "}" % (selfRef, actualTypeName, selfRef, val))
+
+ return (template, declType, None, False)
+
+ if not type.isPrimitive():
+ raise TypeError("Need conversion for argument type '%s'" % str(type))
+
+ typeName = builtinNames[type.tag()]
+
+ conversionBehavior = "eDefault"
+ if isEnforceRange:
+ conversionBehavior = "eEnforceRange"
+ elif isClamp:
+ conversionBehavior = "eClamp"
+
+ if type.nullable():
+ dataLoc = "${declName}.SetValue()"
+ nullCondition = "${val}.isNullOrUndefined()"
+ if defaultValue is not None and isinstance(defaultValue, IDLNullValue):
+ nullCondition = "!(${haveValue}) || " + nullCondition
+ template = (
+ "if (%s) {\n"
+ " ${declName}.SetNull();\n"
+ "} else if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n"
+ " return false;\n"
+ "}" % (nullCondition, typeName, conversionBehavior, dataLoc))
+ declType = CGGeneric("Nullable<" + typeName + ">")
+ else:
+ assert(defaultValue is None or
+ not isinstance(defaultValue, IDLNullValue))
+ dataLoc = "${declName}"
+ #XXXjdm conversionBehavior should be used
+ template = (
+ "match JSValConvertible::from_jsval::<%s>(${val}) {\n"
+ " None => return 0,\n"
+ " Some(v) => %s = v\n"
+ "}" % (typeName, dataLoc))
+ declType = CGGeneric(typeName)
+ if (defaultValue is not None and
+ # We already handled IDLNullValue, so just deal with the other ones
+ not isinstance(defaultValue, IDLNullValue)):
+ tag = defaultValue.type.tag()
+ if tag in numericTags:
+ defaultStr = defaultValue.value
+ else:
+ assert(tag == IDLType.Tags.bool)
+ defaultStr = toStringBool(defaultValue.value)
+ template = CGWrapper(CGIndenter(CGGeneric(template)),
+ pre="if ${haveValue} != 0 {\n",
+ post=("\n"
+ "} else {\n"
+ " %s = %s;\n"
+ "}" % (dataLoc, defaultStr))).define()
+
+ return (template, declType, None, isOptional)
+
+def instantiateJSToNativeConversionTemplate(templateTuple, replacements,
+ argcAndIndex=None):
+ """
+ Take a tuple as returned by getJSToNativeConversionTemplate and a set of
+ replacements as required by the strings in such a tuple, and generate code
+ to convert into stack C++ types.
+
+ If argcAndIndex is not None it must be a dict that can be used to
+ replace ${argc} and ${index}, where ${index} is the index of this
+ argument (0-based) and ${argc} is the total number of arguments.
+ """
+ (templateBody, declType, holderType, dealWithOptional) = templateTuple
+
+ if dealWithOptional and argcAndIndex is None:
+ raise TypeError("Have to deal with optional things, but don't know how")
+ if argcAndIndex is not None and declType is None:
+ raise TypeError("Need to predeclare optional things, so they will be "
+ "outside the check for big enough arg count!");
+
+ result = CGList([], "\n")
+ # Make a copy of "replacements" since we may be about to start modifying it
+ replacements = dict(replacements)
+ originalHolderName = replacements["holderName"]
+ if holderType is not None:
+ if dealWithOptional:
+ replacements["holderName"] = (
+ "const_cast< %s & >(%s.Value())" %
+ (holderType.define(), originalHolderName))
+ mutableHolderType = CGWrapper(holderType, pre="Optional< ", post=" >")
+ holderType = CGWrapper(mutableHolderType, pre="const ")
+ result.append(
+ CGList([holderType, CGGeneric(" "),
+ CGGeneric(originalHolderName),
+ CGGeneric(";")]))
+
+ originalDeclName = replacements["declName"]
+ if declType is not None:
+ if dealWithOptional:
+ replacements["declName"] = (
+ "const_cast< %s & >(%s.Value())" %
+ (declType.define(), originalDeclName))
+ mutableDeclType = CGWrapper(declType, pre="Optional< ", post=" >")
+ declType = CGWrapper(mutableDeclType, pre="const ")
+ result.append(
+ CGList([CGGeneric("let mut "),
+ CGGeneric(originalDeclName),
+ CGGeneric(": "),
+ declType,
+ CGGeneric(";")]))
+
+ conversion = CGGeneric(
+ string.Template(templateBody).substitute(replacements)
+ )
+
+ if argcAndIndex is not None:
+ if dealWithOptional:
+ declConstruct = CGIndenter(
+ CGGeneric("const_cast< %s &>(%s).Construct();" %
+ (mutableDeclType.define(), originalDeclName)))
+ if holderType is not None:
+ holderConstruct = CGIndenter(
+ CGGeneric("const_cast< %s &>(%s).Construct();" %
+ (mutableHolderType.define(), originalHolderName)))
+ else:
+ holderConstruct = None
+ else:
+ declConstruct = None
+ holderConstruct = None
+
+ conversion = CGList(
+ [CGGeneric(
+ string.Template("if (${index} < ${argc}) {").substitute(
+ argcAndIndex
+ )),
+ declConstruct,
+ holderConstruct,
+ CGIndenter(conversion),
+ CGGeneric("}")],
+ "\n")
+
+ result.append(conversion)
+ # Add an empty CGGeneric to get an extra newline after the argument
+ # conversion.
+ 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)
+ self.argument = argument
+ if argument.variadic:
+ raise TypeError("We don't support variadic arguments yet " +
+ str(argument.location))
+ assert(not argument.defaultValue or argument.optional)
+
+ replacer = {
+ "index" : index,
+ "argc" : argc,
+ "argv" : argv
+ }
+ self.replacementVariables = {
+ "declName" : "arg%d" % index,
+ "holderName" : ("arg%d" % index) + "_holder"
+ }
+ self.replacementVariables["val"] = string.Template(
+ "(*${argv}.offset(${index}))"
+ ).substitute(replacer)
+ self.replacementVariables["valPtr"] = (
+ "&" + self.replacementVariables["val"])
+ if argument.defaultValue:
+ self.replacementVariables["haveValue"] = string.Template(
+ "${index} < ${argc}").substitute(replacer)
+ self.descriptorProvider = descriptorProvider
+ if self.argument.optional and not self.argument.defaultValue:
+ self.argcAndIndex = replacer
+ else:
+ self.argcAndIndex = None
+ self.invalidEnumValueFatal = invalidEnumValueFatal
+
+ def define(self):
+ return instantiateJSToNativeConversionTemplate(
+ getJSToNativeConversionTemplate(self.argument.type,
+ self.descriptorProvider,
+ isOptional=(self.argcAndIndex is not None),
+ invalidEnumValueFatal=self.invalidEnumValueFatal,
+ defaultValue=self.argument.defaultValue,
+ treatNullAs=self.argument.treatNullAs,
+ treatUndefinedAs=self.argument.treatUndefinedAs,
+ isEnforceRange=self.argument.enforceRange,
+ isClamp=self.argument.clamp),
+ self.replacementVariables,
+ self.argcAndIndex).define()
+
+def getWrapTemplateForType(type, descriptorProvider, result, successCode,
+ isCreator):
+ """
+ Reflect a C++ value stored in "result", of IDL type "type" into JS. The
+ "successCode" is the code to run once we have successfully done the
+ conversion. The resulting string should be used with string.Template, it
+ needs the following keys when substituting: jsvalPtr/jsvalRef/obj.
+
+ Returns (templateString, infallibility of conversion template)
+ """
+ haveSuccessCode = successCode is not None
+ if not haveSuccessCode:
+ successCode = "return 1;"
+
+ def setValue(value, callWrapValue=False):
+ """
+ Returns the code to set the jsval to value. If "callWrapValue" is true
+ JS_WrapValue will be called on the jsval.
+ """
+ if not callWrapValue:
+ tail = successCode
+ elif haveSuccessCode:
+ tail = ("if (!JS_WrapValue(cx, ${jsvalPtr})) {\n" +
+ " return false;\n" +
+ "}\n" +
+ successCode)
+ else:
+ tail = "return JS_WrapValue(cx, cast::transmute(${jsvalPtr}));"
+ return ("${jsvalRef} = %s;\n" +
+ tail) % (value)
+
+ def wrapAndSetPtr(wrapCall, failureCode=None):
+ """
+ Returns the code to set the jsval by calling "wrapCall". "failureCode"
+ is the code to run if calling "wrapCall" fails
+ """
+ if failureCode is None:
+ if not haveSuccessCode:
+ return wrapCall + ";\n" + "return if (*vp).v != 0 { 1 } else { 0 };"
+ failureCode = "return 0;"
+ str = ("if !%s {\n" +
+ CGIndenter(CGGeneric(failureCode)).define() + "\n" +
+ "}\n" +
+ successCode) % (wrapCall)
+ return str
+
+ if type is None or type.isVoid():
+ return (setValue("JSVAL_VOID"), True)
+
+ if type.isArray():
+ raise TypeError("Can't handle array return values yet")
+
+ if type.isSequence():
+ if type.nullable():
+ # Nullable sequences are Nullable< nsTArray<T> >
+ (recTemplate, recInfall) = getWrapTemplateForType(type.inner, descriptorProvider,
+ "%s.Value()" % result, successCode,
+ isCreator)
+ return ("""
+if (%s.IsNull()) {
+%s
+}
+%s""" % (result, CGIndenter(CGGeneric(setValue("JSVAL_NULL"))).define(), recTemplate), recInfall)
+
+ # Now do non-nullable sequences. We use setting the element
+ # in the array as our succcess code because when we succeed in
+ # wrapping that's what we should do.
+ innerTemplate = wrapForType(
+ type.inner, descriptorProvider,
+ {
+ 'result' : "%s[i]" % result,
+ 'successCode': ("if (!JS_DefineElement(cx, returnArray, i, tmp,\n"
+ " NULL, NULL, JSPROP_ENUMERATE)) {\n"
+ " return false;\n"
+ "}"),
+ 'jsvalRef': "tmp",
+ 'jsvalPtr': "&tmp",
+ 'isCreator': isCreator
+ }
+ )
+ innerTemplate = CGIndenter(CGGeneric(innerTemplate)).define()
+ return (("""
+uint32_t length = %s.Length();
+JSObject *returnArray = JS_NewArrayObject(cx, length, NULL);
+if (!returnArray) {
+ return false;
+}
+jsval tmp;
+for (uint32_t i = 0; i < length; ++i) {
+%s
+}\n""" % (result, innerTemplate)) + setValue("JS::ObjectValue(*returnArray)"), False)
+
+ if type.isGeckoInterface():
+ descriptor = descriptorProvider.getDescriptor(type.unroll().inner.identifier.name)
+ if type.nullable():
+ wrappingCode = ("if %s.is_none() {\n" % (result) +
+ CGIndenter(CGGeneric(setValue("JSVAL_NULL"))).define() + "\n" +
+ "}\n" +
+ "let mut %s = %s.get();\n" % (result, result))
+ else:
+ wrappingCode = ""
+ if (not descriptor.interface.isExternal() and
+ not descriptor.interface.isCallback()):
+ if descriptor.wrapperCache:
+ wrapMethod = "WrapNewBindingObject"
+ else:
+ if not isCreator:
+ raise MethodNotCreatorError(descriptor.interface.identifier.name)
+ wrapMethod = "WrapNewBindingNonWrapperCachedObject"
+ wrap = "%s(cx, ${obj}, %s as @mut CacheableWrapper, ${jsvalPtr})" % (wrapMethod, result)
+ # We don't support prefable stuff in workers.
+ assert(not descriptor.prefable or not descriptor.workers)
+ if not descriptor.prefable:
+ # Non-prefable bindings can only fail to wrap as a new-binding object
+ # if they already threw an exception. Same thing for
+ # non-prefable bindings.
+ failed = ("//MOZ_ASSERT(JS_IsExceptionPending(cx));\n" +
+ "return 0;")
+ else:
+ if descriptor.notflattened:
+ raise TypeError("%s is prefable but not flattened; "
+ "fallback won't work correctly" %
+ descriptor.interface.identifier.name)
+ # Try old-style wrapping for bindings which might be preffed off.
+ failed = wrapAndSetPtr("HandleNewBindingWrappingFailure(cx, ${obj}, %s, ${jsvalPtr})" % result)
+ wrappingCode += wrapAndSetPtr(wrap, failed)
+ else:
+ #wrap = "WrapObject(cx, ${obj}, %s, %s${jsvalPtr})" % (result, getIID)
+ if descriptor.pointerType == '':
+ wrap = "%s.wrap(cx, ${obj}, ${jsvalPtr})" % result
+ else:
+ wrap = "if WrapNewBindingObject(cx, ${obj}, %s as @mut CacheableWrapper, ${jsvalPtr}) { 1 } else { 0 };" % result
+ wrappingCode += wrapAndSetPtr(wrap)
+ return (wrappingCode, False)
+
+ if type.isString():
+ if type.nullable():
+ return (wrapAndSetPtr("*${jsvalPtr} = domstring_to_jsval(cx, &%s)" % result), False)
+ else:
+ #XXXjdm Can we be smarter when we know it's not nullable?
+ return (wrapAndSetPtr("*${jsvalPtr} = domstring_to_jsval(cx, &%s)" % result), False)
+
+ if type.isEnum():
+ if type.nullable():
+ raise TypeError("We don't support nullable enumerated return types "
+ "yet")
+ return ("""MOZ_ASSERT(uint32_t(%(result)s) < ArrayLength(%(strings)s));
+JSString* %(resultStr)s = JS_NewStringCopyN(cx, %(strings)s[uint32_t(%(result)s)].value, %(strings)s[uint32_t(%(result)s)].length);
+if (!%(resultStr)s) {
+ return false;
+}
+""" % { "result" : result,
+ "resultStr" : result + "_str",
+ "strings" : type.inner.identifier.name + "Values::strings" } +
+ setValue("JS::StringValue(%s_str)" % result), False)
+
+ if type.isCallback():
+ assert not type.isInterface()
+ # XXXbz we're going to assume that callback types are always
+ # nullable and always have [TreatNonCallableAsNull] for now.
+ # See comments in WrapNewBindingObject explaining why we need
+ # to wrap here.
+ # NB: setValue(..., True) calls JS_WrapValue(), so is fallible
+ return (setValue("JS::ObjectOrNullValue(%s)" % result, True), False)
+
+ if type.tag() == IDLType.Tags.any:
+ # See comments in WrapNewBindingObject explaining why we need
+ # to wrap here.
+ # NB: setValue(..., True) calls JS_WrapValue(), so is fallible
+ return (setValue(result, True), False)
+
+ if type.isObject() or type.isSpiderMonkeyInterface():
+ # See comments in WrapNewBindingObject explaining why we need
+ # to wrap here.
+ if type.nullable():
+ toValue = "RUST_OBJECT_TO_JSVAL(%s)"
+ else:
+ toValue = "JS::ObjectValue(*%s)"
+ # NB: setValue(..., True) calls JS_WrapValue(), so is fallible
+ return (setValue(toValue % result, True), False)
+
+ if not type.isPrimitive():
+ raise TypeError("Need to learn to wrap %s" % type)
+
+ if type.nullable():
+ (recTemplate, recInfal) = getWrapTemplateForType(type.inner, descriptorProvider,
+ "%s.Value()" % result, successCode,
+ isCreator)
+ return ("if (%s.IsNull()) {\n" % result +
+ CGIndenter(CGGeneric(setValue("JSVAL_NULL"))).define() + "\n" +
+ "}\n" + recTemplate, recInfal)
+
+ tag = type.tag()
+
+ if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
+ IDLType.Tags.uint16, IDLType.Tags.int32]:
+ return (setValue("RUST_INT_TO_JSVAL(%s as i32)" % result), True)
+
+ elif tag in [IDLType.Tags.int64, IDLType.Tags.uint64, IDLType.Tags.float,
+ IDLType.Tags.double]:
+ # XXXbz will cast to double do the "even significand" thing that webidl
+ # calls for for 64-bit ints? Do we care?
+ return (setValue("RUST_JS_NumberValue(%s as f64)" % result), True)
+
+ elif tag == IDLType.Tags.uint32:
+ return (setValue("RUST_UINT_TO_JSVAL(%s)" % result), True)
+
+ elif tag == IDLType.Tags.bool:
+ return (setValue("RUST_BOOLEAN_TO_JSVAL(%s as JSBool)" % result), True)
+
+ else:
+ raise TypeError("Need to learn to wrap primitive: %s" % type)
+
+def wrapForType(type, descriptorProvider, templateValues):
+ """
+ Reflect a C++ value of IDL type "type" into JS. TemplateValues is a dict
+ that should contain:
+
+ * 'jsvalRef': a C++ reference to the jsval in which to store the result of
+ the conversion
+ * 'jsvalPtr': a C++ pointer to the jsval in which to store the result of
+ the conversion
+ * 'obj' (optional): the name of the variable that contains the JSObject to
+ use as a scope when wrapping, if not supplied 'obj'
+ will be used as the name
+ * 'result' (optional): the name of the variable in which the C++ value is
+ stored, if not supplied 'result' will be used as
+ the name
+ * 'successCode' (optional): the code to run once we have successfully done
+ the conversion, if not supplied 'return true;'
+ will be used as the code
+ * 'isCreator' (optional): If true, we're wrapping for the return value of
+ a [Creator] method. Assumed false if not set.
+ """
+ wrap = getWrapTemplateForType(type, descriptorProvider,
+ templateValues.get('result', 'result'),
+ templateValues.get('successCode', None),
+ templateValues.get('isCreator', False))[0]
+
+ defaultValues = {'obj': 'obj'}
+ return string.Template(wrap).substitute(defaultValues, **templateValues)
+
+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.isCallback() or type.isAny() or type.isObject()
+
+def memberIsCreator(member):
+ return member.getExtendedAttribute("Creator") is not None
+
+# Returns a tuple consisting of a CGThing containing the type of the return
+# value, or None if there is no need for a return value, and a boolean signaling
+# whether the return value is passed in an out parameter.
+def getRetvalDeclarationForType(returnType, descriptorProvider,
+ resultAlreadyAddRefed):
+ if returnType is None or returnType.isVoid():
+ # Nothing to declare
+ return None, False
+ if returnType.isPrimitive() and returnType.tag() in builtinNames:
+ result = CGGeneric(builtinNames[returnType.tag()])
+ if returnType.nullable():
+ result = CGWrapper(result, pre="Nullable<", post=">")
+ return result, False
+ if returnType.isString():
+ return CGGeneric("DOMString"), False
+ if returnType.isEnum():
+ if returnType.nullable():
+ raise TypeError("We don't support nullable enum return values")
+ return CGGeneric(returnType.inner.identifier.name), False
+ if returnType.isGeckoInterface():
+ descriptor = descriptorProvider.getDescriptor(
+ returnType.unroll().inner.identifier.name)
+ result = CGGeneric(descriptor.nativeType)
+ if returnType.nullable():
+ result = CGWrapper(result, pre=("Option<" + descriptor.pointerType), post=">")
+ else:
+ result = CGWrapper(result, pre=descriptor.pointerType)
+ return result, False
+ if returnType.isCallback():
+ # XXXbz we're going to assume that callback types are always
+ # nullable for now.
+ return CGGeneric("*JSObject"), False
+ if returnType.isAny():
+ return CGGeneric("JSVal"), False
+ if returnType.isObject() or returnType.isSpiderMonkeyInterface():
+ return CGGeneric("*JSObject"), False
+ if returnType.isSequence():
+ nullable = returnType.nullable()
+ if nullable:
+ returnType = returnType.inner
+ # If our result is already addrefed, use the right type in the
+ # sequence argument here.
+ (result, _) = getRetvalDeclarationForType(returnType.inner,
+ descriptorProvider,
+ resultAlreadyAddRefed)
+ result = CGWrapper(result, pre="nsTArray< ", post=" >")
+ if nullable:
+ result = CGWrapper(result, pre="Nullable< ", post=" >")
+ return result, True
+ raise TypeError("Don't know how to declare return value for %s" %
+ returnType)
+
+def isChromeOnly(m):
+ return m.getExtendedAttribute("ChromeOnly")
+
+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.chrome to the list of
+ things exposed to chrome and self.regular to the list of things exposed to
+ web pages. self.chrome must be a superset of self.regular but also include
+ all the ChromeOnly stuff.
+ """
+ def __init__(self, descriptor, name):
+ self.descriptor = descriptor
+ self.name = name
+ # self.prefCacheData will store an array of (prefname, bool*)
+ # pairs for our bool var caches. generateArray will fill it
+ # in as needed.
+ self.prefCacheData = []
+ def hasChromeOnly(self):
+ return len(self.chrome) > len(self.regular)
+ def hasNonChromeOnly(self):
+ return len(self.regular) > 0
+ def variableName(self, chrome):
+ if chrome and self.hasChromeOnly():
+ return "sChrome" + self.name
+ if self.hasNonChromeOnly():
+ return "s" + self.name
+ return "ptr::null()"
+ def usedForXrays(self, chrome):
+ # We only need Xrays for methods, attributes and constants. And we only
+ # need them for the non-chrome ones if we have no chromeonly things.
+ # Otherwise (we have chromeonly attributes) we need Xrays for the chrome
+ # methods/attributes/constants. Finally, in workers there are no Xrays.
+ return ((self.name is "Methods" or self.name is "Attributes" or
+ self.name is "Constants") and
+ chrome == self.hasChromeOnly() and
+ not self.descriptor.workers)
+
+ def __str__(self):
+ # We only need to generate id arrays for things that will end
+ # up used via ResolveProperty or EnumerateProperties.
+ str = self.generateArray(self.regular, self.variableName(False),
+ self.usedForXrays(False))
+ if self.hasChromeOnly():
+ str += self.generateArray(self.chrome, self.variableName(True),
+ self.usedForXrays(True))
+ return str
+
+ @staticmethod
+ def getControllingPref(interfaceMember):
+ prefName = interfaceMember.getExtendedAttribute("Pref")
+ if prefName is None:
+ return None
+ # It's a list of strings
+ assert(len(prefName) is 1)
+ assert(prefName[0] is not None)
+ return prefName[0]
+
+ def generatePrefableArray(self, array, name, specTemplate, specTerminator,
+ specType, getPref, getDataTuple, doIdArrays):
+ """
+ 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 every time
+ our controlling pref changes and at the end of the array)
+
+ specType is the actual typename of our spec
+
+ getPref is a callback function that takes an array entry and returns
+ the corresponding pref value.
+
+ getDataTuple is a callback function that takes an array entry and
+ returns a tuple suitable for substitution into specTemplate.
+ """
+
+ # We want to generate a single list of specs, but with specTerminator
+ # inserted at every point where the pref name controlling the member
+ # changes. That will make sure the order of the properties as exposed
+ # on the interface and interface prototype objects does not change when
+ # pref control is added to members while still allowing us to define all
+ # the members in the smallest number of JSAPI calls.
+ assert(len(array) is not 0)
+ lastPref = getPref(array[0]) # So we won't put a specTerminator
+ # at the very front of the list.
+ specs = []
+ prefableSpecs = []
+ if doIdArrays:
+ prefableIds = []
+
+ prefableTemplate = ' { true, &%s[%d] }'
+ prefCacheTemplate = '&%s[%d].enabled'
+ def switchToPref(props, pref):
+ # Remember the info about where our pref-controlled
+ # booleans live.
+ if pref is not None:
+ props.prefCacheData.append(
+ (pref, prefCacheTemplate % (name, len(prefableSpecs)))
+ )
+ # Set up pointers to the new sets of specs and ids
+ # inside prefableSpecs and prefableIds
+ prefableSpecs.append(prefableTemplate %
+ (name + "_specs", len(specs)))
+
+ switchToPref(self, lastPref)
+
+ for member in array:
+ curPref = getPref(member)
+ if lastPref != curPref:
+ # Terminate previous list
+ specs.append(specTerminator)
+ # And switch to our new pref
+ switchToPref(self, curPref)
+ lastPref = curPref
+ # And the actual spec
+ specs.append(specTemplate % getDataTuple(member))
+ specs.append(specTerminator)
+ prefableSpecs.append(" { false, NULL }");
+
+ arrays = (("static %s: [%s, ..%i] = [\n" +
+ ',\n'.join(specs) + "\n" +
+ "];\n\n") % (name, specType, len(specs)))
+ #+
+ #"static Prefable<%s> %s[] = [\n" +
+ #',\n'.join(prefableSpecs) + "\n" +
+ #"];\n\n")
+ if doIdArrays:
+ arrays += ("static %s_ids: [jsid, ..%i] = [" % (name, len(specs))) + ", ".join(["JSID_VOID"] * len(specs)) + "];\n\n"
+ return arrays
+
+# 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.chrome = [{"name": m.identifier.name,
+ "length": methodLength(m),
+ "flags": "JSPROP_ENUMERATE",
+ "pref": PropertyDefiner.getControllingPref(m) }
+ for m in methods]
+ self.regular = [{"name": m.identifier.name,
+ "length": methodLength(m),
+ "flags": "JSPROP_ENUMERATE",
+ "pref": PropertyDefiner.getControllingPref(m) }
+ for m in methods if not isChromeOnly(m)]
+
+ # FIXME Check for an existing iterator on the interface first.
+ if any(m.isGetter() and m.isIndexed() for m in methods):
+ self.chrome.append({"name": 'iterator',
+ "methodInfo": False,
+ "nativeName": "crust::JS_ArrayIterator",
+ "length": 0,
+ "flags": "JSPROP_ENUMERATE",
+ "pref": None })
+ self.regular.append({"name": 'iterator',
+ "methodInfo": False,
+ "nativeName": "crust::JS_ArrayIterator",
+ "length": 0,
+ "flags": "JSPROP_ENUMERATE",
+ "pref": None })
+
+ #if not descriptor.interface.parent and not static and not descriptor.workers:
+ # self.chrome.append({"name": 'QueryInterface',
+ # "methodInfo": False,
+ # "length": 1,
+ # "flags": "0",
+ # "pref": None })
+
+ if static:
+ if not descriptor.interface.hasInterfaceObject():
+ # static methods go on the interface object
+ assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
+ else:
+ if not descriptor.interface.hasInterfacePrototypeObject():
+ # non-static methods go on the interface prototype object
+ assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
+
+ def generateArray(self, array, name, doIdArrays):
+ if len(array) == 0:
+ return ""
+
+ def pref(m):
+ return m["pref"]
+
+ def specData(m):
+ if m.get("methodInfo", True):
+ jitinfo = ("&%s_methodinfo" % m["name"])
+ accessor = "genericMethod"
+ else:
+ jitinfo = "0 as *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 *u8 as *libc::c_char, call: JSNativeWrapper {op: %s, info: %s}, nargs: %s, flags: %s as u16, selfHostedName: 0 as *libc::c_char }',
+ ' JSFunctionSpec {name: 0 as *libc::c_char, call: JSNativeWrapper {op: 0 as *u8, info: 0 as *JSJitInfo}, nargs: 0, flags: 0, selfHostedName: 0 as *libc::c_char }',
+ 'JSFunctionSpec',
+ pref, specData, doIdArrays)
+
+class AttrDefiner(PropertyDefiner):
+ def __init__(self, descriptor, name):
+ PropertyDefiner.__init__(self, descriptor, name)
+ self.name = name
+ self.chrome = [m for m in descriptor.interface.members if m.isAttr()]
+ self.regular = [m for m in self.chrome if not isChromeOnly(m)]
+
+ def generateArray(self, array, name, doIdArrays):
+ if len(array) == 0:
+ return ""
+
+ def flags(attr):
+ return "JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS"
+
+ def getter(attr):
+ native = ("genericLenientGetter" if attr.hasLenientThis()
+ else "genericGetter")
+ return ("JSPropertyOpWrapper {op: %(native)s, info: &%(name)s_getterinfo as *JSJitInfo}"
+ % {"name" : attr.identifier.name,
+ "native" : native})
+
+ def setter(attr):
+ if attr.readonly:
+ return "JSStrictPropertyOpWrapper {op: 0 as *u8, info: 0 as *JSJitInfo}"
+ native = ("genericLenientSetter" if attr.hasLenientThis()
+ else "genericSetter")
+ return ("JSStrictPropertyOpWrapper {op: %(native)s, info: &%(name)s_setterinfo as *JSJitInfo}"
+ % {"name" : attr.identifier.name,
+ "native" : native})
+
+ 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 *u8 as *libc::c_char, tinyid: 0, flags: ((%s) & 0xFF) as u8, getter: %s, setter: %s }',
+ ' JSPropertySpec { name: 0 as *libc::c_char, tinyid: 0, flags: 0, getter: JSPropertyOpWrapper {op: 0 as *u8, info: 0 as *JSJitInfo}, setter: JSStrictPropertyOpWrapper {op: 0 as *u8, info: 0 as *JSJitInfo} }',
+ 'JSPropertySpec',
+ PropertyDefiner.getControllingPref, specData, doIdArrays)
+
+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.chrome = [m for m in descriptor.interface.members if m.isConst()]
+ self.regular = [m for m in self.chrome if not isChromeOnly(m)]
+
+ def generateArray(self, array, name, doIdArrays):
+ 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: [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,
+ ' ConstantSpec { name: &%s_name as *u8 as *libc::c_char, value: %s }',
+ ' ConstantSpec { name: 0 as *libc::c_char, value: VoidVal }',
+ 'ConstantSpec',
+ PropertyDefiner.getControllingPref, specData, doIdArrays)
+
+class CGNativePropertyHooks(CGThing):
+ """
+ Generate a NativePropertyHooks for a given descriptor
+ """
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ def declare(self):
+ if self.descriptor.workers:
+ return ""
+ #return "extern const NativePropertyHooks NativeHooks;\n"
+ return ""
+ def define(self):
+ if self.descriptor.workers:
+ return ""
+ if self.descriptor.concrete and self.descriptor.proxy:
+ resolveOwnProperty = "ResolveOwnProperty"
+ enumerateOwnProperties = "EnumerateOwnProperties"
+ else:
+ enumerateOwnProperties = resolveOwnProperty = "0 as *u8"
+ parent = self.descriptor.interface.parent
+ parentHooks = ("&" + toBindingNamespace(parent.identifier.name) + "::NativeHooks"
+ if parent else '0 as *NativePropertyHooks')
+ return """
+static NativeHooks: NativePropertyHooks = NativePropertyHooks { resolve_own_property: /*%s*/ 0 as *u8, resolve_property: ResolveProperty, enumerate_own_properties: /*%s*/ 0 as *u8, enumerate_properties: /*EnumerateProperties*/ 0 as *u8, proto_hooks: %s };
+""" % (resolveOwnProperty, enumerateOwnProperties, parentHooks)
+
+# 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, declareOnly=False):
+ CGThing.__init__(self)
+ self.child = child
+ self.indent = " " * indentLevel
+ self.declareOnly = declareOnly
+ def declare(self):
+ decl = self.child.declare()
+ if decl is not "":
+ return re.sub(lineStartDetector, self.indent, decl)
+ else:
+ return ""
+ def define(self):
+ defn = self.child.define()
+ if defn is not "" and not self.declareOnly:
+ 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="", declarePre=None,
+ declarePost=None, definePre=None, definePost=None,
+ declareOnly=False, defineOnly=False, reindent=False):
+ CGThing.__init__(self)
+ self.child = child
+ self.declarePre = declarePre or pre
+ self.declarePost = declarePost or post
+ self.definePre = definePre or pre
+ self.definePost = definePost or post
+ self.declareOnly = declareOnly
+ self.defineOnly = defineOnly
+ self.reindent = reindent
+ def declare(self):
+ if self.defineOnly:
+ return ''
+ decl = self.child.declare()
+ if self.reindent:
+ # We don't use lineStartDetector because we don't want to
+ # insert whitespace at the beginning of our _first_ line.
+ decl = stripTrailingWhitespace(
+ decl.replace("\n", "\n" + (" " * len(self.declarePre))))
+ return self.declarePre + decl + self.declarePost
+ def define(self):
+ if self.declareOnly:
+ return ''
+ 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.definePre))))
+ return self.definePre + defn + self.definePost
+
+class CGImports(CGWrapper):
+ """
+ Generates the appropriate import/use statements.
+ """
+ def __init__(self, descriptors, dictionaries, declareImports, defineImports, child):
+ """
+ Builds a set of imports to cover |descriptors|.
+
+ Also includes the files in |declareIncludes| in the header
+ file and the files in |defineIncludes| in the .cpp.
+ """
+
+ # TODO imports to cover descriptors, etc.
+
+ def _useString(imports):
+ return '#[allow(unused_imports,unused_variable,unused_unsafe,unused_mut)];' + ''.join(['use %s;\n' % i for i in imports]) + '\n'
+ CGWrapper.__init__(self, child,
+ declarePre=_useString(sorted(declareImports)))
+
+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 CGNamespace(CGWrapper):
+ def __init__(self, namespace, child, declareOnly=False, public=False):
+ pre = "%smod %s {\n" % ("pub " if public else "", namespace)
+ post = "} // mod %s\n" % namespace
+ CGWrapper.__init__(self, child, pre=pre, post=post,
+ declareOnly=declareOnly)
+ @staticmethod
+ def build(namespaces, child, declareOnly=False, public=False):
+ """
+ Static helper method to build multiple wrapped namespaces.
+ """
+ if not namespaces:
+ return CGWrapper(child, declareOnly=declareOnly)
+ inner = CGNamespace.build(namespaces[1:], child, declareOnly=declareOnly, public=public)
+ return CGNamespace(namespaces[0], inner, declareOnly=declareOnly, public=public)
+
+ def declare(self):
+ return ""
+
+def DOMClass(descriptor):
+ protoList = ['prototypes::id::' + proto for proto in descriptor.prototypeChain]
+ # Pad out the list to the right length with _ID_Count so we
+ # guarantee that all the lists are the same length. _ID_Count
+ # is never the ID of any prototype, so it's safe to use as
+ # padding.
+ protoList.extend(['prototypes::id::_ID_Count'] * (descriptor.config.maxProtoChainLength - len(protoList)))
+ prototypeChainString = ', '.join(protoList)
+ nativeHooks = "0 as *NativePropertyHooks" if descriptor.workers else "&NativeHooks as *NativePropertyHooks"
+ return """DOMClass {
+ interface_chain: [ %s ] ,
+ unused: %s, native_hooks: %s
+}""" % (prototypeChainString, "false", #toStringBool(descriptor.nativeIsISupports),
+ nativeHooks)
+
+class CGDOMJSClass(CGThing):
+ """
+ Generate a DOMJSClass for a given descriptor
+ """
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ def declare(self):
+ #return "extern DOMJSClass Class;\n"
+ return ""
+ def define(self):
+ traceHook = TRACE_HOOK_NAME if self.descriptor.customTrace else '0 as *u8'
+ return """
+static Class_name: [u8, ..%i] = %s;
+static Class: DOMJSClass = DOMJSClass {
+ base: JSClass { name: &Class_name as *u8 as *libc::c_char,
+ flags: JSCLASS_IS_DOMJSCLASS | ((1 & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT), //JSCLASS_HAS_RESERVED_SLOTS(1),
+ addProperty: %s, /* addProperty */
+ delProperty: crust::JS_PropertyStub, /* delProperty */
+ getProperty: crust::JS_PropertyStub, /* getProperty */
+ setProperty: crust::JS_StrictPropertyStub, /* setProperty */
+ enumerate: crust::JS_EnumerateStub,
+ resolve: crust::JS_ResolveStub,
+ convert: crust::JS_ConvertStub,
+ finalize: %s, /* finalize */
+ checkAccess: 0 as *u8, /* checkAccess */
+ call: 0 as *u8, /* call */
+ hasInstance: 0 as *u8, /* hasInstance */
+ construct: 0 as *u8, /* construct */
+ trace: %s, /* trace */
+ reserved: (0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 05
+ 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 10
+ 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 15
+ 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 20
+ 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 25
+ 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 30
+ 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 35
+ 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void) // 40
+ },
+ dom_class: %s
+};
+""" % (len(self.descriptor.interface.identifier.name) + 1,
+ str_to_const_array(self.descriptor.interface.identifier.name),
+ #ADDPROPERTY_HOOK_NAME if self.descriptor.concrete and not self.descriptor.workers and self.descriptor.wrapperCache else 'crust::JS_PropertyStub',
+ 'crust::JS_PropertyStub',
+ FINALIZE_HOOK_NAME, traceHook,
+ 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 declare(self):
+ # We're purely for internal consumption
+ return ""
+ def define(self):
+ return """
+static PrototypeClassName__: [u8, ..%s] = %s;
+static PrototypeClass: JSClass = JSClass {
+ name: &PrototypeClassName__ as *u8 as *libc::c_char,
+ flags: (1 & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT, //JSCLASS_HAS_RESERVED_SLOTS(1)
+ addProperty: crust::JS_PropertyStub, /* addProperty */
+ delProperty: crust::JS_PropertyStub, /* delProperty */
+ getProperty: crust::JS_PropertyStub, /* getProperty */
+ setProperty: crust::JS_StrictPropertyStub, /* setProperty */
+ enumerate: crust::JS_EnumerateStub,
+ resolve: crust::JS_ResolveStub,
+ convert: crust::JS_ConvertStub,
+ finalize: 0 as *u8, /* finalize */
+ checkAccess: 0 as *u8, /* checkAccess */
+ call: 0 as *u8, /* call */
+ hasInstance: 0 as *u8, /* hasInstance */
+ construct: 0 as *u8, /* construct */
+ trace: 0 as *u8, /* trace */
+ reserved: (0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 05
+ 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 10
+ 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 15
+ 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 20
+ 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 25
+ 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 30
+ 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 35
+ 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *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 declare(self):
+ # We're purely for internal consumption
+ return ""
+ def define(self):
+ if not self.descriptor.hasInstanceInterface:
+ return ""
+ ctorname = "0 as *u8" if not self.descriptor.interface.ctor() else CONSTRUCT_HOOK_NAME
+ hasinstance = HASINSTANCE_HOOK_NAME
+ return """
+static InterfaceObjectClass: JSClass = {
+ %s, 0,
+ crust::JS_PropertyStub, /* addProperty */
+ crust::JS_PropertyStub, /* delProperty */
+ crust::JS_PropertyStub, /* getProperty */
+ crust::JS_StrictPropertyStub, /* setProperty */
+ crust::JS_EnumerateStub,
+ crust::JS_ResolveStub,
+ crust::JS_ConvertStub,
+ 0 as *u8, /* finalize */
+ 0 as *u8, /* checkAccess */
+ %s, /* call */
+ %s, /* hasInstance */
+ %s, /* construct */
+ 0 as *u8, /* trace */
+ 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 declare(self):
+ return self.join(child.declare() for child in self.children if child is not None)
+ def define(self):
+ return self.join(child.define() for child in self.children if child is not None)
+
+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, define="", declare=""):
+ self.declareText = declare
+ self.defineText = define
+ def declare(self):
+ return self.declareText
+ def define(self):
+ return self.defineText
+
+class Argument():
+ """
+ A class for outputting the type and name of an argument
+ """
+ def __init__(self, argType, name):
+ self.argType = argType
+ self.name = name
+ def __str__(self):
+ return self.name + ': ' + self.argType
+
+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.
+
+ static should be True to generate a static method, which only has
+ a definition.
+
+ 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, static=False, extern=False, pub=False, templateArgs=None):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ self.name = name
+ self.returnType = returnType
+ self.args = args
+ self.inline = inline
+ self.alwaysInline = alwaysInline
+ self.static = static
+ self.extern = extern
+ self.templateArgs = templateArgs
+ self.pub = pub;
+ def _argstring(self):
+ return ', '.join([str(a) 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)]')
+ elif self.inline:
+ #decorators.append('inline')
+ pass
+ if self.extern:
+ decorators.append('extern')
+ if self.static:
+ #decorators.append('static')
+ pass
+ if self.pub:
+ decorators.append('pub')
+ if not decorators:
+ return ''
+ #maybeNewline = " " if self.inline else "\n"
+ maybeNewline = " "
+ return ' '.join(decorators) + maybeNewline
+ def _returnType(self):
+ return (" -> %s" % self.returnType) if self.returnType != "void" else ""
+ def declare(self):
+ if self.inline:
+ return self._define()
+ #return "%sfn %s%s(%s)%s;\n" % (self._decorators(), self.name, self._template(),
+ # self._argstring(), self._returnType())
+ return ""
+
+ def _define(self):
+ return self.definition_prologue() + "\n" + self.definition_body() + self.definition_epilogue()
+ def define(self):
+ return "" if self.inline else self._define()
+ def definition_prologue(self):
+ return "%sfn %s%s(%s)%s {\n unsafe {" % (self._decorators(), self.name, self._template(),
+ self._argstring(), self._returnType())
+ def definition_epilogue(self):
+ return "\n }\n}\n"
+ def definition_body(self):
+ assert(False) # Override me!
+
+def CreateBindingJSObject(descriptor, parent):
+ if descriptor.proxy:
+ handler = """ //let cache = ptr::to_unsafe_ptr(aObject.get_wrappercache());
+
+ let script_context = task_from_context(aCx);
+ let handler = (*script_context).dom_static.proxy_handlers.get(&(prototypes::id::%s as uint));
+""" % descriptor.name
+ create = handler + """ let obj = NewProxyObject(aCx, *handler,
+ ptr::to_unsafe_ptr(&RUST_PRIVATE_TO_JSVAL(squirrel_away(aObject) as *libc::c_void)),
+ proto, %s,
+ ptr::null(), ptr::null());
+ if obj.is_null() {
+ return ptr::null();
+ }
+
+"""
+ else:
+ create = """ let obj = JS_NewObject(aCx, &Class.base, proto, %s);
+ if obj.is_null() {
+ return ptr::null();
+ }
+
+ JS_SetReservedSlot(obj, DOM_OBJECT_SLOT as u32,
+ RUST_PRIVATE_TO_JSVAL(squirrel_away(aObject) as *libc::c_void));
+"""
+ return create % parent
+
+class CGWrapWithCacheMethod(CGAbstractMethod):
+ def __init__(self, descriptor):
+ assert descriptor.interface.hasInterfacePrototypeObject()
+ args = [Argument('*JSContext', 'aCx'), Argument('*JSObject', 'aScope'),
+ Argument('@mut ' + descriptor.nativeType, 'aObject'),
+ Argument('*mut bool', 'aTriedToWrap')]
+ CGAbstractMethod.__init__(self, descriptor, 'Wrap_', '*JSObject', args)
+
+ def definition_body(self):
+ if self.descriptor.workers:
+ return """ *aTriedToWrap = true;
+ return aObject->GetJSObject();"""
+
+ return """ *aTriedToWrap = true;
+ let mut parent = aObject.GetParentObject(aCx);
+ let parent = WrapNativeParent(aCx, aScope, parent);
+ if parent.is_null() {
+ return ptr::null();
+ }
+
+ //JSAutoCompartment ac(aCx, parent);
+ let global = JS_GetGlobalForObject(aCx, parent);
+ let proto = GetProtoObject(aCx, global, global);
+ if proto.is_null() {
+ return ptr::null();
+ }
+
+ let cache = ptr::to_mut_unsafe_ptr(aObject.get_wrappercache());
+%s
+
+ //NS_ADDREF(aObject);
+
+ (*cache).set_wrapper(obj);
+
+ return obj;""" % (CreateBindingJSObject(self.descriptor, "parent"))
+
+class CGWrapMethod(CGAbstractMethod):
+ def __init__(self, descriptor):
+ # XXX can we wrap if we don't have an interface prototype object?
+ assert descriptor.interface.hasInterfacePrototypeObject()
+ args = [Argument('*JSContext', 'aCx'), Argument('*JSObject', 'aScope'),
+ Argument(descriptor.pointerType + descriptor.nativeType, 'aObject'), Argument('*mut bool', 'aTriedToWrap')]
+ CGAbstractMethod.__init__(self, descriptor, 'Wrap', '*JSObject', args, inline=True, pub=True)
+
+ def definition_body(self):
+ return "return Wrap_(aCx, aScope, aObject, aTriedToWrap);"
+
+class CGWrapNonWrapperCacheMethod(CGAbstractMethod):
+ def __init__(self, descriptor):
+ # XXX can we wrap if we don't have an interface prototype object?
+ assert descriptor.interface.hasInterfacePrototypeObject()
+ args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aScope'),
+ Argument('@' + descriptor.nativeType, 'aObject')]
+ CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args)
+
+ def definition_body(self):
+ return """
+ JSObject* global = JS_GetGlobalForObject(aCx, aScope);
+ JSObject* proto = GetProtoObject(aCx, global, global);
+ if (!proto) {
+ return NULL;
+ }
+
+%s
+ NS_ADDREF(aObject);
+
+ return obj;""" % CreateBindingJSObject(self.descriptor, "global")
+
+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)
+ def declare(self):
+ # We only have implementation
+ return ""
+
+class PropertyArrays():
+ def __init__(self, descriptor):
+ self.staticMethods = MethodDefiner(descriptor, "StaticMethods", True)
+ self.methods = MethodDefiner(descriptor, "Methods", False)
+ self.attrs = AttrDefiner(descriptor, "Attributes")
+ self.consts = ConstDefiner(descriptor, "Constants")
+ pass
+
+ @staticmethod
+ def arrayNames():
+ return [ "staticMethods", "methods", "attrs", "consts" ]
+
+ @staticmethod
+ def xrayRelevantArrayNames():
+ return [ "methods", "attrs", "consts" ]
+
+ def hasChromeOnly(self):
+ return reduce(lambda b, a: b or getattr(self, a).hasChromeOnly(),
+ self.arrayNames(), False)
+ def variableNames(self, chrome):
+ names = {}
+ for array in self.arrayNames():
+ names[array] = getattr(self, array).variableName(chrome)
+ return names
+ def __str__(self):
+ define = ""
+ for array in self.arrayNames():
+ define += str(getattr(self, array))
+ return define
+
+class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
+ """
+ Generate the CreateInterfaceObjects method for an interface descriptor.
+
+ properties should be a PropertyArrays instance.
+ """
+ def __init__(self, descriptor, properties):
+ args = [Argument('*JSContext', 'aCx'), Argument('*JSObject', 'aGlobal'),
+ Argument('*JSObject', 'aReceiver')]
+ CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', '*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))
+
+ needInterfaceObject = self.descriptor.interface.hasInterfaceObject()
+ needInterfacePrototypeObject = self.descriptor.interface.hasInterfacePrototypeObject()
+
+ # if we don't need to create anything, why are we generating this?
+ assert needInterfaceObject or needInterfacePrototypeObject
+
+ idsToInit = []
+ # There is no need to init any IDs in workers, because worker bindings
+ # don't have Xrays.
+ if False and not self.descriptor.workers: #XXXjdm don't need xray stuff yet
+ for var in self.properties.xrayRelevantArrayNames():
+ props = getattr(self.properties, var)
+ # We only have non-chrome ids to init if we have no chrome ids.
+ if props.hasChromeOnly():
+ idsToInit.append(props.variableName(True))
+ elif props.hasNonChromeOnly():
+ idsToInit.append(props.variableName(False))
+ if len(idsToInit) > 0:
+ setup = CGList([CGGeneric("let script_context = task_from_context(aCx);"),
+ CGList([CGGeneric("let %s_ids_mut = (*script_context).dom_static.attribute_ids.get(&(prototypes::id::%s as uint));" % (varname, self.descriptor.name)) for varname in idsToInit], '\n')], '\n')
+ initIds = CGList(
+ [CGGeneric("!InitIds(aCx, %s, *%s_ids_mut)" % (varname, varname)) for
+ varname in idsToInit], ' ||\n')
+ if len(idsToInit) > 1:
+ initIds = CGWrapper(initIds, pre="(", post=")", reindent=True)
+ initIds = CGList(
+ [CGGeneric("%s_ids_mut[0] == JSID_VOID &&" % idsToInit[0]), initIds],
+ "\n")
+ initIds = CGWrapper(initIds, pre="if ", post=" {", reindent=True)
+ initIds = CGList(
+ [setup,
+ initIds,
+ CGGeneric((" %s_ids_mut[0] = JSID_VOID;\n"
+ " return ptr::null();") % idsToInit[0]),
+ CGGeneric("}")],
+ "\n")
+ else:
+ initIds = None
+
+ prefCacheData = []
+ for var in self.properties.arrayNames():
+ props = getattr(self.properties, var)
+ prefCacheData.extend(props.prefCacheData)
+ if len(prefCacheData) is not 0:
+ prefCacheData = [
+ CGGeneric('Preferences::AddBoolVarCache(%s, "%s");' % (ptr, pref)) for
+ (pref, ptr) in prefCacheData]
+ prefCache = CGWrapper(CGIndenter(CGList(prefCacheData, "\n")),
+ pre=("static bool sPrefCachesInited = false;\n"
+ "if (!sPrefCachesInited) {\n"
+ " sPrefCachesInited = true;\n"),
+ post="\n}")
+ else:
+ prefCache = None
+
+ getParentProto = ("let parentProto: *JSObject = %s;\n" +
+ "if parentProto.is_null() {\n" +
+ " return ptr::null();\n" +
+ "}\n") % getParentProto
+
+ needInterfaceObjectClass = (needInterfaceObject and
+ self.descriptor.hasInstanceInterface)
+ needConstructor = (needInterfaceObject and
+ not self.descriptor.hasInstanceInterface)
+ if self.descriptor.interface.ctor():
+ constructHook = CONSTRUCT_HOOK_NAME
+ constructArgs = methodLength(self.descriptor.interface.ctor())
+ else:
+ constructHook = "ThrowingConstructor"
+ constructArgs = 0
+
+ if self.descriptor.concrete:
+ if self.descriptor.proxy:
+ domClass = "&Class"
+ else:
+ domClass = "&Class.dom_class"
+ else:
+ domClass = "ptr::null()"
+
+ def arrayPtr(name):
+ val = ('%(' + name + ')s') % self.properties.variableNames(False)
+ if val == "ptr::null()":
+ return val
+ return "ptr::to_unsafe_ptr(&%s[0])" % val
+
+ call = """return CreateInterfaceObjects2(aCx, aGlobal, aReceiver, parentProto,
+ %s, %s, %s, %d,
+ %s,
+ %s,
+ %s,
+ %s,
+ %s,
+ %s);""" % (
+ "&PrototypeClass" if needInterfacePrototypeObject else "ptr::null()",
+ "&InterfaceObjectClass" if needInterfaceObjectClass else "ptr::null()",
+ constructHook if needConstructor else "ptr::null()",
+ constructArgs,
+ domClass,
+ arrayPtr("methods"), arrayPtr("attrs"),
+ arrayPtr("consts"), arrayPtr("staticMethods"),
+ '"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "ptr::null()")
+ if self.properties.hasChromeOnly():
+ if self.descriptor.workers:
+ accessCheck = "mozilla::dom::workers::GetWorkerPrivateFromContext(aCx)->IsChromeWorker()"
+ else:
+ accessCheck = "xpc::AccessCheck::isChrome(js::GetObjectCompartment(aGlobal))"
+ chrome = CGIfWrapper(CGGeneric(call % self.properties.variableNames(True)),
+ accessCheck)
+ chrome = CGWrapper(chrome, pre="\n\n")
+ else:
+ chrome = None
+
+ functionBody = CGList(
+ [CGGeneric(getParentProto), initIds, prefCache, chrome,
+ CGGeneric(call % self.properties.variableNames(False))],
+ "\n\n")
+ #return CGIndenter(CGWrapper(functionBody, pre="/*", post="*/return ptr::null()")).define()
+ return CGIndenter(functionBody).define()
+
+class CGGetPerInterfaceObject(CGAbstractMethod):
+ """
+ A method for getting a per-interface object (a prototype object or interface
+ constructor object).
+ """
+ def __init__(self, descriptor, name, idPrefix=""):
+ args = [Argument('*JSContext', 'aCx'), Argument('*JSObject', 'aGlobal'),
+ Argument('*JSObject', 'aReceiver')]
+ CGAbstractMethod.__init__(self, descriptor, name,
+ '*JSObject', args, inline=True)
+ self.id = idPrefix + "id::" + self.descriptor.name
+ def definition_body(self):
+ return """
+
+ /* 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.
+ */
+
+ /* Make sure our global is sane. Hopefully we can remove this sometime */
+ /*if ((*JS_GetClass(aGlobal)).flags & JSCLASS_DOM_GLOBAL) == 0 {
+ return ptr::null();
+ }*/
+ /* Check to see whether the interface objects are already installed */
+ let protoOrIfaceArray: *mut *JSObject = cast::transmute(GetProtoOrIfaceArray(aGlobal));
+ let cachedObject: *JSObject = *protoOrIfaceArray.offset(%s as uint);
+ if cachedObject.is_null() {
+ let tmp: *JSObject = CreateInterfaceObjects(aCx, aGlobal, aReceiver);
+ *protoOrIfaceArray.offset(%s as uint) = 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",
+ "prototypes::")
+ def definition_body(self):
+ return """
+ /* 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 """
+ /* Get the interface object for this class. This will create the object as
+ needed. */""" + CGGetPerInterfaceObject.definition_body(self)
+
+class CGDefineDOMInterfaceMethod(CGAbstractMethod):
+ """
+ A method for resolve hooks to try to lazily define the interface object for
+ a given interface.
+ """
+ def __init__(self, descriptor):
+ args = [Argument('*JSContext', 'aCx'), Argument('*JSObject', 'aReceiver'),
+ Argument('*mut bool', 'aEnabled')]
+ CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface', 'bool', args, pub=True)
+
+ def declare(self):
+ if self.descriptor.workers:
+ return ''
+ #return CGAbstractMethod.declare(self)
+ return ""
+
+ def define(self):
+ if self.descriptor.workers:
+ return ''
+ return CGAbstractMethod.define(self)
+
+ def definition_body(self):
+ if self.descriptor.interface.hasInterfacePrototypeObject():
+ # We depend on GetProtoObject defining an interface constructor
+ # object as needed.
+ getter = "GetProtoObject"
+ else:
+ getter = "GetConstructorObject"
+
+ body = " let script_context = task_from_context(aCx);\n"
+ #XXXjdm This self.descriptor.concrete check shouldn't be necessary
+ if not self.descriptor.concrete or self.descriptor.proxy:
+ body += """ let traps = ProxyTraps {
+ getPropertyDescriptor: getPropertyDescriptor,
+ getOwnPropertyDescriptor: getOwnPropertyDescriptor,
+ defineProperty: ptr::null(),
+ getOwnPropertyNames: ptr::null(),
+ delete_: ptr::null(),
+ enumerate: ptr::null(),
+
+ has: ptr::null(),
+ hasOwn: ptr::null(),
+ get: get,
+ set: ptr::null(),
+ keys: ptr::null(),
+ iterate: ptr::null(),
+
+ call: ptr::null(),
+ construct: ptr::null(),
+ nativeCall: ptr::null(),
+ hasInstance: ptr::null(),
+ typeOf: ptr::null(),
+ objectClassIs: ptr::null(),
+ obj_toString: obj_toString,
+ fun_toString: ptr::null(),
+ //regexp_toShared: ptr::null(),
+ defaultValue: ptr::null(),
+ iteratorNext: ptr::null(),
+ finalize: ptr::null(),
+ getElementIfPresent: ptr::null(),
+ getPrototypeOf: ptr::null()
+ };
+ (*script_context).dom_static.proxy_handlers.insert(prototypes::id::%s as uint,
+ CreateProxyHandler(ptr::to_unsafe_ptr(&traps)));
+
+""" % self.descriptor.name
+ else:
+ body += """ (*script_context).dom_static.attribute_ids.insert(prototypes::id::%s as uint,
+ vec::cast_to_mut(vec::from_slice(sAttributes_ids)));
+""" % self.descriptor.name
+ body = "" #XXXjdm xray stuff isn't necessary yet
+
+ return (body + " let global: *JSObject = JS_GetGlobalForObject(aCx, aReceiver);\n" +
+ """
+ *aEnabled = true;
+ return %s(aCx, global, aReceiver).is_not_null();""" % (getter))
+
+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.
+
+ errorReport should be a CGThing for an error report or None if no
+ error reporting is needed.
+ """
+ def __init__(self, errorReport, arguments, argsPre, returnType,
+ extendedAttributes, descriptorProvider, nativeMethodName,
+ static, object="self", declareResult=True):
+ CGThing.__init__(self)
+
+ assert errorReport is None or isinstance(errorReport, CGThing)
+
+ isFallible = errorReport is not None
+
+ #resultAlreadyAddRefed = isResultAlreadyAddRefed(descriptorProvider,
+ # extendedAttributes)
+ resultAlreadyAddRefed = True
+ (result, resultOutParam) = getRetvalDeclarationForType(returnType,
+ descriptorProvider,
+ resultAlreadyAddRefed)
+
+ args = CGList([CGGeneric(arg) for arg in argsPre], ", ")
+ for (a, name) in arguments:
+ # This is a workaround for a bug in Apple's clang.
+ if a.type.isObject() and not a.type.nullable() and not a.optional:
+ name = "(JSObject&)" + name
+ #XXXjdm Perhaps we should pass all nontrivial types by borrowed pointer
+ if a.type.isDictionary():
+ name = "&" + name
+ args.append(CGGeneric(name))
+
+ # Return values that go in outparams go here
+ if resultOutParam:
+ args.append(CGGeneric("result"))
+ if isFallible:
+ args.append(CGGeneric("&mut rv"))
+
+ needsCx = (typeNeedsCx(returnType, True) or
+ any(typeNeedsCx(a.type) for (a, _) in arguments) or
+ 'implicitJSContext' in extendedAttributes)
+
+ 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.nativeType)
+ else:
+ call = CGWrapper(call, pre="(*%s)." % object)
+ call = CGList([call, CGWrapper(args, pre="(", post=");")])
+ if result is not None:
+ if declareResult:
+ result = CGWrapper(result, pre="let result: ", post=";")
+ self.cgRoot.prepend(result)
+ if not resultOutParam:
+ call = CGWrapper(call, pre="result = ")
+
+ call = CGWrapper(call)
+ self.cgRoot.append(call)
+
+ if isFallible:
+ self.cgRoot.prepend(CGGeneric("let mut rv: ErrorResult = Ok(());"))
+ self.cgRoot.append(CGGeneric("if (rv.is_err()) {"))
+ self.cgRoot.append(CGIndenter(errorReport))
+ self.cgRoot.append(CGGeneric("}"))
+
+ 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(
+ self.getErrorReport() 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, cast::transmute(vp));\n"
+ def getArgc(self):
+ return "argc"
+ def getArguments(self):
+ return [(a, "arg" + str(i)) for (i, a) in enumerate(self.arguments)]
+
+ def isFallible(self):
+ return not 'infallible' in self.extendedAttributes
+
+ def wrap_return_value(self):
+ isCreator = memberIsCreator(self.idlNode)
+ if isCreator:
+ # We better be returning addrefed things!
+ #assert(isResultAlreadyAddRefed(self.descriptor,
+ # self.extendedAttributes) or
+ # Workers use raw pointers for new-object return
+ # values or something
+ # self.descriptor.workers)
+ pass
+
+ resultTemplateValues = { 'jsvalRef': '*vp', 'jsvalPtr': 'vp',
+ 'isCreator': isCreator}
+ try:
+ return wrapForType(self.returnType, self.descriptor,
+ resultTemplateValues)
+ except MethodNotCreatorError, err:
+ assert not isCreator
+ raise TypeError("%s being returned from non-creator method or property %s.%s" %
+ (err.typename,
+ self.descriptor.interface.identifier.name,
+ self.idlNode.identifier.name))
+
+ def getErrorReport(self):
+ #return CGGeneric('return ThrowMethodFailedWithDetails<%s>(cx, rv, "%s", "%s");'
+ # % (toStringBool(not self.descriptor.workers),
+ # self.descriptor.interface.identifier.name,
+ # self.idlNode.identifier.name))
+ return CGGeneric('return 0'); #XXXjdm
+
+ def define(self):
+ return (self.cgRoot.define() + "\n" + self.wrap_return_value())
+
+class CGGetterCall(CGPerSignatureCall):
+ """
+ A class to generate a native object getter call for a particular IDL
+ getter.
+ """
+ def __init__(self, returnType, nativeMethodName, descriptor, attr):
+ CGPerSignatureCall.__init__(self, returnType, [], [],
+ nativeMethodName, False, 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):
+ self.type = type
+ self.optional = False
+ self.variadic = False
+ self.defaultValue = None
+ self.treatNullAs = interfaceMember.treatNullAs
+ self.treatUndefinedAs = interfaceMember.treatUndefinedAs
+ self.enforceRange = False
+ self.clamp = False
+
+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 = ("return Throw<%s>(cx, rv);" %
+ toStringBool(not descriptor.workers))
+ 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 = CGIndenter(CGGeneric(
+ str(CastableObjectUnwrapper(
+ FakeCastableDescriptor(self.descriptor),
+ "obj", "self", self.unwrapFailureCode))))
+ return CGList([ self.getThis(), unwrapThis,
+ self.generate_code() ], "\n").define()
+
+ def getThis(self):
+ return CGIndenter(
+ CGGeneric("let obj: *JSObject = JS_THIS_OBJECT(cx, vp);\n"
+ "if obj.is_null() {\n"
+ " return false as JSBool;\n"
+ "}\n"
+ "\n"
+ "let self: *rust_box<%s>;" % self.descriptor.nativeType))
+
+ 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('*JSContext', 'cx'), Argument('libc::c_uint', 'argc'),
+ Argument('*JSVal', 'vp')]
+ CGAbstractBindingMethod.__init__(self, descriptor, 'genericMethod', args)
+
+ def generate_code(self):
+ return CGIndenter(CGGeneric(
+ "let _info: *JSJitInfo = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
+ "return CallJitMethodOp(_info, cx, obj, ptr::to_unsafe_ptr(&(*self).payload) as *libc::c_void, argc, vp);"))
+
+class CGAbstractStaticMethod(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, static=True)
+ def declare(self):
+ # We only have implementation
+ return ""
+
+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('*JSContext', 'cx'), Argument('JSHandleObject', 'obj'),
+ Argument('*mut %s' % descriptor.nativeType, 'self'),
+ Argument('libc::c_uint', 'argc'), Argument('*mut JSVal', 'vp')]
+ CGAbstractExternMethod.__init__(self, descriptor, name, 'JSBool', args)
+
+ def definition_body(self):
+ name = self.method.identifier.name
+ nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
+ return CGWrapper(CGMethodCall([], nativeName, self.method.isStatic(),
+ self.descriptor, self.method),
+ pre=" let obj = (*obj.unnamed);\n").define()
+
+class CGGenericGetter(CGAbstractBindingMethod):
+ """
+ A class for generating the C++ code for an IDL attribute getter.
+ """
+ def __init__(self, descriptor, lenientThis=False):
+ args = [Argument('*JSContext', 'cx'), Argument('uint', 'argc'),
+ Argument('*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 CGIndenter(CGGeneric(
+ "let _info: *JSJitInfo = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
+ "return CallJitPropertyOp(_info, cx, obj, ptr::to_unsafe_ptr(&(*self).payload) as *libc::c_void, vp);"))
+
+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('*JSContext', 'cx'),
+ Argument('JSHandleObject', 'obj'),
+ Argument('*%s' % descriptor.nativeType, 'self'),
+ Argument('*mut JSVal', 'vp') ]
+ CGAbstractExternMethod.__init__(self, descriptor, name, "JSBool", args)
+
+ def definition_body(self):
+ name = self.attr.identifier.name
+ nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
+ # resultOutParam does not depend on whether resultAlreadyAddRefed is set
+ (_, resultOutParam) = getRetvalDeclarationForType(self.attr.type,
+ self.descriptor,
+ False)
+ infallible = ('infallible' in
+ self.descriptor.getExtendedAttributes(self.attr,
+ getter=True))
+ if resultOutParam or self.attr.type.nullable() or not infallible:
+ nativeName = "Get" + nativeName
+ return CGWrapper(CGIndenter(CGGetterCall(self.attr.type, nativeName,
+ self.descriptor, self.attr)),
+ pre=" let obj = (*obj.unnamed);\n").define()
+
+def infallibleForMember(member, type, descriptorProvider):
+ """
+ Determine the fallibility of changing a C++ value of IDL type "type" into
+ JS for the given attribute. Apart from isCreator, all the defaults are used,
+ since the fallbility does not change based on the boolean values,
+ and the template will be discarded.
+
+ CURRENT ASSUMPTIONS:
+ We assume that successCode for wrapping up return values cannot contain
+ failure conditions.
+ """
+ return getWrapTemplateForType(type, descriptorProvider, 'result', None,\
+ memberIsCreator(member))[1]
+
+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 declare(self):
+ return ""
+
+ def defineJitInfo(self, infoName, opName, infallible):
+ protoID = "prototypes::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,\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)
+ getterinfal = getterinfal and infallibleForMember(self.member, self.member.type, self.descriptor)
+ 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 and infallibleForMember(self.member, sig[0], self.descriptor):
+ # 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)
+ self.enum = enum
+
+ def declare(self):
+ return ""
+
+ def define(self):
+ return """
+ pub enum valuelist {
+ %s
+ }
+
+ pub static strings: &'static [EnumEntry] = &[
+ %s,
+ ];
+""" % (",\n ".join(map(getEnumValueName, self.enum.values())),
+ ",\n ".join(['EnumEntry {value: &"' + val + '", length: ' + str(len(val)) + '}' for val in self.enum.values()]))
+
+class CGXrayHelper(CGAbstractExternMethod):
+ def __init__(self, descriptor, name, args, properties):
+ CGAbstractExternMethod.__init__(self, descriptor, name, "bool", args)
+ self.properties = properties
+
+ def definition_body(self):
+ varNames = self.properties.variableNames(True)
+
+ setup = "let script_context = task_from_context(cx);\n"
+
+ methods = self.properties.methods
+ if methods.hasNonChromeOnly() or methods.hasChromeOnly():
+ methodArgs = "Some(vec::zip_slice(%(methods)s, *method_ids))" % varNames
+ setup += "let method_ids = (*script_context).dom_static.method_ids.get(&(prototypes::id::ClientRect as uint));\n"
+ else:
+ methodArgs = "None"
+ methodArgs = CGGeneric(methodArgs)
+
+ attrs = self.properties.attrs
+ if attrs.hasNonChromeOnly() or attrs.hasChromeOnly():
+ attrArgs = "Some(vec::zip_slice(%(attrs)s, *attr_ids))" % varNames
+ setup += "let attr_ids = (*script_context).dom_static.attribute_ids.get(&(prototypes::id::ClientRect as uint));\n"
+ else:
+ attrArgs = "None"
+ attrArgs = CGGeneric(attrArgs)
+
+ consts = self.properties.consts
+ if consts.hasNonChromeOnly() or consts.hasChromeOnly():
+ constArgs = "Some(vec::zip_slice(%(consts)s, *const_ids))" % varNames
+ setup += "let const_ids = (*script_context).dom_static.constant_ids.get(&(prototypes::id::ClientRect as uint));\n"
+ else:
+ constArgs = "None"
+ constArgs = CGGeneric(constArgs)
+
+ prefixArgs = CGGeneric(self.getPrefixArgs())
+
+ return CGIndenter(
+ CGWrapper(CGList([prefixArgs, methodArgs, attrArgs, constArgs], ", "),
+ pre=(setup + "return Xray%s(" % self.name),
+ post=");",
+ reindent=True)).define()
+
+class CGResolveProperty(CGXrayHelper):
+ def __init__(self, descriptor, properties):
+ args = [Argument('*JSContext', 'cx'), Argument('*JSObject', 'wrapper'),
+ Argument('jsid', 'id'), Argument('bool', 'set'),
+ Argument('*mut JSPropertyDescriptor', 'desc')]
+ CGXrayHelper.__init__(self, descriptor, "ResolveProperty", args,
+ properties)
+
+ def getPrefixArgs(self):
+ return "cx, wrapper, id, desc"
+
+
+class CGEnumerateProperties(CGXrayHelper):
+ def __init__(self, descriptor, properties):
+ args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'),
+ Argument('JS::AutoIdVector&', 'props')]
+ CGXrayHelper.__init__(self, descriptor, "EnumerateProperties", args,
+ properties)
+
+ def getPrefixArgs(self):
+ return "props"
+
+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(descriptor.binaryNames.get(operation, operation))
+ operation = descriptor.operations[operation]
+ assert len(operation.signatures()) == 1
+ signature = operation.signatures()[0]
+ extendedAttributes = descriptor.getExtendedAttributes(operation)
+
+ (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 = getJSToNativeConversionTemplate(argument.type, descriptor,
+ treatNullAs=argument.treatNullAs,
+ treatUndefinedAs=argument.treatUndefinedAs)
+ templateValues = {
+ "declName": argument.identifier.name,
+ "holderName": argument.identifier.name + "_holder",
+ "val": "desc->value",
+ "valPtr": "&desc->value"
+ }
+ self.cgRoot.prepend(instantiateJSToNativeConversionTemplate(template, templateValues))
+ elif operation.isGetter():
+ self.cgRoot.prepend(CGGeneric("let mut found = false;"))
+
+ def getArguments(self):
+ args = [(a, a.identifier.name) 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.returnType, self.descriptor, 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 CGProxyUnwrap(CGAbstractMethod):
+ def __init__(self, descriptor):
+ args = [Argument('*JSObject', 'obj')]
+ CGAbstractMethod.__init__(self, descriptor, "UnwrapProxy", '*' + descriptor.nativeType, args, alwaysInline=True)
+ def declare(self):
+ return ""
+ def definition_body(self):
+ return """ /*if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
+ obj = js::UnwrapObject(obj);
+ }*/
+ //MOZ_ASSERT(IsProxy(obj));
+ let box: *rust_box<%s> = cast::transmute(RUST_JSVAL_TO_PRIVATE(GetProxyPrivate(obj)));
+ return ptr::to_unsafe_ptr(&(*box).payload);""" % (self.descriptor.nativeType)
+
+class CGDOMJSProxyHandler_get(CGAbstractExternMethod):
+ def __init__(self, descriptor):
+ args = [Argument('*JSContext', 'cx'), Argument('*JSObject', 'proxy'),
+ Argument('*JSObject', 'receiver'), Argument('jsid', 'id'),
+ Argument('*mut JSVal', 'vp')]
+ CGAbstractExternMethod.__init__(self, descriptor, "get", "JSBool", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ getFromExpando = """let expando = GetExpandoObject(proxy);
+if expando.is_not_null() {
+ let hasProp = 0;
+ if JS_HasPropertyById(cx, expando, id, ptr::to_unsafe_ptr(&hasProp)) == 0 {
+ return 0;
+ }
+
+ if hasProp != 0 {
+ return JS_GetPropertyById(cx, expando, id, cast::transmute(vp));
+ }
+}"""
+
+ templateValues = {'jsvalRef': '*vp', 'jsvalPtr': 'vp', 'obj': 'proxy'}
+
+ indexedGetter = self.descriptor.operations['IndexedGetter']
+ if indexedGetter:
+ getIndexedOrExpando = ("let index = GetArrayIndexFromId(cx, id);\n" +
+ "if index.is_some() {\n" +
+ " let index = index.get();\n" +
+ " let self = UnwrapProxy(proxy);\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" +
+ " JS::Value nameVal = STRING_TO_JSVAL(JSID_TO_STRING(id));\n" +
+ " FakeDependentString name;\n"
+ " if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n" +
+ " eStringify, eStringify, name)) {\n" +
+ " return false;\n" +
+ " }\n" +
+ "\n" +
+ " let self = UnwrapProxy(proxy);\n" +
+ CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() +
+ "}\n") % (self.descriptor.nativeType)
+ 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, cast::transmute(vp)) {
+ return 0;
+}
+
+if found {
+ return 1;
+}
+%s
+*vp = JSVAL_VOID;
+return 1;""" % (getIndexedOrExpando, getNamed)
+
+ def definition_body(self):
+ return self.getBody()
+
+class CGDOMJSProxyHandler_obj_toString(CGAbstractExternMethod):
+ def __init__(self, descriptor):
+ args = [Argument('*JSContext', 'cx'), Argument('*JSObject', 'proxy')]
+ CGAbstractExternMethod.__init__(self, descriptor, "obj_toString", "*JSString", args)
+ self.descriptor = descriptor
+ def getBody(self):
+ stringifier = self.descriptor.operations['Stringifier']
+ if stringifier:
+ name = stringifier.identifier.name
+ nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, 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 """ do str::as_c_str("%s") |s| {
+ _obj_toString(cx, s)
+ }""" % self.descriptor.name
+
+ def definition_body(self):
+ return 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 "" #XXXjdm we may want to do a proper unwrap here
+ return """
+ let self: *%s = &(unwrap::<*rust_box<%s>>(obj).payload);
+""" % (self.descriptor.nativeType, self.descriptor.nativeType)
+
+ def definition_body(self):
+ return self.definition_body_prologue() + self.generate_code()
+
+ def generate_code(self):
+ # Override me
+ assert(False)
+
+def finalizeHook(descriptor, hookName, context):
+ if descriptor.customFinalize:
+ return """if (self) {
+ self->%s(%s);
+}""" % (hookName, context)
+ #clearWrapper = "ClearWrapper(self, self);\n" if descriptor.wrapperCache else ""
+ if descriptor.workers:
+ #release = "self->Release();"
+ pass
+ else:
+ assert descriptor.nativeIsISupports
+ release = """let val = JS_GetReservedSlot(obj, 0);
+let _: %s = cast::transmute(RUST_JSVAL_TO_PRIVATE(val));
+""" % (descriptor.pointerType + descriptor.nativeType)
+ #return clearWrapper + release
+ return release
+
+class CGClassConstructHook(CGAbstractExternMethod):
+ """
+ JS-visible constructor for our objects
+ """
+ def __init__(self, descriptor):
+ args = [Argument('*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):
+ return self.generate_code()
+
+ def generate_code(self):
+ preamble = """
+ //JSObject* obj = JS_GetGlobalForObject(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)));
+"""
+ if self.descriptor.workers:
+ preArgs = ["cx", "obj"]
+ else:
+ preamble += """
+ //XXXjdm Gecko obtains a GlobalObject from the global (maybe from the private value,
+ // or through unwrapping a slot or something). We'll punt and get the Window
+ // from the context for now.
+ let script_context = task_from_context(cx);
+ let global = (*script_context).root_frame.get_ref().window;
+ let obj = global.get_wrappercache().get_wrapper();
+"""
+ preArgs = ["global"]
+
+ name = self._ctor.identifier.name
+ nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
+ callGenerator = CGMethodCall(preArgs, nativeName, True,
+ self.descriptor, self._ctor)
+ return preamble + callGenerator.define();
+
+class CGClassHasInstanceHook(CGAbstractStaticMethod):
+ def __init__(self, descriptor):
+ args = [Argument('*JSContext', 'cx'), Argument('JSHandleObject', 'obj'),
+ Argument('JSMutableHandleValue', 'vp'), Argument('*JSBool', 'bp')]
+ CGAbstractStaticMethod.__init__(self, descriptor, HASINSTANCE_HOOK_NAME,
+ 'JSBool', args)
+
+ def define(self):
+ if not self.descriptor.hasInstanceInterface:
+ return ""
+ return CGAbstractStaticMethod.define(self)
+
+ def definition_body(self):
+ return self.generate_code()
+
+ def generate_code(self):
+ return """ if (!vp.isObject()) {
+ *bp = false;
+ return true;
+ }
+
+ jsval protov;
+ if (!JS_GetProperty(cx, obj, "prototype", &protov))
+ return false;
+ if (!protov.isObject()) {
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROTOTYPE,
+ "%s");
+ return false;
+ }
+ JSObject *objProto = &protov.toObject();
+
+ JSObject* instance = &vp.toObject();
+ JSObject* proto;
+ if (!JS_GetPrototype(cx, instance, &proto))
+ return false;
+ while (proto) {
+ if (proto == objProto) {
+ *bp = true;
+ return true;
+ }
+ if (!JS_GetPrototype(cx, proto, &proto))
+ return false;
+ }
+
+ nsISupports* native =
+ nsContentUtils::XPConnect()->GetNativeOfWrapper(cx, instance);
+ nsCOMPtr<%s> qiResult = do_QueryInterface(native);
+ *bp = !!qiResult;
+ return true;
+""" % (self.descriptor.name, self.descriptor.hasInstanceInterface)
+
+class CGClassFinalizeHook(CGAbstractClassHook):
+ """
+ A hook for finalize, used to release our native object.
+ """
+ def __init__(self, descriptor):
+ args = [Argument('*JSFreeOp', 'fop'), Argument('*JSObject', 'obj')]
+ CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME,
+ 'void', args)
+
+ def generate_code(self):
+ return CGIndenter(CGGeneric(finalizeHook(self.descriptor, self.name, self.args[0].name))).define()
+
+class CGDOMJSProxyHandlerDOMClass(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+ self.descriptor = descriptor
+ def declare(self):
+ #return "extern const DOMClass Class;\n"
+ return ""
+ def define(self):
+ return """
+static Class: DOMClass = """ + DOMClass(self.descriptor) + """;
+
+"""
+
+class CGDescriptor(CGThing):
+ def __init__(self, descriptor):
+ CGThing.__init__(self)
+
+ assert not descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject()
+
+ cgThings = []
+ if descriptor.interface.hasInterfacePrototypeObject():
+ (hasMethod, hasGetter, hasLenientGetter,
+ hasSetter, hasLenientSetter) = False, False, False, False, False
+ for m in descriptor.interface.members:
+ if m.isMethod() and not m.isStatic() and not m.isIdentifierLess():
+ cgThings.append(CGSpecializedMethod(descriptor, m))
+ cgThings.append(CGMemberJITInfo(descriptor, m))
+ hasMethod = True
+ elif m.isAttr():
+ cgThings.append(CGSpecializedGetter(descriptor, m))
+ if m.hasLenientThis():
+ hasLenientGetter = True
+ else:
+ hasGetter = True
+ if not m.readonly:
+ #cgThings.append(CGSpecializedSetter(descriptor, m))
+ if m.hasLenientThis():
+ hasLenientSetter = True
+ else:
+ hasSetter = True
+ cgThings.append(CGMemberJITInfo(descriptor, m))
+ if hasMethod: cgThings.append(CGGenericMethod(descriptor))
+ if hasGetter: cgThings.append(CGGenericGetter(descriptor))
+ #if hasLenientGetter: cgThings.append(CGGenericGetter(descriptor,
+ # lenientThis=True))
+ #if hasSetter: cgThings.append(CGGenericSetter(descriptor))
+ #if hasLenientSetter: cgThings.append(CGGenericSetter(descriptor,
+ # lenientThis=True))
+
+ if descriptor.concrete and not descriptor.proxy:
+ if not descriptor.workers and descriptor.wrapperCache:
+ #cgThings.append(CGAddPropertyHook(descriptor))
+ pass
+
+ # Always have a finalize hook, regardless of whether the class wants a
+ # custom hook.
+ cgThings.append(CGClassFinalizeHook(descriptor))
+
+ # Only generate a trace hook if the class wants a custom hook.
+ if (descriptor.customTrace):
+ #cgThings.append(CGClassTraceHook(descriptor))
+ pass
+
+ if descriptor.interface.hasInterfaceObject():
+ cgThings.append(CGClassConstructHook(descriptor))
+ cgThings.append(CGClassHasInstanceHook(descriptor))
+ cgThings.append(CGInterfaceObjectJSClass(descriptor))
+ pass
+
+ if descriptor.interface.hasInterfacePrototypeObject():
+ cgThings.append(CGPrototypeJSClass(descriptor))
+ pass
+
+ properties = PropertyArrays(descriptor)
+ cgThings.append(CGGeneric(define=str(properties)))
+ cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties))
+ if descriptor.interface.hasInterfacePrototypeObject():
+ cgThings.append(CGGetProtoObjectMethod(descriptor))
+ pass
+ else:
+ cgThings.append(CGGetConstructorObjectMethod(descriptor))
+ pass
+
+ # Set up our Xray callbacks as needed. Note that we don't need to do
+ # it in workers.
+ if (descriptor.interface.hasInterfacePrototypeObject() and
+ not descriptor.workers):
+ if descriptor.concrete and descriptor.proxy:
+ #cgThings.append(CGResolveOwnProperty(descriptor))
+ #cgThings.append(CGEnumerateOwnProperties(descriptor))
+ pass
+ cgThings.append(CGResolveProperty(descriptor, properties))
+ #cgThings.append(CGEnumerateProperties(descriptor, properties))
+
+ if descriptor.interface.hasInterfaceObject():
+ cgThings.append(CGDefineDOMInterfaceMethod(descriptor))
+ if (not descriptor.interface.isExternal() and
+ # Workers stuff is never pref-controlled
+ not descriptor.workers and
+ descriptor.interface.getExtendedAttribute("PrefControlled") is not None):
+ #cgThings.append(CGPrefEnabled(descriptor))
+ pass
+
+ if descriptor.interface.hasInterfacePrototypeObject():
+ cgThings.append(CGNativePropertyHooks(descriptor))
+ pass
+
+ if descriptor.concrete:
+ if descriptor.proxy:
+ #cgThings.append(CGProxyIsProxy(descriptor))
+ cgThings.append(CGProxyUnwrap(descriptor))
+ cgThings.append(CGDOMJSProxyHandlerDOMClass(descriptor))
+ cgThings.append(CGDOMJSProxyHandler_obj_toString(descriptor))
+ cgThings.append(CGDOMJSProxyHandler_get(descriptor))
+ #cgThings.append(CGDOMJSProxyHandler(descriptor))
+ #cgThings.append(CGIsMethod(descriptor))
+ pass
+ else:
+ cgThings.append(CGDOMJSClass(descriptor))
+ pass
+
+ if descriptor.wrapperCache:
+ cgThings.append(CGWrapWithCacheMethod(descriptor))
+ cgThings.append(CGWrapMethod(descriptor))
+ pass
+ else:
+ cgThings.append(CGWrapNonWrapperCacheMethod(descriptor))
+ pass
+
+ cgThings = CGList((CGIndenter(t, declareOnly=True) for t in cgThings), "\n")
+ cgThings = CGWrapper(cgThings, pre='\n', post='\n')
+ #self.cgRoot = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name),
+ # cgThings),
+ # post='\n')
+ self.cgRoot = cgThings
+
+ def declare(self):
+ return self.cgRoot.declare()
+ def define(self):
+ return self.cgRoot.define()
+
+class CGDictionary(CGThing):
+ def __init__(self, dictionary, descriptorProvider):
+ self.dictionary = dictionary;
+ self.workers = descriptorProvider.workers
+ 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
+ # Getting a conversion template for interface types can fail
+ # if we don't have a relevant descriptor when self.workers is True.
+ # If that happens, just mark ourselves as not being
+ # generatable and move on.
+ try:
+ self.memberInfo = [
+ (member,
+ getJSToNativeConversionTemplate(member.type,
+ descriptorProvider,
+ isMember=True,
+ isOptional=(not member.defaultValue),
+ defaultValue=member.defaultValue))
+ for member in dictionary.members ]
+ except NoSuchDescriptorError, err:
+ if not self.workers:
+ raise err
+ self.generatable = False
+
+ def declare(self):
+ if not self.generatable:
+ return ""
+ d = self.dictionary
+ if d.parent:
+ inheritance = ": public %s " % self.makeClassName(d.parent) #XXXjdm
+ else:
+ inheritance = ""
+ memberDecls = [" %s: %s," %
+ (m[0].identifier.name, self.getMemberType(m))
+ for m in self.memberInfo]
+
+ return (string.Template(
+ "pub struct ${selfName} {\n" + #XXXjdm deal with inheritance
+ "\n".join(memberDecls) + "\n" +
+ # NOTE: jsids are per-runtime, so don't use them in workers
+ "\n".join(" //static jsid " +
+ self.makeIdName(m.identifier.name) + ";" for
+ m in d.members) + "\n"
+ "}").substitute( { "selfName": self.makeClassName(d),
+ "inheritance": inheritance }))
+
+ def define(self):
+ if not self.generatable:
+ return ""
+ d = self.dictionary
+ if d.parent:
+ initParent = ("// Per spec, we init the parent's members first\n"
+ "if (!%s::Init(cx, val)) {\n"
+ " return false;\n"
+ "}\n" % self.makeClassName(d.parent))
+ else:
+ initParent = ""
+
+ memberInits = [CGIndenter(self.getMemberConversion(m)).define()
+ for m in self.memberInfo]
+ idinit = [CGGeneric('!InternJSString(cx, %s, "%s")' %
+ (m.identifier.name + "_id", m.identifier.name))
+ for m in d.members]
+ idinit = CGList(idinit, " ||\n")
+ idinit = CGWrapper(idinit, pre="if (",
+ post=(") {\n"
+ " return false;\n"
+ "}"),
+ reindent=True)
+
+ def defaultValue(ty):
+ if ty is "bool":
+ return "false"
+ elif ty in ["i32", "u32"]:
+ return "0"
+ elif ty is "nsString":
+ return "\"\""
+ elif ty.startswith("Optional"):
+ return "None"
+ else:
+ return "/* uh oh: %s */" % ty
+
+ return string.Template(
+ # NOTE: jsids are per-runtime, so don't use them in workers
+ ("static initedIds: bool = false;\n" +
+ "\n".join("static %s: jsid = JSID_VOID;" %
+ self.makeIdName(m.identifier.name)
+ for m in d.members) + "\n"
+ "\n"
+ "impl ${selfName} {\n"
+ "fn new() -> ${selfName} {\n"
+ " ${selfName} {\n" +
+ "\n".join(" %s: %s," % (m[0].identifier.name, defaultValue(self.getMemberType(m))) for m in self.memberInfo) + "\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "fn InitIds(cx: *JSContext) -> bool {\n"
+ " //MOZ_ASSERT(!initedIds);\n"
+ "/*${idInit}\n"
+ " initedIds = true;*/ //XXXjdm\n"
+ " return true;\n"
+ "}\n"
+ "\n" if not self.workers else "") +
+ "fn Init(&mut self, cx: *JSContext, val: JSVal) -> JSBool\n"
+ "{\n"
+ " unsafe {\n" +
+ # NOTE: jsids are per-runtime, so don't use them in workers
+ (" if (!initedIds && !${selfName}::InitIds(cx)) {\n"
+ " return 0;\n"
+ " }\n" if not self.workers else "") +
+ "${initParent}"
+ " let mut found: JSBool = 0;\n"
+ " let temp: JSVal = JSVAL_NULL;\n"
+ " let isNull = RUST_JSVAL_IS_NULL(val) != 0 || RUST_JSVAL_IS_VOID(val) != 0;\n"
+ " if !isNull && RUST_JSVAL_IS_PRIMITIVE(val) != 0 {\n"
+ " return 0; //XXXjdm throw properly here\n"
+ " //return Throw<${isMainThread}>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);\n"
+ " }\n"
+ "\n"
+ "${initMembers}\n"
+ " return 1;\n"
+ " }\n"
+ "}\n"
+ "}").substitute({
+ "selfName": self.makeClassName(d),
+ "initParent": CGIndenter(CGGeneric(initParent)).define(),
+ "initMembers": "\n\n".join(memberInits),
+ "idInit": CGIndenter(idinit).define(),
+ "isMainThread": toStringBool(not self.workers)
+ })
+
+ @staticmethod
+ def makeDictionaryName(dictionary, workers):
+ suffix = "Workers" if workers else ""
+ return dictionary.identifier.name + suffix
+
+ def makeClassName(self, dictionary):
+ return self.makeDictionaryName(dictionary, self.workers)
+
+ def getMemberType(self, memberInfo):
+ (member, (templateBody, declType,
+ holderType, dealWithOptional)) = memberInfo
+ # We can't handle having a holderType here
+ assert holderType is None
+ if dealWithOptional:
+ declType = CGWrapper(declType, pre="Optional< ", post=" >")
+ return declType.define()
+
+ def getMemberConversion(self, memberInfo):
+ (member, (templateBody, declType,
+ holderType, dealWithOptional)) = memberInfo
+ replacements = { "val": "temp",
+ "valPtr": "&temp",
+ "declName": ("self.%s" % member.identifier.name),
+ # We need a holder name for external interfaces, but
+ # it's scoped down to the conversion so we can just use
+ # anything we want.
+ "holderName": "holder"}
+ # We can't handle having a holderType here
+ assert holderType is None
+ if dealWithOptional:
+ replacements["declName"] = "(" + replacements["declName"] + ".Value())"
+ if member.defaultValue:
+ replacements["haveValue"] = "found"
+
+ # NOTE: jsids are per-runtime, so don't use them in workers
+ if True or self.workers: #XXXjdm hack until 'static mut' exists for global jsids
+ propName = member.identifier.name
+ propCheck = ('str::as_c_str("%s", |s| { JS_HasProperty(cx, RUST_JSVAL_TO_OBJECT(val), s, ptr::to_unsafe_ptr(&found)) })' %
+ propName)
+ propGet = ('str::as_c_str("%s", |s| { JS_GetProperty(cx, RUST_JSVAL_TO_OBJECT(val), s, ptr::to_unsafe_ptr(&temp)) })' %
+ propName)
+ else:
+ propId = self.makeIdName(member.identifier.name);
+ propCheck = ("JS_HasPropertyById(cx, RUST_JSVAL_TO_OBJECT(val), %s, ptr::to_unsafe_ptr(&found))" %
+ propId)
+ propGet = ("JS_GetPropertyById(cx, RUST_JSVAL_TO_OBJECT(val), %s, ptr::to_unsafe_ptr(&temp))" %
+ propId)
+
+ conversionReplacements = {
+ "prop": "(this->%s)" % member.identifier.name,
+ "convert": string.Template(templateBody).substitute(replacements),
+ "propCheck": propCheck,
+ "propGet": propGet
+ }
+ conversion = ("if isNull {\n"
+ " found = 0;\n"
+ "} else if ${propCheck} == 0 {\n"
+ " return 0;\n"
+ "}\n")
+ if member.defaultValue:
+ conversion += (
+ "if found != 0 {\n"
+ " if ${propGet} == 0 {\n"
+ " return 0;\n"
+ " }\n"
+ "}\n"
+ "${convert}")
+ else:
+ conversion += (
+ "if found != 0 {\n"
+ " ${prop}.Construct();\n"
+ " if ${propGet} == 0 {\n"
+ " return 0;\n"
+ " }\n"
+ "${convert}\n"
+ "}")
+ conversionReplacements["convert"] = CGIndenter(
+ CGGeneric(conversionReplacements["convert"])).define()
+
+ return CGGeneric(
+ string.Template(conversion).substitute(conversionReplacements)
+ )
+
+ @staticmethod
+ def makeIdName(name):
+ return name + "_id"
+
+ @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 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,
+ hasInterfaceOrInterfacePrototypeObject=True)
+ dictionaries = config.getDictionaries(webIDLFile)
+
+ cgthings = []
+
+ # Do codegen for all the enums
+ def makeEnum(e):
+ return CGNamespace.build([e.identifier.name + "Values"],
+ CGList([CGGeneric(" use dom::bindings::utils::EnumEntry;"),
+ CGEnum(e)]), public=True)
+ def makeEnumTypedef(e):
+ return CGGeneric(declare=("pub type %s = self::%sValues::valuelist;\n" %
+ (e.identifier.name, e.identifier.name)))
+ cgthings = [ fun(e) for e in config.getEnums(webIDLFile)
+ for fun in [makeEnum, makeEnumTypedef] ]
+
+ # Do codegen for all the dictionaries. We have to be a bit careful
+ # here, because we have to generate these in order from least derived
+ # to most derived so that class inheritance works out. We also have to
+ # generate members before the dictionary that contains them.
+ #
+ # XXXbz this will fail if we have two webidl files A and B such that A
+ # declares a dictionary which inherits from a dictionary in B and B
+ # declares a dictionary (possibly a different one!) that inherits from a
+ # dictionary in A. The good news is that I expect this to never happen.
+ reSortedDictionaries = []
+ dictionaries = set(dictionaries)
+ while len(dictionaries) != 0:
+ # Find the dictionaries that don't depend on anything else anymore
+ # and move them over.
+ toMove = [d for d in dictionaries if
+ len(CGDictionary.getDictionaryDependencies(d) &
+ dictionaries) == 0]
+ if len(toMove) == 0:
+ raise TypeError("Loop in dictionary dependency graph")
+ dictionaries = dictionaries - set(toMove)
+ reSortedDictionaries.extend(toMove)
+
+ dictionaries = reSortedDictionaries
+ #XXXjdm No codegen for workers right now.
+ #cgthings.extend([CGDictionary(d, config.getDescriptorProvider(True))
+ # for d in dictionaries])
+ cgthings.extend([CGDictionary(d, config.getDescriptorProvider(False))
+ for d in dictionaries])
+
+ # Do codegen for all the descriptors
+ cgthings.extend([CGDescriptor(x) for x in descriptors])
+
+ # 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(descriptors,
+ dictionaries,
+ ['js::*',
+ 'js::jsapi::*',
+ 'js::jsapi::bindgen::*',
+ 'js::jsfriendapi::bindgen::*',
+ 'js::glue::bindgen::*',
+ 'js::glue::*',
+ 'dom::node::AbstractNode', #XXXjdm
+ 'dom::document::Document', #XXXjdm
+ 'dom::bindings::utils::*',
+ 'dom::bindings::conversions::*',
+ 'dom::clientrect::*', #XXXjdm
+ 'dom::clientrectlist::*', #XXXjdm
+ 'dom::htmlcollection::*', #XXXjdm
+ 'dom::bindings::proxyhandler::*',
+ 'dom::domparser::*', #XXXjdm
+ 'dom::event::*', #XXXjdm
+ 'dom::eventtarget::*', #XXXjdm
+ 'script_task::task_from_context',
+ 'dom::bindings::utils::EnumEntry',
+ 'dom::node::ScriptView',
+ ],
+ [],
+ curr)
+
+ # Add the auto-generated comment.
+ curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
+
+ # Store the final result.
+ self.root = curr
+ def declare(self):
+ return stripTrailingWhitespace(self.root.declare())
+ def define(self):
+ return stripTrailingWhitespace(self.root.define())