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