forked from mirrors/gecko-dev
		
	 76f8130812
			
		
	
	
		76f8130812
		
	
	
	
	
		
			
			Source-Repo: https://github.com/servo/servo Source-Revision: 6d1d4b57838d930e6ce39be720ef6c6dbb74af26 --HG-- extra : subtree_source : https%3A//hg.mozilla.org/projects/converted-servo-linear extra : subtree_revision : aecc6676c1dc24a8d8e08123d13172c1646b6387
		
			
				
	
	
		
			7073 lines
		
	
	
	
		
			276 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			7073 lines
		
	
	
	
		
			276 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.
 | |
| 
 | |
| from collections import defaultdict
 | |
| from itertools import groupby
 | |
| 
 | |
| import operator
 | |
| import os
 | |
| import re
 | |
| import string
 | |
| import textwrap
 | |
| import functools
 | |
| 
 | |
| from WebIDL import (
 | |
|     BuiltinTypes,
 | |
|     IDLBuiltinType,
 | |
|     IDLNullValue,
 | |
|     IDLNullableType,
 | |
|     IDLType,
 | |
|     IDLInterfaceMember,
 | |
|     IDLUndefinedValue,
 | |
|     IDLWrapperType,
 | |
| )
 | |
| 
 | |
| from Configuration import (
 | |
|     MakeNativeName,
 | |
|     MemberIsUnforgeable,
 | |
|     getModuleFromObject,
 | |
|     getTypesFromCallback,
 | |
|     getTypesFromDescriptor,
 | |
|     getTypesFromDictionary,
 | |
|     iteratorNativeType
 | |
| )
 | |
| 
 | |
| AUTOGENERATED_WARNING_COMMENT = \
 | |
|     "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n"
 | |
| FINALIZE_HOOK_NAME = '_finalize'
 | |
| TRACE_HOOK_NAME = '_trace'
 | |
| CONSTRUCT_HOOK_NAME = '_constructor'
 | |
| HASINSTANCE_HOOK_NAME = '_hasInstance'
 | |
| 
 | |
| RUST_KEYWORDS = {"abstract", "alignof", "as", "become", "box", "break", "const", "continue",
 | |
|                  "else", "enum", "extern", "false", "final", "fn", "for", "if", "impl", "in",
 | |
|                  "let", "loop", "macro", "match", "mod", "move", "mut", "offsetof", "override",
 | |
|                  "priv", "proc", "pub", "pure", "ref", "return", "static", "self", "sizeof",
 | |
|                  "struct", "super", "true", "trait", "type", "typeof", "unsafe", "unsized",
 | |
|                  "use", "virtual", "where", "while", "yield"}
 | |
| 
 | |
| 
 | |
| 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:
 | |
|     #     with open(filename, 'rb') as oldFile:
 | |
|     #         oldFileContents = ''.join(oldFile.readlines())
 | |
|     # except:
 | |
|     #     pass
 | |
| 
 | |
|     # if newContents == oldFileContents:
 | |
|     #     return False
 | |
| 
 | |
|     with open(filename, 'wb') as f:
 | |
|         f.write(newContents)
 | |
| 
 | |
|     return True
 | |
| 
 | |
| 
 | |
| def toStringBool(arg):
 | |
|     return str(not not arg).lower()
 | |
| 
 | |
| 
 | |
| def toBindingNamespace(arg):
 | |
|     return re.sub("((_workers)?$)", "Binding\\1", MakeNativeName(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 innerContainerType(type):
 | |
|     assert type.isSequence() or type.isMozMap()
 | |
|     return type.inner.inner if type.nullable() else type.inner
 | |
| 
 | |
| 
 | |
| def wrapInNativeContainerType(type, inner):
 | |
|     if type.isSequence():
 | |
|         containerType = "Vec"
 | |
|     elif type.isMozMap():
 | |
|         containerType = "MozMap"
 | |
|     else:
 | |
|         raise TypeError("Unexpected container type %s", type)
 | |
| 
 | |
|     return CGWrapper(inner, pre=containerType + "<", post=">")
 | |
| 
 | |
| 
 | |
| 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.unrestricted_float: 'f32',
 | |
|     IDLType.Tags.float: 'Finite<f32>',
 | |
|     IDLType.Tags.unrestricted_double: 'f64',
 | |
|     IDLType.Tags.double: 'Finite<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.unrestricted_float,
 | |
|     IDLType.Tags.unrestricted_double
 | |
| ]
 | |
| 
 | |
| 
 | |
| # 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)
 | |
| 
 | |
| 
 | |
| def indent(s, indentLevel=2):
 | |
|     """
 | |
|     Indent C++ code.
 | |
| 
 | |
|     Weird secret feature: this doesn't indent lines that start with # (such as
 | |
|     #include lines or #ifdef/#endif).
 | |
|     """
 | |
|     if s == "":
 | |
|         return s
 | |
|     return re.sub(lineStartDetector, indentLevel * " ", s)
 | |
| 
 | |
| 
 | |
| # dedent() and fill() are often called on the same string multiple
 | |
| # times.  We want to memoize their return values so we don't keep
 | |
| # recomputing them all the time.
 | |
| def memoize(fn):
 | |
|     """
 | |
|     Decorator to memoize a function of one argument.  The cache just
 | |
|     grows without bound.
 | |
|     """
 | |
|     cache = {}
 | |
| 
 | |
|     @functools.wraps(fn)
 | |
|     def wrapper(arg):
 | |
|         retval = cache.get(arg)
 | |
|         if retval is None:
 | |
|             retval = cache[arg] = fn(arg)
 | |
|         return retval
 | |
|     return wrapper
 | |
| 
 | |
| 
 | |
| @memoize
 | |
| def dedent(s):
 | |
|     """
 | |
|     Remove all leading whitespace from s, and remove a blank line
 | |
|     at the beginning.
 | |
|     """
 | |
|     if s.startswith('\n'):
 | |
|         s = s[1:]
 | |
|     return textwrap.dedent(s)
 | |
| 
 | |
| 
 | |
| # This works by transforming the fill()-template to an equivalent
 | |
| # string.Template.
 | |
| fill_multiline_substitution_re = re.compile(r"( *)\$\*{(\w+)}(\n)?")
 | |
| 
 | |
| 
 | |
| @memoize
 | |
| def compile_fill_template(template):
 | |
|     """
 | |
|     Helper function for fill().  Given the template string passed to fill(),
 | |
|     do the reusable part of template processing and return a pair (t,
 | |
|     argModList) that can be used every time fill() is called with that
 | |
|     template argument.
 | |
| 
 | |
|     argsModList is list of tuples that represent modifications to be
 | |
|     made to args.  Each modification has, in order: i) the arg name,
 | |
|     ii) the modified name, iii) the indent depth.
 | |
|     """
 | |
|     t = dedent(template)
 | |
|     assert t.endswith("\n") or "\n" not in t
 | |
|     argModList = []
 | |
| 
 | |
|     def replace(match):
 | |
|         """
 | |
|         Replaces a line like '  $*{xyz}\n' with '${xyz_n}',
 | |
|         where n is the indent depth, and add a corresponding entry to
 | |
|         argModList.
 | |
| 
 | |
|         Note that this needs to close over argModList, so it has to be
 | |
|         defined inside compile_fill_template().
 | |
|         """
 | |
|         indentation, name, nl = match.groups()
 | |
|         depth = len(indentation)
 | |
| 
 | |
|         # Check that $*{xyz} appears by itself on a line.
 | |
|         prev = match.string[:match.start()]
 | |
|         if (prev and not prev.endswith("\n")) or nl is None:
 | |
|             raise ValueError("Invalid fill() template: $*{%s} must appear by itself on a line" % name)
 | |
| 
 | |
|         # Now replace this whole line of template with the indented equivalent.
 | |
|         modified_name = name + "_" + str(depth)
 | |
|         argModList.append((name, modified_name, depth))
 | |
|         return "${" + modified_name + "}"
 | |
| 
 | |
|     t = re.sub(fill_multiline_substitution_re, replace, t)
 | |
|     return (string.Template(t), argModList)
 | |
| 
 | |
| 
 | |
| def fill(template, **args):
 | |
|     """
 | |
|     Convenience function for filling in a multiline template.
 | |
| 
 | |
|     `fill(template, name1=v1, name2=v2)` is a lot like
 | |
|     `string.Template(template).substitute({"name1": v1, "name2": v2})`.
 | |
| 
 | |
|     However, it's shorter, and has a few nice features:
 | |
| 
 | |
|       * If `template` is indented, fill() automatically dedents it!
 | |
|         This makes code using fill() with Python's multiline strings
 | |
|         much nicer to look at.
 | |
| 
 | |
|       * If `template` starts with a blank line, fill() strips it off.
 | |
|         (Again, convenient with multiline strings.)
 | |
| 
 | |
|       * fill() recognizes a special kind of substitution
 | |
|         of the form `$*{name}`.
 | |
| 
 | |
|         Use this to paste in, and automatically indent, multiple lines.
 | |
|         (Mnemonic: The `*` is for "multiple lines").
 | |
| 
 | |
|         A `$*` substitution must appear by itself on a line, with optional
 | |
|         preceding indentation (spaces only). The whole line is replaced by the
 | |
|         corresponding keyword argument, indented appropriately.  If the
 | |
|         argument is an empty string, no output is generated, not even a blank
 | |
|         line.
 | |
|     """
 | |
| 
 | |
|     t, argModList = compile_fill_template(template)
 | |
|     # Now apply argModList to args
 | |
|     for (name, modified_name, depth) in argModList:
 | |
|         if not (args[name] == "" or args[name].endswith("\n")):
 | |
|             raise ValueError("Argument %s with value %r is missing a newline" % (name, args[name]))
 | |
|         args[modified_name] = indent(args[name], depth)
 | |
| 
 | |
|     return t.substitute(args)
 | |
| 
 | |
| 
 | |
| 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."""
 | |
|         raise NotImplementedError  # Override me!
 | |
| 
 | |
| 
 | |
| class CGMethodCall(CGThing):
 | |
|     """
 | |
|     A class to generate selection of a method signature from a set of
 | |
|     signatures and generation of a call to that signature.
 | |
|     """
 | |
|     def __init__(self, argsPre, nativeMethodName, static, descriptor, method):
 | |
|         CGThing.__init__(self)
 | |
| 
 | |
|         methodName = '\\"%s.%s\\"' % (descriptor.interface.identifier.name, method.identifier.name)
 | |
| 
 | |
|         def requiredArgCount(signature):
 | |
|             arguments = signature[1]
 | |
|             if len(arguments) == 0:
 | |
|                 return 0
 | |
|             requiredArgs = len(arguments)
 | |
|             while requiredArgs and arguments[requiredArgs - 1].optional:
 | |
|                 requiredArgs -= 1
 | |
|             return requiredArgs
 | |
| 
 | |
|         signatures = method.signatures()
 | |
| 
 | |
|         def getPerSignatureCall(signature, argConversionStartsAt=0):
 | |
|             signatureIndex = signatures.index(signature)
 | |
|             return CGPerSignatureCall(signature[0], argsPre, signature[1],
 | |
|                                       nativeMethodName + '_' * signatureIndex,
 | |
|                                       static, descriptor,
 | |
|                                       method, argConversionStartsAt)
 | |
| 
 | |
|         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 false;\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]
 | |
|                 argCountCases.append(CGCase(str(argCount), getPerSignatureCall(signature)))
 | |
|                 continue
 | |
| 
 | |
|             distinguishingIndex = method.distinguishingIndexForArgCount(argCount)
 | |
| 
 | |
|             # We can't handle unions at the distinguishing index.
 | |
|             for (returnType, args) in possibleSignatures:
 | |
|                 if args[distinguishingIndex].type.isUnion():
 | |
|                     raise TypeError("No support for unions as distinguishing "
 | |
|                                     "arguments yet: %s",
 | |
|                                     args[distinguishingIndex].location)
 | |
| 
 | |
|             # Convert all our arguments up to the distinguishing index.
 | |
|             # Doesn't matter which of the possible signatures we use, since
 | |
|             # they all have the same types up to that point; just use
 | |
|             # possibleSignatures[0]
 | |
|             caseBody = [
 | |
|                 CGArgumentConverter(possibleSignatures[0][1][i],
 | |
|                                     i, "args", "argc", descriptor)
 | |
|                 for i in range(0, distinguishingIndex)]
 | |
| 
 | |
|             # Select the right overload from our set.
 | |
|             distinguishingArg = "args.get(%d)" % distinguishingIndex
 | |
| 
 | |
|             def pickFirstSignature(condition, filterLambda):
 | |
|                 sigs = filter(filterLambda, possibleSignatures)
 | |
|                 assert len(sigs) < 2
 | |
|                 if len(sigs) > 0:
 | |
|                     call = getPerSignatureCall(sigs[0], distinguishingIndex)
 | |
|                     if condition is None:
 | |
|                         caseBody.append(call)
 | |
|                     else:
 | |
|                         caseBody.append(CGGeneric("if " + condition + " {"))
 | |
|                         caseBody.append(CGIndenter(call))
 | |
|                         caseBody.append(CGGeneric("}"))
 | |
|                     return True
 | |
|                 return False
 | |
| 
 | |
|             # First check for null or undefined
 | |
|             pickFirstSignature("%s.get().is_null_or_undefined()" % 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.get().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.
 | |
|                     info = getJSToNativeConversionInfo(
 | |
|                         type, descriptor, failureCode="break;", isDefinitelyObject=True)
 | |
|                     template = info.template
 | |
|                     declType = info.declType
 | |
| 
 | |
|                     testCode = instantiateJSToNativeConversionTemplate(
 | |
|                         template,
 | |
|                         {"val": distinguishingArg},
 | |
|                         declType,
 | |
|                         "arg%d" % distinguishingIndex)
 | |
| 
 | |
|                     # Indent by 4, since we need to indent further than our "do" statement
 | |
|                     caseBody.append(CGIndenter(testCode, 4))
 | |
|                     # If we got this far, we know we unwrapped to the right
 | |
|                     # interface, so just do the call.  Start conversion with
 | |
|                     # distinguishingIndex + 1, since we already converted
 | |
|                     # distinguishingIndex.
 | |
|                     caseBody.append(CGIndenter(
 | |
|                         getPerSignatureCall(sig, distinguishingIndex + 1), 4))
 | |
|                     caseBody.append(CGIndenter(CGGeneric("}")))
 | |
| 
 | |
|                 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.get().is_object() && is_array_like(cx, %s)" %
 | |
|                                (distinguishingArg, distinguishingArg),
 | |
|                                lambda s:
 | |
|                                    (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.get().is_object() && "
 | |
|                                "{ rooted!(in(cx) let obj = %s.get().to_object()); "
 | |
|                                "let mut is_date = false; "
 | |
|                                "assert!(JS_ObjectIsDate(cx, obj.handle(), &mut is_date)); "
 | |
|                                "is_date }" %
 | |
|                                (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.get().is_object() && !is_platform_object(%s.get().to_object())" %
 | |
|                                (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 false;" % methodName)))
 | |
|         # XXXjdm Avoid unreachable statement warnings
 | |
|         # overloadCGThings.append(
 | |
|         #     CGGeneric('panic!("We have an always-returning default case");\n'
 | |
|         #               'return false;'))
 | |
|         self.cgRoot = CGWrapper(CGList(overloadCGThings, "\n"),
 | |
|                                 pre="\n")
 | |
| 
 | |
|     def define(self):
 | |
|         return self.cgRoot.define()
 | |
| 
 | |
| 
 | |
| 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.isDictionary():
 | |
|         return dictionaryHasSequenceMember(type.inner)
 | |
|     if type.isUnion():
 | |
|         return any(typeIsSequenceOrHasSequenceMember(m.type) for m in
 | |
|                    type.flatMemberTypes)
 | |
|     return False
 | |
| 
 | |
| 
 | |
| def union_native_type(t):
 | |
|     name = t.unroll().name
 | |
|     return 'UnionTypes::%s' % name
 | |
| 
 | |
| 
 | |
| class JSToNativeConversionInfo():
 | |
|     """
 | |
|     An object representing information about a JS-to-native conversion.
 | |
|     """
 | |
|     def __init__(self, template, default=None, declType=None):
 | |
|         """
 | |
|         template: A string representing the conversion code.  This will have
 | |
|                   template substitution performed on it as follows:
 | |
| 
 | |
|           ${val} is a handle to the JS::Value in question
 | |
| 
 | |
|         default: A string or None representing rust code for default value(if any).
 | |
| 
 | |
|         declType: A CGThing representing the native C++ type we're converting
 | |
|                   to.  This is allowed to be None if the conversion code is
 | |
|                   supposed to be used as-is.
 | |
|         """
 | |
|         assert isinstance(template, str)
 | |
|         assert declType is None or isinstance(declType, CGThing)
 | |
|         self.template = template
 | |
|         self.default = default
 | |
|         self.declType = declType
 | |
| 
 | |
| 
 | |
| def getJSToNativeConversionInfo(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 an object of JSToNativeConversionInfo 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 false;\n"
 | |
| 
 | |
|     if failureCode is None:
 | |
|         failOrPropagate = "throw_type_error(cx, &error);\n%s" % exceptionCode
 | |
|     else:
 | |
|         failOrPropagate = failureCode
 | |
| 
 | |
|     def handleOptional(template, declType, default):
 | |
|         assert (defaultValue is None) == (default is None)
 | |
|         return JSToNativeConversionInfo(template, default, declType)
 | |
| 
 | |
|     # 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.
 | |
|     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 onFailureInvalidEnumValue(failureCode, passedVarName):
 | |
|         return CGGeneric(
 | |
|             failureCode or
 | |
|             ('throw_type_error(cx, &format!("\'{}\' is not a valid enum value for enumeration \'%s\'.", %s)); %s'
 | |
|              % (type.name, passedVarName, exceptionCode)))
 | |
| 
 | |
|     def onFailureNotCallable(failureCode):
 | |
|         return CGGeneric(
 | |
|             failureCode or
 | |
|             ('throw_type_error(cx, \"%s is not callable.\");\n'
 | |
|              '%s' % (firstCap(sourceDescription), exceptionCode)))
 | |
| 
 | |
|     # 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, nullValue, 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}.get().is_object() {\n" +
 | |
|                 CGIndenter(CGGeneric(templateBody)).define() + "\n")
 | |
|             if type.nullable():
 | |
|                 templateBody += (
 | |
|                     "} else if ${val}.get().is_null_or_undefined() {\n"
 | |
|                     "    %s\n") % nullValue
 | |
|             templateBody += (
 | |
|                 "} else {\n" +
 | |
|                 CGIndenter(onFailureNotAnObject(failureCode)).define() +
 | |
|                 "}")
 | |
|         return templateBody
 | |
| 
 | |
|     assert not (isEnforceRange and isClamp)  # These are mutually exclusive
 | |
| 
 | |
|     if type.isSequence() or type.isMozMap():
 | |
|         innerInfo = getJSToNativeConversionInfo(innerContainerType(type),
 | |
|                                                 descriptorProvider,
 | |
|                                                 isMember=isMember)
 | |
|         declType = wrapInNativeContainerType(type, innerInfo.declType)
 | |
|         config = getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs)
 | |
| 
 | |
|         if type.nullable():
 | |
|             declType = CGWrapper(declType, pre="Option<", post=" >")
 | |
| 
 | |
|         templateBody = ("match FromJSValConvertible::from_jsval(cx, ${val}, %s) {\n"
 | |
|                         "    Ok(ConversionResult::Success(value)) => value,\n"
 | |
|                         "    Ok(ConversionResult::Failure(error)) => {\n"
 | |
|                         "%s\n"
 | |
|                         "    }\n"
 | |
|                         "    _ => { %s },\n"
 | |
|                         "}" % (config, indent(failOrPropagate, 8), exceptionCode))
 | |
| 
 | |
|         return handleOptional(templateBody, declType, handleDefaultNull("None"))
 | |
| 
 | |
|     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(ConversionResult::Success(value)) => value,\n"
 | |
|                         "    Ok(ConversionResult::Failure(error)) => {\n"
 | |
|                         "%s\n"
 | |
|                         "    }\n"
 | |
|                         "    _ => { %s },\n"
 | |
|                         "}" % (indent(failOrPropagate, 8), exceptionCode))
 | |
| 
 | |
|         dictionaries = [
 | |
|             memberType
 | |
|             for memberType in type.unroll().flatMemberTypes
 | |
|             if memberType.isDictionary()
 | |
|         ]
 | |
|         if dictionaries:
 | |
|             if defaultValue:
 | |
|                 assert isinstance(defaultValue, IDLNullValue)
 | |
|                 dictionary, = dictionaries
 | |
|                 default = "%s::%s(%s::%s::empty(cx))" % (
 | |
|                     union_native_type(type),
 | |
|                     dictionary.name,
 | |
|                     CGDictionary.makeModuleName(dictionary.inner),
 | |
|                     CGDictionary.makeDictionaryName(dictionary.inner))
 | |
|             else:
 | |
|                 default = None
 | |
|         else:
 | |
|             default = handleDefaultNull("None")
 | |
| 
 | |
|         return handleOptional(templateBody, declType, default)
 | |
| 
 | |
|     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 = CGWrapper(CGGeneric(name), pre="Rc<", post=">")
 | |
|             template = "%s::new(cx, ${val}.get().to_object())" % name
 | |
|             if type.nullable():
 | |
|                 declType = CGWrapper(declType, pre="Option<", post=">")
 | |
|                 template = wrapObjectTemplate("Some(%s)" % template, "None",
 | |
|                                               isDefinitelyObject, type,
 | |
|                                               failureCode)
 | |
| 
 | |
|             return handleOptional(template, declType, handleDefaultNull("None"))
 | |
| 
 | |
|         conversionFunction = "root_from_handlevalue"
 | |
|         descriptorType = descriptor.returnType
 | |
|         if isMember == "Variadic":
 | |
|             conversionFunction = "native_from_handlevalue"
 | |
|             descriptorType = descriptor.nativeType
 | |
|         elif isArgument:
 | |
|             descriptorType = descriptor.argumentType
 | |
| 
 | |
|         templateBody = ""
 | |
|         isPromise = descriptor.interface.identifier.name == "Promise"
 | |
|         if isPromise:
 | |
|             # Per spec, what we're supposed to do is take the original
 | |
|             # Promise.resolve and call it with the original Promise as this
 | |
|             # value to make a Promise out of whatever value we actually have
 | |
|             # here.  The question is which global we should use.  There are
 | |
|             # a couple cases to consider:
 | |
|             #
 | |
|             # 1) Normal call to API with a Promise argument.  This is a case the
 | |
|             #    spec covers, and we should be using the current Realm's
 | |
|             #    Promise.  That means the current compartment.
 | |
|             # 2) Promise return value from a callback or callback interface.
 | |
|             #    This is in theory a case the spec covers but in practice it
 | |
|             #    really doesn't define behavior here because it doesn't define
 | |
|             #    what Realm we're in after the callback returns, which is when
 | |
|             #    the argument conversion happens.  We will use the current
 | |
|             #    compartment, which is the compartment of the callable (which
 | |
|             #    may itself be a cross-compartment wrapper itself), which makes
 | |
|             #    as much sense as anything else. In practice, such an API would
 | |
|             #    once again be providing a Promise to signal completion of an
 | |
|             #    operation, which would then not be exposed to anyone other than
 | |
|             #    our own implementation code.
 | |
|             templateBody = fill(
 | |
|                 """
 | |
|                 { // Scope for our JSAutoCompartment.
 | |
| 
 | |
|                   rooted!(in(cx) let globalObj = CurrentGlobalOrNull(cx));
 | |
|                   let promiseGlobal = GlobalScope::from_object_maybe_wrapped(globalObj.handle().get());
 | |
| 
 | |
|                   rooted!(in(cx) let mut valueToResolve = $${val}.get());
 | |
|                   if !JS_WrapValue(cx, valueToResolve.handle_mut()) {
 | |
|                     $*{exceptionCode}
 | |
|                   }
 | |
|                   match Promise::Resolve(&promiseGlobal, cx, valueToResolve.handle()) {
 | |
|                       Ok(value) => value,
 | |
|                       Err(error) => {
 | |
|                         throw_dom_exception(cx, &promiseGlobal, error);
 | |
|                         $*{exceptionCode}
 | |
|                       }
 | |
|                   }
 | |
|                 }
 | |
|                 """,
 | |
|                 exceptionCode=exceptionCode)
 | |
|         else:
 | |
|             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 = fill(
 | |
|                 """
 | |
|                 match ${function}($${val}) {
 | |
|                     Ok(val) => val,
 | |
|                     Err(()) => {
 | |
|                         $*{failureCode}
 | |
|                     }
 | |
|                 }
 | |
|                 """,
 | |
|                 failureCode=unwrapFailureCode + "\n",
 | |
|                 function=conversionFunction)
 | |
| 
 | |
|         declType = CGGeneric(descriptorType)
 | |
|         if type.nullable():
 | |
|             templateBody = "Some(%s)" % templateBody
 | |
|             declType = CGWrapper(declType, pre="Option<", post=">")
 | |
| 
 | |
|         templateBody = wrapObjectTemplate(templateBody, "None",
 | |
|                                           isDefinitelyObject, type, failureCode)
 | |
| 
 | |
|         return handleOptional(templateBody, declType, handleDefaultNull("None"))
 | |
| 
 | |
|     if type.isSpiderMonkeyInterface():
 | |
|         raise TypeError("Can't handle SpiderMonkey interface arguments yet")
 | |
| 
 | |
|     if type.isDOMString():
 | |
|         nullBehavior = getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs)
 | |
| 
 | |
|         conversionCode = (
 | |
|             "match FromJSValConvertible::from_jsval(cx, ${val}, %s) {\n"
 | |
|             "    Ok(ConversionResult::Success(strval)) => strval,\n"
 | |
|             "    Ok(ConversionResult::Failure(error)) => {\n"
 | |
|             "%s\n"
 | |
|             "    }\n"
 | |
|             "    _ => { %s },\n"
 | |
|             "}" % (nullBehavior, indent(failOrPropagate, 8), exceptionCode))
 | |
| 
 | |
|         if defaultValue is None:
 | |
|             default = None
 | |
|         elif isinstance(defaultValue, IDLNullValue):
 | |
|             assert type.nullable()
 | |
|             default = "None"
 | |
|         else:
 | |
|             assert defaultValue.type.tag() == IDLType.Tags.domstring
 | |
|             default = 'DOMString::from("%s")' % defaultValue.value
 | |
|             if type.nullable():
 | |
|                 default = "Some(%s)" % default
 | |
| 
 | |
|         declType = "DOMString"
 | |
|         if type.nullable():
 | |
|             declType = "Option<%s>" % declType
 | |
| 
 | |
|         return handleOptional(conversionCode, CGGeneric(declType), default)
 | |
| 
 | |
|     if type.isUSVString():
 | |
|         assert not isEnforceRange and not isClamp
 | |
| 
 | |
|         conversionCode = (
 | |
|             "match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n"
 | |
|             "    Ok(ConversionResult::Success(strval)) => strval,\n"
 | |
|             "    Ok(ConversionResult::Failure(error)) => {\n"
 | |
|             "%s\n"
 | |
|             "    }\n"
 | |
|             "    _ => { %s },\n"
 | |
|             "}" % (indent(failOrPropagate, 8), exceptionCode))
 | |
| 
 | |
|         if defaultValue is None:
 | |
|             default = None
 | |
|         elif isinstance(defaultValue, IDLNullValue):
 | |
|             assert type.nullable()
 | |
|             default = "None"
 | |
|         else:
 | |
|             assert defaultValue.type.tag() in (IDLType.Tags.domstring, IDLType.Tags.usvstring)
 | |
|             default = 'USVString("%s".to_owned())' % defaultValue.value
 | |
|             if type.nullable():
 | |
|                 default = "Some(%s)" % default
 | |
| 
 | |
|         declType = "USVString"
 | |
|         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(ConversionResult::Success(strval)) => strval,\n"
 | |
|             "    Ok(ConversionResult::Failure(error)) => {\n"
 | |
|             "%s\n"
 | |
|             "    }\n"
 | |
|             "    _ => { %s },\n"
 | |
|             "}" % (indent(failOrPropagate, 8), exceptionCode))
 | |
| 
 | |
|         if defaultValue is None:
 | |
|             default = None
 | |
|         elif isinstance(defaultValue, IDLNullValue):
 | |
|             assert type.nullable()
 | |
|             default = "None"
 | |
|         else:
 | |
|             assert defaultValue.type.tag() in (IDLType.Tags.domstring, IDLType.Tags.bytestring)
 | |
|             default = 'ByteString::new(b"%s".to_vec())' % defaultValue.value
 | |
|             if type.nullable():
 | |
|                 default = "Some(%s)" % default
 | |
| 
 | |
|         declType = "ByteString"
 | |
|         if type.nullable():
 | |
|             declType = "Option<%s>" % declType
 | |
| 
 | |
|         return handleOptional(conversionCode, CGGeneric(declType), default)
 | |
| 
 | |
|     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 = onFailureInvalidEnumValue(failureCode, 'search').define()
 | |
|         else:
 | |
|             handleInvalidEnumValueCode = "return true;"
 | |
| 
 | |
|         template = (
 | |
|             "match find_enum_string_index(cx, ${val}, %(values)s) {\n"
 | |
|             "    Err(_) => { %(exceptionCode)s },\n"
 | |
|             "    Ok((None, search)) => { %(handleInvalidEnumValueCode)s },\n"
 | |
|             "    Ok((Some(index), _)) => {\n"
 | |
|             "        //XXXjdm need some range checks up in here.\n"
 | |
|             "        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 = "%s::%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()
 | |
| 
 | |
|         callback = type.unroll().callback
 | |
|         declType = CGGeneric(callback.identifier.name)
 | |
|         finalDeclType = CGTemplatedType("Rc", declType)
 | |
| 
 | |
|         conversion = CGCallbackTempRoot(declType.define())
 | |
| 
 | |
|         if type.nullable():
 | |
|             declType = CGTemplatedType("Option", declType)
 | |
|             finalDeclType = CGTemplatedType("Option", finalDeclType)
 | |
|             conversion = CGWrapper(conversion, pre="Some(", post=")")
 | |
| 
 | |
|         if allowTreatNonObjectAsNull and type.treatNonObjectAsNull():
 | |
|             if not isDefinitelyObject:
 | |
|                 haveObject = "${val}.get().is_object()"
 | |
|                 template = CGIfElseWrapper(haveObject,
 | |
|                                            conversion,
 | |
|                                            CGGeneric("None")).define()
 | |
|             else:
 | |
|                 template = conversion
 | |
|         else:
 | |
|             template = CGIfElseWrapper("IsCallable(${val}.get().to_object())",
 | |
|                                        conversion,
 | |
|                                        onFailureNotCallable(failureCode)).define()
 | |
|             template = wrapObjectTemplate(
 | |
|                 template,
 | |
|                 "None",
 | |
|                 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 JSToNativeConversionInfo(template, default, finalDeclType)
 | |
| 
 | |
|     if type.isAny():
 | |
|         assert not isEnforceRange and not isClamp
 | |
| 
 | |
|         declType = ""
 | |
|         default = ""
 | |
|         if isMember == "Dictionary":
 | |
|             # TODO: Need to properly root dictionaries
 | |
|             # https://github.com/servo/servo/issues/6381
 | |
|             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")
 | |
|         else:
 | |
|             declType = CGGeneric("HandleValue")
 | |
| 
 | |
|             if defaultValue is None:
 | |
|                 default = None
 | |
|             elif isinstance(defaultValue, IDLNullValue):
 | |
|                 default = "HandleValue::null()"
 | |
|             elif isinstance(defaultValue, IDLUndefinedValue):
 | |
|                 default = "HandleValue::undefined()"
 | |
|             else:
 | |
|                 raise TypeError("Can't handle non-null, non-undefined default value here")
 | |
| 
 | |
|         return handleOptional("${val}", declType, default)
 | |
| 
 | |
|     if type.isObject():
 | |
|         assert not isEnforceRange and not isClamp
 | |
| 
 | |
|         # TODO: Need to root somehow
 | |
|         # https://github.com/servo/servo/issues/6382
 | |
|         declType = CGGeneric("*mut JSObject")
 | |
|         templateBody = wrapObjectTemplate("${val}.get().to_object()",
 | |
|                                           "ptr::null_mut()",
 | |
|                                           isDefinitelyObject, type, failureCode)
 | |
| 
 | |
|         return handleOptional(templateBody, declType,
 | |
|                               handleDefaultNull("ptr::null_mut()"))
 | |
| 
 | |
|     if type.isDictionary():
 | |
|         # There are no nullable dictionaries
 | |
|         assert not type.nullable()
 | |
| 
 | |
|         typeName = "%s::%s" % (CGDictionary.makeModuleName(type.inner),
 | |
|                                CGDictionary.makeDictionaryName(type.inner))
 | |
|         declType = CGGeneric(typeName)
 | |
|         template = ("match %s::new(cx, ${val}) {\n"
 | |
|                     "    Ok(ConversionResult::Success(dictionary)) => dictionary,\n"
 | |
|                     "    Ok(ConversionResult::Failure(error)) => {\n"
 | |
|                     "%s\n"
 | |
|                     "    }\n"
 | |
|                     "    _ => { %s },\n"
 | |
|                     "}" % (typeName, indent(failOrPropagate, 8), exceptionCode))
 | |
| 
 | |
|         return handleOptional(template, declType, handleDefaultNull("%s::empty(cx)" % typeName))
 | |
| 
 | |
|     if type.isVoid():
 | |
|         # This one only happens for return values, and its easy: Just
 | |
|         # ignore the jsval.
 | |
|         return JSToNativeConversionInfo("", None, None)
 | |
| 
 | |
|     if not type.isPrimitive():
 | |
|         raise TypeError("Need conversion for argument type '%s'" % str(type))
 | |
| 
 | |
|     conversionBehavior = getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs)
 | |
| 
 | |
|     if failureCode is None:
 | |
|         failureCode = 'return false'
 | |
| 
 | |
|     declType = CGGeneric(builtinNames[type.tag()])
 | |
|     if type.nullable():
 | |
|         declType = CGWrapper(declType, pre="Option<", post=">")
 | |
| 
 | |
|     template = (
 | |
|         "match FromJSValConvertible::from_jsval(cx, ${val}, %s) {\n"
 | |
|         "    Ok(ConversionResult::Success(v)) => v,\n"
 | |
|         "    Ok(ConversionResult::Failure(error)) => {\n"
 | |
|         "%s\n"
 | |
|         "    }\n"
 | |
|         "    _ => { %s }\n"
 | |
|         "}" % (conversionBehavior, indent(failOrPropagate, 8), exceptionCode))
 | |
| 
 | |
|     if defaultValue is not None:
 | |
|         if isinstance(defaultValue, IDLNullValue):
 | |
|             assert type.nullable()
 | |
|             defaultStr = "None"
 | |
|         else:
 | |
|             tag = defaultValue.type.tag()
 | |
|             if tag in [IDLType.Tags.float, IDLType.Tags.double]:
 | |
|                 defaultStr = "Finite::wrap(%s)" % defaultValue.value
 | |
|             elif 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):
 | |
|     """
 | |
|     Take the templateBody and declType as returned by
 | |
|     getJSToNativeConversionInfo, 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(""))
 | |
