diff options
Diffstat (limited to 'src/components/script/dom/bindings/codegen/CodegenRust.py')
-rw-r--r-- | src/components/script/dom/bindings/codegen/CodegenRust.py | 4165 |
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()) |