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