| 
 | |
|     return result
 | |
| 
 | |
| 
 | |
| def convertConstIDLValueToJSVal(value):
 | |
|     if isinstance(value, IDLNullValue):
 | |
|         return "ConstantVal::NullVal"
 | |
|     tag = value.type.tag()
 | |
|     if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
 | |
|                IDLType.Tags.uint16, IDLType.Tags.int32]:
 | |
|         return "ConstantVal::IntVal(%s)" % (value.value)
 | |
|     if tag == IDLType.Tags.uint32:
 | |
|         return "ConstantVal::UintVal(%s)" % (value.value)
 | |
|     if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]:
 | |
|         return "ConstantVal::DoubleVal(%s)" % (value.value)
 | |
|     if tag == IDLType.Tags.bool:
 | |
|         return "ConstantVal::BoolVal(true)" if value.value else "ConstantVal::BoolVal(false)"
 | |
|     if tag in [IDLType.Tags.unrestricted_float, IDLType.Tags.float,
 | |
|                IDLType.Tags.unrestricted_double, IDLType.Tags.double]:
 | |
|         return "ConstantVal::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, args, argc, descriptorProvider,
 | |
|                  invalidEnumValueFatal=True):
 | |
|         CGThing.__init__(self)
 | |
|         assert not argument.defaultValue or argument.optional
 | |
| 
 | |
|         replacer = {
 | |
|             "index": index,
 | |
|             "argc": argc,
 | |
|             "args": args
 | |
|         }
 | |
| 
 | |
|         replacementVariables = {
 | |
|             "val": string.Template("${args}.get(${index})").substitute(replacer),
 | |
|         }
 | |
| 
 | |
|         info = getJSToNativeConversionInfo(
 | |
|             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())
 | |
|         template = info.template
 | |
|         default = info.default
 | |
|         declType = info.declType
 | |
| 
 | |
|         if not argument.variadic:
 | |
|             if argument.optional:
 | |
|                 condition = "{args}.get({index}).is_undefined()".format(**replacer)
 | |
|                 if argument.defaultValue:
 | |
|                     assert default
 | |
|                     template = CGIfElseWrapper(condition,
 | |
|                                                CGGeneric(default),
 | |
|                                                CGGeneric(template)).define()
 | |
|                 else:
 | |
|                     assert not default
 | |
|                     declType = CGWrapper(declType, pre="Option<", post=">")
 | |
|                     template = CGIfElseWrapper(condition,
 | |
|                                                CGGeneric("None"),
 | |
|                                                CGGeneric("Some(%s)" % template)).define()
 | |
|             else:
 | |
|                 assert not default
 | |
| 
 | |
|             self.converter = instantiateJSToNativeConversionTemplate(
 | |
|                 template, replacementVariables, declType, "arg%d" % index)
 | |
|         else:
 | |
|             assert argument.optional
 | |
|             variadicConversion = {
 | |
|                 "val": string.Template("${args}.get(variadicArg)").substitute(replacer),
 | |
|             }
 | |
|             innerConverter = [instantiateJSToNativeConversionTemplate(
 | |
|                 template, variadicConversion, declType, "slot")]
 | |
| 
 | |
|             arg = "arg%d" % index
 | |
|             if argument.type.isGeckoInterface():
 | |
|                 init = "rooted_vec!(let mut %s)" % arg
 | |
|                 innerConverter.append(CGGeneric("%s.push(JS::from_ref(&*slot));" % arg))
 | |
|             else:
 | |
|                 init = "let mut %s = vec![]" % arg
 | |
|                 innerConverter.append(CGGeneric("%s.push(slot);" % arg))
 | |
|             inner = CGIndenter(CGList(innerConverter, "\n"), 8).define()
 | |
| 
 | |
|             self.converter = CGGeneric("""\
 | |
| %(init)s;
 | |
| if %(argc)s > %(index)s {
 | |
|     %(arg)s.reserve(%(argc)s as usize - %(index)s);
 | |
|     for variadicArg in %(index)s..%(argc)s {
 | |
| %(inner)s
 | |
|     }
 | |
| }""" % {'arg': arg, 'argc': argc, 'index': index, 'inner': inner, 'init': init})
 | |
| 
 | |
|     def define(self):
 | |
|         return self.converter.define()
 | |
| 
 | |
| 
 | |
| def wrapForType(jsvalRef, result='result', successCode='return true;', pre=''):
 | |
|     """
 | |
|     Reflect a Rust value into JS.
 | |
| 
 | |
|       * 'jsvalRef': a MutableHandleValue 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.
 | |
|       * 'pre': code to run before the conversion if rooting is necessary
 | |
|     """
 | |
|     wrap = "%s\n(%s).to_jsval(cx, %s);" % (pre, result, jsvalRef)
 | |
|     if successCode:
 | |
|         wrap += "\n%s" % successCode
 | |
|     return wrap
 | |
| 
 | |
| 
 | |
| def typeNeedsCx(type, retVal=False):
 | |
|     if type is None:
 | |
|         return False
 | |
|     if type.nullable():
 | |
|         type = type.inner
 | |
|     if type.isSequence():
 | |
|         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()
 | |
| 
 | |
| 
 | |
| # Returns a conversion behavior suitable for a type
 | |
| def getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs):
 | |
|     if type.isSequence() or type.isMozMap():
 | |
|         return getConversionConfigForType(innerContainerType(type), isEnforceRange, isClamp, treatNullAs)
 | |
|     if type.isDOMString():
 | |
|         assert not isEnforceRange and not isClamp
 | |
| 
 | |
|         treatAs = {
 | |
|             "Default": "StringificationBehavior::Default",
 | |
|             "EmptyString": "StringificationBehavior::Empty",
 | |
|         }
 | |
|         if treatNullAs not in treatAs:
 | |
|             raise TypeError("We don't support [TreatNullAs=%s]" % treatNullAs)
 | |
|         if type.nullable():
 | |
|             # Note: the actual behavior passed here doesn't matter for nullable
 | |
|             # strings.
 | |
|             return "StringificationBehavior::Default"
 | |
|         else:
 | |
|             return treatAs[treatNullAs]
 | |
|     if type.isPrimitive() and type.isInteger():
 | |
|         if isEnforceRange:
 | |
|             return "ConversionBehavior::EnforceRange"
 | |
|         elif isClamp:
 | |
|             return "ConversionBehavior::Clamp"
 | |
|         else:
 | |
|             return "ConversionBehavior::Default"
 | |
|     assert not isEnforceRange and not isClamp
 | |
|     return "()"
 | |
| 
 | |
| 
 | |
| # 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.isUSVString():
 | |
|         result = CGGeneric("USVString")
 | |
|         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():
 | |
|         callback = returnType.unroll().callback
 | |
|         result = CGGeneric('Rc<%s::%s>' % (getModuleFromObject(callback), callback.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
 | |
|     # TODO: Return the value through a MutableHandleValue outparam
 | |
|     # https://github.com/servo/servo/issues/6307
 | |
|     if returnType.isAny():
 | |
|         return CGGeneric("JSVal")
 | |
|     if returnType.isObject() or returnType.isSpiderMonkeyInterface():
 | |
|         result = CGGeneric("NonZero<*mut JSObject>")
 | |
|         if returnType.nullable():
 | |
|             result = CGWrapper(result, pre="Option<", post=">")
 | |
|         return result
 | |
|     if returnType.isSequence() or returnType.isMozMap():
 | |
|         result = getRetvalDeclarationForType(innerContainerType(returnType), descriptorProvider)
 | |
|         result = wrapInNativeContainerType(returnType, result)
 | |
|         if returnType.nullable():
 | |
|             result = CGWrapper(result, pre="Option<", post=">")
 | |
|         return result
 | |
|     if returnType.isDictionary():
 | |
|         nullable = returnType.nullable()
 | |
|         dictName = returnType.inner.name if nullable else returnType.name
 | |
|         result = CGGeneric(dictName)
 | |
|         if nullable:
 | |
|             result = CGWrapper(result, pre="Option<", post=">")
 | |
|         return result
 | |
| 
 | |
|     raise TypeError("Don't know how to declare return value for %s" %
 | |
|                     returnType)
 | |
| 
 | |
| 
 | |
| def MemberCondition(pref, func):
 | |
|     """
 | |
|     A string representing the condition for a member to actually be exposed.
 | |
|     Any of the arguments can be None. If not None, they should have the
 | |
|     following types:
 | |
| 
 | |
|     pref: The name of the preference.
 | |
|     func: The name of the function.
 | |
|     """
 | |
|     assert pref is None or isinstance(pref, str)
 | |
|     assert func is None or isinstance(func, str)
 | |
|     assert func is None or pref is None
 | |
|     if pref:
 | |
|         return 'Condition::Pref("%s")' % pref
 | |
|     if func:
 | |
|         return 'Condition::Func(%s)' % func
 | |
|     return "Condition::Satisfied"
 | |
| 
 | |
| 
 | |
| 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())
 | |
| 
 | |
|     @staticmethod
 | |
|     def getStringAttr(member, name):
 | |
|         attr = member.getExtendedAttribute(name)
 | |
|         if attr is None:
 | |
|             return None
 | |
|         # It's a list of strings
 | |
|         assert len(attr) == 1
 | |
|         assert attr[0] is not None
 | |
|         return attr[0]
 | |
| 
 | |
|     @staticmethod
 | |
|     def getControllingCondition(interfaceMember, descriptor):
 | |
|         return MemberCondition(
 | |
|             PropertyDefiner.getStringAttr(interfaceMember,
 | |
|                                           "Pref"),
 | |
|             PropertyDefiner.getStringAttr(interfaceMember,
 | |
|                                           "Func"))
 | |
| 
 | |
|     def generateGuardedArray(self, array, name, specTemplate, specTerminator,
 | |
|                              specType, getCondition, 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.
 | |
|         """
 | |
| 
 | |
|         # We generate an all-encompassing list of lists of specs, with each sublist
 | |
|         # representing a group of members that share a common pref name. That will
 | |
|         # make sure the order of the properties as exposed on the interface and
 | |
|         # interface prototype objects does not change when pref control is added to
 | |
|         # members while still allowing us to define all the members in the smallest
 | |
|         # number of JSAPI calls.
 | |
|         assert len(array) != 0
 | |
|         specs = []
 | |
|         prefableSpecs = []
 | |
|         prefableTemplate = '    Guard::new(%s, %s[%d])'
 | |
| 
 | |
|         for cond, members in groupby(array, lambda m: getCondition(m, self.descriptor)):
 | |
|             currentSpecs = [specTemplate % getDataTuple(m) for m in members]
 | |
|             if specTerminator:
 | |
|                 currentSpecs.append(specTerminator)
 | |
|             specs.append("&[\n" + ",\n".join(currentSpecs) + "]\n")
 | |
|             prefableSpecs.append(
 | |
|                 prefableTemplate % (cond, name + "_specs", len(specs) - 1))
 | |
| 
 | |
|         specsArray = ("const %s_specs: &'static [&'static[%s]] = &[\n" +
 | |
|                       ",\n".join(specs) + "\n" +
 | |
|                       "];\n") % (name, specType)
 | |
| 
 | |
|         prefArray = ("const %s: &'static [Guard<&'static [%s]>] = &[\n" +
 | |
|                      ",\n".join(prefableSpecs) + "\n" +
 | |
|                      "];\n") % (name, specType)
 | |
|         return specsArray + prefArray
 | |
| 
 | |
| 
 | |
| # The length of a method is the minimum of the lengths of the
 | |
| # argument lists of all its overloads.
 | |
| def methodLength(method):
 | |
|     signatures = method.signatures()
 | |
|     return min(
 | |
|         len([arg for arg in arguments if not arg.optional and not arg.variadic])
 | |
|         for (_, arguments) in signatures)
 | |
| 
 | |
| 
 | |
| class MethodDefiner(PropertyDefiner):
 | |
|     """
 | |
|     A class for defining methods on a prototype object.
 | |
|     """
 | |
|     def __init__(self, descriptor, name, static, unforgeable):
 | |
|         assert not (static and unforgeable)
 | |
|         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 __
 | |
| 
 | |
|         # Ignore non-static methods for callback interfaces
 | |
|         if not descriptor.interface.isCallback() or static:
 | |
|             methods = [m for m in descriptor.interface.members if
 | |
|                        m.isMethod() and m.isStatic() == static and
 | |
|                        not m.isIdentifierLess() and
 | |
|                        MemberIsUnforgeable(m, descriptor) == unforgeable]
 | |
|         else:
 | |
|             methods = []
 | |
|         self.regular = [{"name": m.identifier.name,
 | |
|                          "methodInfo": not m.isStatic(),
 | |
|                          "length": methodLength(m),
 | |
|                          "condition": PropertyDefiner.getControllingCondition(m, descriptor)}
 | |
|                         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,
 | |
|                                  "selfHostedName": "ArrayValues",
 | |
|                                  "length": 0,
 | |
|                                  "condition": "Condition::Satisfied"})
 | |
| 
 | |
|         # Generate the keys/values/entries aliases for value iterables.
 | |
|         maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable
 | |
|         if (not static and not unforgeable and
 | |
|             (maplikeOrSetlikeOrIterable and
 | |
|              maplikeOrSetlikeOrIterable.isIterable() and
 | |
|              maplikeOrSetlikeOrIterable.isValueIterator())):
 | |
|             # Add our keys/values/entries/forEach
 | |
|             self.regular.append({
 | |
|                 "name": "keys",
 | |
|                 "methodInfo": False,
 | |
|                 "selfHostedName": "ArrayKeys",
 | |
|                 "length": 0,
 | |
|                 "condition": PropertyDefiner.getControllingCondition(m,
 | |
|                                                                      descriptor)
 | |
|             })
 | |
|             self.regular.append({
 | |
|                 "name": "values",
 | |
|                 "methodInfo": False,
 | |
|                 "selfHostedName": "ArrayValues",
 | |
|                 "length": 0,
 | |
|                 "condition": PropertyDefiner.getControllingCondition(m,
 | |
|                                                                      descriptor)
 | |
|             })
 | |
|             self.regular.append({
 | |
|                 "name": "entries",
 | |
|                 "methodInfo": False,
 | |
|                 "selfHostedName": "ArrayEntries",
 | |
|                 "length": 0,
 | |
|                 "condition": PropertyDefiner.getControllingCondition(m,
 | |
|                                                                      descriptor)
 | |
|             })
 | |
|             self.regular.append({
 | |
|                 "name": "forEach",
 | |
|                 "methodInfo": False,
 | |
|                 "selfHostedName": "ArrayForEach",
 | |
|                 "length": 1,
 | |
|                 "condition": PropertyDefiner.getControllingCondition(m,
 | |
|                                                                      descriptor)
 | |
|             })
 | |
| 
 | |
|         isUnforgeableInterface = bool(descriptor.interface.getExtendedAttribute("Unforgeable"))
 | |
|         if not static and unforgeable == isUnforgeableInterface:
 | |
|             stringifier = descriptor.operations['Stringifier']
 | |
|             if stringifier:
 | |
|                 self.regular.append({
 | |
|                     "name": "toString",
 | |
|                     "nativeName": stringifier.identifier.name,
 | |
|                     "length": 0,
 | |
|                     "condition": PropertyDefiner.getControllingCondition(stringifier, descriptor)
 | |
|                 })
 | |
|         self.unforgeable = unforgeable
 | |
| 
 | |
|     def generateArray(self, array, name):
 | |
|         if len(array) == 0:
 | |
|             return ""
 | |
| 
 | |
|         def condition(m, d):
 | |
|             return m["condition"]
 | |
| 
 | |
|         flags = "JSPROP_ENUMERATE"
 | |
|         if self.unforgeable:
 | |
|             flags += " | JSPROP_PERMANENT | JSPROP_READONLY"
 | |
| 
 | |
|         def specData(m):
 | |
|             # TODO: Use something like JS_FNSPEC
 | |
|             # https://github.com/servo/servo/issues/6391
 | |
|             if "selfHostedName" in m:
 | |
|                 selfHostedName = '%s as *const u8 as *const libc::c_char' % str_to_const_array(m["selfHostedName"])
 | |
|                 assert not m.get("methodInfo", True)
 | |
|                 accessor = "None"
 | |
|                 jitinfo = "0 as *const JSJitInfo"
 | |
|             else:
 | |
|                 selfHostedName = "0 as *const libc::c_char"
 | |
|                 if m.get("methodInfo", True):
 | |
|                     identifier = m.get("nativeName", m["name"])
 | |
|                     # Go through an intermediate type here, because it's not
 | |
|                     # easy to tell whether the methodinfo is a JSJitInfo or
 | |
|                     # a JSTypedMethodJitInfo here.  The compiler knows, though,
 | |
|                     # so let it do the work.
 | |
|                     jitinfo = "&%s_methodinfo as *const _ as *const JSJitInfo" % identifier
 | |
|                     accessor = "Some(generic_method)"
 | |
|                 else:
 | |
|                     jitinfo = "0 as *const JSJitInfo"
 | |
|                     accessor = 'Some(%s)' % m.get("nativeName", m["name"])
 | |
|             if m["name"].startswith("@@"):
 | |
|                 return ('(SymbolCode::%s as i32 + 1)'
 | |
|                         % m["name"][2:], accessor, jitinfo, m["length"], flags, selfHostedName)
 | |
|             return (str_to_const_array(m["name"]), accessor, jitinfo, m["length"], flags, selfHostedName)
 | |
| 
 | |
|         return self.generateGuardedArray(
 | |
|             array, name,
 | |
|             '    JSFunctionSpec {\n'
 | |
|             '        name: %s as *const u8 as *const libc::c_char,\n'
 | |
|             '        call: JSNativeWrapper { op: %s, info: %s },\n'
 | |
|             '        nargs: %s,\n'
 | |
|             '        flags: (%s) as u16,\n'
 | |
|             '        selfHostedName: %s\n'
 | |
|             '    }',
 | |
|             '    JSFunctionSpec {\n'
 | |
|             '        name: 0 as *const libc::c_char,\n'
 | |
|             '        call: JSNativeWrapper { op: None, info: 0 as *const JSJitInfo },\n'
 | |
|             '        nargs: 0,\n'
 | |
|             '        flags: 0,\n'
 | |
|             '        selfHostedName: 0 as *const libc::c_char\n'
 | |
|             '    }',
 | |
|             'JSFunctionSpec',
 | |
|             condition, specData)
 | |
| 
 | |
| 
 | |
| class AttrDefiner(PropertyDefiner):
 | |
|     def __init__(self, descriptor, name, static, unforgeable):
 | |
|         assert not (static and unforgeable)
 | |
|         PropertyDefiner.__init__(self, descriptor, name)
 | |
|         self.name = name
 | |
|         self.descriptor = descriptor
 | |
|         self.regular = [
 | |
|             m
 | |
|             for m in descriptor.interface.members if
 | |
|             m.isAttr() and m.isStatic() == static and
 | |
|             MemberIsUnforgeable(m, descriptor) == unforgeable
 | |
|         ]
 | |
|         self.static = static
 | |
|         self.unforgeable = unforgeable
 | |
| 
 | |
|     def generateArray(self, array, name):
 | |
|         if len(array) == 0:
 | |
|             return ""
 | |
| 
 | |
|         flags = "JSPROP_ENUMERATE | JSPROP_SHARED"
 | |
|         if self.unforgeable:
 | |
|             flags += " | JSPROP_PERMANENT"
 | |
| 
 | |
|         def getter(attr):
 | |
|             if self.static:
 | |
|                 accessor = 'get_' + self.descriptor.internalNameFor(attr.identifier.name)
 | |
|                 jitinfo = "0 as *const JSJitInfo"
 | |
|             else:
 | |
|                 if attr.hasLenientThis():
 | |
|                     accessor = "generic_lenient_getter"
 | |
|                 else:
 | |
|                     accessor = "generic_getter"
 | |
|                 jitinfo = "&%s_getterinfo" % self.descriptor.internalNameFor(attr.identifier.name)
 | |
| 
 | |
|             return ("JSNativeWrapper { op: Some(%(native)s), info: %(info)s }"
 | |
|                     % {"info": jitinfo,
 | |
|                        "native": accessor})
 | |
| 
 | |
|         def setter(attr):
 | |
|             if (attr.readonly and not attr.getExtendedAttribute("PutForwards")
 | |
|                     and not attr.getExtendedAttribute("Replaceable")):
 | |
|                 return "JSNativeWrapper { op: None, info: 0 as *const JSJitInfo }"
 | |
| 
 | |
|             if self.static:
 | |
|                 accessor = 'set_' + self.descriptor.internalNameFor(attr.identifier.name)
 | |
|                 jitinfo = "0 as *const JSJitInfo"
 | |
|             else:
 | |
|                 if attr.hasLenientThis():
 | |
|                     accessor = "generic_lenient_setter"
 | |
|                 else:
 | |
|                     accessor = "generic_setter"
 | |
|                 jitinfo = "&%s_setterinfo" % self.descriptor.internalNameFor(attr.identifier.name)
 | |
| 
 | |
|             return ("JSNativeWrapper { op: Some(%(native)s), info: %(info)s }"
 | |
|                     % {"info": jitinfo,
 | |
|                        "native": accessor})
 | |
| 
 | |
|         def specData(attr):
 | |
|             return (str_to_const_array(attr.identifier.name), flags, getter(attr),
 | |
|                     setter(attr))
 | |
| 
 | |
|         return self.generateGuardedArray(
 | |
|             array, name,
 | |
|             '    JSPropertySpec {\n'
 | |
|             '        name: %s as *const u8 as *const libc::c_char,\n'
 | |
|             '        flags: (%s) as u8,\n'
 | |
|             '        getter: %s,\n'
 | |
|             '        setter: %s\n'
 | |
|             '    }',
 | |
|             '    JSPropertySpec {\n'
 | |
|             '        name: 0 as *const libc::c_char,\n'
 | |
|             '        flags: 0,\n'
 | |
|             '        getter: JSNativeWrapper { op: None, info: 0 as *const JSJitInfo },\n'
 | |
|             '        setter: JSNativeWrapper { op: None, info: 0 as *const JSJitInfo }\n'
 | |
|             '    }',
 | |
|             'JSPropertySpec',
 | |
|             PropertyDefiner.getControllingCondition, 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 (str_to_const_array(const.identifier.name),
 | |
|                     convertConstIDLValueToJSVal(const.value))
 | |
| 
 | |
|         return self.generateGuardedArray(
 | |
|             array, name,
 | |
|             '    ConstantSpec { name: %s, value: %s }',
 | |
|             None,
 | |
|             'ConstantSpec',
 | |
|             PropertyDefiner.getControllingCondition, 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=4):
 | |
|         CGThing.__init__(self)
 | |
|         self.child = child
 | |
|         self.indent = " " * indentLevel
 | |
| 
 | |
|     def define(self):
 | |
|         defn = self.child.define()
 | |
|         if defn != "":
 | |
|             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, callbacks, dictionaries, enums, imports, config, ignored_warnings=None):
 | |
|         """
 | |
|         Adds a set of imports.
 | |
|         """
 | |
|         if ignored_warnings is None:
 | |
|             ignored_warnings = [
 | |
|                 'non_camel_case_types',
 | |
|                 'non_upper_case_globals',
 | |
|                 'unused_imports',
 | |
|                 'unused_variables',
 | |
|                 'unused_assignments',
 | |
|             ]
 | |
| 
 | |
|         def componentTypes(type):
 | |
|             if type.isType() and type.nullable():
 | |
|                 type = type.unroll()
 | |
|             if type.isUnion():
 | |
|                 return type.flatMemberTypes
 | |
|             if type.isDictionary():
 | |
|                 return [type] + getTypesFromDictionary(type)
 | |
|             return [type]
 | |
| 
 | |
|         def isImportable(type):
 | |
|             if not type.isType():
 | |
|                 assert (type.isInterface() or type.isDictionary() or
 | |
|                         type.isEnum() or type.isNamespace())
 | |
|                 return True
 | |
|             return not (type.builtin or type.isSequence() or type.isUnion())
 | |
| 
 | |
|         def relatedTypesForSignatures(method):
 | |
|             types = []
 | |
|             for (returnType, arguments) in method.signatures():
 | |
|                 types += componentTypes(returnType)
 | |
|                 for arg in arguments:
 | |
|                     types += componentTypes(arg.type)
 | |
|             return types
 | |
| 
 | |
|         def getIdentifier(t):
 | |
|             if t.isType():
 | |
|                 if t.nullable():
 | |
|                     t = t.inner
 | |
|                 if t.isCallback():
 | |
|                     return t.callback.identifier
 | |
|                 return t.identifier
 | |
|             assert t.isInterface() or t.isDictionary() or t.isEnum() or t.isNamespace()
 | |
|             return t.identifier
 | |
| 
 | |
|         def removeWrapperAndNullableTypes(types):
 | |
|             normalized = []
 | |
|             for t in types:
 | |
|                 while (t.isType() and t.nullable()) or isinstance(t, IDLWrapperType):
 | |
|                     t = t.inner
 | |
|                 if isImportable(t):
 | |
|                     normalized += [t]
 | |
|             return normalized
 | |
| 
 | |
|         types = []
 | |
|         for d in descriptors:
 | |
|             if not d.interface.isCallback():
 | |
|                 types += [d.interface]
 | |
| 
 | |
|             if d.interface.isIteratorInterface():
 | |
|                 types += [d.interface.iterableInterface]
 | |
| 
 | |
|             members = d.interface.members + d.interface.namedConstructors
 | |
|             constructor = d.interface.ctor()
 | |
|             if constructor:
 | |
|                 members += [constructor]
 | |
| 
 | |
|             if d.proxy:
 | |
|                 members += [o for o in d.operations.values() if o]
 | |
| 
 | |
|             for m in members:
 | |
|                 if m.isMethod():
 | |
|                     types += relatedTypesForSignatures(m)
 | |
|                 elif m.isAttr():
 | |
|                     types += componentTypes(m.type)
 | |
| 
 | |
|         # Import the type names used in the callbacks that are being defined.
 | |
|         for c in callbacks:
 | |
|             types += relatedTypesForSignatures(c)
 | |
| 
 | |
|         # Import the type names used in the dictionaries that are being defined.
 | |
|         for d in dictionaries:
 | |
|             types += componentTypes(d)
 | |
| 
 | |
|         # Normalize the types we've collected and remove any ones which can't be imported.
 | |
|         types = removeWrapperAndNullableTypes(types)
 | |
| 
 | |
|         descriptorProvider = config.getDescriptorProvider()
 | |
|         extras = []
 | |
|         for t in types:
 | |
|             # Importing these types in the same module that defines them is an error.
 | |
|             if t in dictionaries or t in enums:
 | |
|                 continue
 | |
|             if t.isInterface() or t.isNamespace():
 | |
|                 name = getIdentifier(t).name
 | |
|                 descriptor = descriptorProvider.getDescriptor(name)
 | |
|                 if name != 'GlobalScope':
 | |
|                     extras += [descriptor.path]
 | |
|                 parentName = descriptor.getParentName()
 | |
|                 if parentName:
 | |
|                     descriptor = descriptorProvider.getDescriptor(parentName)
 | |
|                     extras += [descriptor.path, descriptor.bindingPath]
 | |
|             elif t.isType() and t.isMozMap():
 | |
|                 extras += ['dom::bindings::mozmap::MozMap']
 | |
|             else:
 | |
|                 if t.isEnum():
 | |
|                     extras += [getModuleFromObject(t) + '::' + getIdentifier(t).name + 'Values']
 | |
|                 extras += [getModuleFromObject(t) + '::' + getIdentifier(t).name]
 | |
| 
 | |
|         statements = []
 | |
|         if len(ignored_warnings) > 0:
 | |
|             statements.append('#![allow(%s)]' % ','.join(ignored_warnings))
 | |
|         statements.extend('use %s;' % i for i in sorted(set(imports + extras)))
 | |
| 
 | |
|         CGWrapper.__init__(self, child,
 | |
|                            pre='\n'.join(statements) + '\n\n')
 | |
| 
 | |
| 
 | |
| class CGIfWrapper(CGWrapper):
 | |
|     def __init__(self, condition, child):
 | |
|         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" % 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 DOMClassTypeId(desc):
 | |
|     protochain = desc.prototypeChain
 | |
|     inner = ""
 | |
|     if desc.hasDescendants():
 | |
|         if desc.interface.getExtendedAttribute("Abstract"):
 | |
|             return "::dom::bindings::codegen::InheritTypes::TopTypeId { abstract_: () }"
 | |
|         name = desc.interface.identifier.name
 | |
|         inner = "(::dom::bindings::codegen::InheritTypes::%sTypeId::%s)" % (name, name)
 | |
|     elif len(protochain) == 1:
 | |
|         return "::dom::bindings::codegen::InheritTypes::TopTypeId { alone: () }"
 | |
|     reversed_protochain = list(reversed(protochain))
 | |
|     for (child, parent) in zip(reversed_protochain, reversed_protochain[1:]):
 | |
|         inner = "(::dom::bindings::codegen::InheritTypes::%sTypeId::%s%s)" % (parent, child, inner)
 | |
|     return "::dom::bindings::codegen::InheritTypes::TopTypeId { %s: %s }" % (protochain[0].lower(), inner)
 | |
| 
 | |
| 
 | |
| def DOMClass(descriptor):
 | |
|     protoList = ['PrototypeList::ID::' + proto for proto in descriptor.prototypeChain]
 | |
|     # Pad out the list to the right length with ID::Last so we
 | |
|     # guarantee that all the lists are the same length.  ID::Last
 | |
|     # is never the ID of any prototype, so it's safe to use as
 | |
|     # padding.
 | |
|     protoList.extend(['PrototypeList::ID::Last'] * (descriptor.config.maxProtoChainLength - len(protoList)))
 | |
|     prototypeChainString = ', '.join(protoList)
 | |
|     heapSizeOf = 'heap_size_of_raw_self_and_children::<%s>' % descriptor.concreteType
 | |
|     if descriptor.isGlobal():
 | |
|         globals_ = camel_to_upper_snake(descriptor.name)
 | |
|     else:
 | |
|         globals_ = 'EMPTY'
 | |
|     return """\
 | |
| DOMClass {
 | |
|     interface_chain: [ %s ],
 | |
|     type_id: %s,
 | |
|     heap_size_of: %s as unsafe fn(_) -> _,
 | |
|     global: InterfaceObjectMap::%s,
 | |
| }""" % (prototypeChainString, DOMClassTypeId(descriptor), heapSizeOf, globals_)
 | |
| 
 | |
| 
 | |
| class CGDOMJSClass(CGThing):
 | |
|     """
 | |
|     Generate a DOMJSClass for a given descriptor
 | |
|     """
 | |
|     def __init__(self, descriptor):
 | |
|         CGThing.__init__(self)
 | |
|         self.descriptor = descriptor
 | |
| 
 | |
|     def define(self):
 | |
|         args = {
 | |
|             "domClass": DOMClass(self.descriptor),
 | |
|             "enumerateHook": "None",
 | |
|             "finalizeHook": FINALIZE_HOOK_NAME,
 | |
|             "flags": "0",
 | |
|             "name": str_to_const_array(self.descriptor.interface.identifier.name),
 | |
|             "resolveHook": "None",
 | |
|             "slots": "1",
 | |
|             "traceHook": TRACE_HOOK_NAME,
 | |
|         }
 | |
|         if self.descriptor.isGlobal():
 | |
|             assert not self.descriptor.weakReferenceable
 | |
|             args["enumerateHook"] = "Some(enumerate_global)"
 | |
|             args["flags"] = "JSCLASS_IS_GLOBAL | JSCLASS_DOM_GLOBAL"
 | |
|             args["slots"] = "JSCLASS_GLOBAL_SLOT_COUNT + 1"
 | |
|             args["resolveHook"] = "Some(resolve_global)"
 | |
|             args["traceHook"] = "js::jsapi::JS_GlobalObjectTraceHook"
 | |
|         elif self.descriptor.weakReferenceable:
 | |
|             args["slots"] = "2"
 | |
|         return """\
 | |
| static CLASS_OPS: js::jsapi::JSClassOps = js::jsapi::JSClassOps {
 | |
|     addProperty: None,
 | |
|     delProperty: None,
 | |
|     getProperty: None,
 | |
|     setProperty: None,
 | |
|     enumerate: %(enumerateHook)s,
 | |
|     resolve: %(resolveHook)s,
 | |
|     mayResolve: None,
 | |
|     finalize: Some(%(finalizeHook)s),
 | |
|     call: None,
 | |
|     hasInstance: None,
 | |
|     construct: None,
 | |
|     trace: Some(%(traceHook)s),
 | |
| };
 | |
| 
 | |
| static Class: DOMJSClass = DOMJSClass {
 | |
|     base: js::jsapi::JSClass {
 | |
|         name: %(name)s as *const u8 as *const libc::c_char,
 | |
|         flags: JSCLASS_IS_DOMJSCLASS | %(flags)s |
 | |
|                (((%(slots)s) & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT)
 | |
|                /* JSCLASS_HAS_RESERVED_SLOTS(%(slots)s) */,
 | |
|         cOps: &CLASS_OPS,
 | |
|         reserved: [0 as *mut _; 3],
 | |
|     },
 | |
|     dom_class: %(domClass)s
 | |
| };""" % args
 | |
| 
 | |
| 
 | |
| def str_to_const_array(s):
 | |
|     return "b\"%s\\0\"" % s
 | |
| 
 | |
| 
 | |
| class CGPrototypeJSClass(CGThing):
 | |
|     def __init__(self, descriptor):
 | |
|         CGThing.__init__(self)
 | |
|         self.descriptor = descriptor
 | |
| 
 | |
|     def define(self):
 | |
|         name = str_to_const_array(self.descriptor.interface.identifier.name + "Prototype")
 | |
|         slotCount = 0
 | |
|         if self.descriptor.hasUnforgeableMembers:
 | |
|             slotCount += 1
 | |
|         return """\
 | |
| static PrototypeClass: JSClass = JSClass {
 | |
|     name: %(name)s as *const u8 as *const libc::c_char,
 | |
|     flags:
 | |
|         // JSCLASS_HAS_RESERVED_SLOTS(%(slotCount)s)
 | |
|         (%(slotCount)s & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT,
 | |
|     cOps: 0 as *const _,
 | |
|     reserved: [0 as *mut os::raw::c_void; 3]
 | |
| };
 | |
| """ % {'name': name, 'slotCount': slotCount}
 | |
| 
 | |
| 
 | |
| class CGInterfaceObjectJSClass(CGThing):
 | |
|     def __init__(self, descriptor):
 | |
|         assert descriptor.interface.hasInterfaceObject() and not descriptor.interface.isCallback()
 | |
|         CGThing.__init__(self)
 | |
|         self.descriptor = descriptor
 | |
| 
 | |
|     def define(self):
 | |
|         if self.descriptor.interface.isNamespace():
 | |
|             classString = self.descriptor.interface.getExtendedAttribute("ClassString")
 | |
|             if classString:
 | |
|                 classString = classString[0]
 | |
|             else:
 | |
|                 classString = "Object"
 | |
|             return """\
 | |
| static NAMESPACE_OBJECT_CLASS: NamespaceObjectClass = unsafe {
 | |
|     NamespaceObjectClass::new(%s)
 | |
| };
 | |
| """ % str_to_const_array(classString)
 | |
|         if self.descriptor.interface.ctor():
 | |
|             constructorBehavior = "InterfaceConstructorBehavior::call(%s)" % CONSTRUCT_HOOK_NAME
 | |
|         else:
 | |
|             constructorBehavior = "InterfaceConstructorBehavior::throw()"
 | |
|         name = self.descriptor.interface.identifier.name
 | |
|         args = {
 | |
|             "constructorBehavior": constructorBehavior,
 | |
|             "id": name,
 | |
|             "representation": 'b"function %s() {\\n    [native code]\\n}"' % name,
 | |
|             "depth": self.descriptor.prototypeDepth
 | |
|         }
 | |
|         return """\
 | |
| static INTERFACE_OBJECT_CLASS: NonCallbackInterfaceObjectClass =
 | |
|     NonCallbackInterfaceObjectClass::new(
 | |
|         &%(constructorBehavior)s,
 | |
|         %(representation)s,
 | |
|         PrototypeList::ID::%(id)s,
 | |
|         %(depth)s);
 | |
| """ % args
 | |
| 
 | |
| 
 | |
| 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)
 | |
|         # Make a copy of the kids into a list, because if someone passes in a
 | |
|         # generator we won't be able to both declare and define ourselves, or
 | |
|         # define ourselves more than once!
 | |
|         self.children = list(children)
 | |
|         self.joiner = joiner
 | |
| 
 | |
|     def append(self, child):
 | |
|         self.children.append(child)
 | |
| 
 | |
|     def prepend(self, child):
 | |
|         self.children.insert(0, child)
 | |
| 
 | |
|     def join(self, iterable):
 | |
|         return self.joiner.join(s for s in iterable if len(s) > 0)
 | |
| 
 | |
|     def define(self):
 | |
|         return self.join(child.define() for child in self.children if child is not None)
 | |
| 
 | |
|     def __len__(self):
 | |
|         return len(self.children)
 | |
| 
 | |
| 
 | |
| class CGIfElseWrapper(CGList):
 | |
|     def __init__(self, condition, ifTrue, ifFalse):
 | |
|         kids = [CGIfWrapper(condition, ifTrue),
 | |
|                 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):
 | |
|         CGGeneric.__init__(self, "%s::new(cx, ${val}.get().to_object())" % name)
 | |
| 
 | |
| 
 | |
| def getAllTypes(descriptors, dictionaries, callbacks, typedefs):
 | |
|     """
 | |
|     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)
 | |
|     for typedef in typedefs:
 | |
|         yield (typedef.innerType, None, None)
 | |
| 
 | |
| 
 | |
| def UnionTypes(descriptors, dictionaries, callbacks, typedefs, config):
 | |
|     """
 | |
|     Returns a CGList containing CGUnionStructs for every union.
 | |
|     """
 | |
| 
 | |
|     imports = [
 | |
|         'dom',
 | |
|         'dom::bindings::codegen::PrototypeList',
 | |
|         'dom::bindings::conversions::ConversionResult',
 | |
|         'dom::bindings::conversions::FromJSValConvertible',
 | |
|         'dom::bindings::conversions::ToJSValConvertible',
 | |
|         'dom::bindings::conversions::ConversionBehavior',
 | |
|         'dom::bindings::conversions::StringificationBehavior',
 | |
|         'dom::bindings::conversions::root_from_handlevalue',
 | |
|         'dom::bindings::error::throw_not_in_union',
 | |
|         'dom::bindings::js::Root',
 | |
|         'dom::bindings::mozmap::MozMap',
 | |
|         'dom::bindings::str::ByteString',
 | |
|         'dom::bindings::str::DOMString',
 | |
|         'dom::bindings::str::USVString',
 | |
|         'dom::types::*',
 | |
|         'js::error::throw_type_error',
 | |
|         'js::jsapi::HandleValue',
 | |
|         'js::jsapi::JSContext',
 | |
|         'js::jsapi::JSObject',
 | |
|         'js::jsapi::MutableHandleValue',
 | |
|         'js::jsval::JSVal',
 | |
|     ]
 | |
| 
 | |
|     # 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, typedefs):
 | |
|         if dictionary:
 | |
|             imports.append("%s::%s" % (CGDictionary.makeModuleName(dictionary),
 | |
|                                        CGDictionary.makeDictionaryName(dictionary)))
 | |
|         t = t.unroll()
 | |
|         if not t.isUnion():
 | |
|             continue
 | |
|         name = str(t)
 | |
|         if name not in unionStructs:
 | |
|             provider = descriptor or config.getDescriptorProvider()
 | |
|             unionStructs[name] = CGList([
 | |
|                 CGUnionStruct(t, provider),
 | |
|                 CGUnionConversionStruct(t, provider)
 | |
|             ])
 | |
| 
 | |
|     # Sort unionStructs by key, retrieve value
 | |
|     unionStructs = (i[1] for i in sorted(unionStructs.items(), key=operator.itemgetter(0)))
 | |
| 
 | |
|     return CGImports(CGList(unionStructs, "\n\n"),
 | |
|                      descriptors=[],
 | |
|                      callbacks=[],
 | |
|                      dictionaries=[],
 | |
|                      enums=[],
 | |
|                      imports=imports,
 | |
|                      config=config,
 | |
|                      ignored_warnings=[])
 | |
| 
 | |
| 
 | |
| 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.
 | |
| 
 | |
|     docs is None or documentation for the method in a string.
 | |
| 
 | |
|     unsafe is used to add the decorator 'unsafe' to a function, giving as a result
 | |
|     an 'unsafe fn()' declaration.
 | |
|     """
 | |
|     def __init__(self, descriptor, name, returnType, args, inline=False,
 | |
|                  alwaysInline=False, extern=False, unsafe=False, pub=False,
 | |
|                  templateArgs=None, docs=None, doesNotPanic=False):
 | |
|         CGThing.__init__(self)
 | |
|         self.descriptor = descriptor
 | |
|         self.name = name
 | |
|         self.returnType = returnType
 | |
|         self.args = args
 | |
|         self.alwaysInline = alwaysInline
 | |
|         self.extern = extern
 | |
|         self.unsafe = extern or unsafe
 | |
|         self.templateArgs = templateArgs
 | |
|         self.pub = pub
 | |
|         self.docs = docs
 | |
|         self.catchPanic = self.extern and not doesNotPanic
 | |
| 
 | |
|     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 _docs(self):
 | |
|         if self.docs is None:
 | |
|             return ''
 | |
| 
 | |
|         lines = self.docs.splitlines()
 | |
|         return ''.join('/// %s\n' % line for line in lines)
 | |
| 
 | |
|     def _decorators(self):
 | |
|         decorators = []
 | |
|         if self.alwaysInline:
 | |
|             decorators.append('#[inline]')
 | |
| 
 | |
|         if self.pub:
 | |
|             decorators.append('pub')
 | |
| 
 | |
|         if self.unsafe:
 | |
|             decorators.append('unsafe')
 | |
| 
 | |
|         if self.extern:
 | |
|             decorators.append('extern')
 | |
| 
 | |
|         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.catchPanic:
 | |
|             body = CGWrapper(CGIndenter(body),
 | |
|                              pre="return wrap_panic(panic::AssertUnwindSafe(|| {\n",
 | |
|                              post=("""\n}), %s);""" % ("()" if self.returnType == "void" else "false")))
 | |
| 
 | |
|         return CGWrapper(CGIndenter(body),
 | |
|                          pre=self.definition_prologue(),
 | |
|                          post=self.definition_epilogue()).define()
 | |
| 
 | |
|     def definition_prologue(self):
 | |
|         return "%s%sfn %s%s(%s)%s {\n" % (self._docs(), self._decorators(),
 | |
|                                           self.name, self._template(),
 | |
|                                           self._argstring(), self._returnType())
 | |
| 
 | |
|     def definition_epilogue(self):
 | |
|         return "\n}\n"
 | |
| 
 | |
|     def definition_body(self):
 | |
|         raise NotImplementedError  # Override me!
 | |
| 
 | |
| 
 | |
| class CGConstructorEnabled(CGAbstractMethod):
 | |
|     """
 | |
