forked from mirrors/gecko-dev
		
	 f925eec2be
			
		
	
	
		f925eec2be
		
	
	
	
	
		
			
			Source-Repo: https://github.com/servo/servo Source-Revision: 43b452f3b874c4bf0392ceaec27a0e40f18b5e34
		
			
				
	
	
		
			5279 lines
		
	
	
	
		
			203 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			5279 lines
		
	
	
	
		
			203 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # 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('panic!("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 union_native_type(t):
 | |
|     name = t.unroll().name
 | |
|     return 'UnionTypes::%s::%s' % (name, name)
 | |
| 
 | |
| 
 | |
| 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(union_native_type(type))
 | |
|         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(name)
 | |
|             template = "%s::new((${val}).to_object())" % name
 | |
|             if type.nullable():
 | |
|                 declType = CGWrapper(declType, pre="Option<", post=">")
 | |
|                 template = wrapObjectTemplate("Some(%s)" % template, 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 = (
 | |
|                 "const 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(
 | |
|                 "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"
 | |
|             ).substitute({
 | |
|                 "index": index,
 | |
|                 "argc": argc,
 | |
|                 "seqType": seqType.define(),
 | |
|                 "inner": CGIndenter(innerConverter, 4).define(),
 | |
|             })
 | |
| 
 | |
|             variadicConversion = CGIfElseWrapper(condition,
 | |
|                                                  CGGeneric(variadicConversion),
 | |
|                                                  CGGeneric("Vec::new()")).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(union_native_type(returnType))
 | |
|         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 (("const %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 "const %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 "const %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 "const %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_upper_case_globals',
 | |
|             'unused_parens',
 | |
|             'unused_imports',
 | |
|             'unused_variables',
 | |
|             'unused_unsafe',
 | |
|             'unused_mut',
 | |
|             'unused_assignments',
 | |
|             '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 """
 | |
| const 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 """
 | |
| const 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 """
 | |
| const 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('unsafe')
 | |
|             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::null_mut(), ptr::null_mut())
 | |
| });
 | |
| 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.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('panic!("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 = (
 | |
|                 "assert!(JS_IsExceptionPending(cx) == 0);\n"
 | |
|                 "*vp = UndefinedValue();\n"
 | |
|                 "return 1;")
 | |
|         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 = (
 | |
|                 "assert!(JS_IsExceptionPending(cx) == 0);\n"
 | |
|                 "return 1;")
 | |
|         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"
 | |
|                 "const %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(PartialEq)]
 | |
| #[jstraceable]
 | |
| pub enum valuelist {
 | |
|   %s
 | |
| }
 | |
| 
 | |
| pub const 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 const %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
 | |
|         ]
 | |
|         # XXXManishearth The following should be #[must_root],
 | |
|         # however we currently allow it till #2661 is fixed
 | |