|     A method for testing whether we should be exposing this interface object.
 | |
|     This can perform various tests depending on what conditions are specified
 | |
|     on the interface.
 | |
|     """
 | |
|     def __init__(self, descriptor):
 | |
|         CGAbstractMethod.__init__(self, descriptor,
 | |
|                                   'ConstructorEnabled', 'bool',
 | |
|                                   [Argument("*mut JSContext", "aCx"),
 | |
|                                    Argument("HandleObject", "aObj")],
 | |
|                                   unsafe=True)
 | |
| 
 | |
|     def definition_body(self):
 | |
|         conditions = []
 | |
|         iface = self.descriptor.interface
 | |
| 
 | |
|         bits = " | ".join(sorted(
 | |
|             "InterfaceObjectMap::" + camel_to_upper_snake(i) for i in iface.exposureSet
 | |
|         ))
 | |
|         conditions.append("is_exposed_in(aObj, %s)" % bits)
 | |
| 
 | |
|         pref = iface.getExtendedAttribute("Pref")
 | |
|         if pref:
 | |
|             assert isinstance(pref, list) and len(pref) == 1
 | |
|             conditions.append('PREFS.get("%s").as_boolean().unwrap_or(false)' % pref[0])
 | |
| 
 | |
|         func = iface.getExtendedAttribute("Func")
 | |
|         if func:
 | |
|             assert isinstance(func, list) and len(func) == 1
 | |
|             conditions.append("%s(aCx, aObj)" % func[0])
 | |
| 
 | |
|         return CGList((CGGeneric(cond) for cond in conditions), " &&\n")
 | |
| 
 | |
| 
 | |
| def CreateBindingJSObject(descriptor, parent=None):
 | |
|     assert not descriptor.isGlobal()
 | |
|     create = "let raw = Box::into_raw(object);\nlet _rt = RootedTraceable::new(&*raw);\n"
 | |
|     if descriptor.proxy:
 | |
|         create += """
 | |
| let handler = RegisterBindings::PROXY_HANDLERS[PrototypeList::Proxies::%s as usize];
 | |
| rooted!(in(cx) let private = PrivateValue(raw as *const libc::c_void));
 | |
| let obj = NewProxyObject(cx, handler,
 | |
|                          private.handle(),
 | |
|                          proto.get(), %s.get(),
 | |
|                          ptr::null_mut(), ptr::null_mut());
 | |
| assert!(!obj.is_null());
 | |
| rooted!(in(cx) let obj = obj);\
 | |
| """ % (descriptor.name, parent)
 | |
|     else:
 | |
|         create += ("rooted!(in(cx) let obj = JS_NewObjectWithGivenProto(\n"
 | |
|                    "    cx, &Class.base as *const JSClass, proto.handle()));\n"
 | |
|                    "assert!(!obj.is_null());\n"
 | |
|                    "\n"
 | |
|                    "JS_SetReservedSlot(obj.get(), DOM_OBJECT_SLOT,\n"
 | |
|                    "                   PrivateValue(raw as *const libc::c_void));")
 | |
|     if descriptor.weakReferenceable:
 | |
|         create += """
 | |
| JS_SetReservedSlot(obj.get(), DOM_WEAK_SLOT, PrivateValue(ptr::null()));"""
 | |
|     return create
 | |
| 
 | |
| 
 | |
| def InitUnforgeablePropertiesOnHolder(descriptor, properties):
 | |
|     """
 | |
|     Define the unforgeable properties on the unforgeable holder for
 | |
|     the interface represented by descriptor.
 | |
| 
 | |
|     properties is a PropertyArrays instance.
 | |
|     """
 | |
|     unforgeables = []
 | |
| 
 | |
|     defineUnforgeableAttrs = "define_guarded_properties(cx, unforgeable_holder.handle(), %s);"
 | |
|     defineUnforgeableMethods = "define_guarded_methods(cx, unforgeable_holder.handle(), %s);"
 | |
| 
 | |
|     unforgeableMembers = [
 | |
|         (defineUnforgeableAttrs, properties.unforgeable_attrs),
 | |
|         (defineUnforgeableMethods, properties.unforgeable_methods),
 | |
|     ]
 | |
|     for template, array in unforgeableMembers:
 | |
|         if array.length() > 0:
 | |
|             unforgeables.append(CGGeneric(template % array.variableName()))
 | |
|     return CGList(unforgeables, "\n")
 | |
| 
 | |
| 
 | |
| def CopyUnforgeablePropertiesToInstance(descriptor):
 | |
|     """
 | |
|     Copy the unforgeable properties from the unforgeable holder for
 | |
|     this interface to the instance object we have.
 | |
|     """
 | |
|     if not descriptor.hasUnforgeableMembers:
 | |
|         return ""
 | |
|     copyCode = ""
 | |
| 
 | |
|     # For proxies, we want to define on the expando object, not directly on the
 | |
|     # reflector, so we can make sure we don't get confused by named getters.
 | |
|     if descriptor.proxy:
 | |
|         copyCode += """\
 | |
| rooted!(in(cx) let mut expando = ptr::null_mut());
 | |
| ensure_expando_object(cx, obj.handle(), expando.handle_mut());
 | |
| """
 | |
|         obj = "expando"
 | |
|     else:
 | |
|         obj = "obj"
 | |
| 
 | |
|     # We can't do the fast copy for globals, because we can't allocate the
 | |
|     # unforgeable holder for those with the right JSClass. Luckily, there
 | |
|     # aren't too many globals being created.
 | |
|     if descriptor.isGlobal():
 | |
|         copyFunc = "JS_CopyPropertiesFrom"
 | |
|     else:
 | |
|         copyFunc = "JS_InitializePropertiesFromCompatibleNativeObject"
 | |
|     copyCode += """\
 | |
| rooted!(in(cx) let mut unforgeable_holder = ptr::null_mut());
 | |
| unforgeable_holder.handle_mut().set(
 | |
|     JS_GetReservedSlot(proto.get(), DOM_PROTO_UNFORGEABLE_HOLDER_SLOT).to_object());
 | |
| assert!(%(copyFunc)s(cx, %(obj)s.handle(), unforgeable_holder.handle()));
 | |
| """ % {'copyFunc': copyFunc, 'obj': obj}
 | |
| 
 | |
|     return copyCode
 | |
| 
 | |
| 
 | |
| class CGWrapMethod(CGAbstractMethod):
 | |
|     """
 | |
|     Class that generates the FooBinding::Wrap function for non-callback
 | |
|     interfaces.
 | |
|     """
 | |
|     def __init__(self, descriptor):
 | |
|         assert not descriptor.interface.isCallback()
 | |
|         assert not descriptor.isGlobal()
 | |
|         args = [Argument('*mut JSContext', 'cx'),
 | |
|                 Argument('&GlobalScope', 'scope'),
 | |
|                 Argument("Box<%s>" % descriptor.concreteType, 'object')]
 | |
|         retval = 'Root<%s>' % descriptor.concreteType
 | |
|         CGAbstractMethod.__init__(self, descriptor, 'Wrap', retval, args,
 | |
|                                   pub=True, unsafe=True)
 | |
| 
 | |
|     def definition_body(self):
 | |
|         unforgeable = CopyUnforgeablePropertiesToInstance(self.descriptor)
 | |
|         create = CreateBindingJSObject(self.descriptor, "scope")
 | |
|         return CGGeneric("""\
 | |
| let scope = scope.reflector().get_jsobject();
 | |
| assert!(!scope.get().is_null());
 | |
| assert!(((*get_object_class(scope.get())).flags & JSCLASS_IS_GLOBAL) != 0);
 | |
| 
 | |
| rooted!(in(cx) let mut proto = ptr::null_mut());
 | |
| let _ac = JSAutoCompartment::new(cx, scope.get());
 | |
| GetProtoObject(cx, scope, proto.handle_mut());
 | |
| assert!(!proto.is_null());
 | |
| 
 | |
| %(createObject)s
 | |
| 
 | |
| %(copyUnforgeable)s
 | |
| (*raw).init_reflector(obj.get());
 | |
| 
 | |
| Root::from_ref(&*raw)""" % {'copyUnforgeable': unforgeable, 'createObject': create})
 | |
| 
 | |
| 
 | |
| class CGWrapGlobalMethod(CGAbstractMethod):
 | |
|     """
 | |
|     Class that generates the FooBinding::Wrap function for global interfaces.
 | |
|     """
 | |
|     def __init__(self, descriptor, properties):
 | |
|         assert not descriptor.interface.isCallback()
 | |
|         assert descriptor.isGlobal()
 | |
|         args = [Argument('*mut JSContext', 'cx'),
 | |
|                 Argument("Box<%s>" % descriptor.concreteType, 'object')]
 | |
|         retval = 'Root<%s>' % descriptor.concreteType
 | |
|         CGAbstractMethod.__init__(self, descriptor, 'Wrap', retval, args,
 | |
|                                   pub=True, unsafe=True)
 | |
|         self.properties = properties
 | |
| 
 | |
|     def definition_body(self):
 | |
|         values = {
 | |
|             "unforgeable": CopyUnforgeablePropertiesToInstance(self.descriptor)
 | |
|         }
 | |
| 
 | |
|         pairs = [
 | |
|             ("define_guarded_properties", self.properties.attrs),
 | |
|             ("define_guarded_methods", self.properties.methods),
 | |
|             ("define_guarded_constants", self.properties.consts)
 | |
|         ]
 | |
|         members = ["%s(cx, obj.handle(), %s);" % (function, array.variableName())
 | |
|                    for (function, array) in pairs if array.length() > 0]
 | |
|         values["members"] = "\n".join(members)
 | |
| 
 | |
|         return CGGeneric("""\
 | |
| let raw = Box::into_raw(object);
 | |
| let _rt = RootedTraceable::new(&*raw);
 | |
| 
 | |
| rooted!(in(cx) let mut obj = ptr::null_mut());
 | |
| create_global_object(
 | |
|     cx,
 | |
|     &Class.base,
 | |
|     raw as *const libc::c_void,
 | |
|     _trace,
 | |
|     obj.handle_mut());
 | |
| assert!(!obj.is_null());
 | |
| 
 | |
| (*raw).init_reflector(obj.get());
 | |
| 
 | |
| let _ac = JSAutoCompartment::new(cx, obj.get());
 | |
| rooted!(in(cx) let mut proto = ptr::null_mut());
 | |
| GetProtoObject(cx, obj.handle(), proto.handle_mut());
 | |
| assert!(JS_SplicePrototype(cx, obj.handle(), proto.handle()));
 | |
| let mut immutable = false;
 | |
| assert!(JS_SetImmutablePrototype(cx, obj.handle(), &mut immutable));
 | |
| assert!(immutable);
 | |
| 
 | |
| %(members)s
 | |
| 
 | |
| %(unforgeable)s
 | |
| 
 | |
| Root::from_ref(&*raw)\
 | |
| """ % values)
 | |
| 
 | |
| 
 | |
| 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):
 | |
|         interface = self.descriptor.interface
 | |
|         name = self.descriptor.concreteType
 | |
|         if (interface.getUserData("hasConcreteDescendant", False) or
 | |
|                 interface.getUserData("hasProxyDescendant", False)):
 | |
|             depth = self.descriptor.prototypeDepth
 | |
|             check = "class.interface_chain[%s] == PrototypeList::ID::%s" % (depth, name)
 | |
|         elif self.descriptor.proxy:
 | |
|             check = "class as *const _ == &Class as *const _"
 | |
|         else:
 | |
|             check = "class as *const _ == &Class.dom_class as *const _"
 | |
|         return """\
 | |
| impl IDLInterface for %(name)s {
 | |
|     #[inline]
 | |
|     fn derives(class: &'static DOMClass) -> bool {
 | |
|         %(check)s
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl PartialEq for %(name)s {
 | |
|     fn eq(&self, other: &%(name)s) -> bool {
 | |
|         self as *const %(name)s == &*other
 | |
|     }
 | |
| }
 | |
| """ % {'check': check, 'name': name}
 | |
| 
 | |
| 
 | |
| class CGAbstractExternMethod(CGAbstractMethod):
 | |
|     """
 | |
|     Abstract base class for codegen of implementation-only (no
 | |
|     declaration) static methods.
 | |
|     """
 | |
|     def __init__(self, descriptor, name, returnType, args, doesNotPanic=False):
 | |
|         CGAbstractMethod.__init__(self, descriptor, name, returnType, args,
 | |
|                                   inline=False, extern=True, doesNotPanic=doesNotPanic)
 | |
| 
 | |
| 
 | |
| class PropertyArrays():
 | |
|     def __init__(self, descriptor):
 | |
|         self.static_methods = MethodDefiner(descriptor, "StaticMethods",
 | |
|                                             static=True, unforgeable=False)
 | |
|         self.static_attrs = AttrDefiner(descriptor, "StaticAttributes",
 | |
|                                         static=True, unforgeable=False)
 | |
|         self.methods = MethodDefiner(descriptor, "Methods", static=False, unforgeable=False)
 | |
|         self.unforgeable_methods = MethodDefiner(descriptor, "UnforgeableMethods",
 | |
|                                                  static=False, unforgeable=True)
 | |
|         self.attrs = AttrDefiner(descriptor, "Attributes", static=False, unforgeable=False)
 | |
|         self.unforgeable_attrs = AttrDefiner(descriptor, "UnforgeableAttributes",
 | |
|                                              static=False, unforgeable=True)
 | |
|         self.consts = ConstDefiner(descriptor, "Constants")
 | |
|         pass
 | |
| 
 | |
|     @staticmethod
 | |
|     def arrayNames():
 | |
|         return [
 | |
|             "static_methods",
 | |
|             "static_attrs",
 | |
|             "methods",
 | |
|             "unforgeable_methods",
 | |
|             "attrs",
 | |
|             "unforgeable_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 CGCreateInterfaceObjectsMethod(CGAbstractMethod):
 | |
|     """
 | |
|     Generate the CreateInterfaceObjects method for an interface descriptor.
 | |
| 
 | |
|     properties should be a PropertyArrays instance.
 | |
|     """
 | |
|     def __init__(self, descriptor, properties, haveUnscopables):
 | |
|         args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'global'),
 | |
|                 Argument('*mut ProtoOrIfaceArray', 'cache')]
 | |
|         CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'void', args,
 | |
|                                   unsafe=True)
 | |
|         self.properties = properties
 | |
|         self.haveUnscopables = haveUnscopables
 | |
| 
 | |
|     def definition_body(self):
 | |
|         name = self.descriptor.interface.identifier.name
 | |
|         if self.descriptor.interface.isNamespace():
 | |
|             if self.descriptor.interface.getExtendedAttribute("ProtoObjectHack"):
 | |
|                 proto = "JS_GetObjectPrototype(cx, global)"
 | |
|             else:
 | |
|                 proto = "JS_NewPlainObject(cx)"
 | |
|             if self.properties.static_methods.length():
 | |
|                 methods = self.properties.static_methods.variableName()
 | |
|             else:
 | |
|                 methods = "&[]"
 | |
|             return CGGeneric("""\
 | |
| rooted!(in(cx) let proto = %(proto)s);
 | |
| assert!(!proto.is_null());
 | |
| rooted!(in(cx) let mut namespace = ptr::null_mut());
 | |
| create_namespace_object(cx, global, proto.handle(), &NAMESPACE_OBJECT_CLASS,
 | |
|                         %(methods)s, %(name)s, namespace.handle_mut());
 | |
| assert!(!namespace.is_null());
 | |
| assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null());
 | |
| (*cache)[PrototypeList::Constructor::%(id)s as usize] = namespace.get();
 | |
| <*mut JSObject>::post_barrier((*cache).as_mut_ptr().offset(PrototypeList::Constructor::%(id)s as isize),
 | |
|                               ptr::null_mut(),
 | |
|                               namespace.get());
 | |
| """ % {"id": MakeNativeName(name), "methods": methods, "name": str_to_const_array(name), "proto": proto})
 | |
|         if self.descriptor.interface.isCallback():
 | |
|             assert not self.descriptor.interface.ctor() and self.descriptor.interface.hasConstants()
 | |
|             return CGGeneric("""\
 | |
| rooted!(in(cx) let mut interface = ptr::null_mut());
 | |
| create_callback_interface_object(cx, global, sConstants, %(name)s, interface.handle_mut());
 | |
| assert!(!interface.is_null());
 | |
| assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null());
 | |
| (*cache)[PrototypeList::Constructor::%(id)s as usize] = interface.get();
 | |
| <*mut JSObject>::post_barrier((*cache).as_mut_ptr().offset(PrototypeList::Constructor::%(id)s as isize),
 | |
|                               ptr::null_mut(),
 | |
|                               interface.get());
 | |
| """ % {"id": name, "name": str_to_const_array(name)})
 | |
| 
 | |
|         parentName = self.descriptor.getParentName()
 | |
|         if not parentName:
 | |
|             if self.descriptor.interface.getExtendedAttribute("ExceptionClass"):
 | |
|                 getPrototypeProto = "prototype_proto.set(JS_GetErrorPrototype(cx))"
 | |
|             elif self.descriptor.interface.isIteratorInterface():
 | |
|                 getPrototypeProto = "prototype_proto.set(JS_GetIteratorPrototype(cx))"
 | |
|             else:
 | |
|                 getPrototypeProto = "prototype_proto.set(JS_GetObjectPrototype(cx, global))"
 | |
|         else:
 | |
|             getPrototypeProto = ("%s::GetProtoObject(cx, global, prototype_proto.handle_mut())" %
 | |
|                                  toBindingNamespace(parentName))
 | |
| 
 | |
|         code = [CGGeneric("""\
 | |
| rooted!(in(cx) let mut prototype_proto = ptr::null_mut());
 | |
| %s;
 | |
| assert!(!prototype_proto.is_null());""" % getPrototypeProto)]
 | |
| 
 | |
|         properties = {
 | |
|             "id": name,
 | |
|             "unscopables": "unscopable_names" if self.haveUnscopables else "&[]"
 | |
|         }
 | |
|         for arrayName in self.properties.arrayNames():
 | |
|             array = getattr(self.properties, arrayName)
 | |
|             if array.length():
 | |
|                 properties[arrayName] = array.variableName()
 | |
|             else:
 | |
|                 properties[arrayName] = "&[]"
 | |
| 
 | |
|         if self.descriptor.isGlobal():
 | |
|             assert not self.haveUnscopables
 | |
|             proto_properties = {
 | |
|                 "attrs": "&[]",
 | |
|                 "consts": "&[]",
 | |
|                 "id": name,
 | |
|                 "methods": "&[]",
 | |
|                 "unscopables": "&[]",
 | |
|             }
 | |
|         else:
 | |
|             proto_properties = properties
 | |
| 
 | |
|         code.append(CGGeneric("""
 | |
| rooted!(in(cx) let mut prototype = ptr::null_mut());
 | |
| create_interface_prototype_object(cx,
 | |
|                                   prototype_proto.handle(),
 | |
|                                   &PrototypeClass,
 | |
|                                   %(methods)s,
 | |
|                                   %(attrs)s,
 | |
|                                   %(consts)s,
 | |
|                                   %(unscopables)s,
 | |
|                                   prototype.handle_mut());
 | |
| assert!(!prototype.is_null());
 | |
| assert!((*cache)[PrototypeList::ID::%(id)s as usize].is_null());
 | |
| (*cache)[PrototypeList::ID::%(id)s as usize] = prototype.get();
 | |
| <*mut JSObject>::post_barrier((*cache).as_mut_ptr().offset(PrototypeList::ID::%(id)s as isize),
 | |
|                               ptr::null_mut(),
 | |
|                               prototype.get());
 | |
| """ % proto_properties))
 | |
| 
 | |
|         if self.descriptor.interface.hasInterfaceObject():
 | |
|             properties["name"] = str_to_const_array(name)
 | |
|             if self.descriptor.interface.ctor():
 | |
|                 properties["length"] = methodLength(self.descriptor.interface.ctor())
 | |
|             else:
 | |
|                 properties["length"] = 0
 | |
|             parentName = self.descriptor.getParentName()
 | |
|             if parentName:
 | |
|                 parentName = toBindingNamespace(parentName)
 | |
|                 code.append(CGGeneric("""
 | |
| rooted!(in(cx) let mut interface_proto = ptr::null_mut());
 | |
| %s::GetConstructorObject(cx, global, interface_proto.handle_mut());""" % parentName))
 | |
|             else:
 | |
|                 code.append(CGGeneric("""
 | |
| rooted!(in(cx) let interface_proto = JS_GetFunctionPrototype(cx, global));"""))
 | |
|             code.append(CGGeneric("""\
 | |
| assert!(!interface_proto.is_null());
 | |
| 
 | |
| rooted!(in(cx) let mut interface = ptr::null_mut());
 | |
| create_noncallback_interface_object(cx,
 | |
|                                     global,
 | |
|                                     interface_proto.handle(),
 | |
|                                     &INTERFACE_OBJECT_CLASS,
 | |
|                                     %(static_methods)s,
 | |
|                                     %(static_attrs)s,
 | |
|                                     %(consts)s,
 | |
|                                     prototype.handle(),
 | |
|                                     %(name)s,
 | |
|                                     %(length)s,
 | |
|                                     interface.handle_mut());
 | |
| assert!(!interface.is_null());""" % properties))
 | |
|             if self.descriptor.hasDescendants():
 | |
|                 code.append(CGGeneric("""\
 | |
| assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null());
 | |
| (*cache)[PrototypeList::Constructor::%(id)s as usize] = interface.get();
 | |
| <*mut JSObject>::post_barrier((*cache).as_mut_ptr().offset(PrototypeList::Constructor::%(id)s as isize),
 | |
|                               ptr::null_mut(),
 | |
|                               interface.get());
 | |
| """ % properties))
 | |
| 
 | |
|         aliasedMembers = [m for m in self.descriptor.interface.members if m.isMethod() and m.aliases]
 | |
|         if aliasedMembers:
 | |
|             def defineAlias(alias):
 | |
|                 if alias == "@@iterator":
 | |
|                     symbolJSID = "RUST_SYMBOL_TO_JSID(GetWellKnownSymbol(cx, SymbolCode::iterator))"
 | |
|                     getSymbolJSID = CGGeneric(fill("rooted!(in(cx) let iteratorId = ${symbolJSID});",
 | |
|                                                    symbolJSID=symbolJSID))
 | |
|                     defineFn = "JS_DefinePropertyById2"
 | |
|                     prop = "iteratorId.handle()"
 | |
|                 elif alias.startswith("@@"):
 | |
|                     raise TypeError("Can't handle any well-known Symbol other than @@iterator")
 | |
|                 else:
 | |
|                     getSymbolJSID = None
 | |
|                     defineFn = "JS_DefineProperty"
 | |
|                     prop = '"%s"' % alias
 | |
|                 return CGList([
 | |
|                     getSymbolJSID,
 | |
|                     # XXX If we ever create non-enumerable properties that can
 | |
|                     #     be aliased, we should consider making the aliases
 | |
|                     #     match the enumerability of the property being aliased.
 | |
|                     CGGeneric(fill(
 | |
|                         """
 | |
|                         assert!(${defineFn}(cx, prototype.handle(), ${prop}, aliasedVal.handle(),
 | |
|                                             JSPROP_ENUMERATE, None, None));
 | |
|                         """,
 | |
|                         defineFn=defineFn,
 | |
|                         prop=prop))
 | |
|                 ], "\n")
 | |
| 
 | |
|             def defineAliasesFor(m):
 | |
|                 return CGList([
 | |
|                     CGGeneric(fill(
 | |
|                         """
 | |
|                         assert!(JS_GetProperty(cx, prototype.handle(),
 | |
|                                                ${prop} as *const u8 as *const _,
 | |
|                                                aliasedVal.handle_mut()));
 | |
|                         """,
 | |
|                         prop=str_to_const_array(m.identifier.name)))
 | |
|                 ] + [defineAlias(alias) for alias in sorted(m.aliases)])
 | |
| 
 | |
|             defineAliases = CGList([
 | |
|                 CGGeneric(fill("""
 | |
|                     // Set up aliases on the interface prototype object we just created.
 | |
| 
 | |
|                     """)),
 | |
|                 CGGeneric("rooted!(in(cx) let mut aliasedVal = UndefinedValue());\n\n")
 | |
|             ] + [defineAliasesFor(m) for m in sorted(aliasedMembers)])
 | |
|             code.append(defineAliases)
 | |
| 
 | |
|         constructors = self.descriptor.interface.namedConstructors
 | |
|         if constructors:
 | |
|             decl = "let named_constructors: [(ConstructorClassHook, &'static [u8], u32); %d]" % len(constructors)
 | |
|             specs = []
 | |
|             for constructor in constructors:
 | |
|                 hook = CONSTRUCT_HOOK_NAME + "_" + constructor.identifier.name
 | |
|                 name = str_to_const_array(constructor.identifier.name)
 | |
|                 length = methodLength(constructor)
 | |
|                 specs.append(CGGeneric("(%s as ConstructorClassHook, %s, %d)" % (hook, name, length)))
 | |
|             values = CGIndenter(CGList(specs, "\n"), 4)
 | |
|             code.append(CGWrapper(values, pre="%s = [\n" % decl, post="\n];"))
 | |
|             code.append(CGGeneric("create_named_constructors(cx, global, &named_constructors, prototype.handle());"))
 | |
| 
 | |
|         if self.descriptor.hasUnforgeableMembers:
 | |
|             # We want to use the same JSClass and prototype as the object we'll
 | |
|             # end up defining the unforgeable properties on in the end, so that
 | |
|             # we can use JS_InitializePropertiesFromCompatibleNativeObject to do
 | |
|             # a fast copy.  In the case of proxies that's null, because the
 | |
|             # expando object is a vanilla object, but in the case of other DOM
 | |
|             # objects it's whatever our class is.
 | |
|             #
 | |
|             # Also, for a global we can't use the global's class; just use
 | |
|             # nullpr and when we do the copy off the holder we'll take a slower
 | |
|             # path.  This also means that we don't need to worry about matching
 | |
|             # the prototype.
 | |
|             if self.descriptor.proxy or self.descriptor.isGlobal():
 | |
|                 holderClass = "ptr::null()"
 | |
|                 holderProto = "HandleObject::null()"
 | |
|             else:
 | |
|                 holderClass = "&Class.base as *const JSClass"
 | |
|                 holderProto = "prototype.handle()"
 | |
|             code.append(CGGeneric("""
 | |
| rooted!(in(cx) let mut unforgeable_holder = ptr::null_mut());
 | |
| unforgeable_holder.handle_mut().set(
 | |
|     JS_NewObjectWithoutMetadata(cx, %(holderClass)s, %(holderProto)s));
 | |
| assert!(!unforgeable_holder.is_null());
 | |
| """ % {'holderClass': holderClass, 'holderProto': holderProto}))
 | |
|             code.append(InitUnforgeablePropertiesOnHolder(self.descriptor, self.properties))
 | |
|             code.append(CGGeneric("""\
 | |
| JS_SetReservedSlot(prototype.get(), DOM_PROTO_UNFORGEABLE_HOLDER_SLOT,
 | |
|                    ObjectValue(unforgeable_holder.get()))"""))
 | |
| 
 | |
|         return CGList(code, "\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', 'cx'),
 | |
|                 Argument('HandleObject', 'global'),
 | |
|                 Argument('MutableHandleObject', 'rval')]
 | |
|         CGAbstractMethod.__init__(self, descriptor, name,
 | |
|                                   'void', args, pub=pub, unsafe=True)
 | |
|         self.id = idPrefix + "::" + MakeNativeName(self.descriptor.name)
 | |
| 
 | |
|     def definition_body(self):
 | |
|         return CGGeneric("""
 | |
| assert!(((*get_object_class(global.get())).flags & JSCLASS_DOM_GLOBAL) != 0);
 | |
| 
 | |
| /* Check to see whether the interface objects are already installed */
 | |
| let proto_or_iface_array = get_proto_or_iface_array(global.get());
 | |
| rval.set((*proto_or_iface_array)[%(id)s as usize]);
 | |
| if !rval.get().is_null() {
 | |
|     return;
 | |
| }
 | |
| 
 | |
| CreateInterfaceObjects(cx, global, proto_or_iface_array);
 | |
| rval.set((*proto_or_iface_array)[%(id)s as usize]);
 | |
| assert!(!rval.get().is_null());
 | |
| """ % {"id": self.id})
 | |
| 
 | |
| 
 | |
| class CGGetProtoObjectMethod(CGGetPerInterfaceObject):
 | |
|     """
 | |
|     A method for getting the interface prototype object.
 | |
|     """
 | |
|     def __init__(self, descriptor):
 | |
|         CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject",
 | |
|                                          "PrototypeList::ID", 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",
 | |
|                                          "PrototypeList::Constructor",
 | |
|                                          pub=True)
 | |
| 
 | |
|     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, unsafe=True)
 | |
| 
 | |
|     def define(self):
 | |
|         return CGAbstractMethod.define(self)
 | |
| 
 | |
|     def definition_body(self):
 | |
|         customDefineProperty = 'proxyhandler::define_property'
 | |
|         if self.descriptor.operations['IndexedSetter'] or self.descriptor.operations['NamedSetter']:
 | |
|             customDefineProperty = 'defineProperty'
 | |
| 
 | |
|         customDelete = 'proxyhandler::delete'
 | |
|         if self.descriptor.operations['NamedDeleter']:
 | |
|             customDelete = 'delete'
 | |
| 
 | |
|         getOwnEnumerablePropertyKeys = "own_property_keys"
 | |
|         if self.descriptor.interface.getExtendedAttribute("LegacyUnenumerableNamedProperties"):
 | |
|             getOwnEnumerablePropertyKeys = "getOwnEnumerablePropertyKeys"
 | |
| 
 | |
|         args = {
 | |
|             "defineProperty": customDefineProperty,
 | |
|             "delete": customDelete,
 | |
|             "getOwnEnumerablePropertyKeys": getOwnEnumerablePropertyKeys,
 | |
|             "trace": TRACE_HOOK_NAME,
 | |
|             "finalize": FINALIZE_HOOK_NAME,
 | |
|         }
 | |
| 
 | |
|         return CGGeneric("""\
 | |
| let traps = ProxyTraps {
 | |
|     enter: None,
 | |
|     getOwnPropertyDescriptor: Some(getOwnPropertyDescriptor),
 | |
|     defineProperty: Some(%(defineProperty)s),
 | |
|     ownPropertyKeys: Some(own_property_keys),
 | |
|     delete_: Some(%(delete)s),
 | |
|     enumerate: None,
 | |
|     getPrototypeIfOrdinary: Some(proxyhandler::get_prototype_if_ordinary),
 | |
|     preventExtensions: Some(proxyhandler::prevent_extensions),
 | |
|     isExtensible: Some(proxyhandler::is_extensible),
 | |
|     has: None,
 | |
|     get: Some(get),
 | |
|     set: None,
 | |
|     call: None,
 | |
|     construct: None,
 | |
|     getPropertyDescriptor: Some(get_property_descriptor),
 | |
|     hasOwn: Some(hasOwn),
 | |
|     getOwnEnumerablePropertyKeys: Some(%(getOwnEnumerablePropertyKeys)s),
 | |
|     nativeCall: None,
 | |
|     hasInstance: None,
 | |
|     objectClassIs: None,
 | |
|     className: Some(className),
 | |
|     fun_toString: None,
 | |
|     boxedValue_unbox: None,
 | |
|     defaultValue: None,
 | |
|     trace: Some(%(trace)s),
 | |
|     finalize: Some(%(finalize)s),
 | |
|     objectMoved: None,
 | |
|     isCallable: None,
 | |
|     isConstructor: None,
 | |
| };
 | |
| 
 | |
| CreateProxyHandler(&traps, &Class as *const _ as *const _)\
 | |
| """ % args)
 | |
| 
 | |
| 
 | |
| 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('HandleObject', 'global'),
 | |
|         ]
 | |
|         CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface',
 | |
|                                   'void', args, pub=True, unsafe=True)
 | |
| 
 | |
|     def define(self):
 | |
|         return CGAbstractMethod.define(self)
 | |
| 
 | |
|     def definition_body(self):
 | |
|         if self.descriptor.interface.isCallback() or self.descriptor.interface.isNamespace():
 | |
|             function = "GetConstructorObject"
 | |
|         else:
 | |
|             function = "GetProtoObject"
 | |
|         return CGGeneric("""\
 | |
| assert!(!global.get().is_null());
 | |
| 
 | |
| if !ConstructorEnabled(cx, global) {
 | |
|     return;
 | |
| }
 | |
| 
 | |
| rooted!(in(cx) let mut proto = ptr::null_mut());
 | |
| %s(cx, global, proto.handle_mut());
 | |
| assert!(!proto.is_null());""" % (function,))
 | |
| 
 | |
| 
 | |
| 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, descriptor, nativeMethodName,
 | |
|                  static, object="this"):
 | |
|         CGThing.__init__(self)
 | |
| 
 | |
|         assert errorResult is None or isinstance(errorResult, str)
 | |
| 
 | |
|         isFallible = errorResult is not None
 | |
| 
 | |
|         result = getRetvalDeclarationForType(returnType, descriptor)
 | |
|         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 "cx" not 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::" % MakeNativeName(descriptor.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 = "global.upcast::<GlobalScope>()"
 | |
|             else:
 | |
|                 glob = "&this.global()"
 | |
| 
 | |
|             self.cgRoot.append(CGGeneric(
 | |
|                 "let result = match result {\n"
 | |
|                 "    Ok(result) => result,\n"
 | |
|                 "    Err(e) => {\n"
 | |
|                 "        throw_dom_exception(cx, %s, e);\n"
 | |
|                 "        return%s;\n"
 | |
|                 "    },\n"
 | |
|                 "};" % (glob, errorResult)))
 | |
| 
 | |
|     def define(self):
 | |
|         return self.cgRoot.define()
 | |
| 
 | |
| 
 | |
| 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)
 | |
|         cgThings = []
 | |
|         cgThings.extend([CGArgumentConverter(arguments[i], i, self.getArgs(),
 | |
|                                              self.getArgc(), self.descriptor,
 | |
|                                              invalidEnumValueFatal=not setter) for
 | |
|                          i in range(argConversionStartsAt, self.argCount)])
 | |
| 
 | |
|         errorResult = None
 | |
|         if self.isFallible():
 | |
|             errorResult = " false"
 | |
| 
 | |
|         if idlNode.isMethod() and idlNode.isMaplikeOrSetlikeOrIterableMethod():
 | |
|             if idlNode.maplikeOrSetlikeOrIterable.isMaplike() or \
 | |
|                idlNode.maplikeOrSetlikeOrIterable.isSetlike():
 | |
|                 raise TypeError('Maplike/Setlike methods are not supported yet')
 | |
|             else:
 | |
|                 cgThings.append(CGIterableMethodGenerator(descriptor,
 | |
|                                                           idlNode.maplikeOrSetlikeOrIterable,
 | |
|                                                           idlNode.identifier.name))
 | |
|         else:
 | |
|             cgThings.append(CGCallGenerator(
 | |
|                 errorResult,
 | |
|                 self.getArguments(), self.argsPre, returnType,
 | |
|                 self.extendedAttributes, descriptor, nativeMethodName,
 | |
|                 static))
 | |
| 
 | |
|         self.cgRoot = CGList(cgThings, "\n")
 | |
| 
 | |
|     def getArgs(self):
 | |
|         return "args" if self.argCount > 0 else ""
 | |
| 
 | |
|     def getArgc(self):
 | |
|         return "argc"
 | |
| 
 | |
|     def getArguments(self):
 | |
|         return [(a, process_arg("arg" + str(i), a)) for (i, a) in enumerate(self.arguments)]
 | |
| 
 | |
|     def isFallible(self):
 | |
|         return 'infallible' not in self.extendedAttributes
 | |
| 
 | |
|     def wrap_return_value(self):
 | |
|         return wrapForType('args.rval()')
 | |
| 
 | |
|     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 true;"
 | |
| 
 | |
|     def getArgc(self):
 | |
|         return "1"
 | |
| 
 | |
| 
 | |
| 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, "bool", args, extern=True)
 | |
|         self.exposureSet = descriptor.interface.exposureSet
 | |
| 
 | |
|     def definition_body(self):
 | |
|         preamble = "let global = GlobalScope::from_object(JS_CALLEE(cx, vp).to_object());\n"
 | |
|         if len(self.exposureSet) == 1:
 | |
|             preamble += "let global = Root::downcast::<dom::types::%s>(global).unwrap();\n" % list(self.exposureSet)[0]
 | |
|         return CGList([CGGeneric(preamble), self.generate_code()])
 | |
| 
 | |
|     def generate_code(self):
 | |
|         raise NotImplementedError  # Override me!
 | |
| 
 | |
| 
 | |
| 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('HandleObject', '_obj'),
 | |
|                 Argument('*const %s' % descriptor.concreteType, 'this'),
 | |
|                 Argument('*const JSJitMethodCallArgs', 'args')]
 | |
|         CGAbstractExternMethod.__init__(self, descriptor, name, 'bool', 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 = &*this;\n"
 | |
|                              "let args = &*args;\n"
 | |
|                              "let argc = args._base.argc_;\n")
 | |
| 
 | |
|     @staticmethod
 | |
|     def makeNativeName(descriptor, method):
 | |
|         name = method.identifier.name
 | |
|         nativeName = descriptor.binaryNameFor(name)
 | |
|         if nativeName == name:
 | |
|             nativeName = descriptor.internalNameFor(name)
 | |
|         return MakeNativeName(nativeName)
 | |
| 
 | |
| 
 | |
| 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)
 | |
|         setupArgs = CGGeneric("let args = CallArgs::from_vp(vp, argc);\n")
 | |
|         call = CGMethodCall(["&global"], nativeName, True, self.descriptor, self.method)
 | |
|         return CGList([setupArgs, call])
 | |
| 
 | |
| 
 | |
| 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_' + descriptor.internalNameFor(attr.identifier.name)
 | |
|         args = [Argument('*mut JSContext', 'cx'),
 | |
|                 Argument('HandleObject', '_obj'),
 | |
|                 Argument('*const %s' % descriptor.concreteType, 'this'),
 | |
|                 Argument('JSJitGetterCallArgs', 'args')]
 | |
|         CGAbstractExternMethod.__init__(self, descriptor, name, "bool", 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 = &*this;\n")
 | |
| 
 | |
|     @staticmethod
 | |
|     def makeNativeName(descriptor, attr):
 | |
|         name = attr.identifier.name
 | |
|         nativeName = descriptor.binaryNameFor(name)
 | |
|         if nativeName == name:
 | |
|             nativeName = descriptor.internalNameFor(name)
 | |
|         nativeName = MakeNativeName(nativeName)
 | |
|         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)
 | |
|         setupArgs = CGGeneric("let args = CallArgs::from_vp(vp, argc);\n")
 | |
|         call = CGGetterCall(["&global"], self.attr.type, nativeName, self.descriptor,
 | |
|                             self.attr)
 | |
|         return CGList([setupArgs, call])
 | |
| 
 | |
| 
 | |
| 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_' + descriptor.internalNameFor(attr.identifier.name)
 | |
|         args = [Argument('*mut JSContext', 'cx'),
 | |
|                 Argument('HandleObject', 'obj'),
 | |
|                 Argument('*const %s' % descriptor.concreteType, 'this'),
 | |
|                 Argument('JSJitSetterCallArgs', 'args')]
 | |
|         CGAbstractExternMethod.__init__(self, descriptor, name, "bool", 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 = &*this;\n")
 | |
| 
 | |
|     @staticmethod
 | |
|     def makeNativeName(descriptor, attr):
 | |
|         name = attr.identifier.name
 | |
|         nativeName = descriptor.binaryNameFor(name)
 | |
|         if nativeName == name:
 | |
|             nativeName = descriptor.internalNameFor(name)
 | |
|         return "Set" + MakeNativeName(nativeName)
 | |
| 
 | |
| 
 | |
| 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 args = CallArgs::from_vp(vp, argc);\n"
 | |
|             "if argc == 0 {\n"
 | |
|             "    throw_type_error(cx, \"Not enough arguments to %s setter.\");\n"
 | |
|             "    return false;\n"
 | |
|             "}" % self.attr.identifier.name)
 | |
|         call = CGSetterCall(["&global"], self.attr.type, nativeName, self.descriptor,
 | |
|                             self.attr)
 | |
|         return CGList([checkForArg, call])
 | |
| 
 | |
| 
 | |
| class CGSpecializedForwardingSetter(CGSpecializedSetter):
 | |
|     """
 | |
|     A class for generating the code for an IDL attribute forwarding setter.
 | |
|     """
 | |
|     def __init__(self, descriptor, attr):
 | |
|         CGSpecializedSetter.__init__(self, descriptor, attr)
 | |
| 
 | |
|     def definition_body(self):
 | |
|         attrName = self.attr.identifier.name
 | |
|         forwardToAttrName = self.attr.getExtendedAttribute("PutForwards")[0]
 | |
|         # JS_GetProperty and JS_SetProperty can only deal with ASCII
 | |
|         assert all(ord(c) < 128 for c in attrName)
 | |
|         assert all(ord(c) < 128 for c in forwardToAttrName)
 | |
|         return CGGeneric("""\
 | |
| rooted!(in(cx) let mut v = UndefinedValue());
 | |
| if !JS_GetProperty(cx, obj, %s as *const u8 as *const libc::c_char, v.handle_mut()) {
 | |
|     return false;
 | |
| }
 | |
| if !v.is_object() {
 | |
|     throw_type_error(cx, "Value.%s is not an object.");
 | |
|     return false;
 | |
| }
 | |
| rooted!(in(cx) let target_obj = v.to_object());
 | |
| JS_SetProperty(cx, target_obj.handle(), %s as *const u8 as *const libc::c_char, args.get(0))
 | |
| """ % (str_to_const_array(attrName), attrName, str_to_const_array(forwardToAttrName)))
 | |
| 
 | |
| 
 | |
| class CGSpecializedReplaceableSetter(CGSpecializedSetter):
 | |
|     """
 | |
|     A class for generating the code for an IDL replaceable attribute setter.
 | |
|     """
 | |
|     def __init__(self, descriptor, attr):
 | |
|         CGSpecializedSetter.__init__(self, descriptor, attr)
 | |
| 
 | |
|     def definition_body(self):
 | |
|         assert self.attr.readonly
 | |
|         name = str_to_const_array(self.attr.identifier.name)
 | |
|         # JS_DefineProperty can only deal with ASCII.
 | |
|         assert all(ord(c) < 128 for c in name)
 | |
|         return CGGeneric("""\
 | |