|         return ("""#[allow(unrooted_must_root)]
 | |
| 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,
 | |
|                 exceptionCode="return false;")
 | |
|             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 {\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) != 0 {\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::null_mut();
 | |
| 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('*mut 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) != 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(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 attribute_arguments(needCx, argument=None):
 | |
|             if needCx:
 | |
|                 yield "cx", "*mut JSContext"
 | |
| 
 | |
|             if argument:
 | |
|                 yield "value", argument_type(descriptor, argument)
 | |
| 
 | |
| 
 | |
|         def members():
 | |
|             for m in descriptor.interface.members:
 | |
|                 if m.isMethod() and not m.isStatic() \
 | |
|                         and not m.isIdentifierLess():
 | |
|                     name = CGSpecializedMethod.makeNativeName(descriptor, m)
 | |
|                     infallible = 'infallible' in descriptor.getExtendedAttributes(m)
 | |
|                     for idx, (rettype, arguments) in enumerate(m.signatures()):
 | |
|                         arguments = method_arguments(descriptor, rettype, arguments)
 | |
|                         rettype = return_type(descriptor, 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(descriptor, 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)
 | |
|                     if operation.isGetter():
 | |
|                         arguments = method_arguments(descriptor, rettype, arguments, trailing=("found", "&mut bool"))
 | |
|                     else:
 | |
|                         arguments = method_arguments(descriptor, rettype, arguments)
 | |
|                     rettype = return_type(descriptor, 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:
 | |
|             cgThings.append(CGGenericGetter(descriptor, lenientThis=True))
 | |
|         if hasSetter:
 | |
|             cgThings.append(CGGenericSetter(descriptor))
 | |
|         if hasLenientSetter:
 | |
|             cgThings.append(CGGenericSetter(descriptor, lenientThis=True))
 | |
| 
 | |
|         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::null_mut(), 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::null_mut()\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):
 | |
|         return dictionary.module()
 | |
| 
 | |
|     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::global::global_object_for_js_object',
 | |
|             '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',
 | |
|             '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, UintVal}',
 | |
|             'dom::bindings::utils::{Reflectable}',
 | |
|             'dom::bindings::utils::{squirrel_away_unique}',
 | |
|             'dom::bindings::utils::{ThrowingConstructor,  unwrap, unwrap_jsmanaged}',
 | |
|             '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::conversions::jsid_to_str',
 | |
|             'dom::bindings::codegen::{PrototypeList, RegisterBindings, UnionTypes}',
 | |
|             'dom::bindings::codegen::Bindings::*',
 | |
|             '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())
 | |
| 
 | |
| def argument_type(descriptorProvdider, ty, optional=False, defaultValue=None, variadic=False):
 | |
|     _, _, declType, _ = getJSToNativeConversionTemplate(
 | |
|         ty, descriptorProvdider, isArgument=True)
 | |
| 
 | |
|     if variadic:
 | |
|         declType = CGWrapper(declType, pre="Vec<", post=">")
 | |
|     elif optional and not defaultValue:
 | |
|         declType = CGWrapper(declType, pre="Option<", post=">")
 | |
| 
 | |
|     if ty.isDictionary():
 | |
|         declType = CGWrapper(declType, pre="&")
 | |
| 
 | |
|     return declType.define()
 | |
| 
 | |
| def method_arguments(descriptorProvider, returnType, arguments, passJSBits=True, trailing=None):
 | |
|     if needCx(returnType, arguments, passJSBits):
 | |
|         yield "cx", "*mut JSContext"
 | |
| 
 | |
|     for argument in arguments:
 | |
|         ty = argument_type(descriptorProvider, argument.type, argument.optional,
 | |
|             argument.defaultValue, argument.variadic)
 | |
|         yield CGDictionary.makeMemberName(argument.identifier.name), ty
 | |
| 
 | |
|     if trailing:
 | |
|         yield trailing
 | |
| 
 | |
| def return_type(descriptorProvider, rettype, infallible):
 | |
|     result = getRetvalDeclarationForType(rettype, descriptorProvider)
 | |
|     if not infallible:
 | |
|         result = CGWrapper(result, pre="Fallible<", post=">")
 | |
|     return result.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]),
 | |
|                              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):
 | |
|         infallible = 'infallible' in self.extendedAttrs
 | |
|         typeDecl = return_type(self.descriptorProvider, type, infallible)
 | |
|         return typeDecl
 | |
| 
 | |
|     def getArgs(self, returnType, argList):
 | |
|         return [Argument(arg[1], arg[0]) for arg in method_arguments(self.descriptorProvider,
 | |
|                                                                      returnType,
 | |
|                                                                      argList,
 | |
|                                                                      self.passJSBitsAsNeeded)]
 | |
| 
 | |
| 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)]#[jstraceable]")
 | |
| 
 | |
|     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::null_mut()"] + 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.len()" % (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",
 | |
|         }
 | |
| 
 | |
|         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)
 | |
| 
 | |
|         if self.retvalType is None or self.retvalType.isVoid():
 | |
|             retval = "()"
 | |
|         else:
 | |
|             retval = "rvalDecl"
 | |
| 
 | |
|         return "%s\nOk(%s)\n" % (convertType.define(), retval)
 | |
| 
 | |
|     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();
 | |
|         argConversions = [CGGeneric(c) 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[%s]" % jsvalIndex,
 | |
|                 result=argval,
 | |
|                 successCode="")
 | |
|         if arg.variadic:
 | |
|             conversion = string.Template(
 | |
|                 "for idx in range(0, ${arg}.len()) {\n" +
 | |
|                 CGIndenter(CGGeneric(conversion)).define() + "\n"
 | |
|                 "}\n"
 | |
|                 ).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[%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 = %s as u32;" % 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 const 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) |
 | |
|                        set(d.module() for d in config.getDictionaries()))
 | |
|         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 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} {
 | |
|   #[inline]
 | |
|   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, T: ${toBound}+Reflectable>(base: JSRef<'a, T>) -> Option<JSRef<'a, Self>> {
 | |
|     match base.${checkFn}() {
 | |
|         true => unsafe { Some(base.transmute()) },
 | |
|         false => None
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   #[inline(always)]
 | |
|   fn to_borrowed_ref<'a, 'b, T: ${toBound}+Reflectable>(base: &'a JSRef<'b, T>) -> Option<&'a JSRef<'b, Self>> {
 | |
|     match base.${checkFn}() {
 | |
|         true => unsafe { Some(base.transmute_borrowed()) },
 | |
|         false => None
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   #[inline(always)]
 | |
|   #[allow(unrooted_must_root)]
 | |
|   fn to_js<T: ${toBound}+Reflectable>(base: &JS<T>) -> Option<JS<Self>> {
 | |
|     unsafe {
 | |
|         match (*base.unsafe_get()).${checkFn}() {
 | |
|             true =>  Some(base.transmute_copy()),
 | |
|             false => None
 | |
|         }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   #[inline(always)]
 | |
|   fn from_ref<'a, T: ${fromBound}+Reflectable>(derived: JSRef<'a, T>) -> JSRef<'a, Self> {
 | |
|     unsafe { derived.transmute() }
 | |
|   }
 | |
| 
 | |
|   #[inline(always)]
 | |
|   fn from_borrowed_ref<'a, 'b, T: ${fromBound}+Reflectable>(derived: &'a JSRef<'b, T>) -> &'a JSRef<'b, Self> {
 | |
|     unsafe { derived.transmute_borrowed() }
 | |
|   }
 | |
| 
 | |
|   #[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))]
 | |
| 
 | |
|             allprotos += protos + derived + cast
 | |
| 
 | |
|         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
 |