| JS_DefineProperty(cx, obj, %s as *const u8 as *const libc::c_char,
 | |
|                   args.get(0), JSPROP_ENUMERATE, None, None)""" % name)
 | |
| 
 | |
| 
 | |
| 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, opType, infallible, movable,
 | |
|                       aliasSet, alwaysInSlot, lazilyInSlot, slotIndex,
 | |
|                       returnTypes, args):
 | |
|         """
 | |
|         aliasSet is a JSJitInfo_AliasSet value, without the "JSJitInfo_AliasSet::" bit.
 | |
| 
 | |
|         args is None if we don't want to output argTypes for some
 | |
|         reason (e.g. we have overloads or we're not a method) and
 | |
|         otherwise an iterable of the arguments for this method.
 | |
|         """
 | |
|         assert not movable or aliasSet != "AliasEverything"  # Can't move write-aliasing things
 | |
|         assert not alwaysInSlot or movable  # Things always in slots had better be movable
 | |
| 
 | |
|         def jitInfoInitializer(isTypedMethod):
 | |
|             initializer = fill(
 | |
|                 """
 | |
|                 JSJitInfo {
 | |
|                     call: ${opName} as *const os::raw::c_void,
 | |
|                     protoID: PrototypeList::ID::${name} as u16,
 | |
|                     depth: ${depth},
 | |
|                     _bitfield_1:
 | |
|                         JSJitInfo::new_bitfield_1(
 | |
|                             JSJitInfo_OpType::${opType} as u8,
 | |
|                             JSJitInfo_AliasSet::${aliasSet} as u8,
 | |
|                             JSValueType::${returnType} as u8,
 | |
|                             ${isInfallible},
 | |
|                             ${isMovable},
 | |
|                             ${isEliminatable},
 | |
|                             ${isAlwaysInSlot},
 | |
|                             ${isLazilyCachedInSlot},
 | |
|                             ${isTypedMethod},
 | |
|                             ${slotIndex} as u16,
 | |
|                         )
 | |
|                 }
 | |
|                 """,
 | |
|                 opName=opName,
 | |
|                 name=self.descriptor.name,
 | |
|                 depth=self.descriptor.interface.inheritanceDepth(),
 | |
|                 opType=opType,
 | |
|                 aliasSet=aliasSet,
 | |
|                 returnType=reduce(CGMemberJITInfo.getSingleReturnType, returnTypes,
 | |
|                                   ""),
 | |
|                 isInfallible=toStringBool(infallible),
 | |
|                 isMovable=toStringBool(movable),
 | |
|                 # FIXME(nox): https://github.com/servo/servo/issues/10991
 | |
|                 isEliminatable=toStringBool(False),
 | |
|                 isAlwaysInSlot=toStringBool(alwaysInSlot),
 | |
|                 isLazilyCachedInSlot=toStringBool(lazilyInSlot),
 | |
|                 isTypedMethod=toStringBool(isTypedMethod),
 | |
|                 slotIndex=slotIndex)
 | |
|             return initializer.rstrip()
 | |
| 
 | |
|         if args is not None:
 | |
|             argTypes = "%s_argTypes" % infoName
 | |
|             args = [CGMemberJITInfo.getJSArgType(arg.type) for arg in args]
 | |
|             args.append("JSJitInfo_ArgType::ArgTypeListEnd as i32")
 | |
|             argTypesDecl = (
 | |
|                 "const %s: [i32; %d] = [ %s ];\n" %
 | |
|                 (argTypes, len(args), ", ".join(args)))
 | |
|             return fill(
 | |
|                 """
 | |
|                 $*{argTypesDecl}
 | |
|                 const ${infoName}: JSTypedMethodJitInfo = JSTypedMethodJitInfo {
 | |
|                     base: ${jitInfo},
 | |
|                     argTypes: &${argTypes} as *const _ as *const JSJitInfo_ArgType,
 | |
|                 };
 | |
|                 """,
 | |
|                 argTypesDecl=argTypesDecl,
 | |
|                 infoName=infoName,
 | |
|                 jitInfo=indent(jitInfoInitializer(True)),
 | |
|                 argTypes=argTypes)
 | |
| 
 | |
|         return ("\n"
 | |
|                 "const %s: JSJitInfo = %s;\n"
 | |
|                 % (infoName, jitInfoInitializer(False)))
 | |
| 
 | |
|     def define(self):
 | |
|         if self.member.isAttr():
 | |
|             internalMemberName = self.descriptor.internalNameFor(self.member.identifier.name)
 | |
|             getterinfo = ("%s_getterinfo" % internalMemberName)
 | |
|             getter = ("get_%s" % internalMemberName)
 | |
|             getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True)
 | |
| 
 | |
|             movable = self.mayBeMovable() and getterinfal
 | |
|             aliasSet = self.aliasSet()
 | |
| 
 | |
|             isAlwaysInSlot = self.member.getExtendedAttribute("StoreInSlot")
 | |
|             if self.member.slotIndices is not None:
 | |
|                 assert isAlwaysInSlot or self.member.getExtendedAttribute("Cached")
 | |
|                 isLazilyCachedInSlot = not isAlwaysInSlot
 | |
|                 slotIndex = memberReservedSlot(self.member)  # noqa:FIXME: memberReservedSlot is not defined
 | |
|                 # We'll statically assert that this is not too big in
 | |
|                 # CGUpdateMemberSlotsMethod, in the case when
 | |
|                 # isAlwaysInSlot is true.
 | |
|             else:
 | |
|                 isLazilyCachedInSlot = False
 | |
|                 slotIndex = "0"
 | |
| 
 | |
|             result = self.defineJitInfo(getterinfo, getter, "Getter",
 | |
|                                         getterinfal, movable, aliasSet,
 | |
|                                         isAlwaysInSlot, isLazilyCachedInSlot,
 | |
|                                         slotIndex,
 | |
|                                         [self.member.type], None)
 | |
|             if (not self.member.readonly or self.member.getExtendedAttribute("PutForwards")
 | |
|                     or self.member.getExtendedAttribute("Replaceable")):
 | |
|                 setterinfo = ("%s_setterinfo" % internalMemberName)
 | |
|                 setter = ("set_%s" % internalMemberName)
 | |
|                 # Setters are always fallible, since they have to do a typed unwrap.
 | |
|                 result += self.defineJitInfo(setterinfo, setter, "Setter",
 | |
|                                              False, False, "AliasEverything",
 | |
|                                              False, False, "0",
 | |
|                                              [BuiltinTypes[IDLBuiltinType.Types.void]],
 | |
|                                              None)
 | |
|             return result
 | |
|         if self.member.isMethod():
 | |
|             methodinfo = ("%s_methodinfo" % self.member.identifier.name)
 | |
|             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.
 | |
|             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.
 | |
|                 methodInfal = False
 | |
|                 args = None
 | |
|                 movable = False
 | |
|             else:
 | |
|                 sig = sigs[0]
 | |
|                 # For methods that affect nothing, it's OK to set movable to our
 | |
|                 # notion of infallible on the C++ side, without considering
 | |
|                 # argument conversions, since argument conversions that can
 | |
|                 # reliably throw would be effectful anyway and the jit doesn't
 | |
|                 # move effectful things.
 | |
|                 hasInfallibleImpl = "infallible" in self.descriptor.getExtendedAttributes(self.member)
 | |
|                 movable = self.mayBeMovable() and hasInfallibleImpl
 | |
|                 # XXXbz can we move the smarts about fallibility due to arg
 | |
|                 # conversions into the JIT, using our new args stuff?
 | |
|                 if (len(sig[1]) != 0):
 | |
|                     # We have arguments or our return-value boxing can fail
 | |
|                     methodInfal = False
 | |
|                 else:
 | |
|                     methodInfal = hasInfallibleImpl
 | |
|                 # For now, only bother to output args if we're side-effect-free.
 | |
|                 if self.member.affects == "Nothing":
 | |
|                     args = sig[1]
 | |
|                 else:
 | |
|                     args = None
 | |
| 
 | |
|             aliasSet = self.aliasSet()
 | |
|             result = self.defineJitInfo(methodinfo, method, "Method",
 | |
|                                         methodInfal, movable, aliasSet,
 | |
|                                         False, False, "0",
 | |
|                                         [s[0] for s in sigs], args)
 | |
|             return result
 | |
|         raise TypeError("Illegal member type to CGPropertyJITInfo")
 | |
| 
 | |
|     def mayBeMovable(self):
 | |
|         """
 | |
|         Returns whether this attribute or method may be movable, just
 | |
|         based on Affects/DependsOn annotations.
 | |
|         """
 | |
|         affects = self.member.affects
 | |
|         dependsOn = self.member.dependsOn
 | |
|         assert affects in IDLInterfaceMember.AffectsValues
 | |
|         assert dependsOn in IDLInterfaceMember.DependsOnValues
 | |
|         # Things that are DependsOn=DeviceState are not movable, because we
 | |
|         # don't want them coalesced with each other or loop-hoisted, since
 | |
|         # their return value can change even if nothing is going on from our
 | |
|         # point of view.
 | |
|         return (affects == "Nothing" and
 | |
|                 (dependsOn != "Everything" and dependsOn != "DeviceState"))
 | |
| 
 | |
|     def aliasSet(self):
 | |
|         """Returns the alias set to store in the jitinfo.  This may not be the
 | |
|         effective alias set the JIT uses, depending on whether we have enough
 | |
|         information about our args to allow the JIT to prove that effectful
 | |
|         argument conversions won't happen.
 | |
| 
 | |
|         """
 | |
|         dependsOn = self.member.dependsOn
 | |
|         assert dependsOn in IDLInterfaceMember.DependsOnValues
 | |
| 
 | |
|         if dependsOn == "Nothing" or dependsOn == "DeviceState":
 | |
|             assert self.member.affects == "Nothing"
 | |
|             return "AliasNone"
 | |
| 
 | |
|         if dependsOn == "DOMState":
 | |
|             assert self.member.affects == "Nothing"
 | |
|             return "AliasDOMSets"
 | |
| 
 | |
|         return "AliasEverything"
 | |
| 
 | |
|     @staticmethod
 | |
|     def getJSReturnTypeTag(t):
 | |
|         if t.nullable():
 | |
|             # Sometimes it might return null, sometimes not
 | |
|             return "JSVAL_TYPE_UNKNOWN"
 | |
|         if t.isVoid():
 | |
|             # No return, every time
 | |
|             return "JSVAL_TYPE_UNDEFINED"
 | |
|         if t.isSequence():
 | |
|             return "JSVAL_TYPE_OBJECT"
 | |
|         if t.isMozMap():
 | |
|             return "JSVAL_TYPE_OBJECT"
 | |
|         if t.isGeckoInterface():
 | |
|             return "JSVAL_TYPE_OBJECT"
 | |
|         if t.isString():
 | |
|             return "JSVAL_TYPE_STRING"
 | |
|         if t.isEnum():
 | |
|             return "JSVAL_TYPE_STRING"
 | |
|         if t.isCallback():
 | |
|             return "JSVAL_TYPE_OBJECT"
 | |
|         if t.isAny():
 | |
|             # The whole point is to return various stuff
 | |
|             return "JSVAL_TYPE_UNKNOWN"
 | |
|         if t.isObject():
 | |
|             return "JSVAL_TYPE_OBJECT"
 | |
|         if t.isSpiderMonkeyInterface():
 | |
|             return "JSVAL_TYPE_OBJECT"
 | |
|         if t.isUnion():
 | |
|             u = t.unroll()
 | |
|             if u.hasNullableType:
 | |
|                 # Might be null or not
 | |
|                 return "JSVAL_TYPE_UNKNOWN"
 | |
|             return reduce(CGMemberJITInfo.getSingleReturnType,
 | |
|                           u.flatMemberTypes, "")
 | |
|         if t.isDictionary():
 | |
|             return "JSVAL_TYPE_OBJECT"
 | |
|         if t.isDate():
 | |
|             return "JSVAL_TYPE_OBJECT"
 | |
|         if not t.isPrimitive():
 | |
|             raise TypeError("No idea what type " + str(t) + " is.")
 | |
|         tag = t.tag()
 | |
|         if tag == IDLType.Tags.bool:
 | |
|             return "JSVAL_TYPE_BOOLEAN"
 | |
|         if tag in [IDLType.Tags.int8, IDLType.Tags.uint8,
 | |
|                    IDLType.Tags.int16, IDLType.Tags.uint16,
 | |
|                    IDLType.Tags.int32]:
 | |
|             return "JSVAL_TYPE_INT32"
 | |
|         if tag in [IDLType.Tags.int64, IDLType.Tags.uint64,
 | |
|                    IDLType.Tags.unrestricted_float, IDLType.Tags.float,
 | |
|                    IDLType.Tags.unrestricted_double, IDLType.Tags.double]:
 | |
|             # These all use JS_NumberValue, which can return int or double.
 | |
|             # But TI treats "double" as meaning "int or double", so we're
 | |
|             # good to return JSVAL_TYPE_DOUBLE here.
 | |
|             return "JSVAL_TYPE_DOUBLE"
 | |
|         if tag != IDLType.Tags.uint32:
 | |
|             raise TypeError("No idea what type " + str(t) + " is.")
 | |
|         # uint32 is sometimes int and sometimes double.
 | |
|         return "JSVAL_TYPE_DOUBLE"
 | |
| 
 | |
|     @staticmethod
 | |
|     def getSingleReturnType(existingType, t):
 | |
|         type = CGMemberJITInfo.getJSReturnTypeTag(t)
 | |
|         if existingType == "":
 | |
|             # First element of the list; just return its type
 | |
|             return type
 | |
| 
 | |
|         if type == existingType:
 | |
|             return existingType
 | |
|         if ((type == "JSVAL_TYPE_DOUBLE" and
 | |
|              existingType == "JSVAL_TYPE_INT32") or
 | |
|             (existingType == "JSVAL_TYPE_DOUBLE" and
 | |
|              type == "JSVAL_TYPE_INT32")):
 | |
|             # Promote INT32 to DOUBLE as needed
 | |
|             return "JSVAL_TYPE_DOUBLE"
 | |
|         # Different types
 | |
|         return "JSVAL_TYPE_UNKNOWN"
 | |
| 
 | |
|     @staticmethod
 | |
|     def getJSArgType(t):
 | |
|         assert not t.isVoid()
 | |
|         if t.nullable():
 | |
|             # Sometimes it might return null, sometimes not
 | |
|             return "JSJitInfo_ArgType::Null as i32 | %s" % CGMemberJITInfo.getJSArgType(t.inner)
 | |
|         if t.isSequence():
 | |
|             return "JSJitInfo_ArgType::Object as i32"
 | |
|         if t.isGeckoInterface():
 | |
|             return "JSJitInfo_ArgType::Object as i32"
 | |
|         if t.isString():
 | |
|             return "JSJitInfo_ArgType::String as i32"
 | |
|         if t.isEnum():
 | |
|             return "JSJitInfo_ArgType::String as i32"
 | |
|         if t.isCallback():
 | |
|             return "JSJitInfo_ArgType::Object as i32"
 | |
|         if t.isAny():
 | |
|             # The whole point is to return various stuff
 | |
|             return "JSJitInfo_ArgType::Any as i32"
 | |
|         if t.isObject():
 | |
|             return "JSJitInfo_ArgType::Object as i32"
 | |
|         if t.isSpiderMonkeyInterface():
 | |
|             return "JSJitInfo_ArgType::Object as i32"
 | |
|         if t.isUnion():
 | |
|             u = t.unroll()
 | |
|             type = "JSJitInfo::Null as i32" if u.hasNullableType else ""
 | |
|             return reduce(CGMemberJITInfo.getSingleArgType,
 | |
|                           u.flatMemberTypes, type)
 | |
|         if t.isDictionary():
 | |
|             return "JSJitInfo_ArgType::Object as i32"
 | |
|         if t.isDate():
 | |
|             return "JSJitInfo_ArgType::Object as i32"
 | |
|         if not t.isPrimitive():
 | |
|             raise TypeError("No idea what type " + str(t) + " is.")
 | |
|         tag = t.tag()
 | |
|         if tag == IDLType.Tags.bool:
 | |
|             return "JSJitInfo_ArgType::Boolean as i32"
 | |
|         if tag in [IDLType.Tags.int8, IDLType.Tags.uint8,
 | |
|                    IDLType.Tags.int16, IDLType.Tags.uint16,
 | |
|                    IDLType.Tags.int32]:
 | |
|             return "JSJitInfo_ArgType::Integer as i32"
 | |
|         if tag in [IDLType.Tags.int64, IDLType.Tags.uint64,
 | |
|                    IDLType.Tags.unrestricted_float, IDLType.Tags.float,
 | |
|                    IDLType.Tags.unrestricted_double, IDLType.Tags.double]:
 | |
|             # These all use JS_NumberValue, which can return int or double.
 | |
|             # But TI treats "double" as meaning "int or double", so we're
 | |
|             # good to return JSVAL_TYPE_DOUBLE here.
 | |
|             return "JSJitInfo_ArgType::Double as i32"
 | |
|         if tag != IDLType.Tags.uint32:
 | |
|             raise TypeError("No idea what type " + str(t) + " is.")
 | |
|         # uint32 is sometimes int and sometimes double.
 | |
|         return "JSJitInfo_ArgType::Double as i32"
 | |
| 
 | |
|     @staticmethod
 | |
|     def getSingleArgType(existingType, t):
 | |
|         type = CGMemberJITInfo.getJSArgType(t)
 | |
|         if existingType == "":
 | |
|             # First element of the list; just return its type
 | |
|             return type
 | |
| 
 | |
|         if type == existingType:
 | |
|             return existingType
 | |
|         return "%s | %s" % (existingType, type)
 | |
| 
 | |
| 
 | |
| 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)
 | |
| 
 | |
|         decl = """\
 | |
| #[repr(usize)]
 | |
| #[derive(JSTraceable, PartialEq, Copy, Clone, HeapSizeOf, Debug)]
 | |
| pub enum %s {
 | |
|     %s
 | |
| }
 | |
| """ % (enum.identifier.name, ",\n    ".join(map(getEnumValueName, enum.values())))
 | |
| 
 | |
|         inner = """\
 | |
| use dom::bindings::conversions::ToJSValConvertible;
 | |
| use js::jsapi::{JSContext, MutableHandleValue};
 | |
| use js::jsval::JSVal;
 | |
| 
 | |
| pub const strings: &'static [&'static str] = &[
 | |
|     %s,
 | |
| ];
 | |
| 
 | |
| impl ToJSValConvertible for super::%s {
 | |
|     unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
 | |
|         strings[*self as usize].to_jsval(cx, rval);
 | |
|     }
 | |
| }
 | |
| """ % (",\n    ".join(['"%s"' % val for val in enum.values()]), enum.identifier.name)
 | |
| 
 | |
|         self.cgRoot = CGList([
 | |
|             CGGeneric(decl),
 | |
|             CGNamespace.build([enum.identifier.name + "Values"],
 | |
|                               CGIndenter(CGGeneric(inner)), public=True),
 | |
|         ])
 | |
| 
 | |
|     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.unrestricted_float, IDLType.Tags.float,
 | |
|                IDLType.Tags.unrestricted_double, 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):
 | |
|     if type.isGeckoInterface():
 | |
|         name = type.inner.identifier.name
 | |
|         typeName = descriptorProvider.getDescriptor(name).returnType
 | |
|     elif type.isEnum():
 | |
|         name = type.inner.identifier.name
 | |
|         typeName = name
 | |
|     elif type.isDictionary():
 | |
|         name = type.name
 | |
|         typeName = name
 | |
|     elif type.isSequence() or type.isMozMap():
 | |
|         name = type.name
 | |
|         inner = getUnionTypeTemplateVars(innerContainerType(type), descriptorProvider)
 | |
|         typeName = wrapInNativeContainerType(type, CGGeneric(inner["typeName"])).define()
 | |
|     elif type.isByteString():
 | |
|         name = type.name
 | |
|         typeName = "ByteString"
 | |
|     elif type.isDOMString():
 | |
|         name = type.name
 | |
|         typeName = "DOMString"
 | |
|     elif type.isUSVString():
 | |
|         name = type.name
 | |
|         typeName = "USVString"
 | |
|     elif type.isPrimitive():
 | |
|         name = type.name
 | |
|         typeName = builtinNames[type.tag()]
 | |
|     elif type.isObject():
 | |
|         name = type.name
 | |
|         typeName = "*mut JSObject"
 | |
|     else:
 | |
|         raise TypeError("Can't handle %s in unions yet" % type)
 | |
| 
 | |
|     info = getJSToNativeConversionInfo(
 | |
|         type, descriptorProvider, failureCode="return Ok(None);",
 | |
|         exceptionCode='return Err(());',
 | |
|         isDefinitelyObject=True)
 | |
|     template = info.template
 | |
| 
 | |
|     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 = [
 | |
|             "    %s(%s)," % (v["name"], v["typeName"]) for v in templateVars
 | |
|         ]
 | |
|         enumConversions = [
 | |
|             "            %s::%s(ref inner) => inner.to_jsval(cx, rval),"
 | |
|             % (self.type, v["name"]) for v in templateVars
 | |
|         ]
 | |
|         return ("""\
 | |
| pub enum %s {
 | |
| %s
 | |
| }
 | |
| 
 | |
| impl ToJSValConvertible for %s {
 | |
|     unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
 | |
|         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 = []
 | |
| 
 | |
|         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(ConversionResult::Success(%s::%s(value))),\n"
 | |
|                 "    Ok(None) => (),\n"
 | |
|                 "}\n") % (self.type, name, self.type, name)
 | |
| 
 | |
|         interfaceMemberTypes = filter(lambda t: t.isNonCallbackInterface(), memberTypes)
 | |
|         if len(interfaceMemberTypes) > 0:
 | |
|             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.isSequence(), memberTypes)
 | |
|         if len(arrayObjectMemberTypes) > 0:
 | |
|             assert len(arrayObjectMemberTypes) == 1
 | |
|             typeName = arrayObjectMemberTypes[0].name
 | |
|             arrayObject = CGGeneric(get_match(typeName))
 | |
|             names.append(typeName)
 | |
|         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:
 | |
|             assert len(dictionaryMemberTypes) == 1
 | |
|             typeName = dictionaryMemberTypes[0].name
 | |
|             dictionaryObject = CGGeneric(get_match(typeName))
 | |
|             names.append(typeName)
 | |
|         else:
 | |
|             dictionaryObject = None
 | |
| 
 | |
|         objectMemberTypes = filter(lambda t: t.isObject(), memberTypes)
 | |
|         if len(objectMemberTypes) > 0:
 | |
|             assert len(objectMemberTypes) == 1
 | |
|             typeName = objectMemberTypes[0].name
 | |
|             object = CGGeneric(get_match(typeName))
 | |
|             names.append(typeName)
 | |
|         else:
 | |
|             object = None
 | |
| 
 | |
|         mozMapMemberTypes = filter(lambda t: t.isMozMap(), memberTypes)
 | |
|         if len(mozMapMemberTypes) > 0:
 | |
|             assert len(mozMapMemberTypes) == 1
 | |
|             typeName = mozMapMemberTypes[0].name
 | |
|             mozMapObject = CGGeneric(get_match(typeName))
 | |
|             names.append(typeName)
 | |
|         else:
 | |
|             mozMapObject = None
 | |
| 
 | |
|         hasObjectTypes = interfaceObject or arrayObject or dateObject or object or mozMapObject
 | |
|         if hasObjectTypes:
 | |
|             # "object" is not distinguishable from other types
 | |
|             assert not object or not (interfaceObject or arrayObject or dateObject or callbackObject or mozMapObject)
 | |
|             templateBody = CGList([], "\n")
 | |
|             if interfaceObject:
 | |
|                 templateBody.append(interfaceObject)
 | |
|             if arrayObject:
 | |
|                 templateBody.append(arrayObject)
 | |
|             if mozMapObject:
 | |
|                 templateBody.append(mozMapObject)
 | |
|             conversions.append(CGIfWrapper("value.get().is_object()", templateBody))
 | |
| 
 | |
|         if dictionaryObject:
 | |
|             assert not hasObjectTypes
 | |
|             conversions.append(dictionaryObject)
 | |
| 
 | |
|         stringTypes = [t for t in memberTypes if t.isString() or t.isEnum()]
 | |
|         numericTypes = [t for t in memberTypes if t.isNumeric()]
 | |
|         booleanTypes = [t for t in memberTypes if t.isBoolean()]
 | |
|         if stringTypes or numericTypes or booleanTypes:
 | |
|             assert len(stringTypes) <= 1
 | |
|             assert len(numericTypes) <= 1
 | |
|             assert len(booleanTypes) <= 1
 | |
| 
 | |
|             def getStringOrPrimitiveConversion(memberType):
 | |
|                 typename = get_name(memberType)
 | |
|                 return CGGeneric(get_match(typename))
 | |
|             other = []
 | |
|             stringConversion = map(getStringOrPrimitiveConversion, stringTypes)
 | |
|             numericConversion = map(getStringOrPrimitiveConversion, numericTypes)
 | |
|             booleanConversion = map(getStringOrPrimitiveConversion, booleanTypes)
 | |
|             if stringConversion:
 | |
|                 if booleanConversion:
 | |
|                     other.append(CGIfWrapper("value.get().is_boolean()", booleanConversion[0]))
 | |
|                 if numericConversion:
 | |
|                     other.append(CGIfWrapper("value.get().is_number()", numericConversion[0]))
 | |
|                 other.append(stringConversion[0])
 | |
|             elif numericConversion:
 | |
|                 if booleanConversion:
 | |
|                     other.append(CGIfWrapper("value.get().is_boolean()", booleanConversion[0]))
 | |
|                 other.append(numericConversion[0])
 | |
|             else:
 | |
|                 assert booleanConversion
 | |
|                 other.append(booleanConversion[0])
 | |
|             conversions.append(CGList(other, "\n\n"))
 | |
|         conversions.append(CGGeneric(
 | |
|             "throw_not_in_union(cx, \"%s\");\n"
 | |
|             "Err(())" % ", ".join(names)))
 | |
|         method = CGWrapper(
 | |
|             CGIndenter(CGList(conversions, "\n\n")),
 | |
|             pre="unsafe fn from_jsval(cx: *mut JSContext,\n"
 | |
|                 "                     value: HandleValue,\n"
 | |
|                 "                     _option: ())\n"
 | |
|                 "                     -> Result<ConversionResult<%s>, ()> {\n" % self.type,
 | |
|             post="\n}")
 | |
|         return CGWrapper(
 | |
|             CGIndenter(CGList([
 | |
|                 CGGeneric("type Config = ();"),
 | |
|                 method,
 | |
|             ], "\n")),
 | |
|             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),
 | |
|             # TryConvertToObject is unused, but not generating it while generating others is tricky.
 | |
|             pre="#[allow(dead_code)] unsafe fn TryConvertTo%s(cx: *mut JSContext, value: HandleValue) -> %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
 | |
|         assert not (override and static)
 | |
|         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 != 'priv' else ''
 | |
|         })
 | |
| 
 | |
|     def define(self, cgClass):
 | |
|         pass
 | |
| 
 | |
| 
 | |
| 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 + (
 | |
|                 "let mut ret = Rc::new(%s {\n"
 | |
|                 "%s\n"
 | |
|                 "});\n"
 | |
|                 "// Note: callback cannot be moved after calling init.\n"
 | |
|                 "match Rc::get_mut(&mut ret) {\n"
 | |
|                 "    Some(ref mut callback) => unsafe { callback.parent.init(%s, %s) },\n"
 | |
|                 "    None => unreachable!(),\n"
 | |
|                 "};\n"
 | |
|                 "ret") % (cgClass.name, '\n'.join(initializers),
 | |
|                           self.args[0].name, self.args[1].name))
 | |
| 
 | |
|     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}) -> Rc<${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 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 CGClass(CGThing):
 | |
|     def __init__(self, name, bases=[], members=[], constructors=[],
 | |
|                  destructor=None, methods=[],
 | |
|                  typedefs=[], enums=[], unions=[], templateArgs=[],
 | |
|                  templateSpecialization=[],
 | |
|                  disallowCopyConstruction=False, indent='',
 | |
|                  decorators='',
 | |
|                  extradeclarations=''):
 | |
|         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.disallowCopyConstruction = disallowCopyConstruction
 | |
|         self.indent = indent
 | |
|         self.decorators = decorators
 | |
|         self.extradeclarations = extradeclarations
 | |
| 
 | |
|     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'
 | |
| 
 | |
|         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(descriptor.binaryNameFor(operation))
 | |
|         operation = descriptor.operations[operation]
 | |
|         assert len(operation.signatures()) == 1
 | |
|         signature = operation.signatures()[0]
 | |
| 
 | |
|         (returnType, arguments) = signature
 | |
|         if operation.isGetter() and not returnType.nullable():
 | |
|             returnType = IDLNullableType(returnType.location, returnType)
 | |
| 
 | |
|         # 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]
 | |
|             info = getJSToNativeConversionInfo(
 | |
|                 argument.type, descriptor, treatNullAs=argument.treatNullAs,
 | |
|                 exceptionCode="return false;")
 | |
|             template = info.template
 | |
|             declType = info.declType
 | |
| 
 | |
|             templateValues = {
 | |
|                 "val": "value.handle()",
 | |
|             }
 | |
|             self.cgRoot.prepend(instantiateJSToNativeConversionTemplate(
 | |
|                 template, templateValues, declType, argument.identifier.name))
 | |
|             self.cgRoot.prepend(CGGeneric("rooted!(in(cx) let value = desc.value);"))
 | |
| 
 | |
|     def getArguments(self):
 | |
|         args = [(a, process_arg(a.identifier.name, a)) for a in self.arguments]
 | |
|         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("let Some(result) = result", wrap)
 | |
|         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 CGProxyNamedOperation(CGProxySpecialOperation):
 | |
|     """
 | |
|     Class to generate a call to a named operation.
 | |
|     """
 | |
|     def __init__(self, descriptor, name):
 | |
|         CGProxySpecialOperation.__init__(self, descriptor, name)
 | |
| 
 | |
|     def define(self):
 | |
|         # Our first argument is the id we're getting.
 | |
|         argName = self.arguments[0].identifier.name
 | |
|         return ("let %s = string_jsid_to_string(cx, id);\n"
 | |
|                 "let this = UnwrapProxy(proxy);\n"
 | |
|                 "let this = &*this;\n" % argName +
 | |
|                 CGProxySpecialOperation.define(self))
 | |
| 
 | |
| 
 | |
| class CGProxyNamedGetter(CGProxyNamedOperation):
 | |
|     """
 | |
|     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 CGProxyNamedPresenceChecker(CGProxyNamedGetter):
 | |
|     """
 | |
|     Class to generate a call that checks whether a named property exists.
 | |
|     For now, we just delegate to CGProxyNamedGetter
 | |
|     """
 | |
|     def __init__(self, descriptor):
 | |
|         CGProxyNamedGetter.__init__(self, descriptor)
 | |
| 
 | |
| 
 | |
| class CGProxyNamedSetter(CGProxyNamedOperation):
 | |
|     """
 | |
|     Class to generate a call to a named setter.
 | |
|     """
 | |
|     def __init__(self, descriptor):
 | |
|         CGProxySpecialOperation.__init__(self, descriptor, 'NamedSetter')
 | |
| 
 | |
| 
 | |
| class CGProxyNamedDeleter(CGProxyNamedOperation):
 | |
|     """
 | |
|     Class to generate a call to a named deleter.
 | |
|     """
 | |
|     def __init__(self, descriptor):
 | |
|         CGProxySpecialOperation.__init__(self, descriptor, 'NamedDeleter')
 | |
| 
 | |
| 
 | |
| class CGProxyUnwrap(CGAbstractMethod):
 | |
|     def __init__(self, descriptor):
 | |
|         args = [Argument('HandleObject', 'obj')]
 | |
|         CGAbstractMethod.__init__(self, descriptor, "UnwrapProxy",
 | |
|                                   '*const ' + descriptor.concreteType, args,
 | |
|                                   alwaysInline=True, unsafe=True)
 | |
| 
 | |
|     def definition_body(self):
 | |
|         return CGGeneric("""\
 | |
| /*if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
 | |
|     obj = js::UnwrapObject(obj);
 | |
| }*/
 | |
| //MOZ_ASSERT(IsProxy(obj));
 | |
| let box_ = GetProxyPrivate(obj.get()).to_private() as *const %s;
 | |
| return box_;""" % self.descriptor.concreteType)
 | |
| 
 | |
| 
 | |
| class CGDOMJSProxyHandler_getOwnPropertyDescriptor(CGAbstractExternMethod):
 | |
|     def __init__(self, descriptor):
 | |
|         args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'proxy'),
 | |
|                 Argument('HandleId', 'id'),
 | |
|                 Argument('MutableHandle<PropertyDescriptor>', 'desc')]
 | |
|         CGAbstractExternMethod.__init__(self, descriptor, "getOwnPropertyDescriptor",
 | |
|                                         "bool", args)
 | |
|         self.descriptor = descriptor
 | |
| 
 | |
|     def getBody(self):
 | |
|         indexedGetter = self.descriptor.operations['IndexedGetter']
 | |
|         indexedSetter = self.descriptor.operations['IndexedSetter']
 | |
| 
 | |
|         get = ""
 | |
|         if indexedGetter or indexedSetter:
 | |
|             get = "let index = get_array_index_from_id(cx, id);\n"
 | |
| 
 | |
|         if indexedGetter:
 | |
|             attrs = "JSPROP_ENUMERATE"
 | |
|             if self.descriptor.operations['IndexedSetter'] is None:
 | |
|                 attrs += " | JSPROP_READONLY"
 | |
|             # FIXME(#11868) Should assign to desc.value, desc.get() is a copy.
 | |
|             fillDescriptor = ("desc.get().value = result_root.get();\n"
 | |
|                               "fill_property_descriptor(desc, proxy.get(), %s);\n"
 | |
|                               "return true;" % attrs)
 | |
|             templateValues = {
 | |
|                 'jsvalRef': 'result_root.handle_mut()',
 | |
|                 'successCode': fillDescriptor,
 | |
|                 'pre': 'rooted!(in(cx) let mut result_root = UndefinedValue());'
 | |
|             }
 | |
|             get += ("if let Some(index) = index {\n" +
 | |
|                     "    let this = UnwrapProxy(proxy);\n" +
 | |
|                     "    let this = &*this;\n" +
 | |
|                     CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define() + "\n" +
 | |
|                     "}\n")
 | |
| 
 | |
|         namedGetter = self.descriptor.operations['NamedGetter']
 | |
|         if namedGetter:
 | |
|             attrs = []
 | |
|             if not self.descriptor.interface.getExtendedAttribute("LegacyUnenumerableNamedProperties"):
 | |
|                 attrs.append("JSPROP_ENUMERATE")
 | |
|             if self.descriptor.operations['NamedSetter'] is None:
 | |
|                 attrs.append("JSPROP_READONLY")
 | |
|             if attrs:
 | |
|                 attrs = " | ".join(attrs)
 | |
|             else:
 | |
|                 attrs = "0"
 | |
|             # FIXME(#11868) Should assign to desc.value, desc.get() is a copy.
 | |
|             fillDescriptor = ("desc.get().value = result_root.get();\n"
 | |
|                               "fill_property_descriptor(desc, proxy.get(), %s);\n"
 | |
|                               "return true;" % attrs)
 | |
|             templateValues = {
 | |
|                 'jsvalRef': 'result_root.handle_mut()',
 | |
|                 'successCode': fillDescriptor,
 | |
|                 'pre': 'rooted!(in(cx) let mut result_root = UndefinedValue());'
 | |
|             }
 | |
|             # Once we start supporting OverrideBuiltins we need to make
 | |
|             # ResolveOwnProperty or EnumerateOwnProperties filter out named
 | |
|             # properties that shadow prototype properties.
 | |
|             namedGet = """
 | |
| if RUST_JSID_IS_STRING(id) {
 | |
|     let mut has_on_proto = false;
 | |
|     if !has_property_on_prototype(cx, proxy, id, &mut has_on_proto) {
 | |
|         return false;
 | |
|     }
 | |
|     if !has_on_proto {
 | |
|         %s
 | |
|     }
 | |
| }
 | |
| """ % CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues), 8).define()
 | |
|         else:
 | |
|             namedGet = ""
 | |
| 
 | |
|         # FIXME(#11868) Should assign to desc.obj, desc.get() is a copy.
 | |
|         return get + """\
 | |
| rooted!(in(cx) let mut expando = ptr::null_mut());
 | |
| get_expando_object(proxy, expando.handle_mut());
 | |
| //if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) {
 | |
| if !expando.is_null() {
 | |
|     if !JS_GetPropertyDescriptorById(cx, expando.handle(), id, desc) {
 | |
|         return false;
 | |
|     }
 | |
|     if !desc.obj.is_null() {
 | |
|         // Pretend the property lives on the wrapper.
 | |
|         desc.get().obj = proxy.get();
 | |
|         return true;
 | |
|     }
 | |
| }
 | |
| """ + namedGet + """\
 | |
| desc.get().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('HandleObject', 'proxy'),
 | |
|                 Argument('HandleId', 'id'),
 | |
|                 Argument('Handle<PropertyDescriptor>', 'desc'),
 | |
|                 Argument('*mut ObjectOpResult', 'opresult')]
 | |
|         CGAbstractExternMethod.__init__(self, descriptor, "defineProperty", "bool", args)
 | |
|         self.descriptor = descriptor
 | |
| 
 | |
|     def getBody(self):
 | |
|         set = ""
 | |
| 
 | |
|         indexedSetter = self.descriptor.operations['IndexedSetter']
 | |
|         if indexedSetter:
 | |
|             set += ("let index = get_array_index_from_id(cx, id);\n" +
 | |
|                     "if let Some(index) = index {\n" +
 | |
|                     "    let this = UnwrapProxy(proxy);\n" +
 | |
|                     "    let this = &*this;\n" +
 | |
|                     CGIndenter(CGProxyIndexedSetter(self.descriptor)).define() +
 | |
|                     "    return (*opresult).succeed();\n" +
 | |
|                     "}\n")
 | |
|         elif self.descriptor.operations['IndexedGetter']:
 | |
|             set += ("if get_array_index_from_id(cx, id).is_some() {\n" +
 | |
|                     "    return (*opresult).failNoIndexedSetter();\n" +
 | |
|                     "}\n")
 | |
| 
 | |
|         namedSetter = self.descriptor.operations['NamedSetter']
 | |
|         if namedSetter:
 | |
|             if self.descriptor.hasUnforgeableMembers:
 | |
|                 raise TypeError("Can't handle a named setter on an interface that has "
 | |
|                                 "unforgeables. Figure out how that should work!")
 | |
|             set += ("if RUST_JSID_IS_STRING(id) {\n" +
 | |
|                     CGIndenter(CGProxyNamedSetter(self.descriptor)).define() +
 | |
|                     "    return (*opresult).succeed();\n" +
 | |
|                     "} else {\n" +
 | |
|                     "    return false;\n" +
 | |
|                     "}\n")
 | |
|         else:
 | |
|             set += ("if RUST_JSID_IS_STRING(id) {\n" +
 | |
|                     CGIndenter(CGProxyNamedGetter(self.descriptor)).define() +
 | |
|                     "    if result.is_some() {\n"
 | |
|                     "        return (*opresult).failNoNamedSetter();\n"
 | |
|                     "    }\n"
 | |
|                     "}\n")
 | |
|             set += "return proxyhandler::define_property(%s);" % ", ".join(a.name for a in self.args)
 | |
|         return set
 | |
| 
 | |
|     def definition_body(self):
 | |
|         return CGGeneric(self.getBody())
 | |
| 
 | |
| 
 | |
| class CGDOMJSProxyHandler_delete(CGAbstractExternMethod):
 | |
|     def __init__(self, descriptor):
 | |
|         args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'proxy'),
 | |
|                 Argument('HandleId', 'id'),
 | |
|                 Argument('*mut ObjectOpResult', 'res')]
 | |
|         CGAbstractExternMethod.__init__(self, descriptor, "delete", "bool", args)
 | |
|         self.descriptor = descriptor
 | |
| 
 | |
|     def getBody(self):
 | |
|         set = ""
 | |
|         if self.descriptor.operations['NamedDeleter']:
 | |
|             if self.descriptor.hasUnforgeableMembers:
 | |
|                 raise TypeError("Can't handle a deleter on an interface that has "
 | |
|                                 "unforgeables. Figure out how that should work!")
 | |
|             set += CGProxyNamedDeleter(self.descriptor).define()
 | |
|         set += "return proxyhandler::delete(%s);" % ", ".join(a.name for a in self.args)
 | |
|         return set
 | |
| 
 | |
|     def definition_body(self):
 | |
|         return CGGeneric(self.getBody())
 | |
| 
 | |
| 
 | |
| class CGDOMJSProxyHandler_ownPropertyKeys(CGAbstractExternMethod):
 | |
|     def __init__(self, descriptor):
 | |
|         args = [Argument('*mut JSContext', 'cx'),
 | |
|                 Argument('HandleObject', 'proxy'),
 | |
|                 Argument('*mut AutoIdVector', 'props')]
 | |
|         CGAbstractExternMethod.__init__(self, descriptor, "own_property_keys", "bool", args)
 | |
|         self.descriptor = descriptor
 | |
| 
 | |
|     def getBody(self):
 | |
|         body = dedent(
 | |
|             """
 | |
|             let unwrapped_proxy = UnwrapProxy(proxy);
 | |
|             """)
 | |
| 
 | |
|         if self.descriptor.operations['IndexedGetter']:
 | |
|             body += dedent(
 | |
|                 """
 | |
|                 for i in 0..(*unwrapped_proxy).Length() {
 | |
|                     rooted!(in(cx) let rooted_jsid = int_to_jsid(i as i32));
 | |
|                     AppendToAutoIdVector(props, rooted_jsid.handle().get());
 | |
|                 }
 | |
|                 """)
 | |
| 
 | |
|         if self.descriptor.operations['NamedGetter']:
 | |
|             body += dedent(
 | |
|                 """
 | |
|                 for name in (*unwrapped_proxy).SupportedPropertyNames() {
 | |
|                     let cstring = CString::new(name).unwrap();
 | |
|                     let jsstring = JS_AtomizeAndPinString(cx, cstring.as_ptr());
 | |
|                     rooted!(in(cx) let rooted = jsstring);
 | |
|                     let jsid = INTERNED_STRING_TO_JSID(cx, rooted.handle().get());
 | |
|                     rooted!(in(cx) let rooted_jsid = jsid);
 | |
|                     AppendToAutoIdVector(props, rooted_jsid.handle().get());
 | |
|                 }
 | |
|                 """)
 | |
| 
 | |
|         body += dedent(
 | |
|             """
 | |
|             rooted!(in(cx) let mut expando = ptr::null_mut());
 | |
|             get_expando_object(proxy, expando.handle_mut());
 | |
|             if !expando.is_null() {
 | |
|                 GetPropertyKeys(cx, expando.handle(), JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props);
 | |
|             }
 | |
| 
 | |
|             return true;
 | |
|             """)
 | |
| 
 | |
|         return body
 | |
| 
 | |
|     def definition_body(self):
 | |
|         return CGGeneric(self.getBody())
 | |
| 
 | |
| 
 | |
| class CGDOMJSProxyHandler_getOwnEnumerablePropertyKeys(CGAbstractExternMethod):
 | |
|     def __init__(self, descriptor):
 | |
|         assert (descriptor.operations["IndexedGetter"] and
 | |
|                 descriptor.interface.getExtendedAttribute("LegacyUnenumerableNamedProperties"))
 | |
|         args = [Argument('*mut JSContext', 'cx'),
 | |
|                 Argument('HandleObject', 'proxy'),
 | |
|                 Argument('*mut AutoIdVector', 'props')]
 | |
|         CGAbstractExternMethod.__init__(self, descriptor,
 | |
|                                         "getOwnEnumerablePropertyKeys", "bool", args)
 | |
|         self.descriptor = descriptor
 | |
| 
 | |
|     def getBody(self):
 | |
|         body = dedent(
 | |
|             """
 | |
|             let unwrapped_proxy = UnwrapProxy(proxy);
 | |
|             """)
 | |
| 
 | |
|         if self.descriptor.operations['IndexedGetter']:
 | |
|             body += dedent(
 | |
|                 """
 | |
|                 for i in 0..(*unwrapped_proxy).Length() {
 | |
|                     rooted!(in(cx) let rooted_jsid = int_to_jsid(i as i32));
 | |
|                     AppendToAutoIdVector(props, rooted_jsid.handle().get());
 | |
|                 }
 | |
|                 """)
 | |
| 
 | |
|         body += dedent(
 | |
|             """
 | |
|             rooted!(in(cx) let mut expando = ptr::null_mut());
 | |
|             get_expando_object(proxy, expando.handle_mut());
 | |
|             if !expando.is_null() {
 | |
|                 GetPropertyKeys(cx, expando.handle(), JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props);
 | |
|             }
 | |
| 
 | |
|             return true;
 | |
|             """)
 | |
| 
 | |
|         return body
 | |
| 
 | |
|     def definition_body(self):
 | |
|         return CGGeneric(self.getBody())
 | |
| 
 | |
| 
 | |
| class CGDOMJSProxyHandler_hasOwn(CGAbstractExternMethod):
 | |
|     def __init__(self, descriptor):
 | |
|         args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'proxy'),
 | |
|                 Argument('HandleId', '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 = get_array_index_from_id(cx, id);\n" +
 | |
|                        "if let Some(index) = index {\n" +
 | |
|                        "    let this = UnwrapProxy(proxy);\n" +
 | |
|                        "    let this = &*this;\n" +
 | |
|                        CGIndenter(CGProxyIndexedGetter(self.descriptor)).define() + "\n" +
 | |
|                        "    *bp = result.is_some();\n" +
 | |
|                        "    return true;\n" +
 | |
|                        "}\n\n")
 | |
|         else:
 | |
|             indexed = ""
 | |
| 
 | |
|         namedGetter = self.descriptor.operations['NamedGetter']
 | |
|         if namedGetter:
 | |
|             named = """\
 | |
| if RUST_JSID_IS_STRING(id) {
 | |
|     let mut has_on_proto = false;
 | |
|     if !has_property_on_prototype(cx, proxy, id, &mut has_on_proto) {
 | |
|         return false;
 | |
|     }
 | |
|     if !has_on_proto {
 | |
|         %s
 | |
|         *bp = result.is_some();
 | |
|         return true;
 | |
|     }
 | |
| }
 | |
| 
 | |
| """ % CGIndenter(CGProxyNamedGetter(self.descriptor), 8).define()
 | |
|         else:
 | |
|             named = ""
 | |
| 
 | |
|         return indexed + """\
 | |
| rooted!(in(cx) let mut expando = ptr::null_mut());
 | |
| get_expando_object(proxy, expando.handle_mut());
 | |
| if !expando.is_null() {
 | |
|     let ok = JS_HasPropertyById(cx, expando.handle(), id, bp);
 | |
|     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('HandleObject', 'proxy'),
 | |
|                 Argument('HandleValue', 'receiver'), Argument('HandleId', 'id'),
 | |
|                 Argument('MutableHandleValue', 'vp')]
 | |
|         CGAbstractExternMethod.__init__(self, descriptor, "get", "bool", args)
 | |
|         self.descriptor = descriptor
 | |
| 
 | |
|     def getBody(self):
 | |
|         getFromExpando = """\
 | |
| rooted!(in(cx) let mut expando = ptr::null_mut());
 | |
| get_expando_object(proxy, expando.handle_mut());
 | |
| if !expando.is_null() {
 | |
|     let mut hasProp = false;
 | |
|     if !JS_HasPropertyById(cx, expando.handle(), id, &mut hasProp) {
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     if hasProp {
 | |
|         return JS_ForwardGetPropertyTo(cx, expando.handle(), id, receiver, vp);
 | |
|     }
 | |
| }"""
 | |
| 
 | |
|         templateValues = {
 | |
|             'jsvalRef': 'vp',
 | |
|             'successCode': 'return true;',
 | |
|         }
 | |
| 
 | |
|         indexedGetter = self.descriptor.operations['IndexedGetter']
 | |
|         if indexedGetter:
 | |
|             getIndexedOrExpando = ("let index = get_array_index_from_id(cx, id);\n" +
 | |
|                                    "if let Some(index) = index {\n" +
 | |
|                                    "    let this = UnwrapProxy(proxy);\n" +
 | |
|                                    "    let this = &*this;\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:
 | |
|             getNamed = ("if RUST_JSID_IS_STRING(id) {\n" +
 | |
|                         CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() +
 | |
|                         "}\n")
 | |
|         else:
 | |
|             getNamed = ""
 | |
| 
 | |
|         return """\
 | |
| //MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
 | |
| //"Should not have a XrayWrapper here");
 | |
| 
 | |
| %s
 | |
| let mut found = false;
 | |
| if !get_property_on_prototype(cx, proxy, receiver, id, &mut found, vp) {
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| if found {
 | |
|     return true;
 | |
| }
 | |
| %s
 | |
| vp.set(UndefinedValue());
 | |
| return true;""" % (getIndexedOrExpando, getNamed)
 | |
| 
 | |
|     def definition_body(self):
 | |
|         return CGGeneric(self.getBody())
 | |
| 
 | |
| 
 | |
| class CGDOMJSProxyHandler_className(CGAbstractExternMethod):
 | |
|     def __init__(self, descriptor):
 | |
|         args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', '_proxy')]
 | |
|         CGAbstractExternMethod.__init__(self, descriptor, "className", "*const i8", args, doesNotPanic=True)
 | |
|         self.descriptor = descriptor
 | |
| 
 | |
|     def getBody(self):
 | |
|         return '%s as *const u8 as *const i8' % str_to_const_array(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, doesNotPanic=False):
 | |
|         CGAbstractExternMethod.__init__(self, descriptor, name, returnType,
 | |
|                                         args)
 | |
| 
 | |
|     def definition_body_prologue(self):
 | |
|         return CGGeneric("""
 | |
| let this = native_from_object::<%s>(obj).unwrap();
 | |
| """ % self.descriptor.concreteType)
 | |
| 
 | |
|     def definition_body(self):
 | |
|         return CGList([
 | |
|             self.definition_body_prologue(),
 | |
|             self.generate_code(),
 | |
|         ])
 | |
| 
 | |
|     def generate_code(self):
 | |
|         raise NotImplementedError  # Override me!
 | |
| 
 | |
| 
 | |
| def finalizeHook(descriptor, hookName, context):
 | |
|     release = ""
 | |
|     if descriptor.isGlobal():
 | |
|         release += """\
 | |
| finalize_global(obj);
 | |
| """
 | |
|     elif descriptor.weakReferenceable:
 | |
|         release += """\
 | |
| let weak_box_ptr = JS_GetReservedSlot(obj, DOM_WEAK_SLOT).to_private() as *mut WeakBox<%s>;
 | |
| if !weak_box_ptr.is_null() {
 | |
|     let count = {
 | |
|         let weak_box = &*weak_box_ptr;
 | |
|         assert!(weak_box.value.get().is_some());
 | |
|         assert!(weak_box.count.get() > 0);
 | |
|         weak_box.value.set(None);
 | |
|         let count = weak_box.count.get() - 1;
 | |
|         weak_box.count.set(count);
 | |
|         count
 | |
|     };
 | |
|     if count == 0 {
 | |
|         mem::drop(Box::from_raw(weak_box_ptr));
 | |
|     }
 | |
| }
 | |
| """ % descriptor.concreteType
 | |
|     release += """\
 | |
| if !this.is_null() {
 | |
|     // The pointer can be null if the object is the unforgeable holder of that interface.
 | |
|     let _ = Box::from_raw(this as *mut %s);
 | |
| }
 | |
| 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, doesNotPanic=True)
 | |
|         self.traceGlobal = descriptor.isGlobal()
 | |
| 
 | |
|     def generate_code(self):
 | |
|         body = [CGGeneric("if this.is_null() { return; } // GC during obj creation\n"
 | |
|                           "(*this).trace(%s);" % self.args[0].name)]
 | |
|         if self.traceGlobal:
 | |
|             body += [CGGeneric("trace_global(trc, obj);")]
 | |
|         return CGList(body, "\n")
 | |
| 
 | |
| 
 | |
| class CGClassConstructHook(CGAbstractExternMethod):
 | |
|     """
 | |
|     JS-visible constructor for our objects
 | |
|     """
 | |
|     def __init__(self, descriptor, constructor=None):
 | |
|         args = [Argument('*mut JSContext', 'cx'), Argument('u32', 'argc'), Argument('*mut JSVal', 'vp')]
 | |
|         name = CONSTRUCT_HOOK_NAME
 | |
|         if constructor:
 | |
|             name += "_" + constructor.identifier.name
 | |
|         else:
 | |
|             constructor = descriptor.interface.ctor()
 | |
|             assert constructor
 | |
|         CGAbstractExternMethod.__init__(self, descriptor, name, 'bool', args)
 | |
|         self.constructor = constructor
 | |
|         self.exposureSet = descriptor.interface.exposureSet
 | |
| 
 | |
|     def definition_body(self):
 | |
|         preamble = """let global = GlobalScope::from_object(JS_CALLEE(cx, vp).to_object());\n"""
 | |
|         if len(self.exposureSet) == 1:
 | |
|             preamble += "let global = Root::downcast::<dom::types::%s>(global).unwrap();\n" % list(self.exposureSet)[0]
 | |
|         preamble += """let args = CallArgs::from_vp(vp, argc);\n"""
 | |
|         preamble = CGGeneric(preamble)
 | |
|         name = self.constructor.identifier.name
 | |
|         nativeName = MakeNativeName(self.descriptor.binaryNameFor(name))
 | |
|         callGenerator = CGMethodCall(["&global"], nativeName, True,
 | |
|                                      self.descriptor, self.constructor)
 | |
|         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) + ";\n"
 | |
| 
 | |
| 
 | |
| 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.isMaplikeOrSetlikeOrIterableMethod() and
 | |
|                         (not m.isIdentifierLess() or m.isStringifier())):
 | |
|                     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)
 | |
|                     yield (name,
 | |
|                            attribute_arguments(typeNeedsCx(m.type, True)),
 | |
|                            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(typeNeedsCx(m.type, False), m.type), rettype
 | |
| 
 | |
|             if descriptor.proxy:
 | |
|                 for name, operation in descriptor.operations.iteritems():
 | |
|                     if not operation or operation.isStringifier():
 | |
|                         continue
 | |
| 
 | |
|                     assert len(operation.signatures()) == 1
 | |
|                     rettype, arguments = operation.signatures()[0]
 | |
| 
 | |
|                     infallible = 'infallible' in descriptor.getExtendedAttributes(operation)
 | |
|                     if operation.isGetter():
 | |
|                         if not rettype.nullable():
 | |
|                             rettype = IDLNullableType(rettype.location, rettype)
 | |
|                         arguments = method_arguments(descriptor, rettype, arguments)
 | |
| 
 | |
|                         # If this interface 'supports named properties', then we
 | |
|                         # should be able to access 'supported property names'
 | |
|                         #
 | |
|                         # WebIDL, Second Draft, section 3.2.4.5
 | |
|                         # https://heycam.github.io/webidl/#idl-named-properties
 | |
|                         if operation.isNamed():
 | |
|                             yield "SupportedPropertyNames", [], "Vec<DOMString>"
 | |
|                     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)
 | |
| 
 | |
|         def contains_unsafe_arg(arguments):
 | |
|             if not arguments or len(arguments) == 0:
 | |
|                 return False
 | |
|             return reduce((lambda x, y: x or y[1] == '*mut JSContext'), arguments, False)
 | |
| 
 | |
|         methods = []
 | |
|         for name, arguments, rettype in members():
 | |
|             arguments = list(arguments)
 | |
|             methods.append(CGGeneric("%sfn %s(&self%s) -> %s;\n" % (
 | |
|                 'unsafe ' if contains_unsafe_arg(arguments) else '',
 | |
|                 name, fmt(arguments), rettype))
 | |
|             )
 | |
| 
 | |
|         if methods:
 | |
|             self.cgRoot = CGWrapper(CGIndenter(CGList(methods, "")),
 | |
|                                     pre="pub trait %sMethods {\n" % descriptor.interface.identifier.name,
 | |
|                                     post="}")
 | |
|         else:
 | |
|             self.cgRoot = CGGeneric("")
 | |
|         self.empty = not methods
 | |
| 
 | |
|     def define(self):
 | |
|         return self.cgRoot.define()
 | |
| 
 | |
| 
 | |
| class CGWeakReferenceableTrait(CGThing):
 | |
|     def __init__(self, descriptor):
 | |
|         CGThing.__init__(self)
 | |
|         assert descriptor.weakReferenceable
 | |
|         self.code = "impl WeakReferenceable for %s {}" % descriptor.interface.identifier.name
 | |
| 
 | |
|     def define(self):
 | |
|         return self.code
 | |
| 
 | |
| 
 | |
| def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries=None, enums=None):
 | |
|     if not callbacks:
 | |
|         callbacks = []
 | |
|     if not dictionaries:
 | |
|         dictionaries = []
 | |
|     if not enums:
 | |
|         enums = []
 | |
| 
 | |
|     return CGImports(cgthings, descriptors, callbacks, dictionaries, enums, [
 | |
|         'core::nonzero::NonZero',
 | |
|         'js',
 | |
|         'js::JSCLASS_GLOBAL_SLOT_COUNT',
 | |
|         'js::JSCLASS_IS_DOMJSCLASS',
 | |
|         'js::JSCLASS_IS_GLOBAL',
 | |
|         'js::JSCLASS_RESERVED_SLOTS_MASK',
 | |
|         'js::JS_CALLEE',
 | |
|         'js::error::throw_type_error',
 | |
|         'js::jsapi::AutoIdVector',
 | |
|         'js::jsapi::Call',
 | |
|         'js::jsapi::CallArgs',
 | |
|         'js::jsapi::CurrentGlobalOrNull',
 | |
|         'js::jsapi::FreeOp',
 | |
|         'js::jsapi::GetPropertyKeys',
 | |
|         'js::jsapi::GetWellKnownSymbol',
 | |
|         'js::jsapi::Handle',
 | |
|         'js::jsapi::HandleId',
 | |
|         'js::jsapi::HandleObject',
 | |
|         'js::jsapi::HandleValue',
 | |
|         'js::jsapi::HandleValueArray',
 | |
|         'js::jsapi::Heap',
 | |
|         'js::jsapi::INTERNED_STRING_TO_JSID',
 | |
|         'js::jsapi::IsCallable',
 | |
|         'js::jsapi::JSAutoCompartment',
 | |
|         'js::jsapi::JSCLASS_RESERVED_SLOTS_SHIFT',
 | |
|         'js::jsapi::JSClass',
 | |
|         'js::jsapi::JSContext',
 | |
|         'js::jsapi::JSFreeOp',
 | |
|         'js::jsapi::JSFunctionSpec',
 | |
|         'js::jsapi::JSITER_HIDDEN',
 | |
|         'js::jsapi::JSITER_OWNONLY',
 | |
|         'js::jsapi::JSITER_SYMBOLS',
 | |
|         'js::jsapi::JSJitGetterCallArgs',
 | |
|         'js::jsapi::JSJitInfo',
 | |
|         'js::jsapi::JSJitInfo_AliasSet',
 | |
|         'js::jsapi::JSJitInfo_ArgType',
 | |
|         'js::jsapi::JSJitInfo_OpType',
 | |
|         'js::jsapi::JSJitMethodCallArgs',
 | |
|         'js::jsapi::JSJitSetterCallArgs',
 | |
|         'js::jsapi::JSNative',
 | |
|         'js::jsapi::JSNativeWrapper',
 | |
|         'js::jsapi::JSObject',
 | |
|         'js::jsapi::JSPROP_ENUMERATE',
 | |
|         'js::jsapi::JSPROP_PERMANENT',
 | |
|         'js::jsapi::JSPROP_READONLY',
 | |
|         'js::jsapi::JSPROP_SHARED',
 | |
|         'js::jsapi::JSPropertySpec',
 | |
|         'js::jsapi::JSString',
 | |
|         'js::jsapi::JSTracer',
 | |
|         'js::jsapi::JSType',
 | |
|         'js::jsapi::JSTypedMethodJitInfo',
 | |
|         'js::jsapi::JSValueType',
 | |
|         'js::jsapi::JS_AtomizeAndPinString',
 | |
|         'js::jsapi::JS_CallFunctionValue',
 | |
|         'js::jsapi::JS_CopyPropertiesFrom',
 | |
|         'js::jsapi::JS_DefineProperty',
 | |
|         'js::jsapi::JS_DefinePropertyById2',
 | |
|         'js::jsapi::JS_ForwardGetPropertyTo',
 | |
|         'js::jsapi::JS_GetErrorPrototype',
 | |
|         'js::jsapi::JS_GetFunctionPrototype',
 | |
|         'js::jsapi::JS_GetGlobalForObject',
 | |
|         'js::jsapi::JS_GetIteratorPrototype',
 | |
|         'js::jsapi::JS_GetObjectPrototype',
 | |
|         'js::jsapi::JS_GetProperty',
 | |
|         'js::jsapi::JS_GetPropertyById',
 | |
|         'js::jsapi::JS_GetPropertyDescriptorById',
 | |
|         'js::jsapi::JS_GetReservedSlot',
 | |
|         'js::jsapi::JS_HasProperty',
 | |
|         'js::jsapi::JS_HasPropertyById',
 | |
|         'js::jsapi::JS_InitializePropertiesFromCompatibleNativeObject',
 | |
|         'js::jsapi::JS_NewObject',
 | |
|         'js::jsapi::JS_NewObjectWithGivenProto',
 | |
|         'js::jsapi::JS_NewObjectWithoutMetadata',
 | |
|         'js::jsapi::JS_ObjectIsDate',
 | |
|         'js::jsapi::JS_SetImmutablePrototype',
 | |
|         'js::jsapi::JS_SetProperty',
 | |
|         'js::jsapi::JS_SetReservedSlot',
 | |
|         'js::jsapi::JS_SplicePrototype',
 | |
|         'js::jsapi::JS_WrapValue',
 | |
|         'js::jsapi::MutableHandle',
 | |
|         'js::jsapi::MutableHandleObject',
 | |
|         'js::jsapi::MutableHandleValue',
 | |
|         'js::jsapi::ObjectOpResult',
 | |
|         'js::jsapi::PropertyDescriptor',
 | |
|         'js::jsapi::RootedId',
 | |
|         'js::jsapi::RootedObject',
 | |
|         'js::jsapi::RootedString',
 | |
|         'js::jsapi::SymbolCode',
 | |
|         'js::jsapi::jsid',
 | |
|         'js::jsval::JSVal',
 | |
|         'js::jsval::NullValue',
 | |
|         'js::jsval::ObjectValue',
 | |
|         'js::jsval::ObjectOrNullValue',
 | |
|         'js::jsval::PrivateValue',
 | |
|         'js::jsval::UndefinedValue',
 | |
|         'js::glue::AppendToAutoIdVector',
 | |
|         'js::glue::CallJitGetterOp',
 | |
|         'js::glue::CallJitMethodOp',
 | |
|         'js::glue::CallJitSetterOp',
 | |
|         'js::glue::CreateProxyHandler',
 | |
|         'js::glue::GetProxyPrivate',
 | |
|         'js::glue::NewProxyObject',
 | |
|         'js::glue::ProxyTraps',
 | |
|         'js::glue::RUST_JSID_IS_STRING',
 | |
|         'js::glue::RUST_SYMBOL_TO_JSID',
 | |
|         'js::glue::int_to_jsid',
 | |
|         'js::panic::maybe_resume_unwind',
 | |
|         'js::panic::wrap_panic',
 | |
|         'js::rust::GCMethods',
 | |
|         'js::rust::define_methods',
 | |
|         'js::rust::define_properties',
 | |
|         'js::rust::get_object_class',
 | |
|         'dom',
 | |
|         'dom::bindings',
 | |
|         'dom::bindings::codegen::InterfaceObjectMap',
 | |
|         'dom::bindings::constant::ConstantSpec',
 | |
|         'dom::bindings::constant::ConstantVal',
 | |
|         'dom::bindings::interface::ConstructorClassHook',
 | |
|         'dom::bindings::interface::InterfaceConstructorBehavior',
 | |
|         'dom::bindings::interface::NonCallbackInterfaceObjectClass',
 | |
|         'dom::bindings::interface::create_callback_interface_object',
 | |
|         'dom::bindings::interface::create_global_object',
 | |
|         'dom::bindings::interface::create_interface_prototype_object',
 | |
|         'dom::bindings::interface::create_named_constructors',
 | |
|         'dom::bindings::interface::create_noncallback_interface_object',
 | |
|         'dom::bindings::interface::define_guarded_constants',
 | |
|         'dom::bindings::interface::define_guarded_methods',
 | |
|         'dom::bindings::interface::define_guarded_properties',
 | |
|         'dom::bindings::interface::is_exposed_in',
 | |
|         'dom::bindings::iterable::Iterable',
 | |
|         'dom::bindings::iterable::IteratorType',
 | |
|         'dom::bindings::js::JS',
 | |
|         'dom::bindings::js::Root',
 | |
|         'dom::bindings::js::RootedReference',
 | |
|         'dom::bindings::namespace::NamespaceObjectClass',
 | |
|         'dom::bindings::namespace::create_namespace_object',
 | |
|         'dom::bindings::reflector::MutDomObject',
 | |
|         'dom::bindings::reflector::DomObject',
 | |
|         'dom::bindings::utils::DOMClass',
 | |
|         'dom::bindings::utils::DOMJSClass',
 | |
|         'dom::bindings::utils::DOM_PROTO_UNFORGEABLE_HOLDER_SLOT',
 | |
|         'dom::bindings::utils::JSCLASS_DOM_GLOBAL',
 | |
|         'dom::bindings::utils::ProtoOrIfaceArray',
 | |
|         'dom::bindings::utils::enumerate_global',
 | |
|         'dom::bindings::utils::finalize_global',
 | |
|         'dom::bindings::utils::find_enum_string_index',
 | |
|         'dom::bindings::utils::generic_getter',
 | |
|         'dom::bindings::utils::generic_lenient_getter',
 | |
|         'dom::bindings::utils::generic_lenient_setter',
 | |
|         'dom::bindings::utils::generic_method',
 | |
|         'dom::bindings::utils::generic_setter',
 | |
|         'dom::bindings::utils::get_array_index_from_id',
 | |
|         'dom::bindings::utils::get_dictionary_property',
 | |
|         'dom::bindings::utils::get_property_on_prototype',
 | |
|         'dom::bindings::utils::get_proto_or_iface_array',
 | |
|         'dom::bindings::utils::has_property_on_prototype',
 | |
|         'dom::bindings::utils::is_platform_object',
 | |
|         'dom::bindings::utils::resolve_global',
 | |
|         'dom::bindings::utils::set_dictionary_property',
 | |
|         'dom::bindings::utils::trace_global',
 | |
|         'dom::bindings::trace::JSTraceable',
 | |
|         'dom::bindings::trace::RootedTraceable',
 | |
|         'dom::bindings::callback::CallSetup',
 | |
|         'dom::bindings::callback::CallbackContainer',
 | |
|         'dom::bindings::callback::CallbackInterface',
 | |
|         'dom::bindings::callback::CallbackFunction',
 | |
|         'dom::bindings::callback::CallbackObject',
 | |
|         'dom::bindings::callback::ExceptionHandling',
 | |
|         'dom::bindings::callback::wrap_call_this_object',
 | |
|         'dom::bindings::conversions::ConversionBehavior',
 | |
|         'dom::bindings::conversions::ConversionResult',
 | |
|         'dom::bindings::conversions::DOM_OBJECT_SLOT',
 | |
|         'dom::bindings::conversions::FromJSValConvertible',
 | |
|         'dom::bindings::conversions::IDLInterface',
 | |
|         'dom::bindings::conversions::StringificationBehavior',
 | |
|         'dom::bindings::conversions::ToJSValConvertible',
 | |
|         'dom::bindings::conversions::is_array_like',
 | |
|         'dom::bindings::conversions::native_from_handlevalue',
 | |
|         'dom::bindings::conversions::native_from_object',
 | |
|         'dom::bindings::conversions::private_from_object',
 | |
|         'dom::bindings::conversions::root_from_handleobject',
 | |
|         'dom::bindings::conversions::root_from_handlevalue',
 | |
|         'dom::bindings::conversions::root_from_object',
 | |
|         'dom::bindings::conversions::string_jsid_to_string',
 | |
|         'dom::bindings::codegen::PrototypeList',
 | |
|         'dom::bindings::codegen::RegisterBindings',
 | |
|         'dom::bindings::codegen::UnionTypes',
 | |
|         'dom::bindings::error::Error',
 | |
|         'dom::bindings::error::ErrorResult',
 | |
|         'dom::bindings::error::Fallible',
 | |
|         'dom::bindings::error::Error::JSFailed',
 | |
|         'dom::bindings::error::throw_dom_exception',
 | |
|         'dom::bindings::guard::Condition',
 | |
|         'dom::bindings::guard::Guard',
 | |
|         'dom::bindings::inheritance::Castable',
 | |
|         'dom::bindings::proxyhandler',
 | |
|         'dom::bindings::proxyhandler::ensure_expando_object',
 | |
|         'dom::bindings::proxyhandler::fill_property_descriptor',
 | |
|         'dom::bindings::proxyhandler::get_expando_object',
 | |
|         'dom::bindings::proxyhandler::get_property_descriptor',
 | |
|         'dom::bindings::mozmap::MozMap',
 | |
|         'dom::bindings::num::Finite',
 | |
|         'dom::bindings::str::ByteString',
 | |
|         'dom::bindings::str::DOMString',
 | |
|         'dom::bindings::str::USVString',
 | |
|         'dom::bindings::weakref::DOM_WEAK_SLOT',
 | |
|         'dom::bindings::weakref::WeakBox',
 | |
|         'dom::bindings::weakref::WeakReferenceable',
 | |
|         'dom::browsingcontext::BrowsingContext',
 | |
|         'dom::globalscope::GlobalScope',
 | |
|         'mem::heap_size_of_raw_self_and_children',
 | |
|         'libc',
 | |
|         'servo_config::prefs::PREFS',
 | |
|         'std::borrow::ToOwned',
 | |
|         'std::cmp',
 | |
|         'std::mem',
 | |
|         'std::num',
 | |
|         'std::os',
 | |
|         'std::panic',
 | |
|         'std::ptr',
 | |
|         'std::str',
 | |
|         'std::rc',
 | |
|         'std::rc::Rc',
 | |
|         'std::default::Default',
 | |
|         'std::ffi::CString',
 | |
|     ], config)
 | |
| 
 | |
| 
 | |
| class CGDescriptor(CGThing):
 | |
|     def __init__(self, descriptor, config, soleDescriptor):
 | |
|         CGThing.__init__(self)
 | |
| 
 | |
|         assert not descriptor.concrete or not descriptor.interface.isCallback()
 | |
| 
 | |
|         reexports = []
 | |
| 
 | |
|         def reexportedName(name):
 | |
|             if name.startswith(descriptor.name):
 | |
|                 return name
 | |
|             if not soleDescriptor:
 | |
|                 return '%s as %s%s' % (name, descriptor.name, name)
 | |
|             return name
 | |
| 
 | |
|         cgThings = []
 | |
| 
 | |
|         unscopableNames = []
 | |
|         for m in descriptor.interface.members:
 | |
|             if (m.isMethod() and
 | |
|                     (not m.isIdentifierLess() or m == descriptor.operations["Stringifier"])):
 | |
|                 if m.getExtendedAttribute("Unscopable"):
 | |
|                     assert not m.isStatic()
 | |
|                     unscopableNames.append(m.identifier.name)
 | |
|                 if m.isStatic():
 | |
|                     assert descriptor.interface.hasInterfaceObject()
 | |
|                     cgThings.append(CGStaticMethod(descriptor, m))
 | |
|                 elif not descriptor.interface.isCallback():
 | |
|                     cgThings.append(CGSpecializedMethod(descriptor, m))
 | |
|                     cgThings.append(CGMemberJITInfo(descriptor, m))
 | |
|             elif m.isAttr():
 | |
|                 if m.stringifier:
 | |
|                     raise TypeError("Stringifier attributes not supported yet. "
 | |
|                                     "See https://github.com/servo/servo/issues/7590\n"
 | |
|                                     "%s" % m.location)
 | |
|                 if m.getExtendedAttribute("Unscopable"):
 | |
|                     assert not m.isStatic()
 | |
|                     unscopableNames.append(m.identifier.name)
 | |
|                 if m.isStatic():
 | |
|                     assert descriptor.interface.hasInterfaceObject()
 | |
|                     cgThings.append(CGStaticGetter(descriptor, m))
 | |
|                 elif not descriptor.interface.isCallback():
 | |
|                     cgThings.append(CGSpecializedGetter(descriptor, m))
 | |
| 
 | |
|                 if not m.readonly:
 | |
|                     if m.isStatic():
 | |
|                         assert descriptor.interface.hasInterfaceObject()
 | |
|                         cgThings.append(CGStaticSetter(descriptor, m))
 | |
|                     elif not descriptor.interface.isCallback():
 | |
|                         cgThings.append(CGSpecializedSetter(descriptor, m))
 | |
|                 elif m.getExtendedAttribute("PutForwards"):
 | |
|                     cgThings.append(CGSpecializedForwardingSetter(descriptor, m))
 | |
|                 elif m.getExtendedAttribute("Replaceable"):
 | |
|                     cgThings.append(CGSpecializedReplaceableSetter(descriptor, m))
 | |
| 
 | |
|                 if (not m.isStatic() and not descriptor.interface.isCallback()):
 | |
|                     cgThings.append(CGMemberJITInfo(descriptor, m))
 | |
| 
 | |
|         if descriptor.concrete:
 | |
|             cgThings.append(CGClassFinalizeHook(descriptor))
 | |
|             cgThings.append(CGClassTraceHook(descriptor))
 | |
| 
 | |
|         # If there are no constant members, don't make a module for constants
 | |
|         constMembers = [m for m in descriptor.interface.members if m.isConst()]
 | |
|         if constMembers:
 | |
|             cgThings.append(CGNamespace.build([descriptor.name + "Constants"],
 | |
|                                               CGConstant(constMembers),
 | |
|                                               public=True))
 | |
|             reexports.append(descriptor.name + 'Constants')
 | |
| 
 | |
|         if descriptor.proxy:
 | |
|             cgThings.append(CGDefineProxyHandler(descriptor))
 | |
| 
 | |
|         properties = PropertyArrays(descriptor)
 | |
| 
 | |
|         if descriptor.concrete:
 | |
|             if descriptor.proxy:
 | |
|                 # cgThings.append(CGProxyIsProxy(descriptor))
 | |
|                 cgThings.append(CGProxyUnwrap(descriptor))
 | |
|                 cgThings.append(CGDOMJSProxyHandlerDOMClass(descriptor))
 | |
|                 cgThings.append(CGDOMJSProxyHandler_ownPropertyKeys(descriptor))
 | |
|                 if descriptor.interface.getExtendedAttribute("LegacyUnenumerableNamedProperties"):
 | |
|                     cgThings.append(CGDOMJSProxyHandler_getOwnEnumerablePropertyKeys(descriptor))
 | |
|                 cgThings.append(CGDOMJSProxyHandler_getOwnPropertyDescriptor(descriptor))
 | |
|                 cgThings.append(CGDOMJSProxyHandler_className(descriptor))
 | |
|                 cgThings.append(CGDOMJSProxyHandler_get(descriptor))
 | |
|                 cgThings.append(CGDOMJSProxyHandler_hasOwn(descriptor))
 | |
| 
 | |
|                 if descriptor.operations['IndexedSetter'] or descriptor.operations['NamedSetter']:
 | |
|                     cgThings.append(CGDOMJSProxyHandler_defineProperty(descriptor))
 | |
| 
 | |
|                 # We want to prevent indexed deleters from compiling at all.
 | |
|                 assert not descriptor.operations['IndexedDeleter']
 | |
| 
 | |
|                 if descriptor.operations['NamedDeleter']:
 | |
|                     cgThings.append(CGDOMJSProxyHandler_delete(descriptor))
 | |
| 
 | |
|                 # cgThings.append(CGDOMJSProxyHandler(descriptor))
 | |
|                 # cgThings.append(CGIsMethod(descriptor))
 | |
|                 pass
 | |
|             else:
 | |
|                 cgThings.append(CGDOMJSClass(descriptor))
 | |
|                 pass
 | |
| 
 | |
|             if descriptor.isGlobal():
 | |
|                 cgThings.append(CGWrapGlobalMethod(descriptor, properties))
 | |
|             else:
 | |
|                 cgThings.append(CGWrapMethod(descriptor))
 | |
|             reexports.append('Wrap')
 | |
| 
 | |
|         haveUnscopables = False
 | |
|         if not descriptor.interface.isCallback() and not descriptor.interface.isNamespace():
 | |
|             if unscopableNames:
 | |
|                 haveUnscopables = True
 | |
|                 cgThings.append(
 | |
|                     CGList([CGGeneric("const unscopable_names: &'static [&'static [u8]] = &["),
 | |
|                             CGIndenter(CGList([CGGeneric(str_to_const_array(name)) for
 | |
|                                                name in unscopableNames], ",\n")),
 | |
|                             CGGeneric("];\n")], "\n"))
 | |
|             if descriptor.concrete or descriptor.hasDescendants():
 | |
|                 cgThings.append(CGIDLInterface(descriptor))
 | |
| 
 | |
|             interfaceTrait = CGInterfaceTrait(descriptor)
 | |
|             cgThings.append(interfaceTrait)
 | |
|             if not interfaceTrait.empty:
 | |
|                 reexports.append('%sMethods' % descriptor.name)
 | |
| 
 | |
|             if descriptor.weakReferenceable:
 | |
|                 cgThings.append(CGWeakReferenceableTrait(descriptor))
 | |
| 
 | |
|         cgThings.append(CGGeneric(str(properties)))
 | |
| 
 | |
|         if not descriptor.interface.getExtendedAttribute("Inline"):
 | |
|             if not descriptor.interface.isCallback() and not descriptor.interface.isNamespace():
 | |
|                 cgThings.append(CGGetProtoObjectMethod(descriptor))
 | |
|                 reexports.append('GetProtoObject')
 | |
|                 cgThings.append(CGPrototypeJSClass(descriptor))
 | |
|             if descriptor.interface.hasInterfaceObject():
 | |
|                 if descriptor.interface.ctor():
 | |
|                     cgThings.append(CGClassConstructHook(descriptor))
 | |
|                 for ctor in descriptor.interface.namedConstructors:
 | |
|                     cgThings.append(CGClassConstructHook(descriptor, ctor))
 | |
|                 if not descriptor.interface.isCallback():
 | |
|                     cgThings.append(CGInterfaceObjectJSClass(descriptor))
 | |
|                 if descriptor.shouldHaveGetConstructorObjectMethod():
 | |
|                     cgThings.append(CGGetConstructorObjectMethod(descriptor))
 | |
|                     reexports.append('GetConstructorObject')
 | |
|                 if descriptor.register:
 | |
|                     cgThings.append(CGDefineDOMInterfaceMethod(descriptor))
 | |
|                     reexports.append('DefineDOMInterface')
 | |
|                     cgThings.append(CGConstructorEnabled(descriptor))
 | |
|             cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties, haveUnscopables))
 | |
| 
 | |
|         cgThings = generate_imports(config, CGList(cgThings, '\n'), [descriptor])
 | |
|         cgThings = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name),
 | |
|                                          cgThings, public=True),
 | |
|                              post='\n')
 | |
| 
 | |
|         if reexports:
 | |
|             reexports = ', '.join(map(lambda name: reexportedName(name), reexports))
 | |
|             cgThings = CGList([CGGeneric('pub use self::%s::{%s};' % (toBindingNamespace(descriptor.name), reexports)),
 | |
|                                cgThings], '\n')
 | |
| 
 | |
|         self.cgRoot = cgThings
 | |
| 
 | |
|     def define(self):
 | |
|         return self.cgRoot.define()
 | |
| 
 | |
| 
 | |
| class CGNonNamespacedEnum(CGThing):
 | |
|     def __init__(self, enumName, names, first, comment="", deriving="", repr=""):
 | |
|         # Account for first value
 | |
|         entries = ["%s = %s" % (names[0], first)] + names[1:]
 | |
| 
 | |
|         # Append a Last.
 | |
|         entries.append('#[allow(dead_code)] Last = ' + str(first + 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 repr:
 | |
|             enumstr = ('#[repr(%s)]\n' % repr) + enumstr
 | |
|         if deriving:
 | |
|             enumstr = ('#[derive(%s)]\n' % deriving) + enumstr
 | |
|         curr = CGGeneric(enumstr)
 | |
| 
 | |
|         # Add some whitespace padding.
 | |
|         curr = CGWrapper(curr, pre='\n', post='\n')
 | |
| 
 | |
|         # 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,
 | |
|              getJSToNativeConversionInfo(member.type,
 | |
|                                          descriptorProvider,
 | |
|                                          isMember="Dictionary",
 | |
|                                          defaultValue=member.defaultValue,
 | |
|                                          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,\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} {\n" +
 | |
|                 "${inheritance}" +
 | |
|                 "\n".join(memberDecls) + "\n" +
 | |
|                 "}").substitute({"selfName": self.makeClassName(d),
 | |
|                                  "inheritance": inheritance}))
 | |
| 
 | |
|     def impl(self):
 | |
|         d = self.dictionary
 | |
|         if d.parent:
 | |
|             initParent = ("parent: {\n"
 | |
|                           "    match try!(%s::%s::new(cx, val)) {\n"
 | |
|                           "        ConversionResult::Success(v) => v,\n"
 | |
|                           "        ConversionResult::Failure(error) => {\n"
 | |
|                           "            throw_type_error(cx, &error);\n"
 | |
|                           "            return Err(());\n"
 | |
|                           "        }\n"
 | |
|                           "    }\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, member.type)
 | |
|             return CGGeneric("%s: %s,\n" % (name, conversion.define()))
 | |
| 
 | |
|         def varInsert(varName, dictionaryName):
 | |
|             insertion = ("rooted!(in(cx) let mut %s_js = UndefinedValue());\n"
 | |
|                          "%s.to_jsval(cx, %s_js.handle_mut());\n"
 | |
|                          "set_dictionary_property(cx, obj.handle(), \"%s\", %s_js.handle()).unwrap();"
 | |
|                          % (varName, varName, varName, dictionaryName, varName))
 | |
|             return CGGeneric(insertion)
 | |
| 
 | |
|         def memberInsert(memberInfo):
 | |
|             member, _ = memberInfo
 | |
|             name = self.makeMemberName(member.identifier.name)
 | |
|             if member.optional and not member.defaultValue:
 | |
|                 insertion = CGIfWrapper("let Some(ref %s) = self.%s" % (name, name),
 | |
|                                         varInsert(name, member.identifier.name))
 | |
|             else:
 | |
|                 insertion = CGGeneric("let %s = &self.%s;\n%s" %
 | |
|                                       (name, name, varInsert(name, member.identifier.name).define()))
 | |
|             return CGGeneric("%s\n" % insertion.define())
 | |
| 
 | |
|         memberInits = CGList([memberInit(m) for m in self.memberInfo])
 | |
|         memberInserts = CGList([memberInsert(m) for m in self.memberInfo])
 | |
| 
 | |
|         return string.Template(
 | |
|             "impl ${selfName} {\n"
 | |
|             "    pub unsafe fn empty(cx: *mut JSContext) -> ${selfName} {\n"
 | |
|             "        match ${selfName}::new(cx, HandleValue::null()) {\n"
 | |
|             "            Ok(ConversionResult::Success(v)) => v,\n"
 | |
|             "            _ => unreachable!(),\n"
 | |
|             "        }\n"
 | |
|             "    }\n"
 | |
|             "    pub unsafe fn new(cx: *mut JSContext, val: HandleValue) \n"
 | |
|             "                      -> Result<ConversionResult<${selfName}>, ()> {\n"
 | |
|             "        let object = if val.get().is_null_or_undefined() {\n"
 | |
|             "            ptr::null_mut()\n"
 | |
|             "        } else if val.get().is_object() {\n"
 | |
|             "            val.get().to_object()\n"
 | |
|             "        } else {\n"
 | |
|             "            throw_type_error(cx, \"Value not an object.\");\n"
 | |
|             "            return Err(());\n"
 | |
|             "        };\n"
 | |
|             "        rooted!(in(cx) let object = object);\n"
 | |
|             "        Ok(ConversionResult::Success(${selfName} {\n"
 | |
|             "${initParent}"
 | |
|             "${initMembers}"
 | |
|             "        }))\n"
 | |
|             "    }\n"
 | |
|             "}\n"
 | |
|             "\n"
 | |
|             "impl FromJSValConvertible for ${selfName} {\n"
 | |
|             "    type Config = ();\n"
 | |
|             "    unsafe fn from_jsval(cx: *mut JSContext, value: HandleValue, _option: ())\n"
 | |
|             "                         -> Result<ConversionResult<${selfName}>, ()> {\n"
 | |
|             "        ${selfName}::new(cx, value)\n"
 | |
|             "    }\n"
 | |
|             "}\n"
 | |
|             "\n"
 | |
|             "impl ToJSValConvertible for ${selfName} {\n"
 | |
|             "    unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {\n"
 | |
|             "        rooted!(in(cx) let obj = JS_NewObject(cx, ptr::null()));\n"
 | |
|             "${insertMembers}"
 | |
|             "        rval.set(ObjectOrNullValue(obj.get()))\n"
 | |
|             "    }\n"
 | |
|             "}\n").substitute({
 | |
|                 "selfName": self.makeClassName(d),
 | |
|                 "initParent": CGIndenter(CGGeneric(initParent), indentLevel=12).define(),
 | |
|                 "initMembers": CGIndenter(memberInits, indentLevel=12).define(),
 | |
|                 "insertMembers": CGIndenter(memberInserts, indentLevel=8).define(),
 | |
|             })
 | |
| 
 | |
|     @staticmethod
 | |
|     def makeDictionaryName(dictionary):
 | |
|         return dictionary.identifier.name
 | |
| 
 | |
|     def makeClassName(self, dictionary):
 | |
|         return self.makeDictionaryName(dictionary)
 | |
| 
 | |
|     @staticmethod
 | |
|     def makeModuleName(dictionary):
 | |
|         return getModuleFromObject(dictionary)
 | |
| 
 | |
|     def getMemberType(self, memberInfo):
 | |
|         member, info = memberInfo
 | |
|         declType = info.declType
 | |
|         if member.optional and not member.defaultValue:
 | |
|             declType = CGWrapper(info.declType, pre="Option<", post=">")
 | |
|         return declType.define()
 | |
| 
 | |
|     def getMemberConversion(self, memberInfo, memberType):
 | |
|         def indent(s):
 | |
|             return CGIndenter(CGGeneric(s), 12).define()
 | |
| 
 | |
|         member, info = memberInfo
 | |
|         templateBody = info.template
 | |
|         default = info.default
 | |
|         replacements = {"val": "rval.handle()"}
 | |
|         conversion = string.Template(templateBody).substitute(replacements)
 | |
|         if memberType.isAny():
 | |
|             conversion = "%s.get()" % conversion
 | |
| 
 | |
|         assert (member.defaultValue is None) == (default is None)
 | |
|         if not member.optional:
 | |
|             assert default is None
 | |
|             default = ("throw_type_error(cx, \"Missing required member \\\"%s\\\".\");\n"
 | |
|                        "return Err(());") % member.identifier.name
 | |
|         elif not default:
 | |
|             default = "None"
 | |
|             conversion = "Some(%s)" % conversion
 | |
| 
 | |
|         conversion = (
 | |
|             "{\n"
 | |
|             "    rooted!(in(cx) let mut rval = UndefinedValue());\n"
 | |
|             "    match try!(get_dictionary_property(cx, object.handle(), \"%s\", rval.handle_mut())) {\n"
 | |
|             "        true => {\n"
 | |
|             "%s\n"
 | |
|             "        },\n"
 | |
|             "        false => {\n"
 | |
|             "%s\n"
 | |
|             "        },\n"
 | |
|             "    }\n"
 | |
|             "}") % (member.identifier.name, indent(conversion), indent(default))
 | |
| 
 | |
|         return CGGeneric(conversion)
 | |
| 
 | |
|     @staticmethod
 | |
|     def makeMemberName(name):
 | |
|         # Can't use Rust keywords as member names.
 | |
|         if name in RUST_KEYWORDS:
 | |
|             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 CGRegisterProxyHandlersMethod(CGAbstractMethod):
 | |
|     def __init__(self, descriptors):
 | |
|         docs = "Create the global vtables used by the generated DOM bindings to implement JS proxies."
 | |
|         CGAbstractMethod.__init__(self, None, 'RegisterProxyHandlers', 'void', [],
 | |
|                                   unsafe=True, pub=True, docs=docs)
 | |
|         self.descriptors = descriptors
 | |
| 
 | |
|     def definition_body(self):
 | |
|         return CGList([
 | |
|             CGGeneric("PROXY_HANDLERS[Proxies::%s as usize] = Bindings::%s::DefineProxyHandler();"
 | |
|                       % (desc.name, '::'.join([desc.name + 'Binding'] * 2)))
 | |
|             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,
 | |
|                                             hasInterfaceObject=True)
 | |
|         # We also want descriptors that have an interface prototype object
 | |
|         # (isCallback=False), but we don't want to include a second copy
 | |
|         # of descriptors that we also matched in the previous line
 | |
|         # (hence hasInterfaceObject=False).
 | |
|         descriptors.extend(config.getDescriptors(webIDLFile=webIDLFile,
 | |
|                                                  hasInterfaceObject=False,
 | |
|                                                  isCallback=False,
 | |
|                                                  register=True))
 | |
| 
 | |
|         dictionaries = config.getDictionaries(webIDLFile=webIDLFile)
 | |
| 
 | |
|         mainCallbacks = config.getCallbacks(webIDLFile=webIDLFile)
 | |
|         callbackDescriptors = config.getDescriptors(webIDLFile=webIDLFile,
 | |
|                                                     isCallback=True)
 | |
| 
 | |
|         enums = config.getEnums(webIDLFile)
 | |
|         typedefs = config.getTypedefs(webIDLFile)
 | |
| 
 | |
|         if not (descriptors or dictionaries or mainCallbacks or callbackDescriptors or enums):
 | |
|             self.root = None
 | |
|             return
 | |
| 
 | |
|         # Do codegen for all the enums.
 | |
|         cgthings = [CGEnum(e) for e in enums]
 | |
| 
 | |
|         # Do codegen for all the typdefs
 | |
|         for t in typedefs:
 | |
|             typeName = getRetvalDeclarationForType(t.innerType, config.getDescriptorProvider())
 | |
|             substs = {
 | |
|                 "name": t.identifier.name,
 | |
|                 "type": typeName.define(),
 | |
|             }
 | |
| 
 | |
|             if t.innerType.isUnion() and not t.innerType.nullable():
 | |
|                 # Allow using the typedef's name for accessing variants.
 | |
|                 template = "pub use self::%(type)s as %(name)s;"
 | |
|             else:
 | |
|                 template = "pub type %(name)s = %(type)s;"
 | |
| 
 | |
|             cgthings.append(CGGeneric(template % substs))
 | |
| 
 | |
|         # Do codegen for all the dictionaries.
 | |
|         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, config, len(descriptors) == 1) for x in descriptors])
 | |
| 
 | |
|         # Do codegen for all the callback interfaces.
 | |
|         cgthings.extend(CGList([CGCallbackInterface(x),
 | |
|                                 CGCallbackFunctionImpl(x.interface)], "\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")
 | |
| 
 | |
|         # Add imports
 | |
|         curr = generate_imports(config, curr, callbackDescriptors, mainCallbacks,
 | |
|                                 dictionaries, enums)
 | |
| 
 | |
|         # Add the auto-generated comment.
 | |
|         curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
 | |
| 
 | |
|         # Store the final result.
 | |
|         self.root = curr
 | |
| 
 | |
|     def define(self):
 | |
|         if not self.root:
 | |
|             return None
 | |
|         return stripTrailingWhitespace(self.root.define())
 | |
| 
 | |
| 
 | |
| def argument_type(descriptorProvider, ty, optional=False, defaultValue=None, variadic=False):
 | |
|     info = getJSToNativeConversionInfo(
 | |
|         ty, descriptorProvider, isArgument=True)
 | |
|     declType = info.declType
 | |
| 
 | |
|     if variadic:
 | |
|         if ty.isGeckoInterface():
 | |
|             declType = CGWrapper(declType, pre="&[", post="]")
 | |
|         else:
 | |
|             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"):
 | |
|         """
 | |
|         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
 | |
|         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()),
 | |
|                              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="#[derive(JSTraceable, PartialEq)]\n#[allow_unrooted_interior]")
 | |
| 
 | |
|     def getConstructors(self):
 | |
|         return [ClassConstructor(
 | |
|             [Argument("*mut JSContext", "aCx"), Argument("*mut JSObject", "aCallback")],
 | |
|             bodyInHeader=True,
 | |
|             visibility="pub",
 | |
|             explicit=False,
 | |
|             baseConstructors=[
 | |
|                 "%s::new()" % 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 == "HandleObject"
 | |
|         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.get_context()", "thisObjJS.handle()"] + argnames
 | |
|         argnamesWithoutThis = ["s.get_context()", "thisObjJS.handle()"] + 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"))
 | |
| 
 | |
|         # And now insert our template argument.
 | |
|         argsWithoutThis = list(args)
 | |
|         args.insert(0, Argument("&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"
 | |
| 
 | |
|         bodyWithThis = string.Template(
 | |
|             setupCall +
 | |
|             "rooted!(in(s.get_context()) let mut thisObjJS = ptr::null_mut());\n"
 | |
|             "wrap_call_this_object(s.get_context(), thisObj, thisObjJS.handle_mut());\n"
 | |
|             "if thisObjJS.is_null() {\n"
 | |
|             "    return Err(JSFailed);\n"
 | |
|             "}\n"
 | |
|             "return ${methodName}(${callArgs});").substitute({
 | |
|                 "callArgs": ", ".join(argnamesWithThis),
 | |
|                 "methodName": 'self.' + method.name,
 | |
|             })
 | |
|         bodyWithoutThis = string.Template(
 | |
|             setupCall +
 | |
|             "rooted!(in(s.get_context()) let thisObjJS = ptr::null_mut());\n"
 | |
|             "return ${methodName}(${callArgs});").substitute({
 | |
|                 "callArgs": ", ".join(argnamesWithoutThis),
 | |
|                 "methodName": 'self.' + method.name,
 | |
|             })
 | |
|         return [ClassMethod(method.name + '_', method.returnType, args,
 | |
|                             bodyInHeader=True,
 | |
|                             templateArgs=["T: DomObject"],
 | |
|                             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, descriptor):
 | |
|     return "Get" + MakeNativeName(
 | |
|         descriptor.binaryNameFor(attr.identifier.name))
 | |
| 
 | |
| 
 | |
| def callbackSetterName(attr, descriptor):
 | |
|     return "Set" + MakeNativeName(
 | |
|         descriptor.binaryNameFor(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} {
 | |
|     unsafe fn new(cx: *mut JSContext, callback: *mut JSObject) -> Rc<${type}> {
 | |
|         ${type}::new(cx, callback)
 | |
|     }
 | |
| 
 | |
|     fn callback_holder(&self) -> &CallbackObject {
 | |
|         self.parent.callback_holder()
 | |
|     }
 | |
| }
 | |
| 
 | |
| impl ToJSValConvertible for ${type} {
 | |
|     unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
 | |
|         self.callback().to_jsval(cx, rval);
 | |
|     }
 | |
| }\
 | |
| """).substitute({"type": callback.identifier.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):
 | |
|         """
 | |
|         needThisHandling is True if we need to be able to accept a specified
 | |
|         thisObj, False otherwise.
 | |
|         """
 | |
| 
 | |
|         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"
 | |
|         # 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)
 | |
|         # 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(JSFailed);"
 | |
|         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(
 | |
|                 "rooted_vec!(let mut argv);\n"
 | |
|                 "argv.extend((0..${argCount}).map(|_| Heap::new(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 CGWrapper(CGIndenter(CGList([
 | |
|             CGGeneric(pre),
 | |
|             CGGeneric(body),
 | |
|         ], "\n"), 4), pre="unsafe {\n", post="\n}").define()
 | |
| 
 | |
|     def getResultConversion(self):
 | |
|         replacements = {
 | |
|             "val": "rval.handle()",
 | |
|         }
 | |
| 
 | |
|         info = getJSToNativeConversionInfo(
 | |
|             self.retvalType,
 | |
|             self.descriptorProvider,
 | |
|             exceptionCode=self.exceptionCode,
 | |
|             isCallbackReturnValue="Callback",
 | |
|             # XXXbz we should try to do better here
 | |
|             sourceDescription="return value")
 | |
|         template = info.template
 | |
|         declType = info.declType
 | |
| 
 | |
|         convertType = instantiateJSToNativeConversionTemplate(
 | |
|             template, replacements, declType, "rvalDecl")
 | |
| 
 | |
|         if self.retvalType is None or self.retvalType.isVoid():
 | |
|             retval = "()"
 | |
|         elif self.retvalType.isAny():
 | |
|             retval = "rvalDecl.get()"
 | |
|         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].get()"
 | |
|             jsvalIndex = "%d + idx" % i
 | |
|         else:
 | |
|             jsvalIndex = "%d" % i
 | |
|             if arg.optional and not arg.defaultValue:
 | |
|                 argval += ".clone().unwrap()"
 | |
| 
 | |
|         conversion = wrapForType(
 | |
|             "argv_root.handle_mut()", result=argval,
 | |
|             successCode="argv[%s] = Heap::new(argv_root.get());" % jsvalIndex,
 | |
|             pre="rooted!(in(cx) let mut argv_root = UndefinedValue());")
 | |
|         if arg.variadic:
 | |
|             conversion = string.Template(
 | |
|                 "for idx in 0..${arg}.len() {\n" +
 | |
|                 CGIndenter(CGGeneric(conversion)).define() + "\n"
 | |
|                 "}"
 | |
|             ).substitute({"arg": arg.identifier.name})
 | |
|         elif arg.optional and not arg.defaultValue:
 | |
|             conversion = (
 | |
|                 CGIfWrapper("%s.is_some()" % arg.identifier.name,
 | |
|                             CGGeneric(conversion)).define() +
 | |
|                 " else if argc == %d {\n"
 | |
|                 "    // This is our current trailing argument; reduce argc\n"
 | |
|                 "    argc -= 1;\n"
 | |
|                 "} else {\n"
 | |
|                 "    argv[%d] = Heap::new(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.
 | |
|             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("HandleObject", "aThisObj")] + args
 | |
| 
 | |
|     def getCallSetup(self):
 | |
|         if self.needThisHandling:
 | |
|             # It's been done for us already
 | |
|             return ""
 | |
|         return (
 | |
|             "CallSetup s(CallbackPreserveColor(), aRv, aExceptionHandling);\n"
 | |
|             "JSContext* cx = s.get_context();\n"
 | |
|             "if (!cx) {\n"
 | |
|             "    return Err(JSFailed);\n"
 | |
|             "}\n")
 | |
| 
 | |
|     def getArgcDecl(self):
 | |
|         if self.argCount <= 1:
 | |
|             return CGGeneric("let argc = %s;" % self.argCountStr)
 | |
|         return CGGeneric("let mut argc = %s;" % 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):
 | |
|         CallbackMember.__init__(self, sig, name, descriptorProvider,
 | |
|                                 needThisHandling)
 | |
| 
 | |
|     def getRvalDecl(self):
 | |
|         return "rooted!(in(cx) let mut rval = UndefinedValue());\n"
 | |
| 
 | |
|     def getCall(self):
 | |
|         replacements = {
 | |
|             "thisObj": self.getThisObj(),
 | |
|             "getCallable": self.getCallableDecl(),
 | |
|             "callGuard": self.getCallGuard(),
 | |
|         }
 | |
|         if self.argCount > 0:
 | |
|             replacements["argv"] = "argv.as_ptr() as *const JSVal"
 | |
|             replacements["argc"] = "argc"
 | |
|         else:
 | |
|             replacements["argv"] = "ptr::null_mut()"
 | |
|             replacements["argc"] = "0"
 | |
|         return string.Template(
 | |
|             "${getCallable}"
 | |
|             "rooted!(in(cx) let rootedThis = ${thisObj});\n"
 | |
|             "let ok = ${callGuard}JS_CallFunctionValue(\n"
 | |
|             "    cx, rootedThis.handle(), callable.handle(),\n"
 | |
|             "    &HandleValueArray {\n"
 | |
|             "        length_: ${argc} as ::libc::size_t,\n"
 | |
|             "        elements_: ${argv}\n"
 | |
|             "    }, rval.handle_mut());\n"
 | |
|             "maybe_resume_unwind();\n"
 | |
|             "if !ok {\n"
 | |
|             "    return Err(JSFailed);\n"
 | |
|             "}\n").substitute(replacements)
 | |
| 
 | |
| 
 | |
| class CallCallback(CallbackMethod):
 | |
|     def __init__(self, callback, descriptorProvider):
 | |
|         self.callback = callback
 | |
|         CallbackMethod.__init__(self, callback.signatures()[0], "Call",
 | |
|                                 descriptorProvider, needThisHandling=True)
 | |
| 
 | |
|     def getThisObj(self):
 | |
|         return "aThisObj.get()"
 | |
| 
 | |
|     def getCallableDecl(self):
 | |
|         return "rooted!(in(cx) let callable = ObjectValue(self.callback()));\n"
 | |
| 
 | |
|     def getCallGuard(self):
 | |
|         if self.callback._treatNonObjectAsNull:
 | |
|             return "!IsCallable(self.callback()) || "
 | |
|         return ""
 | |
| 
 | |
| 
 | |
| class CallbackOperationBase(CallbackMethod):
 | |
|     """
 | |
|     Common class for implementing various callback operations.
 | |
|     """
 | |
|     def __init__(self, signature, jsName, nativeName, descriptor, singleOperation):
 | |
|         self.singleOperation = singleOperation
 | |
|         self.methodName = jsName
 | |
|         CallbackMethod.__init__(self, signature, nativeName, descriptor, singleOperation)
 | |
| 
 | |
|     def getThisObj(self):
 | |
|         if not self.singleOperation:
 | |
|             return "self.callback()"
 | |
|         # This relies on getCallableDecl declaring a boolean
 | |
|         # isCallable in the case when we're a single-operation
 | |
|         # interface.
 | |
|         return "if isCallable { aThisObj.get() } else { self.callback() }"
 | |
| 
 | |
|     def getCallableDecl(self):
 | |
|         replacements = {
 | |
|             "methodName": self.methodName
 | |
|         }
 | |
|         getCallableFromProp = string.Template(
 | |
|             'try!(self.parent.get_callable_property(cx, "${methodName}"))'
 | |
|         ).substitute(replacements)
 | |
|         if not self.singleOperation:
 | |
|             return 'rooted!(in(cx) let callable =\n' + getCallableFromProp + ');\n'
 | |
|         return (
 | |
|             'let isCallable = IsCallable(self.callback());\n'
 | |
|             'rooted!(in(cx) let callable =\n' +
 | |
|             CGIndenter(
 | |
|                 CGIfElseWrapper('isCallable',
 | |
|                                 CGGeneric('ObjectValue(self.callback())'),
 | |
|                                 CGGeneric(getCallableFromProp))).define() + ');\n')
 | |
| 
 | |
|     def getCallGuard(self):
 | |
|         return ""
 | |
| 
 | |
| 
 | |
| 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(descriptor.binaryNameFor(jsName)),
 | |
|                                        descriptor, descriptor.interface.isSingleOperationInterface())
 | |
| 
 | |
| 
 | |
| 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)
 | |
| 
 | |
|     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(JSFailed);\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)
 | |
| 
 | |
|     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(JSFailed);\n'
 | |
|             '}\n').substitute(replacements)
 | |
| 
 | |
|     def getArgcDecl(self):
 | |
|         return None
 | |
| 
 | |
| 
 | |
| class CGIterableMethodGenerator(CGGeneric):
 | |
|     """
 | |
|     Creates methods for iterable interfaces. Unwrapping/wrapping
 | |
|     will be taken care of by the usual method generation machinery in
 | |
|     CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of
 | |
|     using CGCallGenerator.
 | |
|     """
 | |
|     def __init__(self, descriptor, iterable, methodName):
 | |
|         if methodName == "forEach":
 | |
|             CGGeneric.__init__(self, fill(
 | |
|                 """
 | |
|                 if !IsCallable(arg0) {
 | |
|                   throw_type_error(cx, "Argument 1 of ${ifaceName}.forEach is not callable.");
 | |
|                   return false;
 | |
|                 }
 | |
|                 rooted!(in(cx) let arg0 = ObjectValue(arg0));
 | |
|                 rooted!(in(cx) let mut call_arg1 = UndefinedValue());
 | |
|                 rooted!(in(cx) let mut call_arg2 = UndefinedValue());
 | |
|                 let mut call_args = vec![UndefinedValue(), UndefinedValue(), ObjectValue(*_obj)];
 | |
|                 rooted!(in(cx) let mut ignoredReturnVal = UndefinedValue());
 | |
|                 for i in 0..(*this).get_iterable_length() {
 | |
|                   (*this).get_value_at_index(i).to_jsval(cx, call_arg1.handle_mut());
 | |
|                   (*this).get_key_at_index(i).to_jsval(cx, call_arg2.handle_mut());
 | |
|                   call_args[0] = call_arg1.handle().get();
 | |
|                   call_args[1] = call_arg2.handle().get();
 | |
|                   let call_args = HandleValueArray { length_: 3, elements_: call_args.as_ptr() };
 | |
|                   if !Call(cx, arg1, arg0.handle(), &call_args,
 | |
|                            ignoredReturnVal.handle_mut()) {
 | |
|                     return false;
 | |
|                   }
 | |
|                 }
 | |
| 
 | |
|                 let result = ();
 | |
|                 """,
 | |
|                 ifaceName=descriptor.interface.identifier.name))
 | |
|             return
 | |
|         CGGeneric.__init__(self, fill(
 | |
|             """
 | |
|             let result = ${iterClass}::new(&*this,
 | |
|                                            IteratorType::${itrMethod},
 | |
|                                            super::${ifaceName}IteratorBinding::Wrap);
 | |
|             """,
 | |
|             iterClass=iteratorNativeType(descriptor, True),
 | |
|             ifaceName=descriptor.interface.identifier.name,
 | |
|             itrMethod=methodName.title()))
 | |
| 
 | |
| 
 | |
| def camel_to_upper_snake(s):
 | |
|     return "_".join(m.group(0).upper() for m in re.finditer("[A-Z][a-z]*", s))
 | |
| 
 | |
| 
 | |
| def process_arg(expr, arg):
 | |
|     if arg.type.isGeckoInterface() and not arg.type.unroll().inner.isCallback():
 | |
|         if arg.type.nullable() or arg.type.isSequence() or arg.optional:
 | |
|             expr += ".r()"
 | |
|         else:
 | |
|             expr = "&" + expr
 | |
|     return expr
 | |
| 
 | |
| 
 | |
| 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 InterfaceObjectMap(config):
 | |
|         mods = [
 | |
|             "dom::bindings::codegen",
 | |
|             "js::jsapi::{HandleObject, JSContext}",
 | |
|             "phf",
 | |
|         ]
 | |
|         imports = CGList([CGGeneric("use %s;" % mod) for mod in mods], "\n")
 | |
| 
 | |
|         global_descriptors = config.getDescriptors(isGlobal=True)
 | |
|         flags = [("EMPTY", 0)]
 | |
|         flags.extend(
 | |
|             (camel_to_upper_snake(d.name), 2 ** idx)
 | |
|             for (idx, d) in enumerate(global_descriptors)
 | |
|         )
 | |
|         global_flags = CGWrapper(CGIndenter(CGList([
 | |
|             CGGeneric("const %s = %#x," % args)
 | |
|             for args in flags
 | |
|         ], "\n")), pre="pub flags Globals: u8 {\n", post="\n}")
 | |
|         globals_ = CGWrapper(CGIndenter(global_flags), pre="bitflags! {\n", post="\n}")
 | |
| 
 | |
|         phf = CGGeneric("include!(concat!(env!(\"OUT_DIR\"), \"/InterfaceObjectMapPhf.rs\"));")
 | |
| 
 | |
|         return CGList([
 | |
|             CGGeneric(AUTOGENERATED_WARNING_COMMENT),
 | |
|             CGList([imports, globals_, phf], "\n\n")
 | |
|         ])
 | |
| 
 | |
|     @staticmethod
 | |
|     def InterfaceObjectMapData(config):
 | |
|         pairs = []
 | |
|         for d in config.getDescriptors(hasInterfaceObject=True, isInline=False):
 | |
|             binding = toBindingNamespace(d.name)
 | |
|             pairs.append((d.name, binding, binding))
 | |
|             for ctor in d.interface.namedConstructors:
 | |
|                 pairs.append((ctor.identifier.name, binding, binding))
 | |
|         pairs.sort(key=operator.itemgetter(0))
 | |
|         mappings = [
 | |
|             CGGeneric('"%s": "codegen::Bindings::%s::%s::DefineDOMInterface as unsafe fn(_, _)"' % pair)
 | |
|             for pair in pairs
 | |
|         ]
 | |
|         return CGWrapper(
 | |
|             CGList(mappings, ",\n"),
 | |
|             pre="{\n",
 | |
|             post="\n}\n")
 | |
| 
 | |
|     @staticmethod
 | |
|     def PrototypeList(config):
 | |
|         # Prototype ID enum.
 | |
|         interfaces = config.getDescriptors(isCallback=False, isNamespace=False)
 | |
|         protos = [d.name for d in interfaces]
 | |
|         constructors = sorted([MakeNativeName(d.name)
 | |
|                                for d in config.getDescriptors(hasInterfaceObject=True)
 | |
|                                if d.shouldHaveGetConstructorObjectMethod()])
 | |
| 
 | |
|         proxies = [d.name for d in config.getDescriptors(proxy=True)]
 | |
| 
 | |
|         return CGList([
 | |
|             CGGeneric(AUTOGENERATED_WARNING_COMMENT),
 | |
|             CGGeneric("pub const PROTO_OR_IFACE_LENGTH: usize = %d;\n" % (len(protos) + len(constructors))),
 | |
|             CGGeneric("pub const MAX_PROTO_CHAIN_LENGTH: usize = %d;\n\n" % config.maxProtoChainLength),
 | |
|             CGNonNamespacedEnum('ID', protos, 0, deriving="PartialEq, Copy, Clone", repr="u16"),
 | |
|             CGNonNamespacedEnum('Constructor', constructors, len(protos),
 | |
|                                 deriving="PartialEq, Copy, Clone", repr="u16"),
 | |
|             CGWrapper(CGIndenter(CGList([CGGeneric('"' + name + '"') for name in protos],
 | |
|                                         ",\n"),
 | |
|                                  indentLevel=4),
 | |
|                       pre="static INTERFACES: [&'static str; %d] = [\n" % len(protos),
 | |
|                       post="\n];\n\n"),
 | |
|             CGGeneric("pub fn proto_id_to_name(proto_id: u16) -> &'static str {\n"
 | |
|                       "    debug_assert!(proto_id < ID::Last as u16);\n"
 | |
|                       "    INTERFACES[proto_id as usize]\n"
 | |
|                       "}\n\n"),
 | |
|             CGNonNamespacedEnum('Proxies', proxies, 0, deriving="PartialEq, Copy, Clone"),
 | |
|         ])
 | |
| 
 | |
|     @staticmethod
 | |
|     def RegisterBindings(config):
 | |
|         # TODO - Generate the methods we want
 | |
|         code = CGList([
 | |
|             CGRegisterProxyHandlers(config),
 | |
|         ], "\n")
 | |
| 
 | |
|         return CGImports(code, descriptors=[], callbacks=[], dictionaries=[], enums=[], imports=[
 | |
|             'dom::bindings::codegen::Bindings',
 | |
|             'dom::bindings::codegen::PrototypeList::Proxies',
 | |
|             'libc',
 | |
|         ], config=config, ignored_warnings=[])
 | |
| 
 | |
|     @staticmethod
 | |
|     def InterfaceTypes(config):
 | |
|         descriptors = sorted([MakeNativeName(d.name)
 | |
|                               for d in config.getDescriptors(register=True,
 | |
|                                                              isCallback=False,
 | |
|                                                              isIteratorInterface=False)])
 | |
|         curr = CGList([CGGeneric("pub use dom::%s::%s;\n" % (name.lower(),
 | |
|                                                              MakeNativeName(name)))
 | |
|                        for name in descriptors])
 | |
|         curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
 | |
|         return curr
 | |
| 
 | |
|     @staticmethod
 | |
|     def Bindings(config):
 | |
| 
 | |
|         def leafModule(d):
 | |
|             return getModuleFromObject(d).split('::')[-1]
 | |
| 
 | |
|         descriptors = config.getDescriptors(register=True, isIteratorInterface=False)
 | |
|         descriptors = (set(toBindingNamespace(d.name) for d in descriptors) |
 | |
|                        set(leafModule(d) for d in config.callbacks) |
 | |
|                        set(leafModule(d) 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)
 | |
|         imports = [CGGeneric("use dom::types::*;\n"),
 | |
|                    CGGeneric("use dom::bindings::conversions::{DerivedFrom, get_dom_class};\n"),
 | |
|                    CGGeneric("use dom::bindings::inheritance::Castable;\n"),
 | |
|                    CGGeneric("use dom::bindings::js::{JS, LayoutJS, Root};\n"),
 | |
|                    CGGeneric("use dom::bindings::trace::JSTraceable;\n"),
 | |
|                    CGGeneric("use dom::bindings::reflector::DomObject;\n"),
 | |
|                    CGGeneric("use js::jsapi::JSTracer;\n\n"),
 | |
|                    CGGeneric("use std::mem;\n\n")]
 | |
|         allprotos = []
 | |
|         topTypes = []
 | |
|         hierarchy = defaultdict(list)
 | |
|         for descriptor in descriptors:
 | |
|             name = descriptor.name
 | |
|             chain = descriptor.prototypeChain
 | |
|             upcast = descriptor.hasDescendants()
 | |
|             downcast = len(chain) != 1
 | |
| 
 | |
|             if upcast and not downcast:
 | |
|                 topTypes.append(name)
 | |
| 
 | |
|             if not upcast:
 | |
|                 # No other interface will implement DeriveFrom<Foo> for this Foo, so avoid
 | |
|                 # implementing it for itself.
 | |
|                 chain = chain[:-1]
 | |
| 
 | |
|             # Implement `DerivedFrom<Bar>` for `Foo`, for all `Bar` that `Foo` inherits from.
 | |
|             if chain:
 | |
|                 allprotos.append(CGGeneric("impl Castable for %s {}\n" % name))
 | |
|             for baseName in chain:
 | |
|                 allprotos.append(CGGeneric("impl DerivedFrom<%s> for %s {}\n" % (baseName, name)))
 | |
|             if chain:
 | |
|                 allprotos.append(CGGeneric("\n"))
 | |
| 
 | |
|             if downcast:
 | |
|                 hierarchy[descriptor.interface.parent.identifier.name].append(name)
 | |
| 
 | |
|         typeIdCode = []
 | |
|         topTypeVariants = [
 | |
|             ("ID used by abstract interfaces.", "pub abstract_: ()"),
 | |
|             ("ID used by interfaces that are not castable.", "pub alone: ()"),
 | |
|         ]
 | |
|         topTypeVariants += [
 | |
|             ("ID used by interfaces that derive from %s." % typeName,
 | |
|              "pub %s: %sTypeId" % (typeName.lower(), typeName))
 | |
|             for typeName in topTypes
 | |
|         ]
 | |
|         topTypeVariantsAsStrings = [CGGeneric("/// %s\n%s," % variant) for variant in topTypeVariants]
 | |
|         typeIdCode.append(CGWrapper(CGIndenter(CGList(topTypeVariantsAsStrings, "\n"), 4),
 | |
|                                     pre="#[derive(Copy)]\npub union TopTypeId {\n",
 | |
|                                     post="\n}\n\n"))
 | |
| 
 | |
|         typeIdCode.append(CGGeneric("""\
 | |
| impl Clone for TopTypeId {
 | |
|     fn clone(&self) -> Self { *self }
 | |
| }
 | |
| 
 | |
| """))
 | |
| 
 | |
|         def type_id_variant(name):
 | |
|             # If `name` is present in the hierarchy keys', that means some other interfaces
 | |
|             # derive from it and this enum variant should have an argument with its own
 | |
|             # TypeId enum.
 | |
|             return "%s(%sTypeId)" % (name, name) if name in hierarchy else name
 | |
| 
 | |
|         for base, derived in hierarchy.iteritems():
 | |
|             variants = []
 | |
|             if config.getDescriptor(base).concrete:
 | |
|                 variants.append(CGGeneric(base))
 | |
|             variants += [CGGeneric(type_id_variant(derivedName)) for derivedName in derived]
 | |
|             derives = "Clone, Copy, Debug, PartialEq"
 | |
|             typeIdCode.append(CGWrapper(CGIndenter(CGList(variants, ",\n"), 4),
 | |
|                                         pre="#[derive(%s)]\npub enum %sTypeId {\n" % (derives, base),
 | |
|                                         post="\n}\n\n"))
 | |
|             if base in topTypes:
 | |
|                 typeIdCode.append(CGGeneric("""\
 | |
| impl %(base)s {
 | |
|     pub fn type_id(&self) -> &'static %(base)sTypeId {
 | |
|         unsafe {
 | |
|             &get_dom_class(self.reflector().get_jsobject().get())
 | |
|                 .unwrap()
 | |
|                 .type_id
 | |
|                 .%(field)s
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| """ % {'base': base, 'field': base.lower()}))
 | |
| 
 | |
|         curr = CGList(imports + typeIdCode + allprotos)
 | |
|         curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
 | |
|         return curr
 | |
| 
 | |
|     @staticmethod
 | |
|     def UnionTypes(config):
 | |
| 
 | |
|         curr = UnionTypes(config.getDescriptors(),
 | |
|                           config.getDictionaries(),
 | |
|                           config.getCallbacks(),
 | |
|                           config.typedefs,
 | |
|                           config)
 | |
| 
 | |
|         # Add the auto-generated comment.
 | |
|         curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
 | |
| 
 | |
|         # Done.
 | |
|         return curr
 | |
| 
 | |
|     @staticmethod
 | |
|     def SupportedDomApis(config):
 | |
|         descriptors = config.getDescriptors(isExposedConditionally=False)
 | |
| 
 | |
|         base_path = os.path.join('dom', 'bindings', 'codegen')
 | |
|         with open(os.path.join(base_path, 'apis.html.template')) as f:
 | |
|             base_template = f.read()
 | |
|         with open(os.path.join(base_path, 'api.html.template')) as f:
 | |
|             api_template = f.read()
 | |
|         with open(os.path.join(base_path, 'property.html.template')) as f:
 | |
|             property_template = f.read()
 | |
|         with open(os.path.join(base_path, 'interface.html.template')) as f:
 | |
|             interface_template = f.read()
 | |
| 
 | |
|         apis = []
 | |
|         interfaces = []
 | |
|         for descriptor in descriptors:
 | |
|             props = []
 | |
|             for m in descriptor.interface.members:
 | |
|                 if PropertyDefiner.getStringAttr(m, 'Pref') or \
 | |
|                    PropertyDefiner.getStringAttr(m, 'Func') or \
 | |
|                    (m.isMethod() and m.isIdentifierLess()):
 | |
|                     continue
 | |
|                 display = m.identifier.name + ('()' if m.isMethod() else '')
 | |
|                 props += [property_template.replace('${name}', display)]
 | |
|             name = descriptor.interface.identifier.name
 | |
|             apis += [(api_template.replace('${interface}', name)
 | |
|                                   .replace('${properties}', '\n'.join(props)))]
 | |
|             interfaces += [interface_template.replace('${interface}', name)]
 | |
| 
 | |
|         return CGGeneric((base_template.replace('${apis}', '\n'.join(apis))
 | |
|                                        .replace('${interfaces}', '\n'.join(interfaces))))
 |