mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 06:35:42 +00:00
9308 lines
384 KiB
Python
9308 lines
384 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
|
|
import math
|
|
|
|
from WebIDL import BuiltinTypes, IDLBuiltinType, IDLNullValue, IDLSequenceType, IDLType
|
|
from Configuration import NoSuchDescriptorError, getTypesFromDescriptor, getTypesFromDictionary, getTypesFromCallback, Descriptor
|
|
|
|
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'
|
|
LEGACYCALLER_HOOK_NAME = '_legacycaller'
|
|
HASINSTANCE_HOOK_NAME = '_hasInstance'
|
|
NEWRESOLVE_HOOK_NAME = '_newResolve'
|
|
|
|
def replaceFileIfChanged(filename, newContents):
|
|
"""
|
|
Read a copy of the old file, so that we don't touch it if it hasn't changed.
|
|
Returns True if the file was updated, false otherwise.
|
|
"""
|
|
oldFileContents = ""
|
|
try:
|
|
oldFile = open(filename, 'rb')
|
|
oldFileContents = ''.join(oldFile.readlines())
|
|
oldFile.close()
|
|
except:
|
|
pass
|
|
|
|
if newContents == oldFileContents:
|
|
return False
|
|
|
|
f = open(filename, 'wb')
|
|
f.write(newContents)
|
|
f.close()
|
|
return True
|
|
|
|
def toStringBool(arg):
|
|
return str(not not arg).lower()
|
|
|
|
def toBindingNamespace(arg):
|
|
return re.sub("((_workers)?$)", "Binding\\1", arg);
|
|
|
|
class CGThing():
|
|
"""
|
|
Abstract base class for things that spit out code.
|
|
"""
|
|
def __init__(self):
|
|
pass # Nothing for now
|
|
def declare(self):
|
|
"""Produce code for a header file."""
|
|
assert(False) # Override me!
|
|
def define(self):
|
|
"""Produce code for a cpp file."""
|
|
assert(False) # Override me!
|
|
def deps(self):
|
|
"""Produce the deps for a pp 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 declare(self):
|
|
if self.descriptor.workers:
|
|
return ""
|
|
return "extern const NativePropertyHooks sNativePropertyHooks;\n"
|
|
def define(self):
|
|
if self.descriptor.workers:
|
|
return ""
|
|
if self.descriptor.concrete and self.descriptor.proxy:
|
|
resolveOwnProperty = "ResolveOwnProperty"
|
|
enumerateOwnProperties = "EnumerateOwnProperties"
|
|
else:
|
|
resolveOwnProperty = "nullptr"
|
|
enumerateOwnProperties = "nullptr"
|
|
if self.properties.hasNonChromeOnly():
|
|
regular = "&sNativeProperties"
|
|
else:
|
|
regular = "nullptr"
|
|
if self.properties.hasChromeOnly():
|
|
chrome = "&sChromeOnlyNativeProperties"
|
|
else:
|
|
chrome = "nullptr"
|
|
constructorID = "constructors::id::"
|
|
if self.descriptor.interface.hasInterfaceObject():
|
|
constructorID += self.descriptor.name
|
|
else:
|
|
constructorID += "_ID_Count"
|
|
prototypeID = "prototypes::id::"
|
|
if self.descriptor.interface.hasInterfacePrototypeObject():
|
|
prototypeID += self.descriptor.name
|
|
else:
|
|
prototypeID += "_ID_Count"
|
|
parent = self.descriptor.interface.parent
|
|
parentHooks = ("&" + toBindingNamespace(parent.identifier.name) + "::sNativePropertyHooks"
|
|
if parent else 'NULL')
|
|
|
|
return CGWrapper(CGIndenter(CGList([CGGeneric(resolveOwnProperty),
|
|
CGGeneric(enumerateOwnProperties),
|
|
CGWrapper(CGList([CGGeneric(regular),
|
|
CGGeneric(chrome)],
|
|
", "),
|
|
pre="{ ", post=" }"),
|
|
CGGeneric(prototypeID),
|
|
CGGeneric(constructorID),
|
|
CGGeneric(parentHooks)],
|
|
",\n")),
|
|
pre="const NativePropertyHooks sNativePropertyHooks = {\n",
|
|
post="\n};\n").define()
|
|
|
|
def NativePropertyHooks(descriptor):
|
|
return "&sWorkerNativePropertyHooks" if descriptor.workers else "&sNativePropertyHooks"
|
|
|
|
def DOMClass(descriptor):
|
|
protoList = ['prototypes::id::' + proto for proto in descriptor.prototypeChain]
|
|
# Pad out the list to the right length with _ID_Count so we
|
|
# guarantee that all the lists are the same length. _ID_Count
|
|
# is never the ID of any prototype, so it's safe to use as
|
|
# padding.
|
|
protoList.extend(['prototypes::id::_ID_Count'] * (descriptor.config.maxProtoChainLength - len(protoList)))
|
|
prototypeChainString = ', '.join(protoList)
|
|
if descriptor.workers or descriptor.nativeOwnership != 'refcounted':
|
|
participant = "nullptr"
|
|
else:
|
|
participant = "NS_CYCLE_COLLECTION_PARTICIPANT(%s)" % descriptor.nativeType
|
|
getParentObject = "GetParentObject<%s>::Get" % descriptor.nativeType
|
|
return """{
|
|
{ %s },
|
|
%s,
|
|
%s,
|
|
%s,
|
|
GetProtoObject,
|
|
%s
|
|
}""" % (prototypeChainString, toStringBool(descriptor.nativeOwnership == 'nsisupports'),
|
|
NativePropertyHooks(descriptor),
|
|
getParentObject,
|
|
participant)
|
|
|
|
class CGDOMJSClass(CGThing):
|
|
"""
|
|
Generate a DOMJSClass for a given descriptor
|
|
"""
|
|
def __init__(self, descriptor):
|
|
CGThing.__init__(self)
|
|
self.descriptor = descriptor
|
|
# Our current reserved slot situation is unsafe for globals. Fix bug 760095!
|
|
assert "Window" not in descriptor.interface.identifier.name
|
|
def declare(self):
|
|
return "extern DOMJSClass Class;\n"
|
|
def define(self):
|
|
traceHook = TRACE_HOOK_NAME if self.descriptor.customTrace else 'nullptr'
|
|
callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr'
|
|
classFlags = "JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(3)"
|
|
if self.descriptor.interface.getExtendedAttribute("NeedNewResolve"):
|
|
newResolveHook = "(JSResolveOp)" + NEWRESOLVE_HOOK_NAME
|
|
classFlags += " | JSCLASS_NEW_RESOLVE"
|
|
else:
|
|
newResolveHook = "JS_ResolveStub"
|
|
return """
|
|
DOMJSClass Class = {
|
|
{ "%s",
|
|
%s,
|
|
%s, /* addProperty */
|
|
JS_DeletePropertyStub, /* delProperty */
|
|
JS_PropertyStub, /* getProperty */
|
|
JS_StrictPropertyStub, /* setProperty */
|
|
JS_EnumerateStub,
|
|
%s,
|
|
JS_ConvertStub,
|
|
%s, /* finalize */
|
|
NULL, /* checkAccess */
|
|
%s, /* call */
|
|
NULL, /* hasInstance */
|
|
NULL, /* construct */
|
|
%s, /* trace */
|
|
JSCLASS_NO_INTERNAL_MEMBERS
|
|
},
|
|
%s
|
|
};
|
|
""" % (self.descriptor.interface.identifier.name,
|
|
classFlags,
|
|
ADDPROPERTY_HOOK_NAME if self.descriptor.concrete and not self.descriptor.workers and self.descriptor.wrapperCache else 'JS_PropertyStub',
|
|
newResolveHook, FINALIZE_HOOK_NAME, callHook, traceHook,
|
|
CGIndenter(CGGeneric(DOMClass(self.descriptor))).define())
|
|
|
|
def PrototypeIDAndDepth(descriptor):
|
|
prototypeID = "prototypes::id::"
|
|
if descriptor.interface.hasInterfacePrototypeObject():
|
|
prototypeID += descriptor.interface.identifier.name
|
|
if descriptor.workers:
|
|
prototypeID += "_workers"
|
|
depth = "PrototypeTraits<%s>::Depth" % prototypeID
|
|
else:
|
|
prototypeID += "_ID_Count"
|
|
depth = "0"
|
|
return (prototypeID, depth)
|
|
|
|
def UseHolderForUnforgeable(descriptor):
|
|
return (descriptor.concrete and
|
|
descriptor.proxy and
|
|
any(m for m in descriptor.interface.members if m.isAttr() and m.isUnforgeable()))
|
|
|
|
def CallOnUnforgeableHolder(descriptor, code, isXrayCheck=None):
|
|
"""
|
|
Generate the code to execute the code in "code" on an unforgeable holder if
|
|
needed. code should be a string containing the code to execute. If it
|
|
contains a ${holder} string parameter it will be replaced with the
|
|
unforgeable holder object.
|
|
|
|
If isXrayCheck is not None it should be a string that contains a statement
|
|
returning whether proxy is an Xray. If isXrayCheck is None the generated
|
|
code won't try to unwrap Xrays.
|
|
"""
|
|
code = string.Template(code).substitute({ "holder": "unforgeableHolder" })
|
|
if not isXrayCheck is None:
|
|
pre = """// Scope for 'global', 'ac' and 'unforgeableHolder'
|
|
{
|
|
JSObject* global;
|
|
Maybe<JSAutoCompartment> ac;
|
|
if (""" + isXrayCheck + """) {
|
|
global = js::GetGlobalForObjectCrossCompartment(js::UncheckedUnwrap(proxy));
|
|
ac.construct(cx, global);
|
|
} else {
|
|
global = js::GetGlobalForObjectCrossCompartment(proxy);
|
|
}"""
|
|
else:
|
|
pre = """// Scope for 'global' and 'unforgeableHolder'
|
|
{
|
|
JSObject* global = js::GetGlobalForObjectCrossCompartment(proxy);"""
|
|
|
|
return (pre + """
|
|
JSObject* unforgeableHolder = GetUnforgeableHolder(global, prototypes::id::%s);
|
|
""" + CGIndenter(CGGeneric(code)).define() + """
|
|
}
|
|
""") % descriptor.name
|
|
|
|
class CGPrototypeJSClass(CGThing):
|
|
def __init__(self, descriptor, properties):
|
|
CGThing.__init__(self)
|
|
self.descriptor = descriptor
|
|
self.properties = properties
|
|
def declare(self):
|
|
# We're purely for internal consumption
|
|
return ""
|
|
def define(self):
|
|
(prototypeID, depth) = PrototypeIDAndDepth(self.descriptor)
|
|
slotCount = "DOM_INTERFACE_PROTO_SLOTS_BASE"
|
|
if UseHolderForUnforgeable(self.descriptor):
|
|
slotCount += " + 1 /* slot for the JSObject holding the unforgeable properties */"
|
|
return """static DOMIfaceAndProtoJSClass PrototypeClass = {
|
|
{
|
|
"%sPrototype",
|
|
JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(%s),
|
|
JS_PropertyStub, /* addProperty */
|
|
JS_DeletePropertyStub, /* delProperty */
|
|
JS_PropertyStub, /* getProperty */
|
|
JS_StrictPropertyStub, /* setProperty */
|
|
JS_EnumerateStub,
|
|
JS_ResolveStub,
|
|
JS_ConvertStub,
|
|
nullptr, /* finalize */
|
|
nullptr, /* checkAccess */
|
|
nullptr, /* call */
|
|
nullptr, /* hasInstance */
|
|
nullptr, /* construct */
|
|
nullptr, /* trace */
|
|
JSCLASS_NO_INTERNAL_MEMBERS
|
|
},
|
|
eInterfacePrototype,
|
|
%s,
|
|
"[object %sPrototype]",
|
|
%s,
|
|
%s
|
|
};
|
|
""" % (self.descriptor.interface.identifier.name, slotCount,
|
|
NativePropertyHooks(self.descriptor),
|
|
self.descriptor.interface.identifier.name,
|
|
prototypeID, depth)
|
|
|
|
def NeedsGeneratedHasInstance(descriptor):
|
|
return descriptor.hasXPConnectImpls or descriptor.interface.isConsequential()
|
|
|
|
class CGInterfaceObjectJSClass(CGThing):
|
|
def __init__(self, descriptor, properties):
|
|
CGThing.__init__(self)
|
|
self.descriptor = descriptor
|
|
self.properties = properties
|
|
def declare(self):
|
|
# We're purely for internal consumption
|
|
return ""
|
|
def define(self):
|
|
if self.descriptor.interface.ctor():
|
|
ctorname = CONSTRUCT_HOOK_NAME
|
|
else:
|
|
ctorname = "ThrowingConstructor"
|
|
if NeedsGeneratedHasInstance(self.descriptor):
|
|
hasinstance = HASINSTANCE_HOOK_NAME
|
|
elif self.descriptor.interface.hasInterfacePrototypeObject():
|
|
hasinstance = "InterfaceHasInstance"
|
|
else:
|
|
hasinstance = "nullptr"
|
|
(prototypeID, depth) = PrototypeIDAndDepth(self.descriptor)
|
|
slotCount = "DOM_INTERFACE_SLOTS_BASE"
|
|
if len(self.descriptor.interface.namedConstructors) > 0:
|
|
slotCount += (" + %i /* slots for the named constructors */" %
|
|
len(self.descriptor.interface.namedConstructors))
|
|
return """
|
|
static DOMIfaceAndProtoJSClass InterfaceObjectClass = {
|
|
{
|
|
"Function",
|
|
JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(%s),
|
|
JS_PropertyStub, /* addProperty */
|
|
JS_DeletePropertyStub, /* delProperty */
|
|
JS_PropertyStub, /* getProperty */
|
|
JS_StrictPropertyStub, /* setProperty */
|
|
JS_EnumerateStub,
|
|
JS_ResolveStub,
|
|
JS_ConvertStub,
|
|
nullptr, /* finalize */
|
|
nullptr, /* checkAccess */
|
|
%s, /* call */
|
|
%s, /* hasInstance */
|
|
%s, /* construct */
|
|
nullptr, /* trace */
|
|
JSCLASS_NO_INTERNAL_MEMBERS
|
|
},
|
|
eInterface,
|
|
%s,
|
|
"function %s() {\\n [native code]\\n}",
|
|
%s,
|
|
%s
|
|
};
|
|
""" % (slotCount, ctorname,
|
|
hasinstance, ctorname, NativePropertyHooks(self.descriptor),
|
|
self.descriptor.interface.identifier.name,
|
|
prototypeID, depth)
|
|
|
|
class CGList(CGThing):
|
|
"""
|
|
Generate code for a list of GCThings. Just concatenates them together, with
|
|
an optional joiner string. "\n" is a common joiner.
|
|
"""
|
|
def __init__(self, children, joiner=""):
|
|
CGThing.__init__(self)
|
|
self.children = children
|
|
self.joiner = joiner
|
|
def append(self, child):
|
|
self.children.append(child)
|
|
def prepend(self, child):
|
|
self.children.insert(0, child)
|
|
def join(self, generator):
|
|
return self.joiner.join(filter(lambda s: len(s) > 0, (child for child in generator)))
|
|
def declare(self):
|
|
return self.join(child.declare() for child in self.children if child is not None)
|
|
def define(self):
|
|
return self.join(child.define() for child in self.children if child is not None)
|
|
def deps(self):
|
|
deps = set()
|
|
for child in self.children:
|
|
if child is None:
|
|
continue
|
|
deps = deps.union(child.deps())
|
|
return deps
|
|
|
|
class CGGeneric(CGThing):
|
|
"""
|
|
A class that spits out a fixed string into the codegen. Can spit out a
|
|
separate string for the declaration too.
|
|
"""
|
|
def __init__(self, define="", declare=""):
|
|
self.declareText = declare
|
|
self.defineText = define
|
|
def declare(self):
|
|
return self.declareText
|
|
def define(self):
|
|
return self.defineText
|
|
def deps(self):
|
|
return set()
|
|
|
|
# We'll want to insert the indent at the beginnings of lines, but we
|
|
# don't want to indent empty lines. So only indent lines that have a
|
|
# non-newline character on them.
|
|
lineStartDetector = re.compile("^(?=[^\n#])", re.MULTILINE)
|
|
class CGIndenter(CGThing):
|
|
"""
|
|
A class that takes another CGThing and generates code that indents that
|
|
CGThing by some number of spaces. The default indent is two spaces.
|
|
"""
|
|
def __init__(self, child, indentLevel=2, declareOnly=False):
|
|
CGThing.__init__(self)
|
|
self.child = child
|
|
self.indent = " " * indentLevel
|
|
self.declareOnly = declareOnly
|
|
def declare(self):
|
|
decl = self.child.declare()
|
|
if decl is not "":
|
|
return re.sub(lineStartDetector, self.indent, decl)
|
|
else:
|
|
return ""
|
|
def define(self):
|
|
defn = self.child.define()
|
|
if defn is not "" and not self.declareOnly:
|
|
return re.sub(lineStartDetector, self.indent, defn)
|
|
else:
|
|
return defn
|
|
|
|
class CGWrapper(CGThing):
|
|
"""
|
|
Generic CGThing that wraps other CGThings with pre and post text.
|
|
"""
|
|
def __init__(self, child, pre="", post="", declarePre=None,
|
|
declarePost=None, definePre=None, definePost=None,
|
|
declareOnly=False, defineOnly=False, reindent=False):
|
|
CGThing.__init__(self)
|
|
self.child = child
|
|
self.declarePre = declarePre or pre
|
|
self.declarePost = declarePost or post
|
|
self.definePre = definePre or pre
|
|
self.definePost = definePost or post
|
|
self.declareOnly = declareOnly
|
|
self.defineOnly = defineOnly
|
|
self.reindent = reindent
|
|
def declare(self):
|
|
if self.defineOnly:
|
|
return ''
|
|
decl = self.child.declare()
|
|
if self.reindent:
|
|
# We don't use lineStartDetector because we don't want to
|
|
# insert whitespace at the beginning of our _first_ line.
|
|
decl = stripTrailingWhitespace(
|
|
decl.replace("\n", "\n" + (" " * len(self.declarePre))))
|
|
return self.declarePre + decl + self.declarePost
|
|
def define(self):
|
|
if self.declareOnly:
|
|
return ''
|
|
defn = self.child.define()
|
|
if self.reindent:
|
|
# We don't use lineStartDetector because we don't want to
|
|
# insert whitespace at the beginning of our _first_ line.
|
|
defn = stripTrailingWhitespace(
|
|
defn.replace("\n", "\n" + (" " * len(self.definePre))))
|
|
return self.definePre + defn + self.definePost
|
|
|
|
def deps(self):
|
|
return self.child.deps()
|
|
|
|
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 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 CGTemplatedType(CGWrapper):
|
|
def __init__(self, templateName, child, isConst=False, isReference=False):
|
|
const = "const " if isConst else ""
|
|
pre = "%s%s<" % (const, templateName)
|
|
ref = "&" if isReference else ""
|
|
post = " >%s" % ref
|
|
CGWrapper.__init__(self, child, pre=pre, post=post)
|
|
|
|
class CGNamespace(CGWrapper):
|
|
def __init__(self, namespace, child, declareOnly=False):
|
|
pre = "namespace %s {\n" % namespace
|
|
post = "} // namespace %s\n" % namespace
|
|
CGWrapper.__init__(self, child, pre=pre, post=post,
|
|
declareOnly=declareOnly)
|
|
@staticmethod
|
|
def build(namespaces, child, declareOnly=False):
|
|
"""
|
|
Static helper method to build multiple wrapped namespaces.
|
|
"""
|
|
if not namespaces:
|
|
return CGWrapper(child, declareOnly=declareOnly)
|
|
inner = CGNamespace.build(namespaces[1:], child, declareOnly=declareOnly)
|
|
return CGNamespace(namespaces[0], inner, declareOnly=declareOnly)
|
|
|
|
class CGIncludeGuard(CGWrapper):
|
|
"""
|
|
Generates include guards for a header.
|
|
"""
|
|
def __init__(self, prefix, child):
|
|
"""|prefix| is the filename without the extension."""
|
|
define = 'mozilla_dom_%s_h__' % prefix
|
|
CGWrapper.__init__(self, child,
|
|
declarePre='#ifndef %s\n#define %s\n\n' % (define, define),
|
|
declarePost='\n#endif // %s\n' % define)
|
|
|
|
def getRelevantProviders(descriptor, dictionary, config):
|
|
assert not descriptor or not dictionary
|
|
if descriptor is not None:
|
|
return [descriptor]
|
|
if dictionary is not None:
|
|
# Do both the non-worker and worker versions
|
|
return [
|
|
config.getDescriptorProvider(False),
|
|
config.getDescriptorProvider(True)
|
|
]
|
|
# Do non-workers only for callbacks
|
|
return [ config.getDescriptorProvider(False) ]
|
|
|
|
def callForEachType(descriptors, dictionaries, callbacks, func):
|
|
for d in descriptors:
|
|
if d.interface.isExternal():
|
|
continue
|
|
for t in getTypesFromDescriptor(d):
|
|
func(t, descriptor=d)
|
|
for dictionary in dictionaries:
|
|
for t in getTypesFromDictionary(dictionary):
|
|
func(t, dictionary=dictionary)
|
|
for callback in callbacks:
|
|
for t in getTypesFromCallback(callback):
|
|
func(t)
|
|
|
|
class CGHeaders(CGWrapper):
|
|
"""
|
|
Generates the appropriate include statements.
|
|
"""
|
|
def __init__(self, descriptors, dictionaries, callbacks,
|
|
callbackDescriptors,
|
|
declareIncludes, defineIncludes, child,
|
|
config=None, jsImplementedDescriptors=[]):
|
|
"""
|
|
Builds a set of includes to cover |descriptors|.
|
|
|
|
Also includes the files in |declareIncludes| in the header
|
|
file and the files in |defineIncludes| in the .cpp.
|
|
"""
|
|
|
|
# Determine the filenames for which we need headers.
|
|
interfaceDeps = [d.interface for d in descriptors]
|
|
ancestors = []
|
|
for iface in interfaceDeps:
|
|
while iface.parent:
|
|
ancestors.append(iface.parent)
|
|
iface = iface.parent
|
|
interfaceDeps.extend(ancestors)
|
|
bindingIncludes = set(self.getDeclarationFilename(d) for d in interfaceDeps)
|
|
|
|
# Grab all the implementation declaration files we need.
|
|
implementationIncludes = set(d.headerFile for d in descriptors if d.needsHeaderInclude())
|
|
|
|
# Grab the includes for checking hasInstance
|
|
interfacesImplementingSelf = set()
|
|
for d in descriptors:
|
|
interfacesImplementingSelf |= d.interface.interfacesImplementingSelf
|
|
implementationIncludes |= set(self.getDeclarationFilename(i) for i in
|
|
interfacesImplementingSelf)
|
|
|
|
# Grab the includes for the things that involve XPCOM interfaces
|
|
hasInstanceIncludes = set("nsIDOM" + d.interface.identifier.name + ".h" for d
|
|
in descriptors if
|
|
NeedsGeneratedHasInstance(d) and
|
|
d.interface.hasInterfacePrototypeObject())
|
|
|
|
# Now find all the things we'll need as arguments because we
|
|
# need to wrap or unwrap them.
|
|
bindingHeaders = set()
|
|
declareIncludes = set(declareIncludes)
|
|
def addHeadersForType(t, descriptor=None, dictionary=None):
|
|
"""
|
|
Add the relevant headers for this type. We use descriptor and
|
|
dictionary, if passed, to decide what to do with interface types.
|
|
"""
|
|
assert not descriptor or not dictionary
|
|
unrolled = t.unroll()
|
|
if unrolled.isUnion():
|
|
# UnionConversions.h includes UnionTypes.h
|
|
bindingHeaders.add("mozilla/dom/UnionConversions.h")
|
|
elif unrolled.isInterface():
|
|
if unrolled.isSpiderMonkeyInterface():
|
|
bindingHeaders.add("jsfriendapi.h")
|
|
bindingHeaders.add("mozilla/dom/TypedArray.h")
|
|
else:
|
|
providers = getRelevantProviders(descriptor, dictionary,
|
|
config)
|
|
for p in providers:
|
|
try:
|
|
typeDesc = p.getDescriptor(unrolled.inner.identifier.name)
|
|
except NoSuchDescriptorError:
|
|
continue
|
|
if dictionary:
|
|
# Dictionaries with interface members rely on the
|
|
# actual class definition of that interface member
|
|
# being visible in the binding header, because they
|
|
# store them in nsRefPtr and have inline
|
|
# constructors/destructors.
|
|
#
|
|
# XXXbz maybe dictionaries with interface members
|
|
# should just have out-of-line constructors and
|
|
# destructors?
|
|
declareIncludes.add(typeDesc.headerFile)
|
|
else:
|
|
implementationIncludes.add(typeDesc.headerFile)
|
|
bindingHeaders.add(self.getDeclarationFilename(typeDesc.interface))
|
|
elif unrolled.isDictionary():
|
|
bindingHeaders.add(self.getDeclarationFilename(unrolled.inner))
|
|
elif unrolled.isCallback():
|
|
# Callbacks are both a type and an object
|
|
bindingHeaders.add(self.getDeclarationFilename(t.unroll()))
|
|
elif unrolled.isFloat() and not unrolled.isUnrestricted():
|
|
# Restricted floats are tested for finiteness
|
|
bindingHeaders.add("mozilla/FloatingPoint.h")
|
|
|
|
callForEachType(descriptors + callbackDescriptors, dictionaries,
|
|
callbacks, addHeadersForType)
|
|
|
|
# Now for non-callback descriptors make sure we include any
|
|
# headers needed by Func declarations.
|
|
for desc in descriptors:
|
|
if desc.interface.isExternal():
|
|
continue
|
|
for m in desc.interface.members:
|
|
func = PropertyDefiner.getStringAttr(m, "Func")
|
|
# Include the right class header, which we can only do
|
|
# if this is a class member function.
|
|
if func is not None and "::" in func:
|
|
# Strip out the function name and convert "::" to "/"
|
|
bindingHeaders.add("/".join(func.split("::")[:-1]) + ".h")
|
|
|
|
for d in dictionaries:
|
|
if d.parent:
|
|
declareIncludes.add(self.getDeclarationFilename(d.parent))
|
|
bindingHeaders.add(self.getDeclarationFilename(d))
|
|
|
|
for c in callbacks:
|
|
bindingHeaders.add(self.getDeclarationFilename(c))
|
|
|
|
for c in callbackDescriptors:
|
|
bindingHeaders.add(self.getDeclarationFilename(c.interface))
|
|
|
|
if len(callbacks) != 0:
|
|
# We need CallbackFunction to serve as our parent class
|
|
declareIncludes.add("mozilla/dom/CallbackFunction.h")
|
|
# And we need BindingUtils.h so we can wrap "this" objects
|
|
declareIncludes.add("mozilla/dom/BindingUtils.h")
|
|
|
|
if len(callbackDescriptors) != 0 or len(jsImplementedDescriptors) != 0:
|
|
# We need CallbackInterface to serve as our parent class
|
|
declareIncludes.add("mozilla/dom/CallbackInterface.h")
|
|
# And we need BindingUtils.h so we can wrap "this" objects
|
|
declareIncludes.add("mozilla/dom/BindingUtils.h")
|
|
|
|
# Also need to include the headers for ancestors of
|
|
# JS-implemented interfaces.
|
|
for jsImplemented in jsImplementedDescriptors:
|
|
jsParent = jsImplemented.interface.parent
|
|
if jsParent:
|
|
parentDesc = jsImplemented.getDescriptor(jsParent.identifier.name)
|
|
declareIncludes.add(parentDesc.jsImplParentHeader)
|
|
|
|
if len(jsImplementedDescriptors) != 0:
|
|
bindingHeaders.add("nsIDOMGlobalPropertyInitializer.h")
|
|
|
|
# Let the machinery do its thing.
|
|
def _includeString(includes):
|
|
return ''.join(['#include "%s"\n' % i for i in includes]) + '\n'
|
|
CGWrapper.__init__(self, child,
|
|
declarePre=_includeString(sorted(declareIncludes)),
|
|
definePre=_includeString(sorted(set(defineIncludes) |
|
|
bindingIncludes |
|
|
bindingHeaders |
|
|
hasInstanceIncludes |
|
|
implementationIncludes)))
|
|
@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.h')
|
|
|
|
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 tuple containing a set of header filenames to include in
|
|
UnionTypes.h, a set of header filenames to include in UnionTypes.cpp, a set
|
|
of tuples containing a type declaration and a boolean if the type is a
|
|
struct for member types of the unions and a CGList containing CGUnionStructs
|
|
for every union.
|
|
"""
|
|
|
|
# Now find all the things we'll need as arguments and return values because
|
|
# we need to wrap or unwrap them.
|
|
headers = set()
|
|
implheaders = set(["UnionTypes.h"])
|
|
declarations = set()
|
|
unionStructs = dict()
|
|
|
|
def addInfoForType(t, descriptor=None, dictionary=None):
|
|
"""
|
|
Add info for the given type. descriptor and dictionary, if passed, are
|
|
used to figure out what to do with interface types.
|
|
"""
|
|
assert not descriptor or not dictionary
|
|
t = t.unroll()
|
|
if not t.isUnion():
|
|
return
|
|
name = str(t)
|
|
if not name in unionStructs:
|
|
providers = getRelevantProviders(descriptor, dictionary,
|
|
config)
|
|
# FIXME: Unions are broken in workers. See bug 809899.
|
|
unionStructs[name] = CGUnionStruct(t, providers[0])
|
|
for f in t.flatMemberTypes:
|
|
f = f.unroll()
|
|
if f.isInterface():
|
|
if f.isSpiderMonkeyInterface():
|
|
headers.add("jsfriendapi.h")
|
|
headers.add("mozilla/dom/TypedArray.h")
|
|
else:
|
|
for p in providers:
|
|
try:
|
|
typeDesc = p.getDescriptor(f.inner.identifier.name)
|
|
except NoSuchDescriptorError:
|
|
continue
|
|
declarations.add((typeDesc.nativeType, False))
|
|
implheaders.add(typeDesc.headerFile)
|
|
elif f.isDictionary():
|
|
declarations.add((f.inner.identifier.name, True))
|
|
implheaders.add(CGHeaders.getDeclarationFilename(f.inner))
|
|
|
|
callForEachType(descriptors, dictionaries, callbacks, addInfoForType)
|
|
|
|
return (headers, implheaders, declarations,
|
|
CGList(SortedDictValues(unionStructs), "\n"))
|
|
|
|
def UnionConversions(descriptors, dictionaries, callbacks, config):
|
|
"""
|
|
Returns a CGThing to declare all union argument conversion helper structs.
|
|
"""
|
|
# Now find all the things we'll need as arguments because we
|
|
# need to unwrap them.
|
|
headers = set()
|
|
unionConversions = dict()
|
|
|
|
def addInfoForType(t, descriptor=None, dictionary=None):
|
|
"""
|
|
Add info for the given type. descriptor and dictionary, if passed, are
|
|
used to figure out what to do with interface types.
|
|
"""
|
|
assert not descriptor or not dictionary
|
|
t = t.unroll()
|
|
if not t.isUnion():
|
|
return
|
|
name = str(t)
|
|
if not name in unionConversions:
|
|
providers = getRelevantProviders(descriptor, dictionary,
|
|
config)
|
|
unionConversions[name] = CGUnionConversionStruct(t, providers[0])
|
|
for f in t.flatMemberTypes:
|
|
f = f.unroll()
|
|
if f.isInterface():
|
|
if f.isSpiderMonkeyInterface():
|
|
headers.add("jsfriendapi.h")
|
|
headers.add("mozilla/dom/TypedArray.h")
|
|
elif not f.inner.isExternal():
|
|
headers.add(CGHeaders.getDeclarationFilename(f.inner))
|
|
elif f.isDictionary():
|
|
headers.add(CGHeaders.getDeclarationFilename(f.inner))
|
|
|
|
callForEachType(descriptors, dictionaries, callbacks, addInfoForType)
|
|
|
|
return (headers,
|
|
CGWrapper(CGList(SortedDictValues(unionConversions), "\n"),
|
|
post="\n\n"))
|
|
|
|
class Argument():
|
|
"""
|
|
A class for outputting the type and name of an argument
|
|
"""
|
|
def __init__(self, argType, name, default=None):
|
|
self.argType = argType
|
|
self.name = name
|
|
self.default = default
|
|
def declare(self):
|
|
string = self.argType + ' ' + self.name
|
|
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.
|
|
|
|
static should be True to generate a static method, which only has
|
|
a definition.
|
|
|
|
If templateArgs is not None it should be a list of strings containing
|
|
template arguments, and the function will be templatized using those
|
|
arguments.
|
|
"""
|
|
def __init__(self, descriptor, name, returnType, args, inline=False, alwaysInline=False, static=False, templateArgs=None):
|
|
CGThing.__init__(self)
|
|
self.descriptor = descriptor
|
|
self.name = name
|
|
self.returnType = returnType
|
|
self.args = args
|
|
self.inline = inline
|
|
self.alwaysInline = alwaysInline
|
|
self.static = static
|
|
self.templateArgs = templateArgs
|
|
def _argstring(self, declare):
|
|
return ', '.join([a.declare() if declare else a.define() for a in self.args])
|
|
def _template(self):
|
|
if self.templateArgs is None:
|
|
return ''
|
|
return 'template <%s>\n' % ', '.join(self.templateArgs)
|
|
def _decorators(self):
|
|
decorators = []
|
|
if self.alwaysInline:
|
|
decorators.append('MOZ_ALWAYS_INLINE')
|
|
elif self.inline:
|
|
decorators.append('inline')
|
|
if self.static:
|
|
decorators.append('static')
|
|
decorators.append(self.returnType)
|
|
maybeNewline = " " if self.inline else "\n"
|
|
return ' '.join(decorators) + maybeNewline
|
|
def declare(self):
|
|
if self.inline:
|
|
return self._define(True)
|
|
return "%s%s%s(%s);\n" % (self._template(), self._decorators(), self.name, self._argstring(True))
|
|
def _define(self, fromDeclare=False):
|
|
return self.definition_prologue(fromDeclare) + "\n" + self.definition_body() + self.definition_epilogue()
|
|
def define(self):
|
|
return "" if self.inline else self._define()
|
|
def definition_prologue(self, fromDeclare):
|
|
return "%s%s%s(%s)\n{" % (self._template(), self._decorators(),
|
|
self.name, self._argstring(fromDeclare))
|
|
def definition_epilogue(self):
|
|
return "\n}\n"
|
|
def definition_body(self):
|
|
assert(False) # Override me!
|
|
|
|
class CGAbstractStaticMethod(CGAbstractMethod):
|
|
"""
|
|
Abstract base class for codegen of implementation-only (no
|
|
declaration) static methods.
|
|
"""
|
|
def __init__(self, descriptor, name, returnType, args):
|
|
CGAbstractMethod.__init__(self, descriptor, name, returnType, args,
|
|
inline=False, static=True)
|
|
def declare(self):
|
|
# We only have implementation
|
|
return ""
|
|
|
|
class CGAbstractClassHook(CGAbstractStaticMethod):
|
|
"""
|
|
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):
|
|
CGAbstractStaticMethod.__init__(self, descriptor, name, returnType,
|
|
args)
|
|
|
|
def definition_body_prologue(self):
|
|
if self.descriptor.nativeOwnership == 'nsisupports':
|
|
assertion = (' MOZ_STATIC_ASSERT((IsBaseOf<nsISupports, %s>::value), '
|
|
'"Must be an nsISupports class");') % self.descriptor.nativeType
|
|
else:
|
|
assertion = ''
|
|
return """%s
|
|
%s* self = UnwrapDOMObject<%s>(obj);
|
|
""" % (assertion, self.descriptor.nativeType, self.descriptor.nativeType)
|
|
|
|
def definition_body(self):
|
|
return self.definition_body_prologue() + self.generate_code()
|
|
|
|
def generate_code(self):
|
|
# Override me
|
|
assert(False)
|
|
|
|
class CGAddPropertyHook(CGAbstractClassHook):
|
|
"""
|
|
A hook for addProperty, used to preserve our wrapper from GC.
|
|
"""
|
|
def __init__(self, descriptor):
|
|
args = [Argument('JSContext*', 'cx'), Argument('JSHandleObject', 'obj'),
|
|
Argument('JSHandleId', 'id'), Argument('JSMutableHandleValue', 'vp')]
|
|
CGAbstractClassHook.__init__(self, descriptor, ADDPROPERTY_HOOK_NAME,
|
|
'JSBool', args)
|
|
|
|
def generate_code(self):
|
|
assert not self.descriptor.workers and self.descriptor.wrapperCache
|
|
if self.descriptor.nativeOwnership == 'nsisupports':
|
|
preserveArgs = "reinterpret_cast<nsISupports*>(self), self"
|
|
else:
|
|
preserveArgs = "self, self, NS_CYCLE_COLLECTION_PARTICIPANT(%s)" % self.descriptor.nativeType
|
|
return (" // We don't want to preserve if we don't have a wrapper.\n"
|
|
" if (self->GetWrapperPreserveColor()) {\n"
|
|
" nsContentUtils::PreserveWrapper(%s);\n"
|
|
" }\n"
|
|
" return true;" % preserveArgs)
|
|
|
|
def DeferredFinalizeSmartPtr(descriptor):
|
|
if descriptor.nativeOwnership == 'owned':
|
|
smartPtr = 'nsAutoPtr<%s>'
|
|
else:
|
|
assert descriptor.nativeOwnership == 'refcounted'
|
|
smartPtr = 'nsRefPtr<%s>'
|
|
return smartPtr % descriptor.nativeType
|
|
|
|
class CGDeferredFinalizePointers(CGThing):
|
|
def __init__(self, descriptor):
|
|
CGThing.__init__(self)
|
|
self.descriptor = descriptor
|
|
|
|
def declare(self):
|
|
return ""
|
|
|
|
def define(self):
|
|
return """nsTArray<%s >* sDeferredFinalizePointers;
|
|
""" % DeferredFinalizeSmartPtr(self.descriptor)
|
|
|
|
class CGGetDeferredFinalizePointers(CGAbstractStaticMethod):
|
|
def __init__(self, descriptor):
|
|
CGAbstractStaticMethod.__init__(self, descriptor, "GetDeferredFinalizePointers", "void*", [])
|
|
|
|
def definition_body(self):
|
|
return """ nsTArray<%s >* pointers = sDeferredFinalizePointers;
|
|
sDeferredFinalizePointers = nullptr;
|
|
return pointers;""" % DeferredFinalizeSmartPtr(self.descriptor)
|
|
|
|
class CGDeferredFinalize(CGAbstractStaticMethod):
|
|
def __init__(self, descriptor):
|
|
CGAbstractStaticMethod.__init__(self, descriptor, "DeferredFinalize", "bool", [Argument('uint32_t', 'slice'), Argument('void*', 'data')])
|
|
|
|
def definition_body(self):
|
|
smartPtr = DeferredFinalizeSmartPtr(self.descriptor)
|
|
return """ MOZ_ASSERT(slice > 0, "nonsensical/useless call with slice == 0");
|
|
nsTArray<%(smartPtr)s >* pointers = static_cast<nsTArray<%(smartPtr)s >*>(data);
|
|
uint32_t oldLen = pointers->Length();
|
|
slice = std::min(oldLen, slice);
|
|
uint32_t newLen = oldLen - slice;
|
|
pointers->RemoveElementsAt(newLen, slice);
|
|
if (newLen == 0) {
|
|
delete pointers;
|
|
return true;
|
|
}
|
|
return false;""" % { 'smartPtr': smartPtr }
|
|
|
|
def finalizeHook(descriptor, hookName, context):
|
|
if descriptor.customFinalize:
|
|
finalize = "self->%s(%s);" % (hookName, context)
|
|
else:
|
|
finalize = "JSBindingFinalized<%s>::Finalized(self);\n" % descriptor.nativeType
|
|
finalize += "ClearWrapper(self, self);\n" if descriptor.wrapperCache else ""
|
|
if descriptor.workers:
|
|
finalize += "self->Release();"
|
|
elif descriptor.nativeOwnership == 'nsisupports':
|
|
finalize += """XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance();
|
|
if (rt) {
|
|
rt->DeferredRelease(reinterpret_cast<nsISupports*>(self));
|
|
} else {
|
|
NS_RELEASE(self);
|
|
}"""
|
|
else:
|
|
smartPtr = DeferredFinalizeSmartPtr(descriptor)
|
|
finalize += """static bool registered = false;
|
|
if (!registered) {
|
|
XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance();
|
|
if (!rt) {
|
|
%(smartPtr)s dying;
|
|
Take(dying, self);
|
|
return;
|
|
}
|
|
rt->RegisterDeferredFinalize(GetDeferredFinalizePointers, DeferredFinalize);
|
|
registered = true;
|
|
}
|
|
if (!sDeferredFinalizePointers) {
|
|
sDeferredFinalizePointers = new nsAutoTArray<%(smartPtr)s, 16>();
|
|
}
|
|
%(smartPtr)s* defer = sDeferredFinalizePointers->AppendElement();
|
|
if (!defer) {
|
|
%(smartPtr)s dying;
|
|
Take(dying, self);
|
|
return;
|
|
}
|
|
Take(*defer, self);""" % { 'smartPtr': smartPtr }
|
|
return CGIfWrapper(CGGeneric(finalize), "self")
|
|
|
|
class CGClassFinalizeHook(CGAbstractClassHook):
|
|
"""
|
|
A hook for finalize, used to release our native object.
|
|
"""
|
|
def __init__(self, descriptor):
|
|
args = [Argument('JSFreeOp*', 'fop'), Argument('JSObject*', 'obj')]
|
|
CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME,
|
|
'void', args)
|
|
|
|
def generate_code(self):
|
|
return CGIndenter(finalizeHook(self.descriptor, self.name, self.args[0].name)).define()
|
|
|
|
class CGClassTraceHook(CGAbstractClassHook):
|
|
"""
|
|
A hook to trace through our native object; used for GC and CC
|
|
"""
|
|
def __init__(self, descriptor):
|
|
args = [Argument('JSTracer*', 'trc'), Argument('JSObject*', 'obj')]
|
|
CGAbstractClassHook.__init__(self, descriptor, TRACE_HOOK_NAME, 'void',
|
|
args)
|
|
|
|
def generate_code(self):
|
|
return """ if (self) {
|
|
self->%s(%s);
|
|
}""" % (self.name, self.args[0].name)
|
|
|
|
class CGClassConstructor(CGAbstractStaticMethod):
|
|
"""
|
|
JS-visible constructor for our objects
|
|
"""
|
|
def __init__(self, descriptor, ctor, name=CONSTRUCT_HOOK_NAME):
|
|
args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'), Argument('JS::Value*', 'vp')]
|
|
CGAbstractStaticMethod.__init__(self, descriptor, name, 'JSBool', args)
|
|
self._ctor = ctor
|
|
|
|
def define(self):
|
|
if not self._ctor:
|
|
return ""
|
|
return CGAbstractStaticMethod.define(self)
|
|
|
|
def definition_body(self):
|
|
return self.generate_code()
|
|
|
|
def generate_code(self):
|
|
preamble = """
|
|
JS::Rooted<JSObject*> obj(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)));
|
|
"""
|
|
name = self._ctor.identifier.name
|
|
nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
|
|
callGenerator = CGMethodCall(nativeName, True, self.descriptor,
|
|
self._ctor, isConstructor=True)
|
|
return preamble + callGenerator.define();
|
|
|
|
class CGClassConstructHookHolder(CGGeneric):
|
|
def __init__(self, descriptor):
|
|
if descriptor.interface.ctor():
|
|
constructHook = CONSTRUCT_HOOK_NAME
|
|
else:
|
|
constructHook = "ThrowingConstructor"
|
|
CGGeneric.__init__(self,
|
|
"static const JSNativeHolder " + CONSTRUCT_HOOK_NAME + "_holder = {\n" +
|
|
" " + constructHook + ",\n" +
|
|
" " + NativePropertyHooks(descriptor) + "\n" +
|
|
"};\n")
|
|
|
|
def NamedConstructorName(m):
|
|
return '_' + m.identifier.name
|
|
|
|
class CGNamedConstructors(CGThing):
|
|
def __init__(self, descriptor):
|
|
self.descriptor = descriptor
|
|
CGThing.__init__(self)
|
|
def declare(self):
|
|
return ""
|
|
def define(self):
|
|
if len(self.descriptor.interface.namedConstructors) == 0:
|
|
return ""
|
|
|
|
constructorID = "constructors::id::"
|
|
if self.descriptor.interface.hasInterfaceObject():
|
|
constructorID += self.descriptor.name
|
|
else:
|
|
constructorID += "_ID_Count"
|
|
nativePropertyHooks = """const NativePropertyHooks sNamedConstructorNativePropertyHooks = {
|
|
nullptr,
|
|
nullptr,
|
|
{ nullptr, nullptr },
|
|
prototypes::id::%s,
|
|
%s,
|
|
nullptr
|
|
};
|
|
|
|
""" % (self.descriptor.name, constructorID)
|
|
namedConstructors = CGList([], ",\n")
|
|
for n in self.descriptor.interface.namedConstructors:
|
|
namedConstructors.append(CGGeneric("{ \"%s\", { %s, &sNamedConstructorNativePropertyHooks }, %i }" % (n.identifier.name, NamedConstructorName(n), methodLength(n))))
|
|
namedConstructors.append(CGGeneric("{ nullptr, { nullptr, nullptr }, 0 }"))
|
|
namedConstructors = CGWrapper(CGIndenter(namedConstructors),
|
|
pre="static const NamedConstructor namedConstructors[] = {\n",
|
|
post="\n};\n")
|
|
return nativePropertyHooks + namedConstructors.define()
|
|
|
|
class CGClassHasInstanceHook(CGAbstractStaticMethod):
|
|
def __init__(self, descriptor):
|
|
args = [Argument('JSContext*', 'cx'), Argument('JSHandleObject', 'obj'),
|
|
Argument('JSMutableHandleValue', 'vp'), Argument('JSBool*', 'bp')]
|
|
CGAbstractStaticMethod.__init__(self, descriptor, HASINSTANCE_HOOK_NAME,
|
|
'JSBool', args)
|
|
|
|
def define(self):
|
|
if not NeedsGeneratedHasInstance(self.descriptor):
|
|
return ""
|
|
return CGAbstractStaticMethod.define(self)
|
|
|
|
def definition_body(self):
|
|
return self.generate_code()
|
|
|
|
def generate_code(self):
|
|
assert self.descriptor.nativeOwnership == 'nsisupports'
|
|
header = """
|
|
if (!vp.isObject()) {
|
|
*bp = false;
|
|
return true;
|
|
}
|
|
|
|
JSObject* instance = &vp.toObject();
|
|
"""
|
|
if self.descriptor.interface.hasInterfacePrototypeObject():
|
|
return header + """
|
|
bool ok = InterfaceHasInstance(cx, obj, instance, bp);
|
|
if (!ok || *bp) {
|
|
return ok;
|
|
}
|
|
|
|
// FIXME Limit this to chrome by checking xpc::AccessCheck::isChrome(obj).
|
|
nsISupports* native =
|
|
nsContentUtils::XPConnect()->GetNativeOfWrapper(cx,
|
|
js::UncheckedUnwrap(instance));
|
|
nsCOMPtr<nsIDOM%s> qiResult = do_QueryInterface(native);
|
|
*bp = !!qiResult;
|
|
return true;
|
|
""" % self.descriptor.interface.identifier.name
|
|
|
|
hasInstanceCode = """
|
|
const DOMClass* domClass = GetDOMClass(js::UncheckedUnwrap(instance));
|
|
*bp = false;
|
|
if (!domClass) {
|
|
// Not a DOM object, so certainly not an instance of this interface
|
|
return true;
|
|
}
|
|
"""
|
|
# Sort interaces implementing self by name so we get stable output.
|
|
for iface in sorted(self.descriptor.interface.interfacesImplementingSelf,
|
|
key=lambda iface: iface.identifier.name):
|
|
hasInstanceCode += """
|
|
if (domClass->mInterfaceChain[PrototypeTraits<prototypes::id::%s>::Depth] == prototypes::id::%s) {
|
|
*bp = true;
|
|
return true;
|
|
}
|
|
""" % (iface.identifier.name, iface.identifier.name)
|
|
hasInstanceCode += " return true;"
|
|
return header + hasInstanceCode;
|
|
|
|
def isChromeOnly(m):
|
|
return m.getExtendedAttribute("ChromeOnly")
|
|
|
|
class MemberCondition:
|
|
"""
|
|
An object representing the condition for a member to actually be
|
|
exposed. Either pref or func or both can be None. If not None,
|
|
they should be strings that have the pref name or function name.
|
|
"""
|
|
def __init__(self, pref, func):
|
|
assert pref is None or isinstance(pref, str)
|
|
assert func is None or isinstance(func, str)
|
|
self.pref = pref
|
|
if func is None:
|
|
self.func = "nullptr"
|
|
else:
|
|
self.func = "&" + func
|
|
|
|
def __eq__(self, other):
|
|
return self.pref == other.pref and self.func == other.func
|
|
|
|
def __ne__(self, other):
|
|
return not self.__eq__(other)
|
|
|
|
class PropertyDefiner:
|
|
"""
|
|
A common superclass for defining things on prototype objects.
|
|
|
|
Subclasses should implement generateArray to generate the actual arrays of
|
|
things we're defining. They should also set self.chrome to the list of
|
|
things only exposed to chrome and self.regular to the list of things exposed
|
|
to both chrome and web pages.
|
|
"""
|
|
def __init__(self, descriptor, name):
|
|
self.descriptor = descriptor
|
|
self.name = name
|
|
# self.prefCacheData will store an array of (prefname, bool*)
|
|
# pairs for our bool var caches. generateArray will fill it
|
|
# in as needed.
|
|
self.prefCacheData = []
|
|
def hasChromeOnly(self):
|
|
return len(self.chrome) > 0
|
|
def hasNonChromeOnly(self):
|
|
return len(self.regular) > 0
|
|
def variableName(self, chrome):
|
|
if chrome:
|
|
if self.hasChromeOnly():
|
|
return "sChrome" + self.name
|
|
else:
|
|
if self.hasNonChromeOnly():
|
|
return "s" + self.name
|
|
return "nullptr"
|
|
def usedForXrays(self):
|
|
# No Xrays in workers.
|
|
return not self.descriptor.workers
|
|
|
|
def __str__(self):
|
|
# We only need to generate id arrays for things that will end
|
|
# up used via ResolveProperty or EnumerateProperties.
|
|
str = self.generateArray(self.regular, self.variableName(False),
|
|
self.usedForXrays())
|
|
if self.hasChromeOnly():
|
|
str += self.generateArray(self.chrome, self.variableName(True),
|
|
self.usedForXrays())
|
|
return str
|
|
|
|
@staticmethod
|
|
def getStringAttr(member, name):
|
|
attr = member.getExtendedAttribute(name)
|
|
if attr is None:
|
|
return None
|
|
# It's a list of strings
|
|
assert(len(attr) is 1)
|
|
assert(attr[0] is not None)
|
|
return attr[0]
|
|
|
|
@staticmethod
|
|
def getControllingCondition(interfaceMember):
|
|
return MemberCondition(PropertyDefiner.getStringAttr(interfaceMember,
|
|
"Pref"),
|
|
PropertyDefiner.getStringAttr(interfaceMember,
|
|
"Func"))
|
|
|
|
def generatePrefableArray(self, array, name, specTemplate, specTerminator,
|
|
specType, getCondition, getDataTuple, doIdArrays):
|
|
"""
|
|
This method generates our various arrays.
|
|
|
|
array is an array of interface members as passed to generateArray
|
|
|
|
name is the name as passed to generateArray
|
|
|
|
specTemplate is a template for each entry of the spec array
|
|
|
|
specTerminator is a terminator for the spec array (inserted every time
|
|
our controlling pref changes and at the end of the array)
|
|
|
|
specType is the actual typename of our spec
|
|
|
|
getCondition is a callback function that takes an array entry and
|
|
returns the corresponding MemberCondition.
|
|
|
|
getDataTuple is a callback function that takes an array entry and
|
|
returns a tuple suitable for substitution into specTemplate.
|
|
"""
|
|
|
|
# We want to generate a single list of specs, but with specTerminator
|
|
# inserted at every point where the pref name controlling the member
|
|
# changes. That will make sure the order of the properties as exposed
|
|
# on the interface and interface prototype objects does not change when
|
|
# pref control is added to members while still allowing us to define all
|
|
# the members in the smallest number of JSAPI calls.
|
|
assert(len(array) is not 0)
|
|
lastCondition = getCondition(array[0]) # So we won't put a specTerminator
|
|
# at the very front of the list.
|
|
specs = []
|
|
prefableSpecs = []
|
|
|
|
prefableTemplate = ' { true, %s, &%s[%d] }'
|
|
prefCacheTemplate = '&%s[%d].enabled'
|
|
def switchToCondition(props, condition):
|
|
# Remember the info about where our pref-controlled
|
|
# booleans live.
|
|
if condition.pref is not None:
|
|
props.prefCacheData.append(
|
|
(condition.pref,
|
|
prefCacheTemplate % (name, len(prefableSpecs)))
|
|
)
|
|
# Set up pointers to the new sets of specs inside prefableSpecs
|
|
prefableSpecs.append(prefableTemplate %
|
|
(condition.func, name + "_specs", len(specs)))
|
|
|
|
switchToCondition(self, lastCondition)
|
|
|
|
for member in array:
|
|
curCondition = getCondition(member)
|
|
if lastCondition != curCondition:
|
|
# Terminate previous list
|
|
specs.append(specTerminator)
|
|
# And switch to our new pref
|
|
switchToCondition(self, curCondition)
|
|
lastCondition = curCondition
|
|
# And the actual spec
|
|
specs.append(specTemplate % getDataTuple(member))
|
|
specs.append(specTerminator)
|
|
prefableSpecs.append(" { false, NULL }");
|
|
|
|
specType = "const " + specType
|
|
arrays = (("static %s %s_specs[] = {\n" +
|
|
',\n'.join(specs) + "\n" +
|
|
"};\n\n" +
|
|
"// Can't be const because the pref-enabled boolean needs to be writable\n"
|
|
"static Prefable<%s> %s[] = {\n" +
|
|
',\n'.join(prefableSpecs) + "\n" +
|
|
"};\n\n") % (specType, name, specType, name))
|
|
if doIdArrays:
|
|
arrays += ("static jsid %s_ids[%i] = { JSID_VOID };\n\n" %
|
|
(name, len(specs)))
|
|
return arrays
|
|
|
|
|
|
# The length of a method is the minimum of the lengths of the
|
|
# argument lists of all its overloads.
|
|
def overloadLength(arguments):
|
|
i = len(arguments)
|
|
while i > 0 and arguments[i - 1].optional:
|
|
i -= 1
|
|
return i
|
|
def methodLength(method):
|
|
signatures = method.signatures()
|
|
return min(overloadLength(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 __
|
|
|
|
# Ignore non-static methods for interfaces without a proto object
|
|
if descriptor.interface.hasInterfacePrototypeObject() or static:
|
|
methods = [m for m in descriptor.interface.members if
|
|
m.isMethod() and m.isStatic() == static and
|
|
not m.isIdentifierLess()]
|
|
else:
|
|
methods = []
|
|
self.chrome = []
|
|
self.regular = []
|
|
for m in methods:
|
|
method = { "name": m.identifier.name,
|
|
"methodInfo": not m.isStatic(),
|
|
"length": methodLength(m),
|
|
"flags": "JSPROP_ENUMERATE",
|
|
"condition": PropertyDefiner.getControllingCondition(m) }
|
|
if isChromeOnly(m):
|
|
self.chrome.append(method)
|
|
else:
|
|
self.regular.append(method)
|
|
|
|
# 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",
|
|
"condition": MemberCondition(None, None) })
|
|
|
|
if (not descriptor.interface.parent and not static and
|
|
descriptor.nativeOwnership == 'nsisupports' and
|
|
descriptor.interface.hasInterfacePrototypeObject()):
|
|
self.chrome.append({"name": 'QueryInterface',
|
|
"methodInfo": False,
|
|
"length": 1,
|
|
"flags": "0",
|
|
"condition": MemberCondition(None, None) })
|
|
|
|
if not static:
|
|
stringifier = descriptor.operations['Stringifier']
|
|
if stringifier:
|
|
toStringDesc = { "name": "toString",
|
|
"nativeName": stringifier.identifier.name,
|
|
"length": 0,
|
|
"flags": "JSPROP_ENUMERATE",
|
|
"condition": PropertyDefiner.getControllingCondition(stringifier) }
|
|
if isChromeOnly(stringifier):
|
|
self.chrome.append(toStringDesc)
|
|
else:
|
|
self.regular.append(toStringDesc)
|
|
|
|
if static:
|
|
if not descriptor.interface.hasInterfaceObject():
|
|
# static methods go on the interface object
|
|
assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
|
|
else:
|
|
if not descriptor.interface.hasInterfacePrototypeObject():
|
|
# non-static methods go on the interface prototype object
|
|
assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
|
|
|
|
def generateArray(self, array, name, doIdArrays):
|
|
if len(array) == 0:
|
|
return ""
|
|
|
|
def condition(m):
|
|
return m["condition"]
|
|
|
|
def specData(m):
|
|
accessor = m.get("nativeName", m["name"])
|
|
if m.get("methodInfo", True):
|
|
jitinfo = ("&%s_methodinfo" % accessor)
|
|
accessor = "genericMethod"
|
|
else:
|
|
jitinfo = "nullptr"
|
|
return (m["name"], accessor, jitinfo, m["length"], m["flags"])
|
|
|
|
return self.generatePrefableArray(
|
|
array, name,
|
|
' JS_FNINFO("%s", %s, %s, %s, %s)',
|
|
' JS_FS_END',
|
|
'JSFunctionSpec',
|
|
condition, specData, doIdArrays)
|
|
|
|
class AttrDefiner(PropertyDefiner):
|
|
def __init__(self, descriptor, name, static, unforgeable=False):
|
|
assert not (static and unforgeable)
|
|
PropertyDefiner.__init__(self, descriptor, name)
|
|
self.name = name
|
|
# Ignore non-static attributes for interfaces without a proto object
|
|
if descriptor.interface.hasInterfacePrototypeObject() or static:
|
|
attributes = [m for m in descriptor.interface.members if
|
|
m.isAttr() and m.isStatic() == static and
|
|
m.isUnforgeable() == unforgeable]
|
|
else:
|
|
attributes = []
|
|
self.chrome = [m for m in attributes if isChromeOnly(m)]
|
|
self.regular = [m for m in attributes if not isChromeOnly(m)]
|
|
self.static = static
|
|
self.unforgeable = unforgeable
|
|
|
|
if static:
|
|
if not descriptor.interface.hasInterfaceObject():
|
|
# static attributes go on the interface object
|
|
assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
|
|
else:
|
|
if not descriptor.interface.hasInterfacePrototypeObject():
|
|
# non-static attributes go on the interface prototype object
|
|
assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
|
|
|
|
def generateArray(self, array, name, doIdArrays):
|
|
if len(array) == 0:
|
|
return ""
|
|
|
|
def flags(attr):
|
|
unforgeable = " | JSPROP_PERMANENT" if self.unforgeable else ""
|
|
return ("JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS" +
|
|
unforgeable)
|
|
|
|
def getter(attr):
|
|
if self.static:
|
|
accessor = 'get_' + attr.identifier.name
|
|
jitinfo = "nullptr"
|
|
else:
|
|
accessor = ("genericLenientGetter" if attr.hasLenientThis()
|
|
else "genericGetter")
|
|
jitinfo = "&%s_getterinfo" % attr.identifier.name
|
|
return "{ (JSPropertyOp)%s, %s }" % (accessor, jitinfo)
|
|
|
|
def setter(attr):
|
|
if attr.readonly and attr.getExtendedAttribute("PutForwards") is None:
|
|
return "JSOP_NULLWRAPPER"
|
|
if self.static:
|
|
accessor = 'set_' + attr.identifier.name
|
|
jitinfo = "nullptr"
|
|
else:
|
|
accessor = ("genericLenientSetter" if attr.hasLenientThis()
|
|
else "genericSetter")
|
|
jitinfo = "&%s_setterinfo" % attr.identifier.name
|
|
return "{ (JSStrictPropertyOp)%s, %s }" % (accessor, jitinfo)
|
|
|
|
def specData(attr):
|
|
return (attr.identifier.name, flags(attr), getter(attr),
|
|
setter(attr))
|
|
|
|
return self.generatePrefableArray(
|
|
array, name,
|
|
' { "%s", 0, %s, %s, %s}',
|
|
' { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }',
|
|
'JSPropertySpec',
|
|
PropertyDefiner.getControllingCondition, specData, doIdArrays)
|
|
|
|
class ConstDefiner(PropertyDefiner):
|
|
"""
|
|
A class for definining constants on the interface object
|
|
"""
|
|
def __init__(self, descriptor, name):
|
|
PropertyDefiner.__init__(self, descriptor, name)
|
|
self.name = name
|
|
constants = [m for m in descriptor.interface.members if m.isConst()]
|
|
self.chrome = [m for m in constants if isChromeOnly(m)]
|
|
self.regular = [m for m in constants if not isChromeOnly(m)]
|
|
|
|
def generateArray(self, array, name, doIdArrays):
|
|
if len(array) == 0:
|
|
return ""
|
|
|
|
def specData(const):
|
|
return (const.identifier.name,
|
|
convertConstIDLValueToJSVal(const.value))
|
|
|
|
return self.generatePrefableArray(
|
|
array, name,
|
|
' { "%s", %s }',
|
|
' { 0, JSVAL_VOID }',
|
|
'ConstantSpec',
|
|
PropertyDefiner.getControllingCondition, specData, doIdArrays)
|
|
|
|
class PropertyArrays():
|
|
def __init__(self, descriptor):
|
|
self.staticMethods = MethodDefiner(descriptor, "StaticMethods",
|
|
static=True)
|
|
self.staticAttrs = AttrDefiner(descriptor, "StaticAttributes",
|
|
static=True)
|
|
self.methods = MethodDefiner(descriptor, "Methods", static=False)
|
|
self.attrs = AttrDefiner(descriptor, "Attributes", static=False)
|
|
self.unforgeableAttrs = AttrDefiner(descriptor, "UnforgeableAttributes",
|
|
static=False, unforgeable=True)
|
|
self.consts = ConstDefiner(descriptor, "Constants")
|
|
|
|
@staticmethod
|
|
def arrayNames():
|
|
return [ "staticMethods", "staticAttrs", "methods", "attrs",
|
|
"unforgeableAttrs", "consts" ]
|
|
|
|
def hasChromeOnly(self):
|
|
return any(getattr(self, a).hasChromeOnly() for a in self.arrayNames())
|
|
def hasNonChromeOnly(self):
|
|
return any(getattr(self, a).hasNonChromeOnly() for a in self.arrayNames())
|
|
def __str__(self):
|
|
define = ""
|
|
for array in self.arrayNames():
|
|
define += str(getattr(self, array))
|
|
return define
|
|
|
|
class CGNativeProperties(CGList):
|
|
def __init__(self, descriptor, properties):
|
|
def generateNativeProperties(name, chrome):
|
|
def check(p):
|
|
return p.hasChromeOnly() if chrome else p.hasNonChromeOnly()
|
|
|
|
nativeProps = []
|
|
for array in properties.arrayNames():
|
|
propertyArray = getattr(properties, array)
|
|
if check(propertyArray):
|
|
if propertyArray.usedForXrays():
|
|
ids = "%(name)s_ids"
|
|
else:
|
|
ids = "nullptr"
|
|
props = "%(name)s, " + ids + ", %(name)s_specs"
|
|
props = (props %
|
|
{ 'name': propertyArray.variableName(chrome) })
|
|
else:
|
|
props = "nullptr, nullptr, nullptr"
|
|
nativeProps.append(CGGeneric(props))
|
|
return CGWrapper(CGIndenter(CGList(nativeProps, ",\n")),
|
|
pre="static const NativeProperties %s = {\n" % name,
|
|
post="\n};")
|
|
|
|
nativeProperties = []
|
|
if properties.hasNonChromeOnly():
|
|
nativeProperties.append(
|
|
generateNativeProperties("sNativeProperties", False))
|
|
if properties.hasChromeOnly():
|
|
nativeProperties.append(
|
|
generateNativeProperties("sChromeOnlyNativeProperties", True))
|
|
CGList.__init__(self, nativeProperties, "\n\n")
|
|
|
|
def declare(self):
|
|
return ""
|
|
|
|
class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
|
|
"""
|
|
Generate the CreateInterfaceObjects method for an interface descriptor.
|
|
|
|
properties should be a PropertyArrays instance.
|
|
"""
|
|
def __init__(self, descriptor, properties):
|
|
args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal'),
|
|
Argument('JSObject**', 'protoAndIfaceArray')]
|
|
CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'void', args)
|
|
self.properties = properties
|
|
def definition_body(self):
|
|
protoChain = self.descriptor.prototypeChain
|
|
if len(protoChain) == 1:
|
|
getParentProto = "JS_GetObjectPrototype(aCx, aGlobal)"
|
|
else:
|
|
parentProtoName = self.descriptor.prototypeChain[-2]
|
|
getParentProto = ("%s::GetProtoObject(aCx, aGlobal)" %
|
|
toBindingNamespace(parentProtoName))
|
|
|
|
needInterfaceObject = self.descriptor.interface.hasInterfaceObject()
|
|
needInterfacePrototypeObject = self.descriptor.interface.hasInterfacePrototypeObject()
|
|
|
|
# if we don't need to create anything, why are we generating this?
|
|
assert needInterfaceObject or needInterfacePrototypeObject
|
|
|
|
idsToInit = []
|
|
# There is no need to init any IDs in workers, because worker bindings
|
|
# don't have Xrays.
|
|
if not self.descriptor.workers:
|
|
for var in self.properties.arrayNames():
|
|
props = getattr(self.properties, var)
|
|
# We only have non-chrome ids to init if we have no chrome ids.
|
|
if props.hasChromeOnly():
|
|
idsToInit.append(props.variableName(True))
|
|
if props.hasNonChromeOnly():
|
|
idsToInit.append(props.variableName(False))
|
|
if len(idsToInit) > 0:
|
|
initIds = CGList(
|
|
[CGGeneric("!InitIds(aCx, %s, %s_ids)" % (varname, varname)) for
|
|
varname in idsToInit], ' ||\n')
|
|
if len(idsToInit) > 1:
|
|
initIds = CGWrapper(initIds, pre="(", post=")", reindent=True)
|
|
initIds = CGList(
|
|
[CGGeneric("%s_ids[0] == JSID_VOID &&" % idsToInit[0]), initIds],
|
|
"\n")
|
|
initIds = CGWrapper(initIds, pre="if (", post=") {", reindent=True)
|
|
initIds = CGList(
|
|
[initIds,
|
|
CGGeneric((" %s_ids[0] = JSID_VOID;\n"
|
|
" return;") % idsToInit[0]),
|
|
CGGeneric("}")],
|
|
"\n")
|
|
else:
|
|
initIds = None
|
|
|
|
prefCacheData = []
|
|
for var in self.properties.arrayNames():
|
|
props = getattr(self.properties, var)
|
|
prefCacheData.extend(props.prefCacheData)
|
|
if len(prefCacheData) is not 0:
|
|
prefCacheData = [
|
|
CGGeneric('Preferences::AddBoolVarCache(%s, "%s");' % (ptr, pref)) for
|
|
(pref, ptr) in prefCacheData]
|
|
prefCache = CGWrapper(CGIndenter(CGList(prefCacheData, "\n")),
|
|
pre=("static bool sPrefCachesInited = false;\n"
|
|
"if (!sPrefCachesInited) {\n"
|
|
" sPrefCachesInited = true;\n"),
|
|
post="\n}")
|
|
else:
|
|
prefCache = None
|
|
|
|
if UseHolderForUnforgeable(self.descriptor):
|
|
createUnforgeableHolder = CGGeneric("""JSObject* unforgeableHolder = JS_NewObjectWithGivenProto(aCx, nullptr, nullptr, nullptr);
|
|
if (!unforgeableHolder) {
|
|
return;
|
|
}""")
|
|
defineUnforgeables = InitUnforgeablePropertiesOnObject(self.descriptor,
|
|
"unforgeableHolder",
|
|
self.properties)
|
|
createUnforgeableHolder = CGList([createUnforgeableHolder,
|
|
defineUnforgeables],
|
|
"\n")
|
|
else:
|
|
createUnforgeableHolder = None
|
|
|
|
getParentProto = ("JSObject* parentProto = %s;\n" +
|
|
"if (!parentProto) {\n" +
|
|
" return;\n" +
|
|
"}\n") % getParentProto
|
|
|
|
if (needInterfaceObject and
|
|
self.descriptor.needsConstructHookHolder()):
|
|
constructHookHolder = "&" + CONSTRUCT_HOOK_NAME + "_holder"
|
|
else:
|
|
constructHookHolder = "nullptr"
|
|
if self.descriptor.interface.ctor():
|
|
constructArgs = methodLength(self.descriptor.interface.ctor())
|
|
else:
|
|
constructArgs = 0
|
|
if len(self.descriptor.interface.namedConstructors) > 0:
|
|
namedConstructors = "namedConstructors"
|
|
else:
|
|
namedConstructors = "nullptr"
|
|
|
|
if needInterfacePrototypeObject:
|
|
protoClass = "&PrototypeClass.mBase"
|
|
protoCache = "&protoAndIfaceArray[prototypes::id::%s]" % self.descriptor.name
|
|
else:
|
|
protoClass = "nullptr"
|
|
protoCache = "nullptr"
|
|
if needInterfaceObject:
|
|
interfaceClass = "&InterfaceObjectClass.mBase"
|
|
interfaceCache = "&protoAndIfaceArray[constructors::id::%s]" % self.descriptor.name
|
|
else:
|
|
# We don't have slots to store the named constructors.
|
|
assert len(self.descriptor.interface.namedConstructors) == 0
|
|
interfaceClass = "nullptr"
|
|
interfaceCache = "nullptr"
|
|
|
|
if self.descriptor.concrete:
|
|
if self.descriptor.proxy:
|
|
domClass = "&Class"
|
|
else:
|
|
domClass = "&Class.mClass"
|
|
else:
|
|
domClass = "nullptr"
|
|
|
|
if self.properties.hasNonChromeOnly():
|
|
properties = "&sNativeProperties"
|
|
else:
|
|
properties = "nullptr"
|
|
if self.properties.hasChromeOnly():
|
|
accessCheck = GetAccessCheck(self.descriptor, "aGlobal")
|
|
chromeProperties = accessCheck + " ? &sChromeOnlyNativeProperties : nullptr"
|
|
else:
|
|
chromeProperties = "nullptr"
|
|
call = ("dom::CreateInterfaceObjects(aCx, aGlobal, parentProto,\n"
|
|
" %s, %s,\n"
|
|
" %s, %s, %d, %s,\n"
|
|
" %s,\n"
|
|
" %s,\n"
|
|
" %s,\n"
|
|
" %s,\n"
|
|
" %s);" % (
|
|
protoClass, protoCache,
|
|
interfaceClass, constructHookHolder, constructArgs,
|
|
namedConstructors,
|
|
interfaceCache,
|
|
domClass,
|
|
properties,
|
|
chromeProperties,
|
|
'"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "NULL"))
|
|
if UseHolderForUnforgeable(self.descriptor):
|
|
assert needInterfacePrototypeObject
|
|
setUnforgeableHolder = CGGeneric(
|
|
"JSObject* proto = protoAndIfaceArray[prototypes::id::%s];\n"
|
|
"if (proto) {\n"
|
|
" js::SetReservedSlot(proto, DOM_INTERFACE_PROTO_SLOTS_BASE,\n"
|
|
" JS::ObjectValue(*unforgeableHolder));\n"
|
|
"}" % self.descriptor.name)
|
|
else:
|
|
setUnforgeableHolder = None
|
|
functionBody = CGList(
|
|
[CGGeneric(getParentProto), initIds, prefCache,
|
|
createUnforgeableHolder, CGGeneric(call), setUnforgeableHolder],
|
|
"\n\n")
|
|
return CGIndenter(functionBody).define()
|
|
|
|
class CGGetPerInterfaceObject(CGAbstractMethod):
|
|
"""
|
|
A method for getting a per-interface object (a prototype object or interface
|
|
constructor object).
|
|
"""
|
|
def __init__(self, descriptor, name, idPrefix=""):
|
|
args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal')]
|
|
CGAbstractMethod.__init__(self, descriptor, name,
|
|
'JSObject*', args, inline=True)
|
|
self.id = idPrefix + "id::" + self.descriptor.name
|
|
def definition_body(self):
|
|
return """
|
|
|
|
/* Make sure our global is sane. Hopefully we can remove this sometime */
|
|
if (!(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL)) {
|
|
return NULL;
|
|
}
|
|
/* Check to see whether the interface objects are already installed */
|
|
JSObject** protoAndIfaceArray = GetProtoAndIfaceArray(aGlobal);
|
|
JSObject* cachedObject = protoAndIfaceArray[%s];
|
|
if (!cachedObject) {
|
|
CreateInterfaceObjects(aCx, aGlobal, protoAndIfaceArray);
|
|
cachedObject = protoAndIfaceArray[%s];
|
|
}
|
|
|
|
/* cachedObject might _still_ be null, but that's OK */
|
|
return cachedObject;""" % (self.id, self.id)
|
|
|
|
class CGGetProtoObjectMethod(CGGetPerInterfaceObject):
|
|
"""
|
|
A method for getting the interface prototype object.
|
|
"""
|
|
def __init__(self, descriptor):
|
|
CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject",
|
|
"prototypes::")
|
|
def definition_body(self):
|
|
return """
|
|
/* Get the interface prototype object for this class. This will create the
|
|
object as needed. */""" + CGGetPerInterfaceObject.definition_body(self)
|
|
|
|
class CGGetConstructorObjectMethod(CGGetPerInterfaceObject):
|
|
"""
|
|
A method for getting the interface constructor object.
|
|
"""
|
|
def __init__(self, descriptor):
|
|
CGGetPerInterfaceObject.__init__(self, descriptor, "GetConstructorObject",
|
|
"constructors::")
|
|
def definition_body(self):
|
|
return """
|
|
/* Get the interface object for this class. This will create the object as
|
|
needed. */""" + CGGetPerInterfaceObject.definition_body(self)
|
|
|
|
class CGDefineDOMInterfaceMethod(CGAbstractMethod):
|
|
"""
|
|
A method for resolve hooks to try to lazily define the interface object for
|
|
a given interface.
|
|
"""
|
|
def __init__(self, descriptor):
|
|
args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal'),
|
|
Argument('jsid', 'id'), Argument('bool*', 'aEnabled')]
|
|
CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface', 'JSObject*', args)
|
|
|
|
def declare(self):
|
|
if self.descriptor.workers:
|
|
return ''
|
|
return CGAbstractMethod.declare(self)
|
|
|
|
def define(self):
|
|
if self.descriptor.workers:
|
|
return ''
|
|
return CGAbstractMethod.define(self)
|
|
|
|
def definition_body(self):
|
|
if len(self.descriptor.interface.namedConstructors) > 0:
|
|
getConstructor = """ JSObject* interfaceObject = GetConstructorObject(aCx, aGlobal);
|
|
if (!interfaceObject) {
|
|
return nullptr;
|
|
}
|
|
for (unsigned slot = DOM_INTERFACE_SLOTS_BASE; slot < JSCLASS_RESERVED_SLOTS(&InterfaceObjectClass.mBase); ++slot) {
|
|
JSObject* constructor = &js::GetReservedSlot(interfaceObject, slot).toObject();
|
|
if (JS_GetFunctionId(JS_GetObjectFunction(constructor)) == JSID_TO_STRING(id)) {
|
|
return constructor;
|
|
}
|
|
}
|
|
return interfaceObject;"""
|
|
else:
|
|
getConstructor = " return GetConstructorObject(aCx, aGlobal);"
|
|
return (""" *aEnabled = true;
|
|
|
|
""" + getConstructor)
|
|
|
|
class CGPrefEnabledNative(CGAbstractMethod):
|
|
"""
|
|
A method for testing whether the preference controlling this
|
|
interface is enabled. This delegates to PrefEnabled() on the
|
|
wrapped class. The interface should only be visible on the global
|
|
if the method returns true.
|
|
"""
|
|
def __init__(self, descriptor):
|
|
CGAbstractMethod.__init__(self, descriptor, 'PrefEnabled', 'bool', [])
|
|
|
|
def definition_body(self):
|
|
return " return %s::PrefEnabled();" % self.descriptor.nativeType
|
|
|
|
class CGPrefEnabled(CGAbstractMethod):
|
|
"""
|
|
A method for testing whether the preference controlling this
|
|
interface is enabled. This generates code in the binding to
|
|
check the given preference. The interface should only be visible
|
|
on the global if the pref is true.
|
|
"""
|
|
def __init__(self, descriptor):
|
|
CGAbstractMethod.__init__(self, descriptor, 'PrefEnabled', 'bool', [])
|
|
|
|
def definition_body(self):
|
|
pref = self.descriptor.interface.getExtendedAttribute("Pref")
|
|
assert isinstance(pref, list) and len(pref) == 1
|
|
return " return Preferences::GetBool(\"%s\");" % pref[0]
|
|
|
|
class CGIsMethod(CGAbstractMethod):
|
|
def __init__(self, descriptor):
|
|
args = [Argument('JSObject*', 'obj')]
|
|
CGAbstractMethod.__init__(self, descriptor, 'Is', 'bool', args)
|
|
|
|
def definition_body(self):
|
|
# Non-proxy implementation would check
|
|
# js::GetObjectJSClass(obj) == &Class.mBase
|
|
return """ return IsProxy(obj);"""
|
|
|
|
def CreateBindingJSObject(descriptor, parent):
|
|
if descriptor.proxy:
|
|
create = """ JSObject *obj = NewProxyObject(aCx, DOMProxyHandler::getInstance(),
|
|
JS::PrivateValue(aObject), proto, %s);
|
|
if (!obj) {
|
|
return NULL;
|
|
}
|
|
|
|
"""
|
|
else:
|
|
create = """ JSObject* obj = JS_NewObject(aCx, &Class.mBase, proto, %s);
|
|
if (!obj) {
|
|
return NULL;
|
|
}
|
|
|
|
js::SetReservedSlot(obj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(aObject));
|
|
"""
|
|
if descriptor.nativeOwnership in ['refcounted', 'nsisupports']:
|
|
create += """ NS_ADDREF(aObject);
|
|
"""
|
|
else:
|
|
assert descriptor.nativeOwnership == 'owned'
|
|
create += """ // Make sure the native objects inherit from NonRefcountedDOMObject so that we
|
|
// log their ctor and dtor.
|
|
MustInheritFromNonRefcountedDOMObject(aObject);
|
|
*aTookOwnership = true;
|
|
"""
|
|
return create % parent
|
|
|
|
def GetAccessCheck(descriptor, object):
|
|
"""
|
|
object is the name of a JSObject*
|
|
|
|
returns a string
|
|
"""
|
|
if descriptor.workers:
|
|
accessCheck = "mozilla::dom::workers::GetWorkerPrivateFromContext(aCx)->IsChromeWorker()"
|
|
else:
|
|
accessCheck = "xpc::AccessCheck::isChrome(%s)" % object
|
|
return accessCheck
|
|
|
|
def InitUnforgeablePropertiesOnObject(descriptor, obj, properties, failureReturnValue=""):
|
|
"""
|
|
properties is a PropertyArrays instance
|
|
"""
|
|
failureReturn = "return"
|
|
if len(failureReturnValue) > 0:
|
|
failureReturn += " " + failureReturnValue
|
|
failureReturn += ";"
|
|
|
|
defineUnforgeables = ("if (!DefineUnforgeableAttributes(aCx, " + obj + ", %s)) {\n"
|
|
" " + failureReturn + "\n"
|
|
"}")
|
|
|
|
unforgeableAttrs = properties.unforgeableAttrs
|
|
unforgeables = []
|
|
if unforgeableAttrs.hasNonChromeOnly():
|
|
unforgeables.append(CGGeneric(defineUnforgeables %
|
|
unforgeableAttrs.variableName(False)))
|
|
if unforgeableAttrs.hasChromeOnly():
|
|
unforgeables.append(
|
|
CGIfWrapper(CGGeneric(defineUnforgeables %
|
|
unforgeableAttrs.variableName(True)),
|
|
GetAccessCheck(descriptor, obj)))
|
|
return CGList(unforgeables, "\n")
|
|
|
|
def InitUnforgeableProperties(descriptor, properties):
|
|
"""
|
|
properties is a PropertyArrays instance
|
|
"""
|
|
unforgeableAttrs = properties.unforgeableAttrs
|
|
if not unforgeableAttrs.hasNonChromeOnly() and not unforgeableAttrs.hasChromeOnly():
|
|
return ""
|
|
|
|
if descriptor.proxy:
|
|
unforgeableProperties = CGGeneric(
|
|
"// Unforgeable properties on proxy-based bindings are stored in an object held\n"
|
|
"// by the interface prototype object.\n")
|
|
else:
|
|
unforgeableProperties = CGWrapper(
|
|
InitUnforgeablePropertiesOnObject(descriptor, "obj", properties, "nullptr"),
|
|
pre=(
|
|
"// Important: do unforgeable property setup after we have handed\n"
|
|
"// over ownership of the C++ object to obj as needed, so that if\n"
|
|
"// we fail and it ends up GCed it won't have problems in the\n"
|
|
"// finalizer trying to drop its ownership of the C++ object.\n"))
|
|
return CGIndenter(CGWrapper(unforgeableProperties, pre="\n", post="\n")).define()
|
|
|
|
def AssertInheritanceChain(descriptor):
|
|
asserts = ""
|
|
iface = descriptor.interface
|
|
while iface:
|
|
desc = descriptor.getDescriptor(iface.identifier.name)
|
|
asserts += (
|
|
" MOZ_ASSERT(static_cast<%s*>(aObject) == \n"
|
|
" reinterpret_cast<%s*>(aObject));\n" %
|
|
(desc.nativeType, desc.nativeType))
|
|
iface = iface.parent
|
|
if descriptor.nativeOwnership == 'nsisupports':
|
|
asserts += (
|
|
" MOZ_ASSERT(ToSupports(aObject) == \n"
|
|
" reinterpret_cast<nsISupports*>(aObject));\n")
|
|
return asserts
|
|
|
|
class CGWrapWithCacheMethod(CGAbstractMethod):
|
|
"""
|
|
Create a wrapper JSObject for a given native that implements nsWrapperCache.
|
|
|
|
properties should be a PropertyArrays instance.
|
|
"""
|
|
def __init__(self, descriptor, properties):
|
|
assert descriptor.interface.hasInterfacePrototypeObject()
|
|
args = [Argument('JSContext*', 'aCx'),
|
|
Argument('JS::Handle<JSObject*>', 'aScope'),
|
|
Argument(descriptor.nativeType + '*', 'aObject'),
|
|
Argument('nsWrapperCache*', 'aCache')]
|
|
CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args)
|
|
self.properties = properties
|
|
|
|
def definition_body(self):
|
|
if self.descriptor.workers:
|
|
return """ return aObject->GetJSObject();"""
|
|
|
|
if self.descriptor.nativeOwnership == 'nsisupports':
|
|
assertISupportsInheritance = (
|
|
' MOZ_ASSERT(reinterpret_cast<void*>(aObject) != aCache,\n'
|
|
' "nsISupports must be on our primary inheritance chain");\n')
|
|
else:
|
|
assertISupportsInheritance = ""
|
|
return """%s
|
|
%s
|
|
JS::Rooted<JSObject*> parent(aCx,
|
|
GetRealParentObject(aObject,
|
|
WrapNativeParent(aCx, aScope, aObject->GetParentObject())));
|
|
if (!parent) {
|
|
return NULL;
|
|
}
|
|
|
|
// That might have ended up wrapping us already, due to the wonders
|
|
// of XBL. Check for that, and bail out as needed. Scope so we don't
|
|
// collide with the "obj" we declare in CreateBindingJSObject.
|
|
{
|
|
JSObject* obj = aCache->GetWrapper();
|
|
if (obj) {
|
|
return obj;
|
|
}
|
|
}
|
|
|
|
JSAutoCompartment ac(aCx, parent);
|
|
JSObject* global = JS_GetGlobalForObject(aCx, parent);
|
|
JSObject* proto = GetProtoObject(aCx, global);
|
|
if (!proto) {
|
|
return NULL;
|
|
}
|
|
|
|
%s
|
|
%s
|
|
aCache->SetWrapper(obj);
|
|
|
|
return obj;""" % (AssertInheritanceChain(self.descriptor),
|
|
assertISupportsInheritance,
|
|
CreateBindingJSObject(self.descriptor, "parent"),
|
|
InitUnforgeableProperties(self.descriptor, self.properties))
|
|
|
|
class CGWrapMethod(CGAbstractMethod):
|
|
def __init__(self, descriptor):
|
|
# XXX can we wrap if we don't have an interface prototype object?
|
|
assert descriptor.interface.hasInterfacePrototypeObject()
|
|
args = [Argument('JSContext*', 'aCx'),
|
|
Argument('JS::Handle<JSObject*>', 'aScope'),
|
|
Argument('T*', 'aObject')]
|
|
CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args, inline=True, templateArgs=["class T"])
|
|
|
|
def definition_body(self):
|
|
return " return Wrap(aCx, aScope, aObject, aObject);"
|
|
|
|
class CGWrapNonWrapperCacheMethod(CGAbstractMethod):
|
|
"""
|
|
Create a wrapper JSObject for a given native that does not implement
|
|
nsWrapperCache.
|
|
|
|
properties should be a PropertyArrays instance.
|
|
"""
|
|
def __init__(self, descriptor, properties):
|
|
# XXX can we wrap if we don't have an interface prototype object?
|
|
assert descriptor.interface.hasInterfacePrototypeObject()
|
|
args = [Argument('JSContext*', 'aCx'),
|
|
Argument('JS::Handle<JSObject*>', 'aScope'),
|
|
Argument(descriptor.nativeType + '*', 'aObject')]
|
|
if descriptor.nativeOwnership == 'owned':
|
|
args.append(Argument('bool*', 'aTookOwnership'))
|
|
CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args)
|
|
self.properties = properties
|
|
|
|
def definition_body(self):
|
|
return """%s
|
|
JSObject* global = JS_GetGlobalForObject(aCx, aScope);
|
|
JSObject* proto = GetProtoObject(aCx, global);
|
|
if (!proto) {
|
|
return NULL;
|
|
}
|
|
|
|
%s
|
|
%s
|
|
return obj;""" % (AssertInheritanceChain(self.descriptor),
|
|
CreateBindingJSObject(self.descriptor, "global"),
|
|
InitUnforgeableProperties(self.descriptor, self.properties))
|
|
|
|
builtinNames = {
|
|
IDLType.Tags.bool: 'bool',
|
|
IDLType.Tags.int8: 'int8_t',
|
|
IDLType.Tags.int16: 'int16_t',
|
|
IDLType.Tags.int32: 'int32_t',
|
|
IDLType.Tags.int64: 'int64_t',
|
|
IDLType.Tags.uint8: 'uint8_t',
|
|
IDLType.Tags.uint16: 'uint16_t',
|
|
IDLType.Tags.uint32: 'uint32_t',
|
|
IDLType.Tags.uint64: 'uint64_t',
|
|
IDLType.Tags.unrestricted_float: 'float',
|
|
IDLType.Tags.float: 'float',
|
|
IDLType.Tags.unrestricted_double: 'double',
|
|
IDLType.Tags.double: 'double'
|
|
}
|
|
|
|
numericSuffixes = {
|
|
IDLType.Tags.int8: '',
|
|
IDLType.Tags.uint8: '',
|
|
IDLType.Tags.int16: '',
|
|
IDLType.Tags.uint16: '',
|
|
IDLType.Tags.int32: '',
|
|
IDLType.Tags.uint32: 'U',
|
|
IDLType.Tags.int64: 'LL',
|
|
IDLType.Tags.uint64: 'ULL',
|
|
IDLType.Tags.unrestricted_float: 'F',
|
|
IDLType.Tags.float: 'F',
|
|
IDLType.Tags.unrestricted_double: 'D',
|
|
IDLType.Tags.double: 'D'
|
|
}
|
|
|
|
def numericValue(t, v):
|
|
if (t == IDLType.Tags.unrestricted_double or
|
|
t == IDLType.Tags.unrestricted_float):
|
|
if v == float("inf"):
|
|
return "MOZ_DOUBLE_POSITIVE_INFINITY()"
|
|
if v == float("-inf"):
|
|
return "MOZ_DOUBLE_NEGATIVE_INFINITY()"
|
|
if math.isnan(v):
|
|
return "MOZ_DOUBLE_NaN()"
|
|
return "%s%s" % (v, numericSuffixes[t])
|
|
|
|
class CastableObjectUnwrapper():
|
|
"""
|
|
A class for unwrapping an object named by the "source" argument
|
|
based on the passed-in descriptor and storing it in a variable
|
|
called by the name in the "target" argument.
|
|
|
|
codeOnFailure is the code to run if unwrapping fails.
|
|
"""
|
|
def __init__(self, descriptor, source, target, codeOnFailure):
|
|
self.substitution = { "type" : descriptor.nativeType,
|
|
"protoID" : "prototypes::id::" + descriptor.name,
|
|
"source" : source,
|
|
"target" : target,
|
|
"codeOnFailure" : CGIndenter(CGGeneric(codeOnFailure), 4).define() }
|
|
if descriptor.hasXPConnectImpls:
|
|
# We don't use xpc_qsUnwrapThis because it will always throw on
|
|
# unwrap failure, whereas we want to control whether we throw or
|
|
# not.
|
|
self.substitution["codeOnFailure"] = CGIndenter(CGGeneric(string.Template(
|
|
"${type} *objPtr;\n"
|
|
"xpc_qsSelfRef objRef;\n"
|
|
"JS::Value val = JS::ObjectValue(*${source});\n"
|
|
"nsresult rv = xpc_qsUnwrapArg<${type}>(cx, val, &objPtr, &objRef.ptr, &val);\n"
|
|
"if (NS_FAILED(rv)) {\n"
|
|
"${codeOnFailure}\n"
|
|
"}\n"
|
|
"// We should be castable!\n"
|
|
"MOZ_ASSERT(!objRef.ptr);\n"
|
|
"// We should have an object, too!\n"
|
|
"MOZ_ASSERT(objPtr);\n"
|
|
"${target} = objPtr;").substitute(self.substitution)), 4).define()
|
|
|
|
def __str__(self):
|
|
return string.Template(
|
|
"""{
|
|
nsresult rv = UnwrapObject<${protoID}, ${type}>(cx, ${source}, ${target});
|
|
if (NS_FAILED(rv)) {
|
|
${codeOnFailure}
|
|
}
|
|
}""").substitute(self.substitution)
|
|
|
|
class FailureFatalCastableObjectUnwrapper(CastableObjectUnwrapper):
|
|
"""
|
|
As CastableObjectUnwrapper, but defaulting to throwing if unwrapping fails
|
|
"""
|
|
def __init__(self, descriptor, source, target, exceptionCode):
|
|
CastableObjectUnwrapper.__init__(
|
|
self, descriptor, source, target,
|
|
'ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s");\n'
|
|
'%s' % (descriptor.name, exceptionCode))
|
|
|
|
class CallbackObjectUnwrapper:
|
|
"""
|
|
A class for unwrapping objects implemented in JS.
|
|
|
|
|source| is the JSObject we want to use in native code.
|
|
|target| is an nsCOMPtr of the appropriate type in which we store the result.
|
|
"""
|
|
def __init__(self, descriptor, source, target, exceptionCode,
|
|
codeOnFailure=None):
|
|
if codeOnFailure is None:
|
|
codeOnFailure = (
|
|
'ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s");\n'
|
|
'%s' % (descriptor.name, exceptionCode))
|
|
self.descriptor = descriptor
|
|
self.substitution = { "nativeType" : descriptor.nativeType,
|
|
"source" : source,
|
|
"target" : target,
|
|
"codeOnFailure" : CGIndenter(CGGeneric(codeOnFailure)).define() }
|
|
|
|
def __str__(self):
|
|
checkObjectType = CGIfWrapper(
|
|
CGGeneric(self.substitution["codeOnFailure"]),
|
|
"!IsConvertibleToCallbackInterface(cx, %(source)s)" %
|
|
self.substitution).define() + "\n\n"
|
|
if self.descriptor.workers:
|
|
return checkObjectType + string.Template(
|
|
"${target} = ${source};"
|
|
).substitute(self.substitution)
|
|
|
|
return checkObjectType + string.Template(
|
|
"""nsresult rv;
|
|
XPCCallContext ccx(JS_CALLER, cx);
|
|
if (!ccx.IsValid()) {
|
|
rv = NS_ERROR_XPC_BAD_CONVERT_JS;
|
|
${codeOnFailure}
|
|
}
|
|
|
|
nsISupports* supp = nullptr;
|
|
if (XPCConvert::GetISupportsFromJSObject(${source}, &supp)) {
|
|
nsCOMPtr<nsIXPConnectWrappedNative> xpcwn = do_QueryInterface(supp);
|
|
if (xpcwn) {
|
|
supp = xpcwn->Native();
|
|
}
|
|
}
|
|
|
|
const nsIID& iid = NS_GET_IID(${nativeType});
|
|
nsRefPtr<nsXPCWrappedJS> wrappedJS;
|
|
rv = nsXPCWrappedJS::GetNewOrUsed(ccx, ${source}, iid,
|
|
supp, getter_AddRefs(wrappedJS));
|
|
if (NS_FAILED(rv) || !wrappedJS) {
|
|
${codeOnFailure}
|
|
}
|
|
|
|
// Use a temp nsCOMPtr for the null-check, because ${target} might be
|
|
// OwningNonNull, not an nsCOMPtr.
|
|
nsCOMPtr<${nativeType}> tmp = do_QueryObject(wrappedJS.get());
|
|
if (!tmp) {
|
|
${codeOnFailure}
|
|
}
|
|
${target} = tmp.forget();""").substitute(self.substitution)
|
|
|
|
# If this function is modified, modify CGNativeMember.getArg and
|
|
# CGNativeMember.getRetvalInfo accordingly. The latter cares about the decltype
|
|
# and holdertype we end up using, because it needs to be able to return the code
|
|
# that will convert those to the actual return value of the callback function.
|
|
def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
|
|
isDefinitelyObject=False,
|
|
isMember=False,
|
|
isOptional=False,
|
|
invalidEnumValueFatal=True,
|
|
defaultValue=None,
|
|
treatNullAs="Default",
|
|
treatUndefinedAs="Default",
|
|
isEnforceRange=False,
|
|
isClamp=False,
|
|
isNullOrUndefined=False,
|
|
exceptionCode=None,
|
|
lenientFloatCode=None,
|
|
allowTreatNonCallableAsNull=False,
|
|
isCallbackReturnValue=False):
|
|
"""
|
|
Get a template for converting a JS value to a native object based on the
|
|
given type and descriptor. If failureCode is given, then we're actually
|
|
testing whether we can convert the argument to the desired type. That
|
|
means that failures to convert due to the JS value being the wrong type of
|
|
value need to use failureCode instead of throwing exceptions. Failures to
|
|
convert that are due to JS exceptions (from toString or valueOf methods) or
|
|
out of memory conditions need to throw exceptions no matter what
|
|
failureCode is. However what actually happens when throwing an exception
|
|
can be controlled by exceptionCode. The only requirement on that is that
|
|
exceptionCode must end up doing a return, and every return from this
|
|
function must happen via exceptionCode if exceptionCode is not None.
|
|
|
|
If isDefinitelyObject is True, that means we know the value
|
|
isObject() and we have no need to recheck that.
|
|
|
|
if isMember is not False, 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. Callers can pass "Dictionary" or
|
|
"Variadic" to indicate that the conversion is for something that is a
|
|
dictionary member or a variadic argument respectively.
|
|
|
|
If isOptional is true, then we are doing conversion of an optional
|
|
argument with no default value.
|
|
|
|
invalidEnumValueFatal controls whether an invalid enum value conversion
|
|
attempt will throw (if true) or simply return without doing anything (if
|
|
false).
|
|
|
|
If defaultValue is not None, it's the IDL default value for this conversion
|
|
|
|
If isEnforceRange is true, we're converting an integer and throwing if the
|
|
value is out of range.
|
|
|
|
If isClamp is true, we're converting an integer and clamping if the
|
|
value is out of range.
|
|
|
|
If lenientFloatCode is not None, it should be used in cases when
|
|
we're a non-finite float that's not unrestricted.
|
|
|
|
If allowTreatNonCallableAsNull is true, then [TreatNonCallableAsNull]
|
|
extended attributes on nullable callback functions will be honored.
|
|
|
|
If isCallbackReturnValue is true, then the declType may be adjusted to make
|
|
it easier to return from a callback. Since that type is never directly
|
|
observable by any consumers of the callback code, this is OK.
|
|
|
|
The return value from this function is a tuple consisting of four things:
|
|
|
|
1) A string representing the conversion code. This will have template
|
|
substitution performed on it as follows:
|
|
|
|
${val} replaced by an expression for the JS::Value in question
|
|
${valPtr} is a pointer to the JS::Value in question
|
|
${holderName} replaced by the holder's name, if any
|
|
${declName} replaced by the declaration's name
|
|
${haveValue} replaced by an expression that evaluates to a boolean
|
|
for whether we have a JS::Value. Only used when
|
|
defaultValue is not None.
|
|
|
|
2) A CGThing representing the native C++ type we're converting to
|
|
(declType). This is allowed to be None if the conversion code is
|
|
supposed to be used as-is.
|
|
3) A CGThing representing the type of a "holder" (holderType) which will
|
|
hold a possible reference to the C++ thing whose type we returned in #1,
|
|
or None if no such holder is needed.
|
|
4) A boolean indicating whether the caller has to do optional-argument handling.
|
|
This will only be true if isOptional is true and if the returned template
|
|
expects both declType and holderType to be wrapped in Optional<>, with
|
|
${declName} and ${holderName} adjusted to point to the Value() of the
|
|
Optional, and Construct() calls to be made on the Optional<>s as needed.
|
|
|
|
${declName} must be in scope before the generated code is entered.
|
|
|
|
If holderType is not None then ${holderName} must be in scope
|
|
before the generated code is entered.
|
|
"""
|
|
# If we have a defaultValue then we're not actually optional for
|
|
# purposes of what we need to be declared as.
|
|
assert(defaultValue is None or not isOptional)
|
|
|
|
# Also, we should not have a defaultValue if we know we're an object
|
|
assert(not isDefinitelyObject or defaultValue is None)
|
|
|
|
# And we can't both be an object and be null or undefined
|
|
assert not isDefinitelyObject or not isNullOrUndefined
|
|
|
|
# If exceptionCode is not set, we'll just rethrow the exception we got.
|
|
# Note that we can't just set failureCode to exceptionCode, because setting
|
|
# failureCode will prevent pending exceptions from being set in cases when
|
|
# they really should be!
|
|
if exceptionCode is None:
|
|
exceptionCode = "return false;"
|
|
# We often want exceptionCode to be indented, since it often appears in an
|
|
# if body.
|
|
exceptionCodeIndented = CGIndenter(CGGeneric(exceptionCode))
|
|
|
|
# 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
|
|
('ThrowErrorMessage(cx, MSG_NOT_OBJECT);\n'
|
|
'%s' % exceptionCode)), post="\n")
|
|
def onFailureBadType(failureCode, typeName):
|
|
return CGWrapper(CGGeneric(
|
|
failureCode or
|
|
('ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s");'
|
|
'%s' % (typeName, exceptionCode))), post="\n")
|
|
def onFailureNotCallable(failureCode):
|
|
return CGWrapper(CGGeneric(
|
|
failureCode or
|
|
('ThrowErrorMessage(cx, MSG_NOT_CALLABLE);\n'
|
|
'%s' % exceptionCode)), post="\n")
|
|
|
|
# A helper function for handling default values. Takes a template
|
|
# body and the C++ code to set the default value and wraps the
|
|
# given template body in handling for the default value.
|
|
def handleDefault(template, setDefault):
|
|
if defaultValue is None:
|
|
return template
|
|
return CGWrapper(
|
|
CGIndenter(CGGeneric(template)),
|
|
pre="if (${haveValue}) {\n",
|
|
post=("\n"
|
|
"} else {\n"
|
|
"%s;\n"
|
|
"}" %
|
|
CGIndenter(CGGeneric(setDefault)).define())).define()
|
|
|
|
# A helper function for handling null default values. Much like
|
|
# handleDefault, but checks that the default value, if it exists, is null.
|
|
def handleDefaultNull(template, codeToSetNull):
|
|
if (defaultValue is not None and
|
|
not isinstance(defaultValue, IDLNullValue)):
|
|
raise TypeError("Can't handle non-null default value here")
|
|
return handleDefault(template, codeToSetNull)
|
|
|
|
# A helper function for wrapping up the template body for
|
|
# possibly-nullable objecty stuff
|
|
def wrapObjectTemplate(templateBody, type, codeToSetNull, failureCode=None):
|
|
if isNullOrUndefined:
|
|
assert type.nullable()
|
|
# Just ignore templateBody and set ourselves to null.
|
|
# Note that wedon't have to worry about default values
|
|
# here either, since we already examined this value.
|
|
return "%s;" % codeToSetNull
|
|
|
|
if not isDefinitelyObject:
|
|
# Handle the non-object cases by wrapping up the whole
|
|
# thing in an if cascade.
|
|
templateBody = (
|
|
"if (${val}.isObject()) {\n" +
|
|
CGIndenter(CGGeneric(templateBody)).define() + "\n")
|
|
if type.nullable():
|
|
templateBody += (
|
|
"} else if (${val}.isNullOrUndefined()) {\n"
|
|
"%s;\n" % CGIndenter(CGGeneric(codeToSetNull)).define())
|
|
templateBody += (
|
|
"} else {\n" +
|
|
CGIndenter(onFailureNotAnObject(failureCode)).define() +
|
|
"}")
|
|
if type.nullable():
|
|
templateBody = handleDefaultNull(templateBody, codeToSetNull)
|
|
else:
|
|
assert(defaultValue is None)
|
|
|
|
return templateBody
|
|
|
|
assert not (isEnforceRange and isClamp) # These are mutually exclusive
|
|
|
|
if type.isArray():
|
|
raise TypeError("Can't handle array arguments yet")
|
|
|
|
if type.isSequence():
|
|
assert not isEnforceRange and not isClamp
|
|
|
|
if failureCode is None:
|
|
notSequence = ("ThrowErrorMessage(cx, MSG_NOT_SEQUENCE);\n"
|
|
"%s" % exceptionCode)
|
|
else:
|
|
notSequence = failureCode
|
|
|
|
nullable = type.nullable();
|
|
# Be very careful not to change "type": we need it later
|
|
if nullable:
|
|
elementType = type.inner.inner
|
|
else:
|
|
elementType = type.inner
|
|
|
|
# We want to use auto arrays if we can, but we have to be careful with
|
|
# reallocation behavior for arrays. In particular, if we use auto
|
|
# arrays for sequences and have a sequence of elements which are
|
|
# themselves sequences or have sequences as members, we have a problem.
|
|
# In that case, resizing the outermost nsAutoTarray to the right size
|
|
# will memmove its elements, but nsAutoTArrays are not memmovable and
|
|
# hence will end up with pointers to bogus memory, which is bad. To
|
|
# deal with this, we typically map WebIDL sequences to our Sequence
|
|
# type, which is in fact memmovable. The one exception is when we're
|
|
# passing in a sequence directly as an argument without any sort of
|
|
# optional or nullable complexity going on. In that situation, we can
|
|
# use an AutoSequence instead. We have to keep using Sequence in the
|
|
# nullable and optional cases because we don't want to leak the
|
|
# AutoSequence type to consumers, which would be unavoidable with
|
|
# Nullable<AutoSequence> or Optional<AutoSequence>.
|
|
if isMember or isOptional or nullable or isCallbackReturnValue:
|
|
sequenceClass = "Sequence"
|
|
else:
|
|
sequenceClass = "AutoSequence"
|
|
|
|
(elementTemplate, elementDeclType,
|
|
elementHolderType, dealWithOptional) = getJSToNativeConversionTemplate(
|
|
elementType, descriptorProvider, isMember=True,
|
|
exceptionCode=exceptionCode, lenientFloatCode=lenientFloatCode,
|
|
isCallbackReturnValue=isCallbackReturnValue)
|
|
if dealWithOptional:
|
|
raise TypeError("Shouldn't have optional things in sequences")
|
|
if elementHolderType is not None:
|
|
raise TypeError("Shouldn't need holders for sequences")
|
|
|
|
typeName = CGTemplatedType(sequenceClass, elementDeclType)
|
|
sequenceType = typeName.define()
|
|
if nullable:
|
|
typeName = CGTemplatedType("Nullable", typeName)
|
|
arrayRef = "${declName}.SetValue()"
|
|
else:
|
|
arrayRef = "${declName}"
|
|
|
|
# NOTE: Keep this in sync with variadic conversions as needed
|
|
templateBody = ("""JSObject* seq = &${val}.toObject();\n
|
|
if (!IsArrayLike(cx, seq)) {
|
|
%s
|
|
}
|
|
uint32_t length;
|
|
// JS_GetArrayLength actually works on all objects
|
|
if (!JS_GetArrayLength(cx, seq, &length)) {
|
|
%s
|
|
}
|
|
%s &arr = %s;
|
|
if (!arr.SetCapacity(length)) {
|
|
JS_ReportOutOfMemory(cx);
|
|
%s
|
|
}
|
|
for (uint32_t i = 0; i < length; ++i) {
|
|
JS::Rooted<JS::Value> temp(cx);
|
|
if (!JS_GetElement(cx, seq, i, temp.address())) {
|
|
%s
|
|
}
|
|
%s& slot = *arr.AppendElement();
|
|
""" % (CGIndenter(CGGeneric(notSequence)).define(),
|
|
exceptionCodeIndented.define(),
|
|
sequenceType,
|
|
arrayRef,
|
|
exceptionCodeIndented.define(),
|
|
CGIndenter(exceptionCodeIndented).define(),
|
|
elementDeclType.define()))
|
|
|
|
templateBody += CGIndenter(CGGeneric(
|
|
string.Template(elementTemplate).substitute(
|
|
{
|
|
"val" : "temp",
|
|
"valPtr": "temp.address()",
|
|
"declName" : "slot",
|
|
# We only need holderName here to handle isExternal()
|
|
# interfaces, which use an internal holder for the
|
|
# conversion even when forceOwningType ends up true.
|
|
"holderName": "tempHolder",
|
|
}
|
|
))).define()
|
|
|
|
templateBody += "\n}"
|
|
templateBody = wrapObjectTemplate(templateBody, type,
|
|
"${declName}.SetNull()")
|
|
return (templateBody, typeName, None, isOptional)
|
|
|
|
if type.isUnion():
|
|
if isMember:
|
|
raise TypeError("Can't handle unions as members, we have a "
|
|
"holderType")
|
|
nullable = type.nullable();
|
|
if nullable:
|
|
type = type.inner
|
|
|
|
assert(defaultValue is None or
|
|
(isinstance(defaultValue, IDLNullValue) and nullable))
|
|
|
|
unionArgumentObj = "${holderName}"
|
|
if isOptional or nullable:
|
|
unionArgumentObj += ".ref()"
|
|
|
|
memberTypes = type.flatMemberTypes
|
|
names = []
|
|
|
|
interfaceMemberTypes = filter(lambda t: t.isNonCallbackInterface(), memberTypes)
|
|
if len(interfaceMemberTypes) > 0:
|
|
interfaceObject = []
|
|
for memberType in interfaceMemberTypes:
|
|
if type.isGeckoInterface():
|
|
name = memberType.inner.identifier.name
|
|
else:
|
|
name = memberType.name
|
|
interfaceObject.append(CGGeneric("(failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext" % (unionArgumentObj, name)))
|
|
names.append(name)
|
|
interfaceObject = CGWrapper(CGList(interfaceObject, " ||\n"), pre="done = ", post=";\n", reindent=True)
|
|
else:
|
|
interfaceObject = None
|
|
|
|
arrayObjectMemberTypes = filter(lambda t: t.isArray() or t.isSequence(), memberTypes)
|
|
if len(arrayObjectMemberTypes) > 0:
|
|
assert len(arrayObjectMemberTypes) == 1
|
|
memberType = arrayObjectMemberTypes[0]
|
|
name = memberType.name
|
|
arrayObject = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
|
|
arrayObject = CGIfWrapper(arrayObject, "IsArrayLike(cx, &argObj)")
|
|
names.append(name)
|
|
else:
|
|
arrayObject = None
|
|
|
|
dateObjectMemberTypes = filter(lambda t: t.isDate(), memberTypes)
|
|
if len(dateObjectMemberTypes) > 0:
|
|
assert len(dateObjectMemberTypes) == 1
|
|
memberType = dateObjectMemberTypes[0]
|
|
name = memberType.name
|
|
dateObject = CGGeneric("%s.SetTo%s(cx, ${val}, ${valPtr});\n"
|
|
"done = true;" % (unionArgumentObj, name))
|
|
dateObject = CGIfWrapper(dateObject, "JS_ObjectIsDate(cx, &argObj)");
|
|
names.append(name)
|
|
else:
|
|
dateObject = None
|
|
|
|
callbackMemberTypes = filter(lambda t: t.isCallback() or t.isCallbackInterface(), memberTypes)
|
|
if len(callbackMemberTypes) > 0:
|
|
assert len(callbackMemberTypes) == 1
|
|
memberType = callbackMemberTypes[0]
|
|
name = memberType.name
|
|
callbackObject = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
|
|
names.append(name)
|
|
else:
|
|
callbackObject = None
|
|
|
|
dictionaryMemberTypes = filter(lambda t: t.isDictionary(), memberTypes)
|
|
if len(dictionaryMemberTypes) > 0:
|
|
raise TypeError("No support for unwrapping dictionaries as member "
|
|
"of a union")
|
|
else:
|
|
dictionaryObject = None
|
|
|
|
objectMemberTypes = filter(lambda t: t.isObject(), memberTypes)
|
|
if len(objectMemberTypes) > 0:
|
|
object = CGGeneric("%s.SetToObject(&argObj);\n"
|
|
"done = true;" % unionArgumentObj)
|
|
else:
|
|
object = None
|
|
|
|
hasObjectTypes = interfaceObject or arrayObject or dateObject or callbackObject or dictionaryObject or object
|
|
if hasObjectTypes:
|
|
# "object" is not distinguishable from other types
|
|
assert not object or not (interfaceObject or arrayObject or dateObject or callbackObject or dictionaryObject)
|
|
if arrayObject or dateObject or callbackObject or dictionaryObject:
|
|
# An object can be both an array object and a callback or
|
|
# dictionary, but we shouldn't have both in the union's members
|
|
# because they are not distinguishable.
|
|
assert not (arrayObject and callbackObject)
|
|
assert not (arrayObject and dictionaryObject)
|
|
assert not (dictionaryObject and callbackObject)
|
|
templateBody = CGList([arrayObject, dateObject, callbackObject,
|
|
dictionaryObject], " else ")
|
|
else:
|
|
templateBody = None
|
|
if interfaceObject:
|
|
assert not object
|
|
if templateBody:
|
|
templateBody = CGIfWrapper(templateBody, "!done")
|
|
templateBody = CGList([interfaceObject, templateBody], "\n")
|
|
else:
|
|
templateBody = CGList([templateBody, object], "\n")
|
|
|
|
if any([arrayObject, dateObject, callbackObject, dictionaryObject,
|
|
object]):
|
|
templateBody.prepend(CGGeneric("JSObject& argObj = ${val}.toObject();"))
|
|
templateBody = CGIfWrapper(templateBody, "${val}.isObject()")
|
|
else:
|
|
templateBody = CGGeneric()
|
|
|
|
otherMemberTypes = filter(lambda t: t.isString() or t.isEnum(),
|
|
memberTypes)
|
|
otherMemberTypes.extend(t for t in memberTypes if t.isPrimitive())
|
|
if len(otherMemberTypes) > 0:
|
|
assert len(otherMemberTypes) == 1
|
|
memberType = otherMemberTypes[0]
|
|
if memberType.isEnum():
|
|
name = memberType.inner.identifier.name
|
|
else:
|
|
name = memberType.name
|
|
other = CGGeneric("done = (failed = !%s.TrySetTo%s(cx, ${val}, ${valPtr}, tryNext)) || !tryNext;" % (unionArgumentObj, name))
|
|
names.append(name)
|
|
if hasObjectTypes:
|
|
other = CGWrapper(CGIndenter(other), "{\n", post="\n}")
|
|
if object:
|
|
join = " else "
|
|
else:
|
|
other = CGWrapper(other, pre="if (!done) ")
|
|
join = "\n"
|
|
templateBody = CGList([templateBody, other], join)
|
|
else:
|
|
other = None
|
|
|
|
templateBody = CGWrapper(templateBody, pre="bool done = false, failed = false, tryNext;\n")
|
|
throw = CGGeneric("if (failed) {\n"
|
|
"%s\n"
|
|
"}\n"
|
|
"if (!done) {\n"
|
|
" ThrowErrorMessage(cx, MSG_NOT_IN_UNION, \"%s\");\n"
|
|
"%s\n"
|
|
"}" % (exceptionCodeIndented.define(),
|
|
", ".join(names),
|
|
exceptionCodeIndented.define()))
|
|
templateBody = CGWrapper(CGIndenter(CGList([templateBody, throw], "\n")), pre="{\n", post="\n}")
|
|
|
|
typeName = type.name
|
|
argumentTypeName = typeName + "Argument"
|
|
if nullable:
|
|
typeName = "Nullable<" + typeName + " >"
|
|
|
|
def handleNull(templateBody, setToNullVar, extraConditionForNull=""):
|
|
null = CGGeneric("if (%s${val}.isNullOrUndefined()) {\n"
|
|
" %s.SetNull();\n"
|
|
"}" % (extraConditionForNull, setToNullVar))
|
|
templateBody = CGWrapper(CGIndenter(templateBody), pre="{\n", post="\n}")
|
|
return CGList([null, templateBody], " else ")
|
|
|
|
if type.hasNullableType:
|
|
templateBody = handleNull(templateBody, unionArgumentObj)
|
|
|
|
declType = CGGeneric(typeName)
|
|
holderType = CGGeneric(argumentTypeName)
|
|
if isOptional:
|
|
mutableDecl = "${declName}.Value()"
|
|
declType = CGTemplatedType("Optional", declType)
|
|
holderType = CGTemplatedType("Maybe", holderType)
|
|
constructDecl = CGGeneric("${declName}.Construct();")
|
|
if nullable:
|
|
constructHolder = CGGeneric("${holderName}.construct(%s.SetValue());" % mutableDecl)
|
|
else:
|
|
constructHolder = CGGeneric("${holderName}.construct(${declName}.Value());")
|
|
else:
|
|
mutableDecl = "${declName}"
|
|
constructDecl = None
|
|
if nullable:
|
|
holderType = CGTemplatedType("Maybe", holderType)
|
|
constructHolder = CGGeneric("${holderName}.construct(%s.SetValue());" % mutableDecl)
|
|
else:
|
|
constructHolder = CGWrapper(holderType, post=" ${holderName}(${declName});")
|
|
holderType = None
|
|
|
|
templateBody = CGList([constructHolder, templateBody], "\n")
|
|
if nullable:
|
|
if defaultValue:
|
|
assert(isinstance(defaultValue, IDLNullValue))
|
|
valueMissing = "!(${haveValue}) || "
|
|
else:
|
|
valueMissing = ""
|
|
templateBody = handleNull(templateBody, mutableDecl,
|
|
extraConditionForNull=valueMissing)
|
|
templateBody = CGList([constructDecl, templateBody], "\n")
|
|
|
|
return templateBody.define(), declType, holderType, False
|
|
|
|
if type.isGeckoInterface():
|
|
assert not isEnforceRange and not isClamp
|
|
|
|
descriptor = descriptorProvider.getDescriptor(
|
|
type.unroll().inner.identifier.name)
|
|
|
|
if (descriptor.interface.isCallback() and
|
|
descriptor.interface.identifier.name != "EventListener"):
|
|
if descriptor.workers:
|
|
if type.nullable() or isCallbackReturnValue:
|
|
declType = CGGeneric("JSObject*")
|
|
else:
|
|
declType = CGGeneric("NonNull<JSObject>")
|
|
conversion = " ${declName} = &${val}.toObject();\n"
|
|
else:
|
|
name = descriptor.interface.identifier.name
|
|
if type.nullable() or isCallbackReturnValue:
|
|
declType = CGGeneric("nsRefPtr<%s>" % name);
|
|
else:
|
|
declType = CGGeneric("OwningNonNull<%s>" % name)
|
|
conversion = (
|
|
" ${declName} = new %s(&${val}.toObject());\n" % name)
|
|
|
|
template = wrapObjectTemplate(conversion, type,
|
|
"${declName} = nullptr",
|
|
failureCode)
|
|
return (template, declType, None, isOptional)
|
|
|
|
# This is an interface that we implement as a concrete class
|
|
# or an XPCOM interface.
|
|
|
|
# Allow null pointers for nullable types and old-binding classes, and
|
|
# use an nsRefPtr or raw pointer for callback return values to make
|
|
# them easier to return.
|
|
argIsPointer = (type.nullable() or type.unroll().inner.isExternal() or
|
|
isCallbackReturnValue)
|
|
|
|
# Sequences and non-worker callbacks have to hold a strong ref to the
|
|
# thing being passed down.
|
|
forceOwningType = (descriptor.interface.isCallback() and
|
|
not descriptor.workers) or isMember
|
|
|
|
typeName = descriptor.nativeType
|
|
typePtr = typeName + "*"
|
|
|
|
# Compute a few things:
|
|
# - declType is the type we want to return as the first element of our
|
|
# tuple.
|
|
# - holderType is the type we want to return as the third element
|
|
# of our tuple.
|
|
|
|
# Set up some sensible defaults for these things insofar as we can.
|
|
holderType = None
|
|
if argIsPointer:
|
|
if forceOwningType:
|
|
declType = "nsRefPtr<" + typeName + ">"
|
|
else:
|
|
declType = typePtr
|
|
else:
|
|
if forceOwningType:
|
|
declType = "OwningNonNull<" + typeName + ">"
|
|
else:
|
|
declType = "NonNull<" + typeName + ">"
|
|
|
|
templateBody = ""
|
|
if descriptor.interface.isCallback():
|
|
templateBody += str(CallbackObjectUnwrapper(
|
|
descriptor,
|
|
"&${val}.toObject()",
|
|
"${declName}",
|
|
exceptionCode,
|
|
codeOnFailure=failureCode))
|
|
elif not descriptor.skipGen and not descriptor.interface.isConsequential() and not descriptor.interface.isExternal():
|
|
if failureCode is not None:
|
|
templateBody += str(CastableObjectUnwrapper(
|
|
descriptor,
|
|
"&${val}.toObject()",
|
|
"${declName}",
|
|
failureCode))
|
|
else:
|
|
templateBody += str(FailureFatalCastableObjectUnwrapper(
|
|
descriptor,
|
|
"&${val}.toObject()",
|
|
"${declName}",
|
|
exceptionCode))
|
|
elif descriptor.workers:
|
|
templateBody += "${declName} = &${val}.toObject();"
|
|
else:
|
|
# Either external, or new-binding non-castable. We always have a
|
|
# holder for these, because we don't actually know whether we have
|
|
# to addref when unwrapping or not. So we just pass an
|
|
# getter_AddRefs(nsRefPtr) to XPConnect and if we'll need a release
|
|
# it'll put a non-null pointer in there.
|
|
if forceOwningType:
|
|
# Don't return a holderType in this case; our declName
|
|
# will just own stuff.
|
|
templateBody += "nsRefPtr<" + typeName + "> ${holderName};\n"
|
|
else:
|
|
holderType = "nsRefPtr<" + typeName + ">"
|
|
templateBody += (
|
|
"jsval tmpVal = ${val};\n" +
|
|
typePtr + " tmp;\n"
|
|
"if (NS_FAILED(xpc_qsUnwrapArg<" + typeName + ">(cx, ${val}, &tmp, static_cast<" + typeName + "**>(getter_AddRefs(${holderName})), &tmpVal))) {\n")
|
|
templateBody += CGIndenter(onFailureBadType(failureCode,
|
|
descriptor.interface.identifier.name)).define()
|
|
templateBody += ("}\n"
|
|
"MOZ_ASSERT(tmp);\n")
|
|
|
|
if not isDefinitelyObject and not forceOwningType:
|
|
# Our tmpVal will go out of scope, so we can't rely on it
|
|
# for rooting
|
|
templateBody += (
|
|
"if (tmpVal != ${val} && !${holderName}) {\n"
|
|
" // We have to have a strong ref, because we got this off\n"
|
|
" // some random object that might get GCed\n"
|
|
" ${holderName} = tmp;\n"
|
|
"}\n")
|
|
|
|
# And store our tmp, before it goes out of scope.
|
|
templateBody += "${declName} = tmp;"
|
|
|
|
templateBody = wrapObjectTemplate(templateBody, type,
|
|
"${declName} = NULL",
|
|
failureCode)
|
|
|
|
declType = CGGeneric(declType)
|
|
if holderType is not None:
|
|
holderType = CGGeneric(holderType)
|
|
return (templateBody, declType, holderType, isOptional)
|
|
|
|
if type.isSpiderMonkeyInterface():
|
|
assert not isEnforceRange and not isClamp
|
|
if isMember:
|
|
raise TypeError("Can't handle member arraybuffers or "
|
|
"arraybuffer views because making sure all the "
|
|
"objects are properly rooted is hard")
|
|
name = type.name
|
|
# By default, we use a Maybe<> to hold our typed array. And in the optional
|
|
# non-nullable case we want to pass Optional<TypedArray> to consumers, not
|
|
# Optional<NonNull<TypedArray> >, so jump though some hoops to do that.
|
|
holderType = "Maybe<%s>" % name
|
|
constructLoc = "${holderName}"
|
|
constructMethod = "construct"
|
|
constructInternal = "ref"
|
|
if type.nullable():
|
|
if isOptional:
|
|
declType = "Optional<" + name + "*>"
|
|
else:
|
|
declType = name + "*"
|
|
else:
|
|
if isOptional:
|
|
declType = "Optional<" + name + ">"
|
|
# We don't need a holder in this case
|
|
holderType = None
|
|
constructLoc = "${declName}"
|
|
constructMethod = "Construct"
|
|
constructInternal = "Value"
|
|
else:
|
|
declType = "NonNull<" + name + ">"
|
|
template = (
|
|
"%s.%s(&${val}.toObject());\n"
|
|
"if (!%s.%s().inited()) {\n"
|
|
"%s" # No newline here because onFailureBadType() handles that
|
|
"}\n" %
|
|
(constructLoc, constructMethod, constructLoc, constructInternal,
|
|
CGIndenter(onFailureBadType(failureCode, type.name)).define()))
|
|
nullableTarget = ""
|
|
if type.nullable():
|
|
if isOptional:
|
|
template += "${declName}.Construct();\n"
|
|
nullableTarget = "${declName}.Value()"
|
|
else:
|
|
nullableTarget = "${declName}"
|
|
template += "%s = ${holderName}.addr();" % nullableTarget
|
|
elif not isOptional:
|
|
template += "${declName} = ${holderName}.addr();"
|
|
template = wrapObjectTemplate(template, type,
|
|
"%s = NULL" % nullableTarget,
|
|
failureCode)
|
|
|
|
if holderType is not None:
|
|
holderType = CGGeneric(holderType)
|
|
# We handle all the optional stuff ourselves; no need for caller to do it.
|
|
return (template, CGGeneric(declType), holderType, False)
|
|
|
|
if type.isString():
|
|
assert not isEnforceRange and not isClamp
|
|
|
|
treatAs = {
|
|
"Default": "eStringify",
|
|
"EmptyString": "eEmpty",
|
|
"Null": "eNull"
|
|
}
|
|
if type.nullable():
|
|
# For nullable strings null becomes a null string.
|
|
treatNullAs = "Null"
|
|
# For nullable strings undefined becomes a null string unless
|
|
# specified otherwise.
|
|
if treatUndefinedAs == "Default":
|
|
treatUndefinedAs = "Null"
|
|
nullBehavior = treatAs[treatNullAs]
|
|
if treatUndefinedAs == "Missing":
|
|
raise TypeError("We don't support [TreatUndefinedAs=Missing]")
|
|
undefinedBehavior = treatAs[treatUndefinedAs]
|
|
|
|
def getConversionCode(varName):
|
|
conversionCode = (
|
|
"if (!ConvertJSValueToString(cx, ${val}, ${valPtr}, %s, %s, %s)) {\n"
|
|
"%s\n"
|
|
"}" % (nullBehavior, undefinedBehavior, varName,
|
|
exceptionCodeIndented.define()))
|
|
if defaultValue is None:
|
|
return conversionCode
|
|
|
|
if isinstance(defaultValue, IDLNullValue):
|
|
assert(type.nullable())
|
|
return handleDefault(conversionCode,
|
|
"%s.SetNull()" % varName)
|
|
return handleDefault(
|
|
conversionCode,
|
|
("static const PRUnichar data[] = { %s };\n"
|
|
"%s.SetData(data, ArrayLength(data) - 1)" %
|
|
(", ".join(["'" + char + "'" for char in defaultValue.value] + ["0"]),
|
|
varName)))
|
|
|
|
if isMember:
|
|
# We have to make a copy, because our jsval may well not
|
|
# live as long as our string needs to.
|
|
declType = CGGeneric("nsString")
|
|
return (
|
|
"{\n"
|
|
" FakeDependentString str;\n"
|
|
"%s\n"
|
|
" ${declName} = str;\n"
|
|
"}\n" % CGIndenter(CGGeneric(getConversionCode("str"))).define(),
|
|
declType, None, isOptional)
|
|
|
|
if isOptional:
|
|
declType = "Optional<nsAString>"
|
|
else:
|
|
declType = "NonNull<nsAString>"
|
|
|
|
return (
|
|
"%s\n"
|
|
"${declName} = &${holderName};" %
|
|
getConversionCode("${holderName}"),
|
|
CGGeneric(declType), CGGeneric("FakeDependentString"),
|
|
# No need to deal with Optional here; we have handled it already
|
|
False)
|
|
|
|
if type.isEnum():
|
|
assert not isEnforceRange and not isClamp
|
|
|
|
enumName = type.unroll().inner.identifier.name
|
|
declType = CGGeneric(enumName)
|
|
if type.nullable():
|
|
declType = CGTemplatedType("Nullable", declType)
|
|
declType = declType.define()
|
|
enumLoc = "${declName}.SetValue()"
|
|
else:
|
|
enumLoc = "${declName}"
|
|
declType = declType.define()
|
|
|
|
if invalidEnumValueFatal:
|
|
handleInvalidEnumValueCode = " MOZ_ASSERT(index >= 0);\n"
|
|
else:
|
|
# invalidEnumValueFatal is false only for attributes. So we won't
|
|
# have a non-default exceptionCode here unless attribute "arg
|
|
# conversion" code starts passing in an exceptionCode. At which
|
|
# point we'll need to figure out what that even means.
|
|
assert exceptionCode == "return false;"
|
|
handleInvalidEnumValueCode = (
|
|
" if (index < 0) {\n"
|
|
" return true;\n"
|
|
" }\n")
|
|
|
|
template = (
|
|
"{\n"
|
|
" bool ok;\n"
|
|
" int index = FindEnumStringIndex<%(invalidEnumValueFatal)s>(cx, ${val}, %(values)s, \"%(enumtype)s\", &ok);\n"
|
|
" if (!ok) {\n"
|
|
"%(exceptionCode)s\n"
|
|
" }\n"
|
|
"%(handleInvalidEnumValueCode)s"
|
|
" %(enumLoc)s = static_cast<%(enumtype)s>(index);\n"
|
|
"}" % { "enumtype" : enumName,
|
|
"values" : enumName + "Values::strings",
|
|
"invalidEnumValueFatal" : toStringBool(invalidEnumValueFatal),
|
|
"handleInvalidEnumValueCode" : handleInvalidEnumValueCode,
|
|
"exceptionCode" : CGIndenter(exceptionCodeIndented).define(),
|
|
"enumLoc" : enumLoc,
|
|
})
|
|
|
|
setNull = "${declName}.SetNull();"
|
|
|
|
if type.nullable():
|
|
template = CGIfElseWrapper("${val}.isNullOrUndefined()",
|
|
CGGeneric(setNull),
|
|
CGGeneric(template)).define()
|
|
|
|
if defaultValue is not None:
|
|
if isinstance(defaultValue, IDLNullValue):
|
|
assert type.nullable()
|
|
template = handleDefault(template, setNull)
|
|
else:
|
|
assert(defaultValue.type.tag() == IDLType.Tags.domstring)
|
|
template = handleDefault(template,
|
|
("%s = %sValues::%s" %
|
|
(enumLoc, enumName,
|
|
getEnumValueName(defaultValue.value))))
|
|
return (template, CGGeneric(declType), None, isOptional)
|
|
|
|
if type.isCallback():
|
|
assert not isEnforceRange and not isClamp
|
|
assert not type.treatNonCallableAsNull() or type.nullable()
|
|
|
|
if descriptorProvider.workers:
|
|
if isMember:
|
|
raise NoSuchDescriptorError("Can't handle member callbacks in "
|
|
"workers; need to sort out rooting"
|
|
"issues")
|
|
if type.nullable():
|
|
declType = CGGeneric("JSObject*")
|
|
else:
|
|
declType = CGGeneric("NonNull<JSObject>")
|
|
conversion = " ${declName} = &${val}.toObject();\n"
|
|
else:
|
|
name = type.unroll().identifier.name
|
|
if type.nullable():
|
|
declType = CGGeneric("nsRefPtr<%s>" % name);
|
|
else:
|
|
declType = CGGeneric("OwningNonNull<%s>" % name)
|
|
conversion = (
|
|
" ${declName} = new %s(&${val}.toObject());\n" % name)
|
|
|
|
if allowTreatNonCallableAsNull and type.treatNonCallableAsNull():
|
|
haveCallable = "JS_ObjectIsCallable(cx, &${val}.toObject())"
|
|
if not isDefinitelyObject:
|
|
haveCallable = "${val}.isObject() && " + haveCallable
|
|
if defaultValue is not None:
|
|
assert(isinstance(defaultValue, IDLNullValue))
|
|
haveCallable = "${haveValue} && " + haveCallable
|
|
template = (
|
|
("if (%s) {\n" % haveCallable) +
|
|
conversion +
|
|
"} else {\n"
|
|
" ${declName} = nullptr;\n"
|
|
"}")
|
|
else:
|
|
template = wrapObjectTemplate(
|
|
"if (JS_ObjectIsCallable(cx, &${val}.toObject())) {\n" +
|
|
conversion +
|
|
"} else {\n"
|
|
"%s"
|
|
"}" % CGIndenter(onFailureNotCallable(failureCode)).define(),
|
|
type,
|
|
"${declName} = nullptr",
|
|
failureCode)
|
|
return (template, declType, None, isOptional)
|
|
|
|
if type.isAny():
|
|
assert not isEnforceRange and not isClamp
|
|
|
|
if isMember == "Dictionary":
|
|
declType = "RootedJSValue"
|
|
templateBody = ("if (!${declName}.SetValue(cx, ${val})) {\n"
|
|
" return false;\n"
|
|
"}")
|
|
nullHandling = "${declName}.SetValue(nullptr, JS::NullValue())"
|
|
elif isMember and isMember != "Variadic":
|
|
# Variadic arguments are rooted by being in argv
|
|
raise TypeError("Can't handle sequence member 'any'; need to sort "
|
|
"out rooting issues")
|
|
else:
|
|
declType = "JS::Value"
|
|
templateBody = "${declName} = ${val};"
|
|
nullHandling = "${declName} = JS::NullValue()"
|
|
templateBody = handleDefaultNull(templateBody, nullHandling)
|
|
return (templateBody, CGGeneric(declType), None, isOptional)
|
|
|
|
if type.isObject():
|
|
assert not isEnforceRange and not isClamp
|
|
|
|
if isMember and isMember != "Dictionary":
|
|
raise TypeError("Can't handle member 'object' not in a dictionary;"
|
|
"need to sort out rooting issues")
|
|
|
|
if isMember == "Dictionary":
|
|
if type.nullable():
|
|
declType = CGGeneric("LazyRootedObject")
|
|
else:
|
|
declType = CGGeneric("NonNullLazyRootedObject")
|
|
|
|
# If cx is null then LazyRootedObject will not be
|
|
# constructed and behave as nullptr.
|
|
templateBody = ("if (cx) {\n"
|
|
" ${declName}.construct(cx, &${val}.toObject());\n"
|
|
"}")
|
|
|
|
# Cast of nullptr to (JSObject*) is required for template deduction.
|
|
setToNullCode = ("if (cx) {\n"
|
|
" ${declName}.construct(cx, (JSObject*) nullptr);\n"
|
|
"}")
|
|
else:
|
|
if type.nullable():
|
|
declType = CGGeneric("JSObject*")
|
|
else:
|
|
declType = CGGeneric("NonNull<JSObject>")
|
|
|
|
templateBody = "${declName} = &${val}.toObject();"
|
|
setToNullCode = "${declName} = NULL"
|
|
|
|
template = wrapObjectTemplate(templateBody, type, setToNullCode,
|
|
failureCode)
|
|
|
|
return (template, declType, None, isOptional)
|
|
|
|
if type.isDictionary():
|
|
if failureCode is not None and not isDefinitelyObject:
|
|
raise TypeError("Can't handle dictionaries when failureCode is "
|
|
"not None and we don't know we're an object")
|
|
|
|
# There are no nullable dictionaries
|
|
assert not type.nullable()
|
|
# All optional dictionaries always have default values, so we
|
|
# should be able to assume not isOptional here.
|
|
assert not isOptional
|
|
|
|
typeName = CGDictionary.makeDictionaryName(type.inner,
|
|
descriptorProvider.workers)
|
|
actualTypeName = typeName
|
|
|
|
declType = CGGeneric(actualTypeName)
|
|
|
|
# We do manual default value handling here, because we
|
|
# actually do want a jsval, and we only handle null anyway
|
|
# NOTE: if isNullOrUndefined or isDefinitelyObject are true,
|
|
# we know we have a value, so we don't have to worry about the
|
|
# default value.
|
|
if (not isNullOrUndefined and not isDefinitelyObject and
|
|
defaultValue is not None):
|
|
assert(isinstance(defaultValue, IDLNullValue))
|
|
val = "(${haveValue}) ? ${val} : JSVAL_NULL"
|
|
else:
|
|
val = "${val}"
|
|
|
|
if failureCode is not None:
|
|
assert isDefinitelyObject
|
|
# Check that the value we have can in fact be converted to
|
|
# a dictionary, and return failureCode if not.
|
|
template = CGIfWrapper(
|
|
CGGeneric(failureCode),
|
|
"!IsConvertibleToDictionary(cx, &${val}.toObject())").define() + "\n\n"
|
|
else:
|
|
template = ""
|
|
|
|
template += ("if (!${declName}.Init(cx, %s)) {\n"
|
|
"%s\n"
|
|
"}" % (val, exceptionCodeIndented.define()))
|
|
|
|
return (template, declType, None, False)
|
|
|
|
if type.isVoid():
|
|
assert not isOptional
|
|
# This one only happens for return values, and its easy: Just
|
|
# ignore the jsval.
|
|
return ("", None, None, False)
|
|
|
|
if not type.isPrimitive():
|
|
raise TypeError("Need conversion for argument type '%s'" % str(type))
|
|
|
|
typeName = builtinNames[type.tag()]
|
|
|
|
conversionBehavior = "eDefault"
|
|
if isEnforceRange:
|
|
assert type.isInteger()
|
|
conversionBehavior = "eEnforceRange"
|
|
elif isClamp:
|
|
assert type.isInteger()
|
|
conversionBehavior = "eClamp"
|
|
|
|
if type.nullable():
|
|
declType = CGGeneric("Nullable<" + typeName + ">")
|
|
writeLoc = "${declName}.SetValue()"
|
|
readLoc = "${declName}.Value()"
|
|
nullCondition = "${val}.isNullOrUndefined()"
|
|
if defaultValue is not None and isinstance(defaultValue, IDLNullValue):
|
|
nullCondition = "!(${haveValue}) || " + nullCondition
|
|
template = (
|
|
"if (%s) {\n"
|
|
" ${declName}.SetNull();\n"
|
|
"} else if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n"
|
|
"%s\n"
|
|
"}" % (nullCondition, typeName, conversionBehavior,
|
|
writeLoc, exceptionCodeIndented.define()))
|
|
else:
|
|
assert(defaultValue is None or
|
|
not isinstance(defaultValue, IDLNullValue))
|
|
writeLoc = "${declName}"
|
|
readLoc = writeLoc
|
|
template = (
|
|
"if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n"
|
|
"%s\n"
|
|
"}" % (typeName, conversionBehavior, writeLoc,
|
|
exceptionCodeIndented.define()))
|
|
declType = CGGeneric(typeName)
|
|
|
|
if type.isFloat() and not type.isUnrestricted():
|
|
if lenientFloatCode is not None:
|
|
nonFiniteCode = CGIndenter(CGGeneric(lenientFloatCode)).define()
|
|
else:
|
|
nonFiniteCode = (" ThrowErrorMessage(cx, MSG_NOT_FINITE);\n"
|
|
"%s" % exceptionCodeIndented.define())
|
|
template += (" else if (!MOZ_DOUBLE_IS_FINITE(%s)) {\n"
|
|
" // Note: MOZ_DOUBLE_IS_FINITE will do the right thing\n"
|
|
" // when passed a non-finite float too.\n"
|
|
"%s\n"
|
|
"}" % (readLoc, nonFiniteCode))
|
|
|
|
if (defaultValue is not None and
|
|
# We already handled IDLNullValue, so just deal with the other ones
|
|
not isinstance(defaultValue, IDLNullValue)):
|
|
tag = defaultValue.type.tag()
|
|
if tag in numericSuffixes:
|
|
# Some numeric literals require a suffix to compile without warnings
|
|
defaultStr = numericValue(tag, defaultValue.value)
|
|
else:
|
|
assert(tag == IDLType.Tags.bool)
|
|
defaultStr = toStringBool(defaultValue.value)
|
|
template = CGWrapper(CGIndenter(CGGeneric(template)),
|
|
pre="if (${haveValue}) {\n",
|
|
post=("\n"
|
|
"} else {\n"
|
|
" %s = %s;\n"
|
|
"}" % (writeLoc, defaultStr))).define()
|
|
|
|
return (template, declType, None, isOptional)
|
|
|
|
def instantiateJSToNativeConversionTemplate(templateTuple, replacements,
|
|
argcAndIndex=None):
|
|
"""
|
|
Take a tuple as returned by getJSToNativeConversionTemplate and a set of
|
|
replacements as required by the strings in such a tuple, and generate code
|
|
to convert into stack C++ types.
|
|
|
|
If argcAndIndex is not None it must be a dict that can be used to
|
|
replace ${argc} and ${index}, where ${index} is the index of this
|
|
argument (0-based) and ${argc} is the total number of arguments.
|
|
"""
|
|
(templateBody, declType, holderType, dealWithOptional) = templateTuple
|
|
|
|
if dealWithOptional and argcAndIndex is None:
|
|
raise TypeError("Have to deal with optional things, but don't know how")
|
|
if argcAndIndex is not None and declType is None:
|
|
raise TypeError("Need to predeclare optional things, so they will be "
|
|
"outside the check for big enough arg count!");
|
|
|
|
result = CGList([], "\n")
|
|
# Make a copy of "replacements" since we may be about to start modifying it
|
|
replacements = dict(replacements)
|
|
originalHolderName = replacements["holderName"]
|
|
if holderType is not None:
|
|
if dealWithOptional:
|
|
replacements["holderName"] = "%s.Value()" % originalHolderName
|
|
holderType = CGTemplatedType("Optional", holderType)
|
|
result.append(
|
|
CGList([holderType, CGGeneric(" "),
|
|
CGGeneric(originalHolderName),
|
|
CGGeneric(";")]))
|
|
|
|
originalDeclName = replacements["declName"]
|
|
if declType is not None:
|
|
if dealWithOptional:
|
|
replacements["declName"] = "%s.Value()" % originalDeclName
|
|
declType = CGTemplatedType("Optional", declType)
|
|
result.append(
|
|
CGList([declType, CGGeneric(" "),
|
|
CGGeneric(originalDeclName),
|
|
CGGeneric(";")]))
|
|
|
|
conversion = CGGeneric(
|
|
string.Template(templateBody).substitute(replacements)
|
|
)
|
|
|
|
if argcAndIndex is not None:
|
|
if dealWithOptional:
|
|
declConstruct = CGIndenter(
|
|
CGGeneric("%s.Construct();" % originalDeclName))
|
|
if holderType is not None:
|
|
holderConstruct = CGIndenter(
|
|
CGGeneric("%s.Construct();" % originalHolderName))
|
|
else:
|
|
holderConstruct = None
|
|
else:
|
|
declConstruct = None
|
|
holderConstruct = None
|
|
|
|
conversion = CGList(
|
|
[CGGeneric(
|
|
string.Template("if (${index} < ${argc}) {").substitute(
|
|
argcAndIndex
|
|
)),
|
|
declConstruct,
|
|
holderConstruct,
|
|
CGIndenter(conversion),
|
|
CGGeneric("}")],
|
|
"\n")
|
|
|
|
result.append(conversion)
|
|
# Add an empty CGGeneric to get an extra newline after the argument
|
|
# conversion.
|
|
result.append(CGGeneric(""))
|
|
return result;
|
|
|
|
def convertConstIDLValueToJSVal(value):
|
|
if isinstance(value, IDLNullValue):
|
|
return "JSVAL_NULL"
|
|
tag = value.type.tag()
|
|
if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
|
|
IDLType.Tags.uint16, IDLType.Tags.int32]:
|
|
return "INT_TO_JSVAL(%s)" % (value.value)
|
|
if tag == IDLType.Tags.uint32:
|
|
return "UINT_TO_JSVAL(%sU)" % (value.value)
|
|
if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]:
|
|
return "DOUBLE_TO_JSVAL(%s)" % numericValue(tag, value.value)
|
|
if tag == IDLType.Tags.bool:
|
|
return "JSVAL_TRUE" if value.value else "JSVAL_FALSE"
|
|
if tag in [IDLType.Tags.float, IDLType.Tags.double]:
|
|
return "DOUBLE_TO_JSVAL(%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, lenientFloatCode=None,
|
|
allowTreatNonCallableAsNull=False):
|
|
CGThing.__init__(self)
|
|
self.argument = argument
|
|
assert(not argument.defaultValue or argument.optional)
|
|
|
|
replacer = {
|
|
"index" : index,
|
|
"argc" : argc,
|
|
"argv" : argv
|
|
}
|
|
self.replacementVariables = {
|
|
"declName" : "arg%d" % index,
|
|
"holderName" : ("arg%d" % index) + "_holder",
|
|
"obj" : "obj"
|
|
}
|
|
self.replacementVariables["val"] = string.Template(
|
|
"${argv}[${index}]"
|
|
).substitute(replacer)
|
|
self.replacementVariables["valPtr"] = (
|
|
"&" + self.replacementVariables["val"])
|
|
if argument.defaultValue:
|
|
self.replacementVariables["haveValue"] = string.Template(
|
|
"${index} < ${argc}").substitute(replacer)
|
|
self.descriptorProvider = descriptorProvider
|
|
if self.argument.optional and not self.argument.defaultValue:
|
|
self.argcAndIndex = replacer
|
|
else:
|
|
self.argcAndIndex = None
|
|
self.invalidEnumValueFatal = invalidEnumValueFatal
|
|
self.lenientFloatCode = lenientFloatCode
|
|
self.allowTreatNonCallableAsNull = allowTreatNonCallableAsNull
|
|
|
|
def define(self):
|
|
typeConversion = getJSToNativeConversionTemplate(
|
|
self.argument.type,
|
|
self.descriptorProvider,
|
|
isOptional=(self.argcAndIndex is not None and
|
|
not self.argument.variadic),
|
|
invalidEnumValueFatal=self.invalidEnumValueFatal,
|
|
defaultValue=self.argument.defaultValue,
|
|
treatNullAs=self.argument.treatNullAs,
|
|
treatUndefinedAs=self.argument.treatUndefinedAs,
|
|
isEnforceRange=self.argument.enforceRange,
|
|
isClamp=self.argument.clamp,
|
|
lenientFloatCode=self.lenientFloatCode,
|
|
isMember="Variadic" if self.argument.variadic else False,
|
|
allowTreatNonCallableAsNull=self.allowTreatNonCallableAsNull)
|
|
|
|
if not self.argument.variadic:
|
|
return instantiateJSToNativeConversionTemplate(
|
|
typeConversion,
|
|
self.replacementVariables,
|
|
self.argcAndIndex).define()
|
|
|
|
# Variadic arguments get turned into a sequence.
|
|
(elementTemplate, elementDeclType,
|
|
elementHolderType, dealWithOptional) = typeConversion
|
|
if dealWithOptional:
|
|
raise TypeError("Shouldn't have optional things in variadics")
|
|
if elementHolderType is not None:
|
|
raise TypeError("Shouldn't need holders for variadics")
|
|
|
|
replacer = dict(self.argcAndIndex, **self.replacementVariables)
|
|
replacer["seqType"] = CGTemplatedType("AutoSequence", elementDeclType).define()
|
|
replacer["elemType"] = elementDeclType.define()
|
|
|
|
# NOTE: Keep this in sync with sequence conversions as needed
|
|
variadicConversion = string.Template("""${seqType} ${declName};
|
|
if (${argc} > ${index}) {
|
|
if (!${declName}.SetCapacity(${argc} - ${index})) {
|
|
JS_ReportOutOfMemory(cx);
|
|
return false;
|
|
}
|
|
for (uint32_t variadicArg = ${index}; variadicArg < ${argc}; ++variadicArg) {
|
|
${elemType}& slot = *${declName}.AppendElement();
|
|
""").substitute(replacer)
|
|
|
|
val = string.Template("${argv}[variadicArg]").substitute(replacer)
|
|
variadicConversion += CGIndenter(CGGeneric(
|
|
string.Template(elementTemplate).substitute(
|
|
{
|
|
"val" : val,
|
|
"valPtr": "&" + val,
|
|
"declName" : "slot",
|
|
# We only need holderName here to handle isExternal()
|
|
# interfaces, which use an internal holder for the
|
|
# conversion even when forceOwningType ends up true.
|
|
"holderName": "tempHolder",
|
|
# Use the same ${obj} as for the variadic arg itself
|
|
"obj": replacer["obj"]
|
|
}
|
|
)), 4).define()
|
|
|
|
variadicConversion += ("\n"
|
|
" }\n"
|
|
"}")
|
|
return variadicConversion
|
|
|
|
sequenceWrapLevel = 0
|
|
|
|
def getWrapTemplateForType(type, descriptorProvider, result, successCode,
|
|
isCreator, exceptionCode, isMember=False):
|
|
"""
|
|
Reflect a C++ value stored in "result", of IDL type "type" into JS. The
|
|
"successCode" is the code to run once we have successfully done the
|
|
conversion. The resulting string should be used with string.Template, it
|
|
needs the following keys when substituting: jsvalPtr/jsvalRef/obj.
|
|
|
|
Returns (templateString, infallibility of conversion template)
|
|
"""
|
|
if successCode is None:
|
|
successCode = "return true;"
|
|
|
|
# We often want exceptionCode to be indented, since it often appears in an
|
|
# if body.
|
|
exceptionCodeIndented = CGIndenter(CGGeneric(exceptionCode))
|
|
|
|
def setValue(value, callWrapValue=False):
|
|
"""
|
|
Returns the code to set the jsval to value. If "callWrapValue" is true
|
|
MaybeWrapValue will be called on the jsval.
|
|
"""
|
|
if not callWrapValue:
|
|
tail = successCode
|
|
else:
|
|
tail = ("if (!MaybeWrapValue(cx, ${jsvalPtr})) {\n" +
|
|
("%s\n" % exceptionCodeIndented.define()) +
|
|
"}\n" +
|
|
successCode)
|
|
return ("${jsvalRef} = %s;\n" +
|
|
tail) % (value)
|
|
|
|
def wrapAndSetPtr(wrapCall, failureCode=None):
|
|
"""
|
|
Returns the code to set the jsval by calling "wrapCall". "failureCode"
|
|
is the code to run if calling "wrapCall" fails
|
|
"""
|
|
if failureCode is None:
|
|
failureCode = exceptionCode
|
|
str = ("if (!%s) {\n" +
|
|
CGIndenter(CGGeneric(failureCode)).define() + "\n" +
|
|
"}\n" +
|
|
successCode) % (wrapCall)
|
|
return str
|
|
|
|
if type is None or type.isVoid():
|
|
return (setValue("JSVAL_VOID"), True)
|
|
|
|
if type.isArray():
|
|
raise TypeError("Can't handle array return values yet")
|
|
|
|
if type.isSequence():
|
|
if type.nullable():
|
|
# Nullable sequences are Nullable< nsTArray<T> >
|
|
(recTemplate, recInfall) = getWrapTemplateForType(type.inner, descriptorProvider,
|
|
"%s.Value()" % result, successCode,
|
|
isCreator, exceptionCode)
|
|
return ("""
|
|
if (%s.IsNull()) {
|
|
%s
|
|
}
|
|
%s""" % (result, CGIndenter(CGGeneric(setValue("JSVAL_NULL"))).define(), recTemplate), recInfall)
|
|
|
|
# Now do non-nullable sequences. Our success code is just to break to
|
|
# where we set the element in the array. Note that we bump the
|
|
# sequenceWrapLevel around this call so that nested sequence conversions
|
|
# will use different iteration variables.
|
|
global sequenceWrapLevel
|
|
index = "sequenceIdx%d" % sequenceWrapLevel
|
|
sequenceWrapLevel += 1
|
|
innerTemplate = wrapForType(
|
|
type.inner, descriptorProvider,
|
|
{
|
|
'result' : "%s[%s]" % (result, index),
|
|
'successCode': "break;",
|
|
'jsvalRef': "tmp",
|
|
'jsvalPtr': "&tmp",
|
|
'isCreator': isCreator,
|
|
'exceptionCode': exceptionCode,
|
|
'obj': "returnArray"
|
|
}
|
|
)
|
|
sequenceWrapLevel -= 1
|
|
innerTemplate = CGIndenter(CGGeneric(innerTemplate), 6).define()
|
|
return (("""
|
|
uint32_t length = %s.Length();
|
|
JS::Rooted<JSObject*> returnArray(cx, JS_NewArrayObject(cx, length, nullptr));
|
|
if (!returnArray) {
|
|
%s
|
|
}
|
|
// Scope for 'tmp'
|
|
{
|
|
jsval tmp;
|
|
for (uint32_t %s = 0; %s < length; ++%s) {
|
|
// Control block to let us common up the JS_DefineElement calls when there
|
|
// are different ways to succeed at wrapping the object.
|
|
do {
|
|
%s
|
|
} while (0);
|
|
if (!JS_DefineElement(cx, returnArray, %s, tmp,
|
|
nullptr, nullptr, JSPROP_ENUMERATE)) {
|
|
%s
|
|
}
|
|
}
|
|
}\n""" % (result, exceptionCodeIndented.define(),
|
|
index, index, index,
|
|
innerTemplate, index,
|
|
CGIndenter(exceptionCodeIndented, 4).define())) +
|
|
setValue("JS::ObjectValue(*returnArray)"), False)
|
|
|
|
if (type.isGeckoInterface() and
|
|
(not type.isCallbackInterface() or
|
|
type.unroll().inner.identifier.name == "EventListener")):
|
|
descriptor = descriptorProvider.getDescriptor(type.unroll().inner.identifier.name)
|
|
if type.nullable():
|
|
wrappingCode = ("if (!%s) {\n" % (result) +
|
|
CGIndenter(CGGeneric(setValue("JSVAL_NULL"))).define() + "\n" +
|
|
"}\n")
|
|
else:
|
|
wrappingCode = ""
|
|
|
|
if not descriptor.interface.isExternal() and not descriptor.skipGen:
|
|
if descriptor.wrapperCache:
|
|
assert descriptor.nativeOwnership != 'owned'
|
|
wrapMethod = "WrapNewBindingObject"
|
|
else:
|
|
if not isCreator:
|
|
raise MethodNotCreatorError(descriptor.interface.identifier.name)
|
|
if descriptor.nativeOwnership == 'owned':
|
|
wrapMethod = "WrapNewBindingNonWrapperCachedOwnedObject"
|
|
else:
|
|
wrapMethod = "WrapNewBindingNonWrapperCachedObject"
|
|
wrap = "%s(cx, ${obj}, %s, ${jsvalPtr})" % (wrapMethod, result)
|
|
if not descriptor.hasXPConnectImpls:
|
|
# Can only fail to wrap as a new-binding object
|
|
# if they already threw an exception.
|
|
failed = ("MOZ_ASSERT(JS_IsExceptionPending(cx));\n" +
|
|
"%s" % exceptionCode)
|
|
else:
|
|
if descriptor.notflattened:
|
|
raise TypeError("%s has XPConnect impls but not flattened; "
|
|
"fallback won't work correctly" %
|
|
descriptor.interface.identifier.name)
|
|
# Try old-style wrapping for bindings which might be XPConnect impls.
|
|
failed = wrapAndSetPtr("HandleNewBindingWrappingFailure(cx, ${obj}, %s, ${jsvalPtr})" % result)
|
|
else:
|
|
if descriptor.notflattened:
|
|
getIID = "&NS_GET_IID(%s), " % descriptor.nativeType
|
|
else:
|
|
getIID = ""
|
|
wrap = "WrapObject(cx, ${obj}, %s, %s${jsvalPtr})" % (result, getIID)
|
|
failed = None
|
|
|
|
wrappingCode += wrapAndSetPtr(wrap, failed)
|
|
return (wrappingCode, False)
|
|
|
|
if type.isString():
|
|
if type.nullable():
|
|
return (wrapAndSetPtr("xpc::StringToJsval(cx, %s, ${jsvalPtr})" % result), False)
|
|
else:
|
|
return (wrapAndSetPtr("xpc::NonVoidStringToJsval(cx, %s, ${jsvalPtr})" % result), False)
|
|
|
|
if type.isEnum():
|
|
if type.nullable():
|
|
resultLoc = "%s.Value()" % result
|
|
else:
|
|
resultLoc = result
|
|
conversion = ("""{
|
|
// Scope for resultStr
|
|
MOZ_ASSERT(uint32_t(%(result)s) < ArrayLength(%(strings)s));
|
|
JSString* resultStr = JS_NewStringCopyN(cx, %(strings)s[uint32_t(%(result)s)].value, %(strings)s[uint32_t(%(result)s)].length);
|
|
if (!resultStr) {
|
|
%(exceptionCode)s
|
|
}
|
|
""" % { "result" : resultLoc,
|
|
"strings" : type.unroll().inner.identifier.name + "Values::strings",
|
|
"exceptionCode" : CGIndenter(exceptionCodeIndented).define() } +
|
|
CGIndenter(CGGeneric(setValue("JS::StringValue(resultStr)"))).define() +
|
|
"\n}")
|
|
|
|
if type.nullable():
|
|
conversion = CGIfElseWrapper(
|
|
"%s.IsNull()" % result,
|
|
CGGeneric(setValue("JS::NullValue()", False)),
|
|
CGGeneric(conversion)).define()
|
|
return conversion, False
|
|
|
|
if type.isCallback() or type.isCallbackInterface():
|
|
# See comments in WrapNewBindingObject explaining why we need
|
|
# to wrap here.
|
|
# NB: setValue(..., True) calls JS_WrapValue(), so is fallible
|
|
if descriptorProvider.workers:
|
|
return (setValue("JS::ObjectOrNullValue(%s)" % result, True), False)
|
|
|
|
wrapCode = setValue(
|
|
"JS::ObjectValue(*GetCallbackFromCallbackObject(%(result)s))",
|
|
True)
|
|
if type.nullable():
|
|
wrapCode = (
|
|
"if (%(result)s) {\n" +
|
|
CGIndenter(CGGeneric(wrapCode)).define() + "\n"
|
|
"} else {\n" +
|
|
CGIndenter(CGGeneric(setValue("JS::NullValue()"))).define() + "\n"
|
|
"}")
|
|
wrapCode = wrapCode % { "result": result }
|
|
return wrapCode, False
|
|
|
|
if type.tag() == IDLType.Tags.any:
|
|
# See comments in WrapNewBindingObject explaining why we need
|
|
# to wrap here.
|
|
# NB: setValue(..., True) calls JS_WrapValue(), so is fallible
|
|
return (setValue(result, True), False)
|
|
|
|
if type.isObject() or type.isSpiderMonkeyInterface():
|
|
# See comments in WrapNewBindingObject explaining why we need
|
|
# to wrap here.
|
|
if type.nullable():
|
|
toValue = "JS::ObjectOrNullValue(%s)"
|
|
else:
|
|
if isMember:
|
|
toValue = "JS::ObjectValue(%s)"
|
|
else:
|
|
toValue = "JS::ObjectValue(*%s)"
|
|
# NB: setValue(..., True) calls JS_WrapValue(), so is fallible
|
|
return (setValue(toValue % result, True), False)
|
|
|
|
if type.isUnion():
|
|
if type.nullable():
|
|
prefix = "%s->"
|
|
else:
|
|
prefix = "%s."
|
|
return (wrapAndSetPtr((prefix % result) +
|
|
"ToJSVal(cx, ${obj}, ${jsvalPtr})"), False)
|
|
|
|
if not (type.isPrimitive() or type.isDictionary()):
|
|
raise TypeError("Need to learn to wrap %s" % type)
|
|
|
|
if type.nullable():
|
|
(recTemplate, recInfal) = getWrapTemplateForType(type.inner, descriptorProvider,
|
|
"%s.Value()" % result, successCode,
|
|
isCreator, exceptionCode)
|
|
return ("if (%s.IsNull()) {\n" % result +
|
|
CGIndenter(CGGeneric(setValue("JSVAL_NULL"))).define() + "\n" +
|
|
"}\n" + recTemplate, recInfal)
|
|
|
|
if type.isDictionary():
|
|
return (wrapAndSetPtr("%s.ToObject(cx, ${obj}, ${jsvalPtr})" % result),
|
|
False)
|
|
|
|
|
|
tag = type.tag()
|
|
|
|
if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
|
|
IDLType.Tags.uint16, IDLType.Tags.int32]:
|
|
return (setValue("INT_TO_JSVAL(int32_t(%s))" % result), True)
|
|
|
|
elif tag in [IDLType.Tags.int64, IDLType.Tags.uint64,
|
|
IDLType.Tags.unrestricted_float, IDLType.Tags.float,
|
|
IDLType.Tags.unrestricted_double, IDLType.Tags.double]:
|
|
# XXXbz will cast to double do the "even significand" thing that webidl
|
|
# calls for for 64-bit ints? Do we care?
|
|
return (setValue("JS_NumberValue(double(%s))" % result), True)
|
|
|
|
elif tag == IDLType.Tags.uint32:
|
|
return (setValue("UINT_TO_JSVAL(%s)" % result), True)
|
|
|
|
elif tag == IDLType.Tags.bool:
|
|
return (setValue("BOOLEAN_TO_JSVAL(%s)" % result), True)
|
|
|
|
else:
|
|
raise TypeError("Need to learn to wrap primitive: %s" % type)
|
|
|
|
def wrapForType(type, descriptorProvider, templateValues, isMember=False):
|
|
"""
|
|
Reflect a C++ value of IDL type "type" into JS. isMember should
|
|
be true if the type is of a dictionary member. TemplateValues is a dict
|
|
that should contain:
|
|
|
|
* 'jsvalRef': a C++ reference to the jsval in which to store the result of
|
|
the conversion
|
|
* 'jsvalPtr': a C++ pointer to the jsval in which to store the result of
|
|
the conversion
|
|
* 'obj' (optional): the name of the variable that contains the JSObject to
|
|
use as a scope when wrapping, if not supplied 'obj'
|
|
will be used as the name
|
|
* 'result' (optional): the name of the variable in which the C++ value is
|
|
stored, if not supplied 'result' will be used as
|
|
the name
|
|
* 'successCode' (optional): the code to run once we have successfully done
|
|
the conversion, if not supplied 'return true;'
|
|
will be used as the code
|
|
* 'isCreator' (optional): If true, we're wrapping for the return value of
|
|
a [Creator] method. Assumed false if not set.
|
|
* 'exceptionCode' (optional): Code to run when a JS exception is thrown.
|
|
The default is "return false;". The code
|
|
passed here must return.
|
|
"""
|
|
wrap = getWrapTemplateForType(type, descriptorProvider,
|
|
templateValues.get('result', 'result'),
|
|
templateValues.get('successCode', None),
|
|
templateValues.get('isCreator', False),
|
|
templateValues.get('exceptionCode',
|
|
"return false;"),
|
|
isMember)[0]
|
|
|
|
defaultValues = {'obj': 'obj'}
|
|
return string.Template(wrap).substitute(defaultValues, **templateValues)
|
|
|
|
def infallibleForMember(member, type, descriptorProvider):
|
|
"""
|
|
Determine the fallibility of changing a C++ value of IDL type "type" into
|
|
JS for the given attribute. Apart from isCreator, all the defaults are used,
|
|
since the fallbility does not change based on the boolean values,
|
|
and the template will be discarded.
|
|
|
|
CURRENT ASSUMPTIONS:
|
|
We assume that successCode for wrapping up return values cannot contain
|
|
failure conditions.
|
|
"""
|
|
return getWrapTemplateForType(type, descriptorProvider, 'result', None,\
|
|
memberIsCreator(member), "return false;")[1]
|
|
|
|
def typeNeedsCx(type, descriptorProvider, 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, descriptorProvider) for t in
|
|
type.unroll().flatMemberTypes)
|
|
if type.isDictionary():
|
|
return dictionaryNeedsCx(type.inner, descriptorProvider)
|
|
if retVal and type.isSpiderMonkeyInterface():
|
|
return True
|
|
if type.isCallback():
|
|
return descriptorProvider.workers
|
|
return type.isAny() or type.isObject()
|
|
|
|
def dictionaryNeedsCx(dictionary, descriptorProvider):
|
|
return (any(typeNeedsCx(m.type, descriptorProvider) for m in dictionary.members) or
|
|
(dictionary.parent and dictionaryNeedsCx(dictionary.parent, descriptorProvider)))
|
|
|
|
# Returns a tuple consisting of a CGThing containing the type of the return
|
|
# value, or None if there is no need for a return value, and a boolean signaling
|
|
# whether the return value is passed in an out parameter.
|
|
#
|
|
# Whenever this is modified, please update CGNativeMember.getRetvalInfo as
|
|
# needed to keep the types compatible.
|
|
def getRetvalDeclarationForType(returnType, descriptorProvider,
|
|
resultAlreadyAddRefed,
|
|
isMember=False):
|
|
if returnType is None or returnType.isVoid():
|
|
# Nothing to declare
|
|
return None, False
|
|
if returnType.isPrimitive() and returnType.tag() in builtinNames:
|
|
result = CGGeneric(builtinNames[returnType.tag()])
|
|
if returnType.nullable():
|
|
result = CGTemplatedType("Nullable", result)
|
|
return result, False
|
|
if returnType.isString():
|
|
if isMember:
|
|
return CGGeneric("nsString"), True
|
|
return CGGeneric("DOMString"), True
|
|
if returnType.isEnum():
|
|
result = CGGeneric(returnType.unroll().inner.identifier.name)
|
|
if returnType.nullable():
|
|
result = CGTemplatedType("Nullable", result)
|
|
return result, False
|
|
if returnType.isGeckoInterface():
|
|
result = CGGeneric(descriptorProvider.getDescriptor(
|
|
returnType.unroll().inner.identifier.name).nativeType)
|
|
if resultAlreadyAddRefed:
|
|
result = CGTemplatedType("nsRefPtr", result)
|
|
elif descriptorProvider.getDescriptor(
|
|
returnType.unroll().inner.identifier.name).nativeOwnership == 'owned':
|
|
result = CGTemplatedType("nsAutoPtr", result)
|
|
else:
|
|
result = CGWrapper(result, post="*")
|
|
return result, False
|
|
if returnType.isCallback():
|
|
name = returnType.unroll().identifier.name
|
|
if descriptorProvider.workers:
|
|
return CGGeneric("JSObject*"), False
|
|
return CGGeneric("nsRefPtr<%s>" % name), False
|
|
if returnType.isAny():
|
|
return CGGeneric("JS::Value"), False
|
|
if returnType.isObject() or returnType.isSpiderMonkeyInterface():
|
|
return CGGeneric("JSObject*"), False
|
|
if returnType.isSequence():
|
|
nullable = returnType.nullable()
|
|
if nullable:
|
|
returnType = returnType.inner
|
|
# If our result is already addrefed, use the right type in the
|
|
# sequence argument here.
|
|
(result, _) = getRetvalDeclarationForType(returnType.inner,
|
|
descriptorProvider,
|
|
resultAlreadyAddRefed,
|
|
isMember=True)
|
|
result = CGTemplatedType("nsTArray", result)
|
|
if nullable:
|
|
result = CGTemplatedType("Nullable", result)
|
|
return result, True
|
|
if returnType.isDictionary():
|
|
nullable = returnType.nullable()
|
|
result = CGGeneric(
|
|
CGDictionary.makeDictionaryName(returnType.unroll().inner,
|
|
descriptorProvider.workers) +
|
|
"Initializer")
|
|
if nullable:
|
|
result = CGTemplatedType("Nullable", result)
|
|
return result, True
|
|
if returnType.isUnion():
|
|
raise TypeError("Need to sort out ownership model for union retvals");
|
|
raise TypeError("Don't know how to declare return value for %s" %
|
|
returnType)
|
|
|
|
def isResultAlreadyAddRefed(descriptor, extendedAttributes):
|
|
# Default to already_AddRefed on the main thread, raw pointer in workers
|
|
return not descriptor.workers and not 'resultNotAddRefed' in extendedAttributes
|
|
|
|
def needCx(returnType, arguments, extendedAttributes, descriptorProvider):
|
|
return (typeNeedsCx(returnType, descriptorProvider, True) or
|
|
any(typeNeedsCx(a.type, descriptorProvider) for a in arguments) or
|
|
'implicitJSContext' in extendedAttributes)
|
|
|
|
class CGCallGenerator(CGThing):
|
|
"""
|
|
A class to generate an actual call to a C++ object. Assumes that the C++
|
|
object is stored in a variable whose name is given by the |object| argument.
|
|
|
|
errorReport should be a CGThing for an error report or None if no
|
|
error reporting is needed.
|
|
"""
|
|
def __init__(self, errorReport, arguments, argsPre, returnType,
|
|
extendedAttributes, descriptorProvider, nativeMethodName,
|
|
static, object="self"):
|
|
CGThing.__init__(self)
|
|
|
|
assert errorReport is None or isinstance(errorReport, CGThing)
|
|
|
|
isFallible = errorReport is not None
|
|
|
|
resultAlreadyAddRefed = isResultAlreadyAddRefed(descriptorProvider,
|
|
extendedAttributes)
|
|
(result, resultOutParam) = getRetvalDeclarationForType(returnType,
|
|
descriptorProvider,
|
|
resultAlreadyAddRefed)
|
|
|
|
args = CGList([CGGeneric(arg) for arg in argsPre], ", ")
|
|
for (a, name) in arguments:
|
|
# This is a workaround for a bug in Apple's clang.
|
|
if a.type.isObject() and not a.type.nullable() and not a.optional:
|
|
name = "(JSObject&)" + name
|
|
arg = CGGeneric(name)
|
|
# Now constify the things that need it
|
|
def needsConst(a):
|
|
if a.type.isDictionary():
|
|
return True
|
|
if a.type.isSequence():
|
|
return True
|
|
if a.type.nullable():
|
|
return True
|
|
if a.type.isString():
|
|
return True
|
|
if a.optional and not a.defaultValue:
|
|
# If a.defaultValue, then it's not going to use an Optional,
|
|
# so doesn't need to be const just due to being optional.
|
|
# This also covers variadic arguments.
|
|
return True
|
|
if a.type.isUnion():
|
|
return True
|
|
return False
|
|
if needsConst(a):
|
|
arg = CGWrapper(arg, pre="Constify(", post=")")
|
|
args.append(arg)
|
|
|
|
# Return values that go in outparams go here
|
|
if resultOutParam:
|
|
args.append(CGGeneric("result"))
|
|
if isFallible:
|
|
args.append(CGGeneric("rv"))
|
|
|
|
# Build up our actual call
|
|
self.cgRoot = CGList([], "\n")
|
|
|
|
call = CGGeneric(nativeMethodName)
|
|
if not static:
|
|
call = CGWrapper(call, pre="%s->" % object)
|
|
call = CGList([call, CGWrapper(args, pre="(", post=");")])
|
|
if result is not None:
|
|
result = CGWrapper(result, post=" result;")
|
|
self.cgRoot.prepend(result)
|
|
if not resultOutParam:
|
|
call = CGWrapper(call, pre="result = ")
|
|
|
|
call = CGWrapper(call)
|
|
self.cgRoot.append(call)
|
|
|
|
if isFallible:
|
|
self.cgRoot.prepend(CGGeneric("ErrorResult rv;"))
|
|
self.cgRoot.append(CGGeneric("rv.WouldReportJSException();"));
|
|
self.cgRoot.append(CGGeneric("if (rv.Failed()) {"))
|
|
self.cgRoot.append(CGIndenter(errorReport))
|
|
self.cgRoot.append(CGGeneric("}"))
|
|
|
|
def define(self):
|
|
return self.cgRoot.define()
|
|
|
|
class MethodNotCreatorError(Exception):
|
|
def __init__(self, typename):
|
|
self.typename = typename
|
|
|
|
# A counter for making sure that when we're wrapping up things in
|
|
# nested sequences we don't use the same variable name to iterate over
|
|
# different sequences.
|
|
sequenceWrapLevel = 0
|
|
|
|
def wrapTypeIntoCurrentCompartment(type, value):
|
|
"""
|
|
Take the thing named by "value" and if it contains "any",
|
|
"object", or spidermonkey-interface types inside return a CGThing
|
|
that will wrap them into the current compartment.
|
|
"""
|
|
if type.isAny():
|
|
assert not type.nullable()
|
|
return CGGeneric("if (!JS_WrapValue(cx, &%s)) {\n"
|
|
" return false;\n"
|
|
"}" % value)
|
|
|
|
if type.isObject():
|
|
if not type.nullable():
|
|
value = "%s.Slot()" % value
|
|
else:
|
|
value = "&%s" % value
|
|
return CGGeneric("if (!JS_WrapObject(cx, %s)) {\n"
|
|
" return false;\n"
|
|
"}" % value)
|
|
|
|
if type.isSpiderMonkeyInterface():
|
|
raise TypeError("Can't handle wrapping of spidermonkey interfaces in "
|
|
"constructor arguments yet")
|
|
|
|
if type.isSequence():
|
|
if type.nullable():
|
|
type = type.inner
|
|
value = "%s.Value()" % value
|
|
global sequenceWrapLevel
|
|
index = "indexName%d" % sequenceWrapLevel
|
|
sequenceWrapLevel += 1
|
|
wrapElement = wrapTypeIntoCurrentCompartment(type.inner,
|
|
"%s[%s]" % (value, index))
|
|
sequenceWrapLevel -= 1
|
|
if not wrapElement:
|
|
return None
|
|
return CGWrapper(CGIndenter(wrapElement),
|
|
pre=("for (uint32_t %s = 0; %s < %s.Length(); ++%s) {\n" %
|
|
(index, index, value, index)),
|
|
post="\n}")
|
|
|
|
if type.isDictionary():
|
|
assert not type.nullable()
|
|
myDict = type.inner
|
|
memberWraps = []
|
|
while myDict:
|
|
for member in myDict.members:
|
|
memberWrap = wrapArgIntoCurrentCompartment(
|
|
member,
|
|
"%s.%s" % (value, CGDictionary.makeMemberName(member.identifier.name)))
|
|
if memberWrap:
|
|
memberWraps.append(memberWrap)
|
|
myDict = myDict.parent
|
|
return CGList(memberWraps, "\n") if len(memberWraps) != 0 else None
|
|
|
|
if type.isUnion():
|
|
raise TypeError("Can't handle wrapping of unions in constructor "
|
|
"arguments yet")
|
|
|
|
if (type.isString() or type.isPrimitive() or type.isEnum() or
|
|
type.isGeckoInterface() or type.isCallback()):
|
|
# All of these don't need wrapping
|
|
return None
|
|
|
|
raise TypeError("Unknown type; we don't know how to wrap it in constructor "
|
|
"arguments: %s" % type)
|
|
|
|
def wrapArgIntoCurrentCompartment(arg, value):
|
|
"""
|
|
As wrapTypeIntoCurrentCompartment but handles things being optional
|
|
"""
|
|
origValue = value
|
|
isOptional = arg.optional and not arg.defaultValue
|
|
if isOptional:
|
|
value = value + ".Value()"
|
|
wrap = wrapTypeIntoCurrentCompartment(arg.type, value)
|
|
if wrap and isOptional:
|
|
wrap = CGIfWrapper(wrap, "%s.WasPassed()" % origValue)
|
|
return wrap
|
|
|
|
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, arguments, nativeMethodName, static,
|
|
descriptor, idlNode, argConversionStartsAt=0, getter=False,
|
|
setter=False, isConstructor=False):
|
|
assert idlNode.isMethod() == (not getter and not setter)
|
|
assert idlNode.isAttr() == (getter or setter)
|
|
|
|
CGThing.__init__(self)
|
|
self.returnType = returnType
|
|
self.descriptor = descriptor
|
|
self.idlNode = idlNode
|
|
self.extendedAttributes = descriptor.getExtendedAttributes(idlNode,
|
|
getter=getter,
|
|
setter=setter)
|
|
self.arguments = arguments
|
|
self.argCount = len(arguments)
|
|
if self.argCount > argConversionStartsAt:
|
|
# Insert our argv in there
|
|
cgThings = [CGGeneric(self.getArgvDecl())]
|
|
else:
|
|
cgThings = []
|
|
lenientFloatCode = None
|
|
if idlNode.getExtendedAttribute('LenientFloat') is not None:
|
|
if setter:
|
|
lenientFloatCode = "return true;"
|
|
elif idlNode.isMethod():
|
|
lenientFloatCode = ("*vp = JSVAL_VOID;\n"
|
|
"return true;")
|
|
|
|
argsPre = []
|
|
if static:
|
|
nativeMethodName = "%s::%s" % (descriptor.nativeType,
|
|
nativeMethodName)
|
|
globalObjectType = "GlobalObject"
|
|
if descriptor.workers:
|
|
globalObjectType = "Worker" + globalObjectType
|
|
cgThings.append(CGGeneric("""%s global(cx, obj);
|
|
if (global.Failed()) {
|
|
return false;
|
|
}
|
|
""" % globalObjectType))
|
|
argsPre.append("global")
|
|
|
|
needsCx = (not descriptor.interface.isJSImplemented() and
|
|
needCx(returnType, arguments, self.extendedAttributes,
|
|
descriptor))
|
|
if needsCx and not (static and descriptor.workers):
|
|
argsPre.append("cx")
|
|
|
|
if idlNode.isMethod() and idlNode.isLegacycaller():
|
|
# If we can have legacycaller with identifier, we can't
|
|
# just use the idlNode to determine whether we're
|
|
# generating code for the legacycaller or not.
|
|
assert idlNode.isIdentifierLess()
|
|
# Pass in our thisVal
|
|
argsPre.append("JS_THIS_VALUE(cx, vp)")
|
|
|
|
cgThings.extend([CGArgumentConverter(arguments[i], i, self.getArgv(),
|
|
self.getArgc(), self.descriptor,
|
|
invalidEnumValueFatal=not setter,
|
|
allowTreatNonCallableAsNull=setter,
|
|
lenientFloatCode=lenientFloatCode) for
|
|
i in range(argConversionStartsAt, self.argCount)])
|
|
|
|
if isConstructor:
|
|
# If we're called via an xray, we need to enter the underlying
|
|
# object's compartment and then wrap up all of our arguments into
|
|
# that compartment as needed. This is all happening after we've
|
|
# already done the conversions from JS values to WebIDL (C++)
|
|
# values, so we only need to worry about cases where there are 'any'
|
|
# or 'object' types, or other things that we represent as actual
|
|
# JSAPI types, present. Effectively, we're emulating a
|
|
# CrossCompartmentWrapper, but working with the C++ types, not the
|
|
# original list of JS::Values.
|
|
cgThings.append(CGGeneric("Maybe<JSAutoCompartment> ac;"))
|
|
xraySteps = [
|
|
CGGeneric("obj = js::CheckedUnwrap(obj);\n"
|
|
"if (!obj) {\n"
|
|
" return false;\n"
|
|
"}\n"
|
|
"ac.construct(cx, obj);") ]
|
|
xraySteps.extend(
|
|
wrapArgIntoCurrentCompartment(arg, argname)
|
|
for (arg, argname) in self.getArguments())
|
|
cgThings.append(
|
|
CGIfWrapper(CGList(xraySteps, "\n"),
|
|
"xpc::WrapperFactory::IsXrayWrapper(obj)"))
|
|
|
|
cgThings.append(CGCallGenerator(
|
|
self.getErrorReport() if self.isFallible() else None,
|
|
self.getArguments(), 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 "\nJS::Value* argv = JS_ARGV(cx, vp);\n"
|
|
def getArgc(self):
|
|
return "argc"
|
|
def getArguments(self):
|
|
return [(a, "arg" + str(i)) for (i, a) in enumerate(self.arguments)]
|
|
|
|
def isFallible(self):
|
|
return not 'infallible' in self.extendedAttributes
|
|
|
|
def wrap_return_value(self):
|
|
isCreator = memberIsCreator(self.idlNode)
|
|
if isCreator:
|
|
# We better be returning addrefed things!
|
|
assert(isResultAlreadyAddRefed(self.descriptor,
|
|
self.extendedAttributes) or
|
|
# Creators can return raw pointers to owned objects
|
|
(self.returnType.isGeckoInterface() and
|
|
self.descriptor.getDescriptor(self.returnType.unroll().inner.identifier.name).nativeOwnership == 'owned') or
|
|
# Workers use raw pointers for new-object return
|
|
# values or something
|
|
self.descriptor.workers)
|
|
|
|
resultTemplateValues = { 'jsvalRef': '*vp', 'jsvalPtr': 'vp',
|
|
'isCreator': isCreator}
|
|
try:
|
|
return wrapForType(self.returnType, self.descriptor,
|
|
resultTemplateValues)
|
|
except MethodNotCreatorError, err:
|
|
assert not isCreator
|
|
raise TypeError("%s being returned from non-creator method or property %s.%s" %
|
|
(err.typename,
|
|
self.descriptor.interface.identifier.name,
|
|
self.idlNode.identifier.name))
|
|
|
|
def getErrorReport(self):
|
|
return CGGeneric('return ThrowMethodFailedWithDetails<%s>(cx, rv, "%s", "%s");'
|
|
% (toStringBool(not self.descriptor.workers),
|
|
self.descriptor.interface.identifier.name,
|
|
self.idlNode.identifier.name))
|
|
|
|
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="switch (", post=") {"));
|
|
if default is not None:
|
|
self.append(
|
|
CGIndenter(
|
|
CGWrapper(
|
|
CGIndenter(default),
|
|
pre="default: {\n",
|
|
post="\n break;\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), pre="case ", post=": {"))
|
|
bodyList = CGList([body], "\n")
|
|
if fallThrough:
|
|
bodyList.append(CGGeneric("/* Fall through */"))
|
|
else:
|
|
bodyList.append(CGGeneric("break;"))
|
|
self.append(CGIndenter(bodyList));
|
|
self.append(CGGeneric("}"))
|
|
|
|
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, nativeMethodName, static, descriptor, method,
|
|
isConstructor=False):
|
|
CGThing.__init__(self)
|
|
|
|
methodName = '"%s.%s"' % (descriptor.interface.identifier.name, method.identifier.name)
|
|
|
|
def requiredArgCount(signature):
|
|
arguments = signature[1]
|
|
if len(arguments) == 0:
|
|
return 0
|
|
requiredArgs = len(arguments)
|
|
while requiredArgs and arguments[requiredArgs-1].optional:
|
|
requiredArgs -= 1
|
|
return requiredArgs
|
|
|
|
def getPerSignatureCall(signature, argConversionStartsAt=0):
|
|
return CGPerSignatureCall(signature[0], signature[1],
|
|
nativeMethodName, static, descriptor,
|
|
method,
|
|
argConversionStartsAt=argConversionStartsAt,
|
|
isConstructor=isConstructor)
|
|
|
|
|
|
signatures = method.signatures()
|
|
if len(signatures) == 1:
|
|
# Special case: we can just do a per-signature method call
|
|
# here for our one signature and not worry about switching
|
|
# on anything.
|
|
signature = signatures[0]
|
|
self.cgRoot = CGList([ CGIndenter(getPerSignatureCall(signature)) ])
|
|
requiredArgs = requiredArgCount(signature)
|
|
|
|
|
|
if requiredArgs > 0:
|
|
code = (
|
|
"if (argc < %d) {\n"
|
|
" return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, %s);\n"
|
|
"}" % (requiredArgs, methodName))
|
|
self.cgRoot.prepend(
|
|
CGWrapper(CGIndenter(CGGeneric(code)), pre="\n", post="\n"))
|
|
return
|
|
|
|
# Need to find the right overload
|
|
maxArgCount = method.maxArgCount
|
|
allowedArgCounts = method.allowedArgCounts
|
|
|
|
argCountCases = []
|
|
for argCount in allowedArgCounts:
|
|
possibleSignatures = method.signaturesForArgCount(argCount)
|
|
if len(possibleSignatures) == 1:
|
|
# easy case!
|
|
signature = possibleSignatures[0]
|
|
|
|
# (possibly) important optimization: if signature[1] has >
|
|
# argCount arguments and signature[1][argCount] is optional and
|
|
# there is only one signature for argCount+1, then the
|
|
# signature for argCount+1 is just ourselves and we can fall
|
|
# through.
|
|
if (len(signature[1]) > argCount and
|
|
signature[1][argCount].optional and
|
|
(argCount+1) in allowedArgCounts and
|
|
len(method.signaturesForArgCount(argCount+1)) == 1):
|
|
argCountCases.append(
|
|
CGCase(str(argCount), None, True))
|
|
else:
|
|
argCountCases.append(
|
|
CGCase(str(argCount), getPerSignatureCall(signature)))
|
|
continue
|
|
|
|
distinguishingIndex = method.distinguishingIndexForArgCount(argCount)
|
|
|
|
def distinguishingArgument(signature):
|
|
args = signature[1]
|
|
if distinguishingIndex < len(args):
|
|
return args[distinguishingIndex]
|
|
assert args[-1].variadic
|
|
return args[-1]
|
|
|
|
def distinguishingType(signature):
|
|
return distinguishingArgument(signature).type
|
|
|
|
for sig in possibleSignatures:
|
|
# We should not have "any" args at distinguishingIndex,
|
|
# since we have multiple possible signatures remaining,
|
|
# but "any" is never distinguishable from anything else.
|
|
assert not distinguishingType(sig).isAny()
|
|
# We can't handle unions at the distinguishing index.
|
|
if distinguishingType(sig).isUnion():
|
|
raise TypeError("No support for unions as distinguishing "
|
|
"arguments yet: %s" %
|
|
distinguishingArgument(sig).location)
|
|
# We don't support variadics as the distinguishingArgument yet.
|
|
# If you want to add support, consider this case:
|
|
#
|
|
# void(long... foo);
|
|
# void(long bar, Int32Array baz);
|
|
#
|
|
# in which we have to convert argument 0 to long before picking
|
|
# an overload... but all the variadic stuff needs to go into a
|
|
# single array in case we pick that overload, so we have to have
|
|
# machinery for converting argument 0 to long and then either
|
|
# placing it in the variadic bit or not. Or something. We may
|
|
# be able to loosen this restriction if the variadic arg is in
|
|
# fact at distinguishingIndex, perhaps. Would need to
|
|
# double-check.
|
|
if distinguishingArgument(sig).variadic:
|
|
raise TypeError("No support for variadics as distinguishing "
|
|
"arguments yet: %s" %
|
|
distinguishingArgument(sig).location)
|
|
|
|
# Convert all our arguments up to the distinguishing index.
|
|
# Doesn't matter which of the possible signatures we use, since
|
|
# they all have the same types up to that point; just use
|
|
# possibleSignatures[0]
|
|
caseBody = [CGGeneric("JS::Value* argv_start = JS_ARGV(cx, vp);")]
|
|
caseBody.extend([ CGArgumentConverter(possibleSignatures[0][1][i],
|
|
i, "argv_start", "argc",
|
|
descriptor) for i in
|
|
range(0, distinguishingIndex) ])
|
|
|
|
# Select the right overload from our set.
|
|
distinguishingArg = "argv_start[%d]" % distinguishingIndex
|
|
|
|
def pickFirstSignature(condition, filterLambda):
|
|
sigs = filter(filterLambda, possibleSignatures)
|
|
assert len(sigs) < 2
|
|
if len(sigs) > 0:
|
|
if condition is None:
|
|
caseBody.append(
|
|
getPerSignatureCall(sigs[0], distinguishingIndex))
|
|
else:
|
|
caseBody.append(CGGeneric("if (" + condition + ") {"))
|
|
caseBody.append(CGIndenter(
|
|
getPerSignatureCall(sigs[0], distinguishingIndex)))
|
|
caseBody.append(CGGeneric("}"))
|
|
return True
|
|
return False
|
|
|
|
def tryCall(signature, indent, isDefinitelyObject=False,
|
|
isNullOrUndefined=False):
|
|
assert not isDefinitelyObject or not isNullOrUndefined
|
|
assert isDefinitelyObject or isNullOrUndefined
|
|
if isDefinitelyObject:
|
|
failureCode = "break;"
|
|
else:
|
|
failureCode = None
|
|
type = distinguishingType(signature)
|
|
# The argument at index distinguishingIndex can't possibly
|
|
# be unset here, because we've already checked that argc is
|
|
# large enough that we can examine this argument.
|
|
testCode = instantiateJSToNativeConversionTemplate(
|
|
getJSToNativeConversionTemplate(type, descriptor,
|
|
failureCode=failureCode,
|
|
isDefinitelyObject=isDefinitelyObject,
|
|
isNullOrUndefined=isNullOrUndefined),
|
|
{
|
|
"declName" : "arg%d" % distinguishingIndex,
|
|
"holderName" : ("arg%d" % distinguishingIndex) + "_holder",
|
|
"val" : distinguishingArg,
|
|
"obj" : "obj"
|
|
})
|
|
caseBody.append(CGIndenter(testCode, indent));
|
|
# If we got this far, we know we unwrapped to the right
|
|
# C++ type, so just do the call. Start conversion with
|
|
# distinguishingIndex + 1, since we already converted
|
|
# distinguishingIndex.
|
|
caseBody.append(CGIndenter(
|
|
getPerSignatureCall(signature, distinguishingIndex + 1),
|
|
indent))
|
|
|
|
# First check for null or undefined. That means looking for
|
|
# nullable arguments at the distinguishing index and outputting a
|
|
# separate branch for them. But if the nullable argument is a
|
|
# primitive, string, or enum, we don't need to do that. The reason
|
|
# for that is that at most one argument at the distinguishing index
|
|
# is nullable (since two nullable arguments are not
|
|
# distinguishable), and all the argument types other than
|
|
# primitive/string/enum end up inside isObject() checks. So if our
|
|
# nullable is a primitive/string/enum it's safe to not output the
|
|
# extra branch: we'll fall through to conversion for those types,
|
|
# which correctly handles null as needed, because isObject() will be
|
|
# false for null and undefined.
|
|
nullOrUndefSigs = [s for s in possibleSignatures
|
|
if ((distinguishingType(s).nullable() and not
|
|
distinguishingType(s).isString() and not
|
|
distinguishingType(s).isEnum() and not
|
|
distinguishingType(s).isPrimitive()) or
|
|
distinguishingType(s).isDictionary())]
|
|
# Can't have multiple nullable types here
|
|
assert len(nullOrUndefSigs) < 2
|
|
if len(nullOrUndefSigs) > 0:
|
|
caseBody.append(CGGeneric("if (%s.isNullOrUndefined()) {" %
|
|
distinguishingArg))
|
|
tryCall(nullOrUndefSigs[0], 2, isNullOrUndefined=True)
|
|
caseBody.append(CGGeneric("}"))
|
|
|
|
# Now check for distinguishingArg being various kinds of objects.
|
|
# The spec says to check for the following things in order:
|
|
# 1) A platform object that's not a platform array object, being
|
|
# passed to an interface or "object" arg.
|
|
# 2) A Date object being passed to a Date or "object" arg.
|
|
# 3) A RegExp object being passed to a RegExp or "object" arg.
|
|
# 4) A callable object being passed to a callback or "object" arg.
|
|
# 5) Any non-Date and non-RegExp object being passed to a
|
|
# array or sequence or callback interface dictionary or
|
|
# "object" arg.
|
|
#
|
|
# We can can coalesce these five cases together, as long as we make
|
|
# sure to check whether our object works as an interface argument
|
|
# before checking whether it works as an arraylike or dictionary or
|
|
# callback function or callback interface.
|
|
|
|
# First grab all the overloads that have a non-callback interface
|
|
# (which includes typed arrays and arraybuffers) at the
|
|
# distinguishing index. We can also include the ones that have an
|
|
# "object" here, since if those are present no other object-typed
|
|
# argument will be.
|
|
objectSigs = [
|
|
s for s in possibleSignatures
|
|
if (distinguishingType(s).isObject() or
|
|
distinguishingType(s).isNonCallbackInterface()) ]
|
|
|
|
# And all the overloads that take Date
|
|
objectSigs.extend(s for s in possibleSignatures
|
|
if distinguishingType(s).isDate())
|
|
|
|
# And all the overloads that take callbacks
|
|
objectSigs.extend(s for s in possibleSignatures
|
|
if distinguishingType(s).isCallback())
|
|
|
|
# Now append all the overloads that take an array or sequence or
|
|
# dictionary or callback interface:
|
|
objectSigs.extend(s for s in possibleSignatures
|
|
if (distinguishingType(s).isArray() or
|
|
distinguishingType(s).isSequence() or
|
|
distinguishingType(s).isDictionary() or
|
|
distinguishingType(s).isCallbackInterface()))
|
|
|
|
# There might be more than one thing in objectSigs; we need to check
|
|
# which ones we unwrap to.
|
|
if len(objectSigs) > 0:
|
|
# Here it's enough to guard on our argument being an object. The
|
|
# code for unwrapping non-callback interfaces, typed arrays,
|
|
# sequences, arrays, and Dates will just bail out and move on to
|
|
# the next overload if the object fails to unwrap correctly,
|
|
# while "object" accepts any object anyway. We could even not
|
|
# do the isObject() check up front here, but in cases where we
|
|
# have multiple object overloads it makes sense to do it only
|
|
# once instead of for each overload. That will also allow the
|
|
# unwrapping test to skip having to do codegen for the
|
|
# null-or-undefined case, which we already handled above.
|
|
caseBody.append(CGGeneric("if (%s.isObject()) {" %
|
|
distinguishingArg))
|
|
for sig in objectSigs:
|
|
caseBody.append(CGIndenter(CGGeneric("do {")));
|
|
# Indent by 4, since we need to indent further
|
|
# than our "do" statement
|
|
tryCall(sig, 4, isDefinitelyObject=True)
|
|
caseBody.append(CGIndenter(CGGeneric("} while (0);")))
|
|
|
|
caseBody.append(CGGeneric("}"))
|
|
|
|
# The remaining cases are mutually exclusive. The
|
|
# pickFirstSignature calls are what change caseBody
|
|
# Check for strings or enums
|
|
if pickFirstSignature(None,
|
|
lambda s: (distinguishingType(s).isString() or
|
|
distinguishingType(s).isEnum())):
|
|
pass
|
|
# Check for primitives
|
|
elif pickFirstSignature(None,
|
|
lambda s: distinguishingType(s).isPrimitive()):
|
|
pass
|
|
else:
|
|
# Just throw; we have no idea what we're supposed to
|
|
# do with this.
|
|
caseBody.append(CGGeneric(
|
|
'return ThrowErrorMessage(cx, MSG_INVALID_ARG, "%s", "%s");'
|
|
% (str(distinguishingIndex), str(argCount))))
|
|
|
|
argCountCases.append(CGCase(str(argCount),
|
|
CGList(caseBody, "\n")))
|
|
|
|
overloadCGThings = []
|
|
overloadCGThings.append(
|
|
CGGeneric("unsigned argcount = std::min(argc, %du);" %
|
|
maxArgCount))
|
|
overloadCGThings.append(
|
|
CGSwitch("argcount",
|
|
argCountCases,
|
|
CGGeneric("return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, %s);\n" % methodName)))
|
|
overloadCGThings.append(
|
|
CGGeneric('MOZ_NOT_REACHED("We have an always-returning default case");\n'
|
|
'return false;'))
|
|
self.cgRoot = CGWrapper(CGIndenter(CGList(overloadCGThings, "\n")),
|
|
pre="\n")
|
|
|
|
def define(self):
|
|
return self.cgRoot.define()
|
|
|
|
class CGGetterCall(CGPerSignatureCall):
|
|
"""
|
|
A class to generate a native object getter call for a particular IDL
|
|
getter.
|
|
"""
|
|
def __init__(self, returnType, nativeMethodName, descriptor, attr):
|
|
CGPerSignatureCall.__init__(self, returnType, [], nativeMethodName,
|
|
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, name="arg"):
|
|
self.type = type
|
|
self.optional = False
|
|
self.variadic = False
|
|
self.defaultValue = None
|
|
self.treatNullAs = interfaceMember.treatNullAs
|
|
self.treatUndefinedAs = interfaceMember.treatUndefinedAs
|
|
self.enforceRange = False
|
|
self.clamp = False
|
|
class FakeIdentifier():
|
|
def __init__(self):
|
|
self.name = name
|
|
self.identifier = FakeIdentifier()
|
|
|
|
class CGSetterCall(CGPerSignatureCall):
|
|
"""
|
|
A class to generate a native object setter call for a particular IDL
|
|
setter.
|
|
"""
|
|
def __init__(self, argType, nativeMethodName, descriptor, attr):
|
|
CGPerSignatureCall.__init__(self, None, [FakeArgument(argType, attr)],
|
|
nativeMethodName, attr.isStatic(),
|
|
descriptor, attr, setter=True)
|
|
def wrap_return_value(self):
|
|
# We have no return value
|
|
return "\nreturn true;"
|
|
def getArgc(self):
|
|
return "1"
|
|
def getArgvDecl(self):
|
|
# We just get our stuff from our last arg no matter what
|
|
return ""
|
|
|
|
class CGAbstractBindingMethod(CGAbstractStaticMethod):
|
|
"""
|
|
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,
|
|
getThisObj="JS_THIS_OBJECT(cx, vp)"):
|
|
CGAbstractStaticMethod.__init__(self, descriptor, name, "JSBool", args)
|
|
|
|
if unwrapFailureCode is None:
|
|
self.unwrapFailureCode = 'return ThrowErrorMessage(cx, MSG_DOES_NOT_IMPLEMENT_INTERFACE, "%s");' % self.descriptor.name
|
|
else:
|
|
self.unwrapFailureCode = unwrapFailureCode
|
|
self.getThisObj = getThisObj
|
|
|
|
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 CastableObjectUnwrapper.
|
|
getThis = CGGeneric("""JS::RootedObject obj(cx, %s);
|
|
if (!obj) {
|
|
return false;
|
|
}
|
|
|
|
%s* self;""" % (self.getThisObj, self.descriptor.nativeType))
|
|
unwrapThis = CGGeneric(
|
|
str(CastableObjectUnwrapper(
|
|
self.descriptor,
|
|
"obj", "self", self.unwrapFailureCode)))
|
|
return CGList([ CGIndenter(getThis), CGIndenter(unwrapThis),
|
|
self.generate_code() ], "\n").define()
|
|
|
|
def generate_code(self):
|
|
assert(False) # Override me
|
|
|
|
class CGAbstractStaticBindingMethod(CGAbstractStaticMethod):
|
|
"""
|
|
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):
|
|
CGAbstractStaticMethod.__init__(self, descriptor, name, "JSBool", args)
|
|
|
|
def definition_body(self):
|
|
unwrap = CGGeneric("""JS::RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
|
|
if (!obj) {
|
|
return false;
|
|
}
|
|
""")
|
|
return CGList([ CGIndenter(unwrap),
|
|
self.generate_code() ], "\n\n").define()
|
|
|
|
def generate_code(self):
|
|
assert(False) # Override me
|
|
|
|
def MakeNativeName(name):
|
|
return name[0].upper() + name[1:]
|
|
|
|
class CGGenericMethod(CGAbstractBindingMethod):
|
|
"""
|
|
A class for generating the C++ code for an IDL method..
|
|
"""
|
|
def __init__(self, descriptor):
|
|
args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
|
|
Argument('JS::Value*', 'vp')]
|
|
CGAbstractBindingMethod.__init__(self, descriptor, 'genericMethod', args)
|
|
|
|
def generate_code(self):
|
|
return CGIndenter(CGGeneric(
|
|
"const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
|
|
"MOZ_ASSERT(info->type == JSJitInfo::Method);\n"
|
|
"JSJitMethodOp method = (JSJitMethodOp)info->op;\n"
|
|
"return method(cx, obj, self, argc, vp);"))
|
|
|
|
class CGSpecializedMethod(CGAbstractStaticMethod):
|
|
"""
|
|
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 = CppKeywords.checkMethodName(method.identifier.name)
|
|
args = [Argument('JSContext*', 'cx'), Argument('JSHandleObject', 'obj'),
|
|
Argument('%s*' % descriptor.nativeType, 'self'),
|
|
Argument('unsigned', 'argc'), Argument('JS::Value*', 'vp')]
|
|
CGAbstractStaticMethod.__init__(self, descriptor, name, 'bool', args)
|
|
|
|
def definition_body(self):
|
|
nativeName = CGSpecializedMethod.makeNativeName(self.descriptor,
|
|
self.method)
|
|
return CGMethodCall(nativeName, self.method.isStatic(), self.descriptor,
|
|
self.method).define()
|
|
|
|
@staticmethod
|
|
def makeNativeName(descriptor, method):
|
|
name = method.identifier.name
|
|
return MakeNativeName(descriptor.binaryNames.get(name, name))
|
|
|
|
class CGLegacyCallHook(CGAbstractBindingMethod):
|
|
"""
|
|
Call hook for our object
|
|
"""
|
|
def __init__(self, descriptor):
|
|
self._legacycaller = descriptor.operations["LegacyCaller"]
|
|
args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
|
|
Argument('JS::Value*', 'vp')]
|
|
# Our "self" is actually the callee in this case, not the thisval.
|
|
CGAbstractBindingMethod.__init__(
|
|
self, descriptor, LEGACYCALLER_HOOK_NAME,
|
|
args, getThisObj="&JS_CALLEE(cx, vp).toObject()")
|
|
|
|
def define(self):
|
|
if not self._legacycaller:
|
|
return ""
|
|
return CGAbstractBindingMethod.define(self)
|
|
|
|
def generate_code(self):
|
|
name = self._legacycaller.identifier.name
|
|
nativeName = MakeNativeName(self.descriptor.binaryNames.get(name, name))
|
|
return CGMethodCall(nativeName, False, self.descriptor,
|
|
self._legacycaller)
|
|
|
|
class CGNewResolveHook(CGAbstractBindingMethod):
|
|
"""
|
|
NewResolve hook for our object
|
|
"""
|
|
def __init__(self, descriptor):
|
|
self._needNewResolve = descriptor.interface.getExtendedAttribute("NeedNewResolve")
|
|
args = [Argument('JSContext*', 'cx'), Argument('JSHandleObject', 'obj_'),
|
|
Argument('JSHandleId', 'id'), Argument('unsigned', 'flags'),
|
|
Argument('JSMutableHandleObject', 'objp')]
|
|
# Our "self" is actually the callee in this case, not the thisval.
|
|
CGAbstractBindingMethod.__init__(
|
|
self, descriptor, NEWRESOLVE_HOOK_NAME,
|
|
args, getThisObj="obj_")
|
|
|
|
def define(self):
|
|
if not self._needNewResolve:
|
|
return ""
|
|
return CGAbstractBindingMethod.define(self)
|
|
|
|
def generate_code(self):
|
|
return CGIndenter(CGGeneric("return self->DoNewResolve(cx, obj, id, flags, objp);"))
|
|
|
|
class CppKeywords():
|
|
"""
|
|
A class for checking if method names declared in webidl
|
|
are not in conflict with C++ keywords.
|
|
"""
|
|
keywords = frozenset(['alignas', 'alignof', 'and', 'and_eq', 'asm', 'auto', 'bitand', 'bitor', 'bool',
|
|
'break', 'case', 'catch', 'char', 'char16_t', 'char32_t', 'class', 'compl', 'const', 'constexpr',
|
|
'const_cast', 'continue', 'decltype', 'default', 'delete', 'do', 'double', 'dynamic_cast', 'else', 'enum',
|
|
'explicit', 'export', 'extern', 'false', 'final', 'float', 'for', 'friend', 'goto', 'if', 'inline',
|
|
'int', 'long', 'mutable', 'namespace', 'new', 'noexcept', 'not', 'not_eq', 'nullptr', 'operator',
|
|
'or', 'or_eq', 'override', 'private', 'protected', 'public', 'register', 'reinterpret_cast', 'return',
|
|
'short', 'signed', 'sizeof', 'static', 'static_assert', 'static_cast', 'struct', 'switch', 'template',
|
|
'this', 'thread_local', 'throw', 'true', 'try', 'typedef', 'typeid', 'typename', 'union', 'unsigned',
|
|
'using', 'virtual', 'void', 'volatile', 'wchar_t', 'while', 'xor', 'xor_eq'])
|
|
|
|
@staticmethod
|
|
def checkMethodName(name):
|
|
if name in CppKeywords.keywords:
|
|
name = '_' + name
|
|
return name
|
|
|
|
class CGStaticMethod(CGAbstractStaticBindingMethod):
|
|
"""
|
|
A class for generating the C++ code for an IDL static method.
|
|
"""
|
|
def __init__(self, descriptor, method):
|
|
self.method = method
|
|
name = method.identifier.name
|
|
args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
|
|
Argument('JS::Value*', 'vp')]
|
|
CGAbstractStaticBindingMethod.__init__(self, descriptor, name, args)
|
|
|
|
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('JSContext*', 'cx'), Argument('unsigned', 'argc'),
|
|
Argument('JS::Value*', 'vp')]
|
|
if lenientThis:
|
|
name = "genericLenientGetter"
|
|
unwrapFailureCode = (
|
|
"MOZ_ASSERT(!JS_IsExceptionPending(cx));\n"
|
|
"ReportLenientThisUnwrappingFailure(cx, obj);\n"
|
|
"JS_SET_RVAL(cx, vp, JS::UndefinedValue());\n"
|
|
"return true;")
|
|
else:
|
|
name = "genericGetter"
|
|
unwrapFailureCode = None
|
|
CGAbstractBindingMethod.__init__(self, descriptor, name, args,
|
|
unwrapFailureCode)
|
|
|
|
def generate_code(self):
|
|
return CGIndenter(CGGeneric(
|
|
"const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
|
|
"MOZ_ASSERT(info->type == JSJitInfo::Getter);\n"
|
|
"JSJitPropertyOp getter = info->op;\n"
|
|
"return getter(cx, obj, self, vp);"))
|
|
|
|
class CGSpecializedGetter(CGAbstractStaticMethod):
|
|
"""
|
|
A class for generating the code for a specialized attribute getter
|
|
that the JIT can call with lower overhead.
|
|
"""
|
|
def __init__(self, descriptor, attr):
|
|
self.attr = attr
|
|
name = 'get_' + attr.identifier.name
|
|
args = [ Argument('JSContext*', 'cx'),
|
|
Argument('JSHandleObject', 'obj'),
|
|
Argument('%s*' % descriptor.nativeType, 'self'),
|
|
Argument('JS::Value*', 'vp') ]
|
|
CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
|
|
|
|
def definition_body(self):
|
|
nativeName = CGSpecializedGetter.makeNativeName(self.descriptor,
|
|
self.attr)
|
|
return CGIndenter(CGGetterCall(self.attr.type, nativeName,
|
|
self.descriptor, self.attr)).define()
|
|
|
|
@staticmethod
|
|
def makeNativeName(descriptor, attr):
|
|
name = attr.identifier.name
|
|
nativeName = MakeNativeName(descriptor.binaryNames.get(name, name))
|
|
# resultOutParam does not depend on whether resultAlreadyAddRefed is set
|
|
(_, resultOutParam) = getRetvalDeclarationForType(attr.type, descriptor,
|
|
False)
|
|
infallible = ('infallible' in
|
|
descriptor.getExtendedAttributes(attr, getter=True))
|
|
if resultOutParam or attr.type.nullable() or not infallible:
|
|
nativeName = "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
|
|
args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
|
|
Argument('JS::Value*', 'vp')]
|
|
CGAbstractStaticBindingMethod.__init__(self, descriptor, name, args)
|
|
|
|
def generate_code(self):
|
|
nativeName = CGSpecializedGetter.makeNativeName(self.descriptor,
|
|
self.attr)
|
|
return CGIndenter(CGGetterCall(self.attr.type, nativeName,
|
|
self.descriptor, self.attr))
|
|
|
|
class CGGenericSetter(CGAbstractBindingMethod):
|
|
"""
|
|
A class for generating the C++ code for an IDL attribute setter.
|
|
"""
|
|
def __init__(self, descriptor, lenientThis=False):
|
|
args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
|
|
Argument('JS::Value*', 'vp')]
|
|
if lenientThis:
|
|
name = "genericLenientSetter"
|
|
unwrapFailureCode = (
|
|
"MOZ_ASSERT(!JS_IsExceptionPending(cx));\n"
|
|
"ReportLenientThisUnwrappingFailure(cx, obj);\n"
|
|
"return true;")
|
|
else:
|
|
name = "genericSetter"
|
|
unwrapFailureCode = None
|
|
CGAbstractBindingMethod.__init__(self, descriptor, name, args,
|
|
unwrapFailureCode)
|
|
|
|
def generate_code(self):
|
|
return CGIndenter(CGGeneric(
|
|
"if (argc == 0) {\n"
|
|
' return ThrowErrorMessage(cx, MSG_MISSING_ARGUMENTS, "%s attribute setter");\n'
|
|
"}\n"
|
|
"JS::Value* argv = JS_ARGV(cx, vp);\n"
|
|
"const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n"
|
|
"MOZ_ASSERT(info->type == JSJitInfo::Setter);\n"
|
|
"JSJitPropertyOp setter = info->op;\n"
|
|
"if (!setter(cx, obj, self, argv)) {\n"
|
|
" return false;\n"
|
|
"}\n"
|
|
"*vp = JSVAL_VOID;\n"
|
|
"return true;" % self.descriptor.interface.identifier.name))
|
|
|
|
class CGSpecializedSetter(CGAbstractStaticMethod):
|
|
"""
|
|
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('JSContext*', 'cx'),
|
|
Argument('JSHandleObject', 'obj'),
|
|
Argument('%s*' % descriptor.nativeType, 'self'),
|
|
Argument('JS::Value*', 'argv')]
|
|
CGAbstractStaticMethod.__init__(self, descriptor, name, "bool", args)
|
|
|
|
def definition_body(self):
|
|
nativeName = CGSpecializedSetter.makeNativeName(self.descriptor,
|
|
self.attr)
|
|
return CGIndenter(CGSetterCall(self.attr.type, nativeName,
|
|
self.descriptor, self.attr)).define()
|
|
|
|
@staticmethod
|
|
def makeNativeName(descriptor, attr):
|
|
name = attr.identifier.name
|
|
return "Set" + MakeNativeName(descriptor.binaryNames.get(name, 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
|
|
args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
|
|
Argument('JS::Value*', 'vp')]
|
|
CGAbstractStaticBindingMethod.__init__(self, descriptor, name, args)
|
|
|
|
def generate_code(self):
|
|
nativeName = CGSpecializedSetter.makeNativeName(self.descriptor,
|
|
self.attr)
|
|
argv = CGGeneric("""JS::Value* argv = JS_ARGV(cx, vp);
|
|
JS::Value undef = JS::UndefinedValue();
|
|
if (argc == 0) {
|
|
argv = &undef;
|
|
}""")
|
|
call = CGSetterCall(self.attr.type, nativeName, self.descriptor,
|
|
self.attr)
|
|
return CGIndenter(CGList([ argv, call ], "\n"))
|
|
|
|
class CGSpecializedForwardingSetter(CGSpecializedSetter):
|
|
"""
|
|
A class for generating the code for a specialized attribute setter with
|
|
PutForwards that the JIT can call with lower overhead.
|
|
"""
|
|
def __init__(self, descriptor, attr):
|
|
CGSpecializedSetter.__init__(self, descriptor, attr)
|
|
|
|
def definition_body(self):
|
|
attrName = self.attr.identifier.name
|
|
forwardToAttrName = self.attr.getExtendedAttribute("PutForwards")[0]
|
|
# JS_GetProperty and JS_SetProperty can only deal with ASCII
|
|
assert all(ord(c) < 128 for c in attrName)
|
|
assert all(ord(c) < 128 for c in forwardToAttrName)
|
|
return CGIndenter(CGGeneric("""JS::RootedValue v(cx);
|
|
if (!JS_GetProperty(cx, obj, "%s", v.address())) {
|
|
return false;
|
|
}
|
|
|
|
if (!v.isObject()) {
|
|
return ThrowErrorMessage(cx, MSG_NOT_OBJECT);
|
|
}
|
|
|
|
return JS_SetProperty(cx, &v.toObject(), "%s", argv);""" % (attrName, forwardToAttrName))).define()
|
|
|
|
def memberIsCreator(member):
|
|
return member.getExtendedAttribute("Creator") is not None
|
|
|
|
class CGMemberJITInfo(CGThing):
|
|
"""
|
|
A class for generating the JITInfo for a property that points to
|
|
our specialized getter and setter.
|
|
"""
|
|
def __init__(self, descriptor, member):
|
|
self.member = member
|
|
self.descriptor = descriptor
|
|
|
|
def declare(self):
|
|
return ""
|
|
|
|
def defineJitInfo(self, infoName, opName, opType, infallible, constant,
|
|
pure, returnTypes):
|
|
assert(not constant or pure) # constants are always pure
|
|
protoID = "prototypes::id::%s" % self.descriptor.name
|
|
depth = "PrototypeTraits<%s>::Depth" % protoID
|
|
failstr = toStringBool(infallible)
|
|
conststr = toStringBool(constant)
|
|
purestr = toStringBool(pure)
|
|
returnType = reduce(CGMemberJITInfo.getSingleReturnType, returnTypes,
|
|
"")
|
|
return ("\n"
|
|
"static const JSJitInfo %s = {\n"
|
|
" %s,\n"
|
|
" %s,\n"
|
|
" %s,\n"
|
|
" JSJitInfo::%s,\n"
|
|
" %s, /* isInfallible. False in setters. */\n"
|
|
" %s, /* isConstant. Only relevant for getters. */\n"
|
|
" %s, /* isPure. Only relevant for getters. */\n"
|
|
" %s /* returnType. Only relevant for getters/methods. */\n"
|
|
"};\n" % (infoName, opName, protoID, depth, opType, failstr,
|
|
conststr, purestr, returnType))
|
|
|
|
def define(self):
|
|
if self.member.isAttr():
|
|
getterinfo = ("%s_getterinfo" % self.member.identifier.name)
|
|
getter = ("(JSJitPropertyOp)get_%s" % self.member.identifier.name)
|
|
getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True)
|
|
getterconst = self.member.getExtendedAttribute("Constant")
|
|
getterpure = getterconst or self.member.getExtendedAttribute("Pure")
|
|
assert (getterinfal or (not getterconst and not getterpure))
|
|
|
|
getterinfal = getterinfal and infallibleForMember(self.member, self.member.type, self.descriptor)
|
|
result = self.defineJitInfo(getterinfo, getter, "Getter",
|
|
getterinfal, getterconst, getterpure,
|
|
[self.member.type])
|
|
if not self.member.readonly or self.member.getExtendedAttribute("PutForwards") is not None:
|
|
setterinfo = ("%s_setterinfo" % self.member.identifier.name)
|
|
setter = ("(JSJitPropertyOp)set_%s" % self.member.identifier.name)
|
|
# Setters are always fallible, since they have to do a typed unwrap.
|
|
result += self.defineJitInfo(setterinfo, setter, "Setter",
|
|
False, False, False,
|
|
[BuiltinTypes[IDLBuiltinType.Types.void]])
|
|
return result
|
|
if self.member.isMethod():
|
|
methodinfo = ("%s_methodinfo" % self.member.identifier.name)
|
|
name = CppKeywords.checkMethodName(self.member.identifier.name)
|
|
# Actually a JSJitMethodOp, but JSJitPropertyOp by struct definition.
|
|
method = ("(JSJitPropertyOp)%s" % name)
|
|
|
|
# Methods are infallible if they are infallible, have no arguments
|
|
# to unwrap, and have a return type that's infallible to wrap up for
|
|
# return.
|
|
sigs = self.member.signatures()
|
|
if len(sigs) != 1:
|
|
# Don't handle overloading. If there's more than one signature,
|
|
# one of them must take arguments.
|
|
methodInfal = False
|
|
else:
|
|
sig = sigs[0]
|
|
if (len(sig[1]) != 0 or
|
|
not infallibleForMember(self.member, sig[0], self.descriptor)):
|
|
# We have arguments or our return-value boxing can fail
|
|
methodInfal = False
|
|
else:
|
|
methodInfal = "infallible" in self.descriptor.getExtendedAttributes(self.member)
|
|
|
|
result = self.defineJitInfo(methodinfo, method, "Method",
|
|
methodInfal, False, False,
|
|
[s[0] for s in sigs])
|
|
return result
|
|
raise TypeError("Illegal member type to CGPropertyJITInfo")
|
|
|
|
@staticmethod
|
|
def getJSReturnTypeTag(t):
|
|
if t.nullable():
|
|
# Sometimes it might return null, sometimes not
|
|
return "JSVAL_TYPE_UNKNOWN"
|
|
if t.isVoid():
|
|
# No return, every time
|
|
return "JSVAL_TYPE_UNDEFINED"
|
|
if t.isArray():
|
|
# No idea yet
|
|
assert False
|
|
if t.isSequence():
|
|
return "JSVAL_TYPE_OBJECT"
|
|
if t.isGeckoInterface():
|
|
return "JSVAL_TYPE_OBJECT"
|
|
if t.isString():
|
|
return "JSVAL_TYPE_STRING"
|
|
if t.isEnum():
|
|
return "JSVAL_TYPE_STRING"
|
|
if t.isCallback():
|
|
return "JSVAL_TYPE_OBJECT"
|
|
if t.isAny():
|
|
# The whole point is to return various stuff
|
|
return "JSVAL_TYPE_UNKNOWN"
|
|
if t.isObject():
|
|
return "JSVAL_TYPE_OBJECT"
|
|
if t.isSpiderMonkeyInterface():
|
|
return "JSVAL_TYPE_OBJECT"
|
|
if t.isUnion():
|
|
u = t.unroll();
|
|
if u.hasNullableType:
|
|
# Might be null or not
|
|
return "JSVAL_TYPE_UNKNOWN"
|
|
return reduce(CGMemberJITInfo.getSingleReturnType,
|
|
u.flatMemberTypes, "")
|
|
if t.isDictionary():
|
|
return "JSVAL_TYPE_OBJECT"
|
|
if not t.isPrimitive():
|
|
raise TypeError("No idea what type " + str(t) + " is.")
|
|
tag = t.tag()
|
|
if tag in [IDLType.Tags.int8, IDLType.Tags.uint8,
|
|
IDLType.Tags.int16, IDLType.Tags.uint16,
|
|
IDLType.Tags.int32, IDLType.Tags.bool]:
|
|
return "JSVAL_TYPE_INT32"
|
|
if tag in [IDLType.Tags.int64, IDLType.Tags.uint64,
|
|
IDLType.Tags.unrestricted_float, IDLType.Tags.float,
|
|
IDLType.Tags.unrestricted_double, IDLType.Tags.double]:
|
|
# These all use JS_NumberValue, which can return int or double.
|
|
# But TI treats "double" as meaning "int or double", so we're
|
|
# good to return JSVAL_TYPE_DOUBLE here.
|
|
return "JSVAL_TYPE_DOUBLE"
|
|
if tag != IDLType.Tags.uint32:
|
|
raise TypeError("No idea what type " + str(t) + " is.")
|
|
# uint32 is sometimes int and sometimes double.
|
|
return "JSVAL_TYPE_DOUBLE"
|
|
|
|
@staticmethod
|
|
def getSingleReturnType(existingType, t):
|
|
type = CGMemberJITInfo.getJSReturnTypeTag(t)
|
|
if existingType == "":
|
|
# First element of the list; just return its type
|
|
return type
|
|
|
|
if type == existingType:
|
|
return existingType
|
|
if ((type == "JSVAL_TYPE_DOUBLE" and
|
|
existingType == "JSVAL_TYPE_INT32") or
|
|
(existingType == "JSVAL_TYPE_DOUBLE" and
|
|
type == "JSVAL_TYPE_INT32")):
|
|
# Promote INT32 to DOUBLE as needed
|
|
return "JSVAL_TYPE_DOUBLE"
|
|
# Different types
|
|
return "JSVAL_TYPE_UNKNOWN"
|
|
|
|
def getEnumValueName(value):
|
|
# Some enum values can be empty strings. Others might have weird
|
|
# characters in them. Deal with the former by returning "_empty",
|
|
# deal with possible name collisions from that by throwing if the
|
|
# enum value is actually "_empty", and throw on any value
|
|
# containing non-ASCII chars for now. Replace all chars other than
|
|
# [0-9A-Za-z_] with '_'.
|
|
if re.match("[^\x20-\x7E]", value):
|
|
raise SyntaxError('Enum value "' + value + '" contains non-ASCII characters')
|
|
if re.match("^[0-9]", value):
|
|
raise SyntaxError('Enum value "' + value + '" starts with a digit')
|
|
value = re.sub(r'[^0-9A-Za-z_]', '_', value)
|
|
if re.match("^_[A-Z]|__", value):
|
|
raise SyntaxError('Enum value "' + value + '" is reserved by the C++ spec')
|
|
if value == "_empty":
|
|
raise SyntaxError('"_empty" is not an IDL enum value we support yet')
|
|
if value == "":
|
|
return "_empty"
|
|
return MakeNativeName(value)
|
|
|
|
class CGEnum(CGThing):
|
|
def __init__(self, enum):
|
|
CGThing.__init__(self)
|
|
self.enum = enum
|
|
|
|
def declare(self):
|
|
return """
|
|
enum valuelist {
|
|
%s
|
|
};
|
|
|
|
extern const EnumEntry strings[%d];
|
|
""" % (",\n ".join(map(getEnumValueName, self.enum.values())),
|
|
len(self.enum.values()) + 1)
|
|
|
|
def define(self):
|
|
return """
|
|
const EnumEntry strings[%d] = {
|
|
%s,
|
|
{ NULL, 0 }
|
|
};
|
|
""" % (len(self.enum.values()) + 1,
|
|
",\n ".join(['{"' + val + '", ' + str(len(val)) + '}' for val in self.enum.values()]))
|
|
|
|
def deps(self):
|
|
return self.enum.getDeps()
|
|
|
|
def getUnionAccessorSignatureType(type, descriptorProvider):
|
|
"""
|
|
Returns the types that are used in the getter and setter signatures for
|
|
union types
|
|
"""
|
|
if type.isArray():
|
|
raise TypeError("Can't handle array arguments yet")
|
|
|
|
if type.isSequence():
|
|
nullable = type.nullable();
|
|
if nullable:
|
|
type = type.inner.inner
|
|
else:
|
|
type = type.inner
|
|
(elementTemplate, elementDeclType,
|
|
elementHolderType, dealWithOptional) = getJSToNativeConversionTemplate(
|
|
type, descriptorProvider, isSequenceMember=True)
|
|
typeName = CGTemplatedType("Sequence", elementDeclType, isReference=True)
|
|
if nullable:
|
|
typeName = CGTemplatedType("Nullable", typeName, isReference=True)
|
|
|
|
return typeName
|
|
|
|
if type.isUnion():
|
|
typeName = CGGeneric(type.name)
|
|
if type.nullable():
|
|
typeName = CGTemplatedType("Nullable", typeName, isReference=True)
|
|
|
|
return typeName
|
|
|
|
if type.isGeckoInterface():
|
|
descriptor = descriptorProvider.getDescriptor(
|
|
type.unroll().inner.identifier.name)
|
|
typeName = CGGeneric(descriptor.nativeType)
|
|
# Allow null pointers for nullable types and old-binding classes
|
|
if type.nullable() or type.unroll().inner.isExternal():
|
|
typeName = CGWrapper(typeName, post="*")
|
|
else:
|
|
typeName = CGWrapper(typeName, post="&")
|
|
return typeName
|
|
|
|
if type.isSpiderMonkeyInterface():
|
|
typeName = CGGeneric(type.name)
|
|
if type.nullable():
|
|
typeName = CGWrapper(typeName, post="*")
|
|
else:
|
|
typeName = CGWrapper(typeName, post="&")
|
|
return typeName
|
|
|
|
if type.isString():
|
|
return CGGeneric("const nsAString&")
|
|
|
|
if type.isEnum():
|
|
if type.nullable():
|
|
raise TypeError("We don't support nullable enumerated arguments or "
|
|
"union members yet")
|
|
return CGGeneric(type.inner.identifier.name)
|
|
|
|
if type.isCallback():
|
|
if descriptorProvider.workers:
|
|
if type.nullable():
|
|
return CGGeneric("JSObject*")
|
|
return CGGeneric("JSObject&")
|
|
if type.nullable():
|
|
typeName = "%s*"
|
|
else:
|
|
typeName = "%s&"
|
|
return CGGeneric(typeName % type.unroll().identifier.name)
|
|
|
|
if type.isAny():
|
|
return CGGeneric("JS::Value")
|
|
|
|
if type.isObject():
|
|
typeName = CGGeneric("JSObject")
|
|
if type.nullable():
|
|
typeName = CGWrapper(typeName, post="*")
|
|
else:
|
|
typeName = CGWrapper(typeName, post="&")
|
|
return typeName
|
|
|
|
if not type.isPrimitive():
|
|
raise TypeError("Need native type for argument type '%s'" % str(type))
|
|
|
|
typeName = CGGeneric(builtinNames[type.tag()])
|
|
if type.nullable():
|
|
typeName = CGTemplatedType("Nullable", typeName, isReference=True)
|
|
return typeName
|
|
|
|
def getUnionTypeTemplateVars(type, descriptorProvider):
|
|
# For dictionaries and sequences we need to pass None as the failureCode
|
|
# for getJSToNativeConversionTemplate.
|
|
# Also, for dictionaries we would need to handle conversion of
|
|
# null/undefined to the dictionary correctly.
|
|
if type.isDictionary() or type.isSequence():
|
|
raise TypeError("Can't handle dictionaries or sequences in unions")
|
|
|
|
if type.isGeckoInterface():
|
|
name = type.inner.identifier.name
|
|
elif type.isEnum():
|
|
name = type.inner.identifier.name
|
|
elif type.isArray() or type.isSequence():
|
|
name = str(type)
|
|
else:
|
|
name = type.name
|
|
|
|
tryNextCode = """tryNext = true;
|
|
return true;"""
|
|
if type.isGeckoInterface():
|
|
tryNextCode = ("""if (mUnion.mType != mUnion.eUninitialized) {
|
|
mUnion.Destroy%s();
|
|
}""" % name) + tryNextCode
|
|
(template, declType, holderType,
|
|
dealWithOptional) = getJSToNativeConversionTemplate(
|
|
type, descriptorProvider, failureCode=tryNextCode,
|
|
isDefinitelyObject=True)
|
|
|
|
# This is ugly, but UnionMember needs to call a constructor with no
|
|
# arguments so the type can't be const.
|
|
structType = declType.define()
|
|
if structType.startswith("const "):
|
|
structType = structType[6:]
|
|
externalType = getUnionAccessorSignatureType(type, descriptorProvider).define()
|
|
|
|
if type.isObject():
|
|
setter = CGGeneric("void SetToObject(JSObject* obj)\n"
|
|
"{\n"
|
|
" mUnion.mValue.mObject.SetValue() = obj;\n"
|
|
" mUnion.mType = mUnion.eObject;\n"
|
|
"}")
|
|
else:
|
|
jsConversion = string.Template(template).substitute(
|
|
{
|
|
"val": "value",
|
|
"valPtr": "pvalue",
|
|
"declName": "SetAs" + name + "()",
|
|
"holderName": "m" + name + "Holder",
|
|
}
|
|
)
|
|
jsConversion = CGWrapper(CGGeneric(jsConversion),
|
|
post="\n"
|
|
"return true;")
|
|
setter = CGWrapper(CGIndenter(jsConversion),
|
|
pre="bool TrySetTo" + name + "(JSContext* cx, const JS::Value& value, JS::Value* pvalue, bool& tryNext)\n"
|
|
"{\n"
|
|
" tryNext = false;\n",
|
|
post="\n"
|
|
"}")
|
|
|
|
return {
|
|
"name": name,
|
|
"structType": structType,
|
|
"externalType": externalType,
|
|
"setter": CGIndenter(setter).define(),
|
|
"holderType": holderType.define() if holderType else None
|
|
}
|
|
|
|
def mapTemplate(template, templateVarArray):
|
|
return map(lambda v: string.Template(template).substitute(v),
|
|
templateVarArray)
|
|
|
|
class CGUnionStruct(CGThing):
|
|
def __init__(self, type, descriptorProvider):
|
|
CGThing.__init__(self)
|
|
self.type = type.unroll()
|
|
self.descriptorProvider = descriptorProvider
|
|
self.templateVars = map(
|
|
lambda t: getUnionTypeTemplateVars(t, self.descriptorProvider),
|
|
self.type.flatMemberTypes)
|
|
|
|
|
|
def declare(self):
|
|
templateVars = self.templateVars
|
|
|
|
callDestructors = []
|
|
enumValues = []
|
|
methods = []
|
|
if self.type.hasNullableType:
|
|
callDestructors.append(" case eNull:\n"
|
|
" break;")
|
|
enumValues.append("eNull")
|
|
methods.append(""" bool IsNull() const
|
|
{
|
|
return mType == eNull;
|
|
}""")
|
|
|
|
destructorTemplate = """ void Destroy${name}()
|
|
{
|
|
MOZ_ASSERT(Is${name}(), "Wrong type!");
|
|
mValue.m${name}.Destroy();
|
|
mType = eUninitialized;
|
|
}"""
|
|
destructors = mapTemplate(destructorTemplate, templateVars)
|
|
callDestructors.extend(mapTemplate(" case e${name}:\n"
|
|
" Destroy${name}();\n"
|
|
" break;", templateVars))
|
|
enumValues.extend(mapTemplate("e${name}", templateVars))
|
|
methodTemplate = """ bool Is${name}() const
|
|
{
|
|
return mType == e${name};
|
|
}
|
|
${externalType} GetAs${name}() const
|
|
{
|
|
MOZ_ASSERT(Is${name}(), "Wrong type!");
|
|
return const_cast<${structType}&>(mValue.m${name}.Value());
|
|
}
|
|
${structType}& SetAs${name}()
|
|
{
|
|
mType = e${name};
|
|
return mValue.m${name}.SetValue();
|
|
}"""
|
|
methods.extend(mapTemplate(methodTemplate, templateVars))
|
|
values = mapTemplate("UnionMember<${structType} > m${name};", templateVars)
|
|
return string.Template("""
|
|
class ${structName} {
|
|
public:
|
|
${structName}() : mType(eUninitialized)
|
|
{
|
|
}
|
|
~${structName}()
|
|
{
|
|
switch (mType) {
|
|
${callDestructors}
|
|
case eUninitialized:
|
|
break;
|
|
}
|
|
}
|
|
|
|
${methods}
|
|
|
|
bool ToJSVal(JSContext* cx, JS::Handle<JSObject*> scopeObj, JS::Value* vp) const;
|
|
|
|
private:
|
|
friend class ${structName}Argument;
|
|
|
|
${destructors}
|
|
|
|
enum Type {
|
|
eUninitialized,
|
|
${enumValues}
|
|
};
|
|
union Value {
|
|
${values}
|
|
};
|
|
|
|
Type mType;
|
|
Value mValue;
|
|
};
|
|
|
|
""").substitute(
|
|
{
|
|
"structName": self.type.__str__(),
|
|
"callDestructors": "\n".join(callDestructors),
|
|
"destructors": "\n".join(destructors),
|
|
"methods": "\n\n".join(methods),
|
|
"enumValues": ",\n ".join(enumValues),
|
|
"values": "\n ".join(values)
|
|
})
|
|
|
|
def define(self):
|
|
templateVars = self.templateVars
|
|
conversionsToJS = []
|
|
if self.type.hasNullableType:
|
|
conversionsToJS.append(" case eNull:\n"
|
|
" {\n"
|
|
" *vp = JS::NullValue();\n"
|
|
" return true;\n"
|
|
" }")
|
|
conversionsToJS.extend(
|
|
map(self.getConversionToJS,
|
|
zip(templateVars, self.type.flatMemberTypes)))
|
|
|
|
return string.Template("""bool
|
|
${structName}::ToJSVal(JSContext* cx, JS::Handle<JSObject*> scopeObj, JS::Value* vp) const
|
|
{
|
|
switch (mType) {
|
|
${doConversionsToJS}
|
|
|
|
case eUninitialized:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
""").substitute({
|
|
"structName": str(self.type),
|
|
"doConversionsToJS": "\n\n".join(conversionsToJS)
|
|
})
|
|
|
|
def getConversionToJS(self, arg):
|
|
(templateVars, type) = arg
|
|
assert not type.nullable() # flatMemberTypes never has nullable types
|
|
val = "mValue.m%(name)s.Value()" % templateVars
|
|
if type.isObject():
|
|
# We'll have a NonNull<JSObject> while the wrapping code
|
|
# wants a JSObject*
|
|
val = "%s.get()" % val
|
|
elif type.isSpiderMonkeyInterface():
|
|
# We have a NonNull<TypedArray> object while the wrapping code
|
|
# wants a JSObject*. Cheat with .get() so we don't have to
|
|
# figure out the right reference type to cast to.
|
|
val = "%s.get()->Obj()" % val
|
|
wrapCode = wrapForType(
|
|
type, self.descriptorProvider,
|
|
{
|
|
"jsvalRef": "*vp",
|
|
"jsvalPtr": "vp",
|
|
"obj": "scopeObj",
|
|
"result": val,
|
|
"objectCanBeNonNull": True
|
|
})
|
|
return CGIndenter(CGList([CGGeneric("case e%(name)s:" % templateVars),
|
|
CGWrapper(CGIndenter(CGGeneric(wrapCode)),
|
|
pre="{\n",
|
|
post="\n}")],
|
|
"\n"),
|
|
4).define()
|
|
|
|
class CGUnionConversionStruct(CGThing):
|
|
def __init__(self, type, descriptorProvider):
|
|
CGThing.__init__(self)
|
|
self.type = type.unroll()
|
|
self.descriptorProvider = descriptorProvider
|
|
|
|
def declare(self):
|
|
setters = []
|
|
|
|
if self.type.hasNullableType:
|
|
setters.append(""" bool SetNull()
|
|
{
|
|
mUnion.mType = mUnion.eNull;
|
|
return true;
|
|
}""")
|
|
|
|
templateVars = map(lambda t: getUnionTypeTemplateVars(t, self.descriptorProvider),
|
|
self.type.flatMemberTypes)
|
|
structName = self.type.__str__()
|
|
|
|
setters.extend(mapTemplate("${setter}", templateVars))
|
|
private = "\n".join(mapTemplate(""" ${structType}& SetAs${name}()
|
|
{
|
|
mUnion.mType = mUnion.e${name};
|
|
return mUnion.mValue.m${name}.SetValue();
|
|
}""", templateVars))
|
|
private += "\n\n"
|
|
holders = filter(lambda v: v["holderType"] is not None, templateVars)
|
|
if len(holders) > 0:
|
|
private += "\n".join(mapTemplate(" ${holderType} m${name}Holder;", holders))
|
|
private += "\n\n"
|
|
private += " " + structName + "& mUnion;"
|
|
return string.Template("""
|
|
class ${structName}Argument {
|
|
public:
|
|
// Argument needs to be a const ref because that's all Maybe<> allows
|
|
${structName}Argument(const ${structName}& aUnion) : mUnion(const_cast<${structName}&>(aUnion))
|
|
{
|
|
}
|
|
|
|
${setters}
|
|
|
|
private:
|
|
${private}
|
|
};
|
|
""").substitute({"structName": structName,
|
|
"setters": "\n\n".join(setters),
|
|
"private": private
|
|
})
|
|
|
|
def define(self):
|
|
return """
|
|
"""
|
|
|
|
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='public'):
|
|
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 = inline or bodyInHeader
|
|
self.static = static
|
|
self.virtual = virtual
|
|
self.const = const
|
|
self.bodyInHeader = bodyInHeader
|
|
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 = 'template <%s>\n' % ', '.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{\n' + body + '\n}'
|
|
else:
|
|
body = ';'
|
|
|
|
return string.Template("${templateClause}${decorators}${returnType}%s"
|
|
"${name}(${args})${const}${override}${body}%s" %
|
|
(self.breakAfterReturnDecl, self.breakAfterSelf)
|
|
).substitute({
|
|
'templateClause': templateClause,
|
|
'decorators': self.getDecorators(True),
|
|
'returnType': self.returnType,
|
|
'name': self.name,
|
|
'const': ' const' if self.const else '',
|
|
'override': ' MOZ_OVERRIDE' if self.override else '',
|
|
'args': args,
|
|
'body': body
|
|
})
|
|
|
|
def define(self, cgClass):
|
|
if self.bodyInHeader:
|
|
return ''
|
|
|
|
templateArgs = cgClass.templateArgs
|
|
if templateArgs:
|
|
if cgClass.templateSpecialization:
|
|
templateArgs = \
|
|
templateArgs[len(cgClass.templateSpecialization):]
|
|
|
|
if templateArgs:
|
|
templateClause = \
|
|
'template <%s>\n' % ', '.join([str(a) for a in templateArgs])
|
|
else:
|
|
templateClause = ''
|
|
|
|
args = ', '.join([a.define() for a in self.args])
|
|
|
|
body = CGIndenter(CGGeneric(self.getBody())).define()
|
|
|
|
return string.Template("""${templateClause}${decorators}${returnType}
|
|
${className}::${name}(${args})${const}
|
|
{
|
|
${body}
|
|
}\n
|
|
""").substitute({ 'templateClause': templateClause,
|
|
'decorators': self.getDecorators(False),
|
|
'returnType': self.returnType,
|
|
'className': cgClass.getNameString(),
|
|
'name': self.name,
|
|
'args': args,
|
|
'const': ' const' if self.const else '',
|
|
'body': body })
|
|
|
|
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="private", explicit=False, baseConstructors=None,
|
|
body=""):
|
|
self.args = args
|
|
self.inline = inline or bodyInHeader
|
|
self.bodyInHeader = bodyInHeader
|
|
self.explicit = explicit
|
|
self.baseConstructors = baseConstructors
|
|
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.getBody()
|
|
if initialize:
|
|
items.append(m.name + "(" + initialize + ")")
|
|
|
|
if len(items) > 0:
|
|
return '\n : ' + ',\n '.join(items)
|
|
return ''
|
|
|
|
def getBody(self):
|
|
return self.body
|
|
|
|
def declare(self, cgClass):
|
|
args = ', '.join([a.declare() for a in self.args])
|
|
if self.bodyInHeader:
|
|
body = ' ' + self.getBody();
|
|
body = stripTrailingWhitespace(body.replace('\n', '\n '))
|
|
if len(body) > 0:
|
|
body += '\n'
|
|
body = self.getInitializationList(cgClass) + '\n{\n' + body + '}'
|
|
else:
|
|
body = ';'
|
|
|
|
return string.Template("""${decorators}${className}(${args})${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}}\n
|
|
""").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}}\n
|
|
""").substitute({ 'decorators': self.getDecorators(False),
|
|
'className': cgClass.getNameString(),
|
|
'body': body })
|
|
|
|
class ClassMember(ClassItem):
|
|
def __init__(self, name, type, visibility="private", 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' % ('static ' if self.static else '', self.type,
|
|
self.name)
|
|
|
|
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 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 CGClass(CGThing):
|
|
def __init__(self, name, bases=[], members=[], constructors=[],
|
|
destructor=None, methods=[],
|
|
typedefs = [], enums=[], templateArgs=[],
|
|
templateSpecialization=[], isStruct=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.templateArgs = templateArgs
|
|
self.templateSpecialization = templateSpecialization
|
|
self.isStruct = isStruct
|
|
self.indent = indent
|
|
self.defaultVisibility ='public' if isStruct else 'private'
|
|
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 declare(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])
|
|
|
|
type = 'struct' if self.isStruct else 'class'
|
|
|
|
if self.templateSpecialization:
|
|
specialization = \
|
|
'<%s>' % ', '.join([str(a) for a in self.templateSpecialization])
|
|
else:
|
|
specialization = ''
|
|
|
|
myself = '%s%s %s%s' % (self.indent, type, self.name, specialization)
|
|
if self.decorators != '':
|
|
myself += " " + self.decorators
|
|
result += myself
|
|
|
|
if self.bases:
|
|
inherit = ' : '
|
|
result += inherit
|
|
# Grab our first base
|
|
baseItems = [CGGeneric(b.declare(self)) for b in self.bases]
|
|
bases = baseItems[:1]
|
|
# Indent the rest
|
|
bases.extend(CGIndenter(b, len(myself) + len(inherit)) for
|
|
b in baseItems[1:])
|
|
result += ",\n".join(b.define() for b in bases)
|
|
|
|
result = result + '\n%s{\n' % self.indent
|
|
|
|
result += CGIndenter(CGGeneric(self.extradeclarations),
|
|
len(self.indent)).define()
|
|
|
|
def declareMembers(cgClass, memberList, defaultVisibility, itemCount,
|
|
separator=''):
|
|
members = { 'private': [], 'protected': [], 'public': [] }
|
|
|
|
for member in memberList:
|
|
members[member.visibility].append(member)
|
|
|
|
|
|
if defaultVisibility == 'public':
|
|
order = [ 'public', 'protected', 'private' ]
|
|
else:
|
|
order = [ 'private', 'protected', 'public' ]
|
|
|
|
result = ''
|
|
|
|
lastVisibility = defaultVisibility
|
|
for visibility in order:
|
|
list = members[visibility]
|
|
if list:
|
|
if visibility != lastVisibility:
|
|
if itemCount:
|
|
result = result + '\n'
|
|
result = result + visibility + ':\n'
|
|
itemCount = 0
|
|
for member in list:
|
|
if itemCount != 0:
|
|
result = result + separator
|
|
declaration = member.declare(cgClass)
|
|
declaration = CGIndenter(CGGeneric(declaration)).define()
|
|
result = result + declaration
|
|
itemCount = itemCount + 1
|
|
lastVisibility = visibility
|
|
return (result, lastVisibility, itemCount)
|
|
|
|
order = [(self.enums, ''), (self.typedefs, ''), (self.members, ''),
|
|
(self.constructors, '\n'), (self.destructors, '\n'),
|
|
(self.methods, '\n')]
|
|
|
|
lastVisibility = self.defaultVisibility
|
|
itemCount = 0
|
|
for (memberList, separator) in order:
|
|
(memberString, lastVisibility, itemCount) = \
|
|
declareMembers(self, memberList, lastVisibility, itemCount,
|
|
separator)
|
|
if self.indent:
|
|
memberString = CGIndenter(CGGeneric(memberString),
|
|
len(self.indent)).define()
|
|
result = result + memberString
|
|
|
|
result = result + self.indent + '};\n'
|
|
return result
|
|
|
|
def define(self):
|
|
def defineMembers(cgClass, memberList, itemCount, separator=''):
|
|
result = ''
|
|
for member in memberList:
|
|
if itemCount != 0:
|
|
result = result + separator
|
|
result = result + member.define(cgClass)
|
|
itemCount = itemCount + 1
|
|
return (result, itemCount)
|
|
|
|
order = [(self.members, '\n'), (self.constructors, '\n'),
|
|
(self.destructors, '\n'), (self.methods, '\n')]
|
|
|
|
result = self.extradefinitions
|
|
itemCount = 0
|
|
for (memberList, separator) in order:
|
|
(memberString, itemCount) = defineMembers(self, memberList,
|
|
itemCount, separator)
|
|
result = result + memberString
|
|
return result
|
|
|
|
class CGResolveOwnProperty(CGAbstractMethod):
|
|
def __init__(self, descriptor):
|
|
args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper_'),
|
|
Argument('JSObject*', 'obj'), Argument('jsid', 'id_'),
|
|
Argument('JSPropertyDescriptor*', 'desc'), Argument('unsigned', 'flags'),
|
|
]
|
|
CGAbstractMethod.__init__(self, descriptor, "ResolveOwnProperty", "bool", args)
|
|
def definition_body(self):
|
|
return """ // We rely on getOwnPropertyDescriptor not shadowing prototype properties by named
|
|
// properties. If that changes we'll need to filter here.
|
|
js::RootedObject wrapper(cx, wrapper_);
|
|
js::RootedId id(cx, id_);
|
|
return js::GetProxyHandler(obj)->getOwnPropertyDescriptor(cx, wrapper, id, desc, flags);
|
|
"""
|
|
|
|
class CGEnumerateOwnProperties(CGAbstractMethod):
|
|
def __init__(self, descriptor):
|
|
args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper_'),
|
|
Argument('JSObject*', 'obj'),
|
|
Argument('JS::AutoIdVector&', 'props')]
|
|
CGAbstractMethod.__init__(self, descriptor, "EnumerateOwnProperties", "bool", args)
|
|
def definition_body(self):
|
|
return """ // We rely on getOwnPropertyNames not shadowing prototype properties by named
|
|
// properties. If that changes we'll need to filter here.
|
|
JS::Rooted<JSObject*> wrapper(cx, wrapper_);
|
|
return js::GetProxyHandler(obj)->getOwnPropertyNames(cx, wrapper, props);
|
|
"""
|
|
|
|
class CGPrototypeTraitsClass(CGClass):
|
|
def __init__(self, descriptor, indent=''):
|
|
templateArgs = [Argument('prototypes::ID', 'PrototypeID')]
|
|
templateSpecialization = ['prototypes::id::' + descriptor.name]
|
|
enums = [ClassEnum('', ['Depth'],
|
|
[descriptor.interface.inheritanceDepth()])]
|
|
typedefs = [ClassTypedef('NativeType', descriptor.nativeType)]
|
|
CGClass.__init__(self, 'PrototypeTraits', indent=indent,
|
|
templateArgs=templateArgs,
|
|
templateSpecialization=templateSpecialization,
|
|
enums=enums, typedefs=typedefs, isStruct=True)
|
|
def deps(self):
|
|
return set()
|
|
|
|
class CGPrototypeIDMapClass(CGClass):
|
|
def __init__(self, descriptor, indent=''):
|
|
templateArgs = [Argument('class', 'ConcreteClass')]
|
|
templateSpecialization = [descriptor.nativeType]
|
|
enums = [ClassEnum('', ['PrototypeID'],
|
|
['prototypes::id::' + descriptor.name])]
|
|
CGClass.__init__(self, 'PrototypeIDMap', indent=indent,
|
|
templateArgs=templateArgs,
|
|
templateSpecialization=templateSpecialization,
|
|
enums=enums, isStruct=True)
|
|
def deps(self):
|
|
return set()
|
|
|
|
class CGClassForwardDeclare(CGThing):
|
|
def __init__(self, name, isStruct=False):
|
|
CGThing.__init__(self)
|
|
self.name = name
|
|
self.isStruct = isStruct
|
|
def declare(self):
|
|
type = 'struct' if self.isStruct else 'class'
|
|
return '%s %s;\n' % (type, self.name)
|
|
def define(self):
|
|
# Header only
|
|
return ''
|
|
def deps(self):
|
|
return set()
|
|
|
|
class CGProxySpecialOperation(CGPerSignatureCall):
|
|
"""
|
|
Base class for classes for calling an indexed or named special operation
|
|
(don't use this directly, use the derived classes below).
|
|
"""
|
|
def __init__(self, descriptor, operation):
|
|
nativeName = MakeNativeName(descriptor.binaryNames.get(operation, operation))
|
|
operation = descriptor.operations[operation]
|
|
assert len(operation.signatures()) == 1
|
|
signature = operation.signatures()[0]
|
|
|
|
(returnType, arguments) = signature
|
|
|
|
# We pass len(arguments) as the final argument so that the
|
|
# CGPerSignatureCall won't do any argument conversion of its own.
|
|
CGPerSignatureCall.__init__(self, returnType, arguments, nativeName,
|
|
False, descriptor, operation,
|
|
len(arguments))
|
|
|
|
if operation.isSetter() or operation.isCreator():
|
|
# arguments[0] is the index or name of the item that we're setting.
|
|
argument = arguments[1]
|
|
template = getJSToNativeConversionTemplate(argument.type, descriptor,
|
|
treatNullAs=argument.treatNullAs,
|
|
treatUndefinedAs=argument.treatUndefinedAs)
|
|
templateValues = {
|
|
"declName": argument.identifier.name,
|
|
"holderName": argument.identifier.name + "_holder",
|
|
"val": "desc->value",
|
|
"valPtr": "&desc->value",
|
|
"obj": "obj"
|
|
}
|
|
self.cgRoot.prepend(instantiateJSToNativeConversionTemplate(template, templateValues))
|
|
elif operation.isGetter() or operation.isDeleter():
|
|
self.cgRoot.prepend(CGGeneric("bool found;"))
|
|
|
|
def getArguments(self):
|
|
args = [(a, a.identifier.name) for a in self.arguments]
|
|
if self.idlNode.isGetter() or self.idlNode.isDeleter():
|
|
args.append((FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean],
|
|
self.idlNode),
|
|
"found"))
|
|
return args
|
|
|
|
def wrap_return_value(self):
|
|
if not self.idlNode.isGetter() or self.templateValues is None:
|
|
return ""
|
|
|
|
wrap = CGGeneric(wrapForType(self.returnType, self.descriptor, self.templateValues))
|
|
wrap = CGIfWrapper(wrap, "found")
|
|
return "\n" + wrap.define()
|
|
|
|
class CGProxyIndexedOperation(CGProxySpecialOperation):
|
|
"""
|
|
Class to generate a call to an indexed operation.
|
|
"""
|
|
def __init__(self, descriptor, name):
|
|
CGProxySpecialOperation.__init__(self, descriptor, name)
|
|
def define(self):
|
|
# Our first argument is the id we're getting.
|
|
argName = self.arguments[0].identifier.name
|
|
if argName == "index":
|
|
# We already have our index in a variable with that name
|
|
setIndex = ""
|
|
else:
|
|
setIndex = "uint32_t %s = index;\n" % argName
|
|
return (setIndex +
|
|
"%s* self = UnwrapProxy(proxy);\n" +
|
|
CGProxySpecialOperation.define(self))
|
|
|
|
class CGProxyIndexedGetter(CGProxyIndexedOperation):
|
|
"""
|
|
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
|
|
CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedGetter')
|
|
|
|
class CGProxyIndexedPresenceChecker(CGProxyIndexedGetter):
|
|
"""
|
|
Class to generate a call that checks whether an indexed property exists.
|
|
|
|
For now, we just delegate to CGProxyIndexedGetter
|
|
"""
|
|
def __init__(self, descriptor):
|
|
CGProxyIndexedGetter.__init__(self, descriptor)
|
|
self.cgRoot.append(CGGeneric("(void)result;"))
|
|
|
|
class CGProxyIndexedSetter(CGProxyIndexedOperation):
|
|
"""
|
|
Class to generate a call to an indexed setter.
|
|
"""
|
|
def __init__(self, descriptor):
|
|
CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedSetter')
|
|
|
|
class CGProxyNamedOperation(CGProxySpecialOperation):
|
|
"""
|
|
Class to generate a call to a named operation.
|
|
|
|
'value' is the jsval to use for the name; None indicates that it should be
|
|
gotten from the property id.
|
|
"""
|
|
def __init__(self, descriptor, name, value=None):
|
|
CGProxySpecialOperation.__init__(self, descriptor, name)
|
|
if value is None:
|
|
value = "js::IdToValue(id)"
|
|
self.value = value
|
|
def define(self):
|
|
# Our first argument is the id we're getting.
|
|
argName = self.arguments[0].identifier.name
|
|
return (("JS::Value nameVal = %s;\n"
|
|
"FakeDependentString %s;\n"
|
|
"if (!ConvertJSValueToString(cx, nameVal, &nameVal,\n"
|
|
" eStringify, eStringify, %s)) {\n"
|
|
" return false;\n"
|
|
"}\n"
|
|
"\n"
|
|
"%s* self = UnwrapProxy(proxy);\n" %
|
|
(self.value, argName, argName, self.descriptor.nativeType)) +
|
|
CGProxySpecialOperation.define(self))
|
|
|
|
class CGProxyNamedGetter(CGProxyNamedOperation):
|
|
"""
|
|
Class to generate a call to an named getter. If templateValues is not None
|
|
the returned value will be wrapped with wrapForType using templateValues.
|
|
'value' is the jsval to use for the name; None indicates that it should be
|
|
gotten from the property id.
|
|
"""
|
|
def __init__(self, descriptor, templateValues=None, value=None):
|
|
self.templateValues = templateValues
|
|
CGProxyNamedOperation.__init__(self, descriptor, 'NamedGetter', value)
|
|
|
|
class CGProxyNamedPresenceChecker(CGProxyNamedGetter):
|
|
"""
|
|
Class to generate a call that checks whether a named property exists.
|
|
|
|
For now, we just delegate to CGProxyNamedGetter
|
|
"""
|
|
def __init__(self, descriptor):
|
|
CGProxyNamedGetter.__init__(self, descriptor)
|
|
self.cgRoot.append(CGGeneric("(void)result;"))
|
|
|
|
class CGProxyNamedSetter(CGProxyNamedOperation):
|
|
"""
|
|
Class to generate a call to a named setter.
|
|
"""
|
|
def __init__(self, descriptor):
|
|
CGProxyNamedOperation.__init__(self, descriptor, 'NamedSetter')
|
|
|
|
class CGProxyIndexedDeleter(CGProxyIndexedOperation):
|
|
"""
|
|
Class to generate a call to an indexed deleter.
|
|
"""
|
|
def __init__(self, descriptor):
|
|
CGProxyIndexedOperation.__init__(self, descriptor, 'IndexedDeleter')
|
|
|
|
class CGProxyNamedDeleter(CGProxyNamedOperation):
|
|
"""
|
|
Class to generate a call to a named deleter.
|
|
"""
|
|
def __init__(self, descriptor):
|
|
CGProxyNamedOperation.__init__(self, descriptor, 'NamedDeleter')
|
|
|
|
class CGProxyIsProxy(CGAbstractMethod):
|
|
def __init__(self, descriptor):
|
|
args = [Argument('JSObject*', 'obj')]
|
|
CGAbstractMethod.__init__(self, descriptor, "IsProxy", "bool", args, alwaysInline=True)
|
|
def declare(self):
|
|
return ""
|
|
def definition_body(self):
|
|
return " return js::IsProxy(obj) && js::GetProxyHandler(obj) == DOMProxyHandler::getInstance();"
|
|
|
|
class CGProxyUnwrap(CGAbstractMethod):
|
|
def __init__(self, descriptor):
|
|
args = [Argument('JSObject*', 'obj')]
|
|
CGAbstractMethod.__init__(self, descriptor, "UnwrapProxy", descriptor.nativeType + '*', args, alwaysInline=True)
|
|
def declare(self):
|
|
return ""
|
|
def definition_body(self):
|
|
return """ MOZ_ASSERT(js::IsProxy(obj));
|
|
if (js::GetProxyHandler(obj) != DOMProxyHandler::getInstance()) {
|
|
MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(obj));
|
|
obj = js::UncheckedUnwrap(obj);
|
|
}
|
|
MOZ_ASSERT(IsProxy(obj));
|
|
return static_cast<%s*>(js::GetProxyPrivate(obj).toPrivate());""" % (self.descriptor.nativeType)
|
|
|
|
class CGDOMJSProxyHandlerDOMClass(CGThing):
|
|
def __init__(self, descriptor):
|
|
CGThing.__init__(self)
|
|
self.descriptor = descriptor
|
|
def declare(self):
|
|
return "extern const DOMClass Class;\n"
|
|
def define(self):
|
|
return """
|
|
const DOMClass Class = """ + DOMClass(self.descriptor) + """;
|
|
|
|
"""
|
|
|
|
class CGDOMJSProxyHandler_CGDOMJSProxyHandler(ClassConstructor):
|
|
def __init__(self):
|
|
ClassConstructor.__init__(self, [], inline=True, visibility="private",
|
|
baseConstructors=["mozilla::dom::DOMProxyHandler(Class)"])
|
|
|
|
class CGDOMJSProxyHandler_getOwnPropertyDescriptor(ClassMethod):
|
|
def __init__(self, descriptor):
|
|
args = [Argument('JSContext*', 'cx'), Argument('JS::Handle<JSObject*>', 'proxy'),
|
|
Argument('JS::Handle<jsid>', 'id'),
|
|
Argument('JSPropertyDescriptor*', 'desc'), Argument('unsigned', 'flags')]
|
|
ClassMethod.__init__(self, "getOwnPropertyDescriptor", "bool", args)
|
|
self.descriptor = descriptor
|
|
def getBody(self):
|
|
indexedGetter = self.descriptor.operations['IndexedGetter']
|
|
indexedSetter = self.descriptor.operations['IndexedSetter']
|
|
|
|
setOrIndexedGet = "bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy);\n"
|
|
if self.descriptor.supportsIndexedProperties():
|
|
setOrIndexedGet += "int32_t index = GetArrayIndexFromId(cx, id);\n"
|
|
readonly = toStringBool(indexedSetter is None)
|
|
fillDescriptor = "FillPropertyDescriptor(desc, proxy, %s);\nreturn true;" % readonly
|
|
templateValues = {'jsvalRef': 'desc->value', 'jsvalPtr': '&desc->value',
|
|
'obj': 'proxy', 'successCode': fillDescriptor}
|
|
get = ("if (IsArrayIndex(index)) {\n" +
|
|
CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define() + "\n" +
|
|
"}\n") % (self.descriptor.nativeType)
|
|
|
|
if UseHolderForUnforgeable(self.descriptor):
|
|
getUnforgeable = """if (!JS_GetPropertyDescriptorById(cx, ${holder}, id, flags, desc)) {
|
|
return false;
|
|
}
|
|
MOZ_ASSERT_IF(desc->obj, desc->obj == ${holder});"""
|
|
getUnforgeable = CallOnUnforgeableHolder(self.descriptor,
|
|
getUnforgeable, "isXray")
|
|
getUnforgeable += """if (desc->obj) {
|
|
desc->obj = proxy;
|
|
return !isXray || JS_WrapPropertyDescriptor(cx, desc);
|
|
}
|
|
|
|
"""
|
|
else:
|
|
getUnforgeable = ""
|
|
|
|
if indexedSetter or self.descriptor.operations['NamedSetter']:
|
|
setOrIndexedGet += "if (flags & JSRESOLVE_ASSIGNING) {\n"
|
|
if indexedSetter:
|
|
setOrIndexedGet += (" if (IsArrayIndex(index)) {\n")
|
|
if not 'IndexedCreator' in self.descriptor.operations:
|
|
# FIXME need to check that this is a 'supported property
|
|
# index'. But if that happens, watch out for the assumption
|
|
# below that the name setter always returns for
|
|
# IsArrayIndex(index).
|
|
assert False
|
|
setOrIndexedGet += (" FillPropertyDescriptor(desc, proxy, JSVAL_VOID, false);\n" +
|
|
" return true;\n" +
|
|
" }\n")
|
|
setOrIndexedGet += CGIndenter(CGGeneric(getUnforgeable)).define()
|
|
if self.descriptor.operations['NamedSetter']:
|
|
if not 'NamedCreator' in self.descriptor.operations:
|
|
# FIXME need to check that this is a 'supported property name'
|
|
assert False
|
|
create = CGGeneric("FillPropertyDescriptor(desc, proxy, JSVAL_VOID, false);\n"
|
|
"return true;")
|
|
# If we have an indexed setter we've already returned
|
|
if (self.descriptor.supportsIndexedProperties() and
|
|
not indexedSetter):
|
|
create = CGIfWrapper(create, "!IsArrayIndex(index)")
|
|
setOrIndexedGet += CGIndenter(create).define() + "\n"
|
|
setOrIndexedGet += "}"
|
|
if indexedGetter:
|
|
setOrIndexedGet += (" else {\n" +
|
|
CGIndenter(CGGeneric(get + "\n" + getUnforgeable)).define() +
|
|
"}")
|
|
else:
|
|
setOrIndexedGet += (" else {\n" +
|
|
CGIndenter(CGGeneric(getUnforgeable)).define() +
|
|
"}")
|
|
setOrIndexedGet += "\n\n"
|
|
else:
|
|
if indexedGetter:
|
|
setOrIndexedGet += ("if (!(flags & JSRESOLVE_ASSIGNING)) {\n" +
|
|
CGIndenter(CGGeneric(get)).define() +
|
|
"}\n\n")
|
|
setOrIndexedGet += getUnforgeable
|
|
|
|
if self.descriptor.supportsNamedProperties():
|
|
readonly = toStringBool(self.descriptor.operations['NamedSetter'] is None)
|
|
fillDescriptor = "FillPropertyDescriptor(desc, proxy, %s);\nreturn true;" % readonly
|
|
templateValues = {'jsvalRef': 'desc->value', 'jsvalPtr': '&desc->value',
|
|
'obj': 'proxy', 'successCode': fillDescriptor}
|
|
# Once we start supporting OverrideBuiltins we need to make
|
|
# ResolveOwnProperty or EnumerateOwnProperties filter out named
|
|
# properties that shadow prototype properties.
|
|
condition = "!(flags & JSRESOLVE_ASSIGNING) && !HasPropertyOnPrototype(cx, proxy, this, id)"
|
|
if self.descriptor.supportsIndexedProperties():
|
|
condition = "!IsArrayIndex(index) && " + condition
|
|
namedGet = ("\n" +
|
|
CGIfWrapper(CGProxyNamedGetter(self.descriptor, templateValues),
|
|
condition).define() +
|
|
"\n")
|
|
else:
|
|
namedGet = ""
|
|
|
|
return setOrIndexedGet + """JSObject* expando;
|
|
if (!isXray && (expando = GetExpandoObject(proxy))) {
|
|
if (!JS_GetPropertyDescriptorById(cx, expando, id, flags, desc)) {
|
|
return false;
|
|
}
|
|
if (desc->obj) {
|
|
// Pretend the property lives on the wrapper.
|
|
desc->obj = proxy;
|
|
return true;
|
|
}
|
|
}
|
|
""" + namedGet + """
|
|
desc->obj = NULL;
|
|
return true;"""
|
|
|
|
class CGDOMJSProxyHandler_defineProperty(ClassMethod):
|
|
def __init__(self, descriptor):
|
|
args = [Argument('JSContext*', 'cx'), Argument('JS::Handle<JSObject*>', 'proxy'),
|
|
Argument('JS::Handle<jsid>', 'id'),
|
|
Argument('JSPropertyDescriptor*', 'desc')]
|
|
ClassMethod.__init__(self, "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 += ("int32_t index = GetArrayIndexFromId(cx, id);\n" +
|
|
"if (IsArrayIndex(index)) {\n" +
|
|
CGIndenter(CGProxyIndexedSetter(self.descriptor)).define() +
|
|
" return true;\n" +
|
|
"}\n") % (self.descriptor.nativeType)
|
|
elif self.descriptor.supportsIndexedProperties():
|
|
# XXXbz Once this is fixed to only throw in strict mode, update the
|
|
# code that decides whether to do a
|
|
# CGDOMJSProxyHandler_defineProperty at all.
|
|
set += ("if (IsArrayIndex(GetArrayIndexFromId(cx, id))) {\n" +
|
|
" return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" +
|
|
"}\n") % self.descriptor.name
|
|
|
|
if UseHolderForUnforgeable(self.descriptor):
|
|
defineOnUnforgeable = ("JSBool hasUnforgeable;\n"
|
|
"if (!JS_HasPropertyById(cx, ${holder}, id, &hasUnforgeable)) {\n"
|
|
" return false;\n"
|
|
"}\n"
|
|
"if (hasUnforgeable) {\n"
|
|
" JSBool defined;\n"
|
|
" return js_DefineOwnProperty(cx, ${holder}, id, *desc, &defined);\n"
|
|
"}\n")
|
|
set += CallOnUnforgeableHolder(self.descriptor,
|
|
defineOnUnforgeable,
|
|
"xpc::WrapperFactory::IsXrayWrapper(proxy)")
|
|
|
|
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")
|
|
# If we support indexed properties, we won't get down here for
|
|
# indices, so we can just do our setter unconditionally here.
|
|
set += (CGProxyNamedSetter(self.descriptor).define() + "\n" +
|
|
"return true;\n")
|
|
else:
|
|
if self.descriptor.supportsNamedProperties():
|
|
# XXXbz Once this is fixed to only throw in strict mode, update
|
|
# the code that decides whether to do a
|
|
# CGDOMJSProxyHandler_defineProperty at all. If we support
|
|
# indexed properties, we won't get down here for indices, so we
|
|
# can just do our setter unconditionally here.
|
|
set += (CGProxyNamedPresenceChecker(self.descriptor).define() + "\n" +
|
|
"if (found) {\n"
|
|
" return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n"
|
|
"}" % self.descriptor.name)
|
|
set += ("return mozilla::dom::DOMProxyHandler::defineProperty(%s);" %
|
|
", ".join(a.name for a in self.args))
|
|
return set
|
|
|
|
class CGDOMJSProxyHandler_delete(ClassMethod):
|
|
def __init__(self, descriptor):
|
|
args = [Argument('JSContext*', 'cx'), Argument('JS::Handle<JSObject*>', 'proxy'),
|
|
Argument('JS::Handle<jsid>', 'id'),
|
|
Argument('bool*', 'bp')]
|
|
ClassMethod.__init__(self, "delete_", "bool", args)
|
|
self.descriptor = descriptor
|
|
|
|
def getBody(self):
|
|
def getDeleterBody(type):
|
|
"""
|
|
type should be "Named" or "Indexed"
|
|
"""
|
|
assert type is "Named" or type is "Indexed"
|
|
deleter = self.descriptor.operations[type + 'Deleter']
|
|
if deleter:
|
|
if (not deleter.signatures()[0][0].isPrimitive() or
|
|
deleter.signatures()[0][0].nullable() or
|
|
deleter.signatures()[0][0].tag() != IDLType.Tags.bool):
|
|
setBp = "*bp = true;"
|
|
else:
|
|
setBp = ("if (found) {\n"
|
|
" *bp = result;\n"
|
|
"} else {\n"
|
|
" *bp = true;\n"
|
|
"}")
|
|
body = (eval("CGProxy%sDeleter" % type)(self.descriptor).define() +
|
|
setBp)
|
|
elif eval("self.descriptor.supports%sProperties()" % type):
|
|
body = (eval("CGProxy%sPresenceChecker" % type)(self.descriptor).define() +
|
|
"if (found) {\n"
|
|
" *bp = false;\n"
|
|
"} else {\n"
|
|
" *bp = true;\n"
|
|
"}")
|
|
else:
|
|
body = None
|
|
return body
|
|
|
|
delete = """MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
|
|
"Should not have a XrayWrapper here");
|
|
|
|
"""
|
|
|
|
indexedBody = getDeleterBody("Indexed")
|
|
if indexedBody is not None:
|
|
delete += ("int32_t index = GetArrayIndexFromId(cx, id);\n" +
|
|
"if (IsArrayIndex(index)) {\n" +
|
|
CGIndenter(CGGeneric(indexedBody)).define() + "\n"
|
|
" // We always return here, even if the property was not found\n"
|
|
" return true;\n" +
|
|
"}\n") % self.descriptor.nativeType
|
|
|
|
if UseHolderForUnforgeable(self.descriptor):
|
|
unforgeable = ("JSBool hasUnforgeable;\n"
|
|
"if (!JS_HasPropertyById(cx, ${holder}, id, &hasUnforgeable)) {\n"
|
|
" return false;\n"
|
|
"}\n"
|
|
"if (hasUnforgeable) {\n"
|
|
" *bp = false;\n"
|
|
" return true;\n"
|
|
"}")
|
|
delete += CallOnUnforgeableHolder(self.descriptor, unforgeable)
|
|
delete += "\n"
|
|
|
|
namedBody = getDeleterBody("Named")
|
|
if namedBody is not None:
|
|
# We always return above for an index id in the case when we support
|
|
# indexed properties, so we can just treat the id as a name
|
|
# unconditionally here.
|
|
delete += ("if (!HasPropertyOnPrototype(cx, proxy, this, id)) {\n" +
|
|
CGIndenter(CGGeneric(namedBody)).define() + "\n"
|
|
" if (found) {\n"
|
|
" return true;\n"
|
|
" }\n"
|
|
"}\n")
|
|
|
|
delete += "return dom::DOMProxyHandler::delete_(cx, proxy, id, bp);";
|
|
|
|
return delete
|
|
|
|
class CGDOMJSProxyHandler_getOwnPropertyNames(ClassMethod):
|
|
def __init__(self, descriptor):
|
|
args = [Argument('JSContext*', 'cx'),
|
|
Argument('JS::Handle<JSObject*>', 'proxy'),
|
|
Argument('JS::AutoIdVector&', 'props')]
|
|
ClassMethod.__init__(self, "getOwnPropertyNames", "bool", args)
|
|
self.descriptor = descriptor
|
|
def getBody(self):
|
|
# Per spec, we do indices, then named props, then everything else
|
|
if self.descriptor.supportsIndexedProperties():
|
|
addIndices = """
|
|
uint32_t length = UnwrapProxy(proxy)->Length();
|
|
MOZ_ASSERT(int32_t(length) >= 0);
|
|
for (int32_t i = 0; i < int32_t(length); ++i) {
|
|
if (!props.append(INT_TO_JSID(i))) {
|
|
return false;
|
|
}
|
|
}
|
|
"""
|
|
else:
|
|
addIndices = ""
|
|
|
|
if self.descriptor.supportsNamedProperties():
|
|
addNames = """
|
|
nsTArray<nsString> names;
|
|
UnwrapProxy(proxy)->GetSupportedNames(names);
|
|
if (!AppendNamedPropertyIds(cx, proxy, names, props)) {
|
|
return false;
|
|
}
|
|
"""
|
|
else:
|
|
addNames = ""
|
|
|
|
if UseHolderForUnforgeable(self.descriptor):
|
|
addUnforgeable = (
|
|
"if (!js::GetPropertyNames(cx, ${holder}, JSITER_OWNONLY | JSITER_HIDDEN, &props)) {\n"
|
|
" return false;\n"
|
|
"}")
|
|
addUnforgeable = CallOnUnforgeableHolder(self.descriptor,
|
|
addUnforgeable,
|
|
"isXray")
|
|
else:
|
|
addUnforgeable = ""
|
|
return """bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy);
|
|
""" + addIndices + addUnforgeable + addNames + """
|
|
JSObject* expando;
|
|
if (!isXray && (expando = DOMProxyHandler::GetExpandoObject(proxy)) &&
|
|
!js::GetPropertyNames(cx, expando, JSITER_OWNONLY | JSITER_HIDDEN, &props)) {
|
|
return false;
|
|
}
|
|
|
|
return true;"""
|
|
|
|
class CGDOMJSProxyHandler_hasOwn(ClassMethod):
|
|
def __init__(self, descriptor):
|
|
args = [Argument('JSContext*', 'cx'),
|
|
Argument('JS::Handle<JSObject*>', 'proxy'),
|
|
Argument('JS::Handle<jsid>', 'id'),
|
|
Argument('bool*', 'bp')]
|
|
ClassMethod.__init__(self, "hasOwn", "bool", args)
|
|
self.descriptor = descriptor
|
|
def getBody(self):
|
|
if self.descriptor.supportsIndexedProperties():
|
|
indexed = ("int32_t index = GetArrayIndexFromId(cx, id);\n" +
|
|
"if (IsArrayIndex(index)) {\n" +
|
|
CGIndenter(CGProxyIndexedPresenceChecker(self.descriptor)).define() + "\n" +
|
|
" *bp = found;\n" +
|
|
" return true;\n" +
|
|
"}\n\n") % (self.descriptor.nativeType)
|
|
else:
|
|
indexed = ""
|
|
|
|
if UseHolderForUnforgeable(self.descriptor):
|
|
unforgeable = ("JSBool b = true;\n"
|
|
"JSBool ok = JS_AlreadyHasOwnPropertyById(cx, ${holder}, id, &b);\n"
|
|
"*bp = !!b;\n"
|
|
"if (!ok || *bp) {\n"
|
|
" return ok;\n"
|
|
"}")
|
|
unforgeable = CallOnUnforgeableHolder(self.descriptor, unforgeable)
|
|
else:
|
|
unforgeable = ""
|
|
|
|
if self.descriptor.supportsNamedProperties():
|
|
# If we support indexed properties we always return above for index
|
|
# property names, so no need to check for those here.
|
|
named = ("if (!HasPropertyOnPrototype(cx, proxy, this, id)) {\n" +
|
|
CGIndenter(CGProxyNamedPresenceChecker(self.descriptor)).define() + "\n" +
|
|
" *bp = found;\n"
|
|
" return true;\n"
|
|
"}\n" +
|
|
"\n")
|
|
else:
|
|
named = ""
|
|
|
|
return """MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
|
|
"Should not have a XrayWrapper here");
|
|
|
|
""" + indexed + unforgeable + """
|
|
JSObject* expando = GetExpandoObject(proxy);
|
|
if (expando) {
|
|
JSBool b = true;
|
|
JSBool ok = JS_HasPropertyById(cx, expando, id, &b);
|
|
*bp = !!b;
|
|
if (!ok || *bp) {
|
|
return ok;
|
|
}
|
|
}
|
|
|
|
""" + named + """*bp = false;
|
|
return true;"""
|
|
|
|
class CGDOMJSProxyHandler_get(ClassMethod):
|
|
def __init__(self, descriptor):
|
|
args = [Argument('JSContext*', 'cx'),
|
|
Argument('JS::Handle<JSObject*>', 'proxy'),
|
|
Argument('JS::Handle<JSObject*>', 'receiver'),
|
|
Argument('JS::Handle<jsid>', 'id'),
|
|
Argument('JS::MutableHandle<JS::Value>', 'vp')]
|
|
ClassMethod.__init__(self, "get", "bool", args)
|
|
self.descriptor = descriptor
|
|
def getBody(self):
|
|
if UseHolderForUnforgeable(self.descriptor):
|
|
hasUnforgeable = (
|
|
"JSBool hasUnforgeable;\n"
|
|
"if (!JS_AlreadyHasOwnPropertyById(cx, ${holder}, id, &hasUnforgeable)) {\n"
|
|
" return false;\n"
|
|
"}\n"
|
|
"if (hasUnforgeable) {\n"
|
|
" return JS_ForwardGetPropertyTo(cx, ${holder}, id, proxy, vp.address());\n"
|
|
"}")
|
|
getUnforgeableOrExpando = CallOnUnforgeableHolder(self.descriptor,
|
|
hasUnforgeable)
|
|
else:
|
|
getUnforgeableOrExpando = ""
|
|
getUnforgeableOrExpando += """JSObject* expando = DOMProxyHandler::GetExpandoObject(proxy);
|
|
if (expando) {
|
|
JSBool hasProp;
|
|
if (!JS_HasPropertyById(cx, expando, id, &hasProp)) {
|
|
return false;
|
|
}
|
|
|
|
if (hasProp) {
|
|
return JS_GetPropertyById(cx, expando, id, vp.address());
|
|
}
|
|
}"""
|
|
|
|
templateValues = {'jsvalRef': '*vp.address()', 'jsvalPtr': 'vp.address()', 'obj': 'proxy'}
|
|
|
|
if self.descriptor.supportsIndexedProperties():
|
|
getIndexedOrExpando = ("int32_t index = GetArrayIndexFromId(cx, id);\n" +
|
|
"if (IsArrayIndex(index)) {\n" +
|
|
CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define()) % (self.descriptor.nativeType)
|
|
getIndexedOrExpando += """
|
|
// Even if we don't have this index, we don't forward the
|
|
// get on to our expando object.
|
|
} else {
|
|
%s
|
|
}
|
|
""" % (stripTrailingWhitespace(getUnforgeableOrExpando.replace('\n', '\n ')))
|
|
else:
|
|
getIndexedOrExpando = getUnforgeableOrExpando + "\n"
|
|
|
|
if self.descriptor.supportsNamedProperties():
|
|
getNamed = CGProxyNamedGetter(self.descriptor, templateValues)
|
|
if self.descriptor.supportsIndexedProperties():
|
|
getNamed = CGIfWrapper(getNamed, "!IsArrayIndex(index)")
|
|
getNamed = getNamed.define() + "\n"
|
|
else:
|
|
getNamed = ""
|
|
|
|
return """MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
|
|
"Should not have a XrayWrapper here");
|
|
|
|
%s
|
|
{ // Scope for this "found" so it doesn't leak to things below
|
|
bool found;
|
|
if (!GetPropertyOnPrototype(cx, proxy, id, &found, vp.address())) {
|
|
return false;
|
|
}
|
|
|
|
if (found) {
|
|
return true;
|
|
}
|
|
}
|
|
%s
|
|
vp.setUndefined();
|
|
return true;""" % (getIndexedOrExpando, getNamed)
|
|
|
|
class CGDOMJSProxyHandler_obj_toString(ClassMethod):
|
|
def __init__(self, descriptor):
|
|
args = [Argument('JSContext*', 'cx'), Argument('JS::Handle<JSObject*>', 'proxy')]
|
|
ClassMethod.__init__(self, "obj_toString", "JSString*", args)
|
|
self.descriptor = descriptor
|
|
def getBody(self):
|
|
return "return mozilla::dom::DOMProxyHandler::obj_toString(cx, \"%s\");" % self.descriptor.name
|
|
|
|
class CGDOMJSProxyHandler_finalizeInBackground(ClassMethod):
|
|
def __init__(self, descriptor):
|
|
args = [Argument('JS::Value', 'priv')]
|
|
ClassMethod.__init__(self, "finalizeInBackground", "bool", args)
|
|
self.descriptor = descriptor
|
|
def getBody(self):
|
|
return ("return false;")
|
|
|
|
class CGDOMJSProxyHandler_finalize(ClassMethod):
|
|
def __init__(self, descriptor):
|
|
args = [Argument('JSFreeOp*', 'fop'), Argument('JSObject*', 'proxy')]
|
|
ClassMethod.__init__(self, "finalize", "void", args)
|
|
self.descriptor = descriptor
|
|
def getBody(self):
|
|
return ("%s self = UnwrapProxy(proxy);\n\n" % (self.descriptor.nativeType + "*") +
|
|
finalizeHook(self.descriptor, FINALIZE_HOOK_NAME, self.args[0].name).define())
|
|
|
|
class CGDOMJSProxyHandler_getElementIfPresent(ClassMethod):
|
|
def __init__(self, descriptor):
|
|
args = [Argument('JSContext*', 'cx'),
|
|
Argument('JS::Handle<JSObject*>', 'proxy'),
|
|
Argument('JS::Handle<JSObject*>', 'receiver'),
|
|
Argument('uint32_t', 'index'),
|
|
Argument('JS::MutableHandle<JS::Value>', 'vp'),
|
|
Argument('bool*', 'present')]
|
|
ClassMethod.__init__(self, "getElementIfPresent", "bool", args)
|
|
self.descriptor = descriptor
|
|
def getBody(self):
|
|
successCode = ("*present = found;\n"
|
|
"return true;")
|
|
templateValues = {'jsvalRef': '*vp.address()', 'jsvalPtr': 'vp.address()',
|
|
'obj': 'proxy', 'successCode': successCode}
|
|
if self.descriptor.supportsIndexedProperties():
|
|
get = (CGProxyIndexedGetter(self.descriptor, templateValues).define() + "\n"
|
|
"// We skip the expando object and any named getters if\n"
|
|
"// there is an indexed getter.\n" +
|
|
"\n") % (self.descriptor.nativeType)
|
|
else:
|
|
if self.descriptor.supportsNamedProperties():
|
|
get = CGProxyNamedGetter(self.descriptor, templateValues,
|
|
"UINT_TO_JSVAL(index)").define()
|
|
get += """
|
|
|
|
JSObject* expando = GetExpandoObject(proxy);
|
|
if (expando) {
|
|
JSBool isPresent;
|
|
if (!JS_GetElementIfPresent(cx, expando, index, expando, vp.address(), &isPresent)) {
|
|
return false;
|
|
}
|
|
if (isPresent) {
|
|
*present = true;
|
|
return true;
|
|
}
|
|
}
|
|
"""
|
|
|
|
return """MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(proxy),
|
|
"Should not have a XrayWrapper here");
|
|
|
|
""" + get + """
|
|
JSObject *proto;
|
|
if (!js::GetObjectProto(cx, proxy, &proto)) {
|
|
return false;
|
|
}
|
|
if (proto) {
|
|
JSBool isPresent;
|
|
if (!JS_GetElementIfPresent(cx, proto, index, proxy, vp.address(), &isPresent)) {
|
|
return false;
|
|
}
|
|
*present = isPresent;
|
|
return true;
|
|
}
|
|
|
|
*present = false;
|
|
// Can't Debug_SetValueRangeToCrashOnTouch because it's not public
|
|
return true;"""
|
|
|
|
class CGDOMJSProxyHandler_getInstance(ClassMethod):
|
|
def __init__(self):
|
|
ClassMethod.__init__(self, "getInstance", "DOMProxyHandler*", [], static=True)
|
|
def getBody(self):
|
|
return """static DOMProxyHandler instance;
|
|
return &instance;"""
|
|
|
|
class CGDOMJSProxyHandler(CGClass):
|
|
def __init__(self, descriptor):
|
|
constructors = [CGDOMJSProxyHandler_CGDOMJSProxyHandler()]
|
|
methods = [CGDOMJSProxyHandler_getOwnPropertyDescriptor(descriptor)]
|
|
# XXXbz This should really just test supportsIndexedProperties() and
|
|
# supportsNamedProperties(), but that would make us throw in all cases
|
|
# because we don't know whether we're in strict mode.
|
|
if (descriptor.operations['IndexedSetter'] or
|
|
descriptor.operations['NamedSetter'] or
|
|
UseHolderForUnforgeable(descriptor)):
|
|
methods.append(CGDOMJSProxyHandler_defineProperty(descriptor))
|
|
methods.extend([CGDOMJSProxyHandler_getOwnPropertyNames(descriptor),
|
|
CGDOMJSProxyHandler_hasOwn(descriptor),
|
|
CGDOMJSProxyHandler_get(descriptor),
|
|
CGDOMJSProxyHandler_obj_toString(descriptor),
|
|
CGDOMJSProxyHandler_finalizeInBackground(descriptor),
|
|
CGDOMJSProxyHandler_finalize(descriptor),
|
|
CGDOMJSProxyHandler_getElementIfPresent(descriptor),
|
|
CGDOMJSProxyHandler_getInstance(),
|
|
CGDOMJSProxyHandler_delete(descriptor)])
|
|
CGClass.__init__(self, 'DOMProxyHandler',
|
|
bases=[ClassBase('mozilla::dom::DOMProxyHandler')],
|
|
constructors=constructors,
|
|
methods=methods)
|
|
|
|
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
|
|
|
|
class CGDescriptor(CGThing):
|
|
def __init__(self, descriptor):
|
|
CGThing.__init__(self)
|
|
|
|
assert not descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject()
|
|
|
|
if descriptor.nativeOwnership == 'owned' and (
|
|
descriptor.interface.hasChildInterfaces() or
|
|
descriptor.interface.parent):
|
|
raise TypeError("Owned interface cannot have a parent or children")
|
|
|
|
self._deps = descriptor.interface.getDeps()
|
|
|
|
cgThings = []
|
|
# These are set to true if at least one non-static
|
|
# method/getter/setter exist on the interface.
|
|
(hasMethod, hasGetter, hasLenientGetter,
|
|
hasSetter, hasLenientSetter) = False, False, False, False, False
|
|
for n in descriptor.interface.namedConstructors:
|
|
cgThings.append(CGClassConstructor(descriptor, n,
|
|
NamedConstructorName(n)))
|
|
for m in descriptor.interface.members:
|
|
if (m.isMethod() and
|
|
(not m.isIdentifierLess() or m == descriptor.operations['Stringifier'])):
|
|
if m.isStatic():
|
|
assert descriptor.interface.hasInterfaceObject
|
|
cgThings.append(CGStaticMethod(descriptor, m))
|
|
elif descriptor.interface.hasInterfacePrototypeObject():
|
|
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))
|
|
elif descriptor.interface.hasInterfacePrototypeObject():
|
|
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))
|
|
elif descriptor.interface.hasInterfacePrototypeObject():
|
|
cgThings.append(CGSpecializedSetter(descriptor, m))
|
|
if m.hasLenientThis():
|
|
hasLenientSetter = True
|
|
else:
|
|
hasSetter = True
|
|
elif m.getExtendedAttribute("PutForwards"):
|
|
cgThings.append(CGSpecializedForwardingSetter(descriptor, m))
|
|
hasSetter = True
|
|
if (not m.isStatic() and
|
|
descriptor.interface.hasInterfacePrototypeObject()):
|
|
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:
|
|
if descriptor.nativeOwnership == 'owned' or descriptor.nativeOwnership == 'refcounted':
|
|
cgThings.append(CGDeferredFinalizePointers(descriptor))
|
|
cgThings.append(CGGetDeferredFinalizePointers(descriptor))
|
|
cgThings.append(CGDeferredFinalize(descriptor))
|
|
|
|
if not descriptor.proxy:
|
|
if not descriptor.workers and descriptor.wrapperCache:
|
|
cgThings.append(CGAddPropertyHook(descriptor))
|
|
|
|
# Always have a finalize hook, regardless of whether the class
|
|
# wants a custom hook.
|
|
cgThings.append(CGClassFinalizeHook(descriptor))
|
|
|
|
# Only generate a trace hook if the class wants a custom hook.
|
|
if (descriptor.customTrace):
|
|
cgThings.append(CGClassTraceHook(descriptor))
|
|
|
|
properties = PropertyArrays(descriptor)
|
|
cgThings.append(CGGeneric(define=str(properties)))
|
|
cgThings.append(CGNativeProperties(descriptor, properties))
|
|
|
|
cgThings.append(CGNativePropertyHooks(descriptor, properties))
|
|
|
|
if descriptor.interface.hasInterfaceObject():
|
|
cgThings.append(CGClassConstructor(descriptor,
|
|
descriptor.interface.ctor()))
|
|
cgThings.append(CGClassHasInstanceHook(descriptor))
|
|
cgThings.append(CGInterfaceObjectJSClass(descriptor, properties))
|
|
if descriptor.needsConstructHookHolder():
|
|
cgThings.append(CGClassConstructHookHolder(descriptor))
|
|
cgThings.append(CGNamedConstructors(descriptor))
|
|
|
|
cgThings.append(CGLegacyCallHook(descriptor))
|
|
cgThings.append(CGNewResolveHook(descriptor))
|
|
|
|
if descriptor.interface.hasInterfacePrototypeObject():
|
|
cgThings.append(CGPrototypeJSClass(descriptor, properties))
|
|
|
|
cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties))
|
|
if descriptor.interface.hasInterfacePrototypeObject():
|
|
cgThings.append(CGGetProtoObjectMethod(descriptor))
|
|
if descriptor.interface.hasInterfaceObject():
|
|
cgThings.append(CGGetConstructorObjectMethod(descriptor))
|
|
|
|
# Set up our Xray callbacks as needed. Note that we don't need to do
|
|
# it in workers.
|
|
if not descriptor.workers and descriptor.concrete and descriptor.proxy:
|
|
cgThings.append(CGResolveOwnProperty(descriptor))
|
|
cgThings.append(CGEnumerateOwnProperties(descriptor))
|
|
|
|
if descriptor.interface.hasInterfaceObject():
|
|
cgThings.append(CGDefineDOMInterfaceMethod(descriptor))
|
|
if (not descriptor.interface.isExternal() and
|
|
# Workers stuff is never pref-controlled
|
|
not descriptor.workers):
|
|
if descriptor.interface.getExtendedAttribute("PrefControlled") is not None:
|
|
cgThings.append(CGPrefEnabledNative(descriptor))
|
|
elif descriptor.interface.getExtendedAttribute("Pref") is not None:
|
|
cgThings.append(CGPrefEnabled(descriptor))
|
|
|
|
if descriptor.concrete:
|
|
if descriptor.proxy:
|
|
cgThings.append(CGProxyIsProxy(descriptor))
|
|
cgThings.append(CGProxyUnwrap(descriptor))
|
|
cgThings.append(CGDOMJSProxyHandlerDOMClass(descriptor))
|
|
cgThings.append(CGDOMJSProxyHandler(descriptor))
|
|
cgThings.append(CGIsMethod(descriptor))
|
|
else:
|
|
cgThings.append(CGDOMJSClass(descriptor))
|
|
|
|
if descriptor.wrapperCache:
|
|
cgThings.append(CGWrapWithCacheMethod(descriptor, properties))
|
|
cgThings.append(CGWrapMethod(descriptor))
|
|
else:
|
|
cgThings.append(CGWrapNonWrapperCacheMethod(descriptor,
|
|
properties))
|
|
|
|
cgThings = CGList((CGIndenter(t, declareOnly=True) for t in cgThings), "\n")
|
|
cgThings = CGWrapper(cgThings, pre='\n', post='\n')
|
|
self.cgRoot = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name),
|
|
cgThings),
|
|
post='\n')
|
|
|
|
def declare(self):
|
|
return self.cgRoot.declare()
|
|
def define(self):
|
|
return self.cgRoot.define()
|
|
def deps(self):
|
|
return self._deps
|
|
|
|
class CGNamespacedEnum(CGThing):
|
|
def __init__(self, namespace, enumName, names, values, comment=""):
|
|
|
|
if not values:
|
|
values = []
|
|
|
|
# Account for explicit enum values.
|
|
entries = []
|
|
for i in range(0, len(names)):
|
|
if len(values) > i and values[i] is not None:
|
|
entry = "%s = %s" % (names[i], values[i])
|
|
else:
|
|
entry = names[i]
|
|
entries.append(entry)
|
|
|
|
# Append a Count.
|
|
entries.append('_' + enumName + '_Count')
|
|
|
|
# Indent.
|
|
entries = [' ' + e for e in entries]
|
|
|
|
# Build the enum body.
|
|
enumstr = comment + 'enum %s\n{\n%s\n};\n' % (enumName, ',\n'.join(entries))
|
|
curr = CGGeneric(declare=enumstr)
|
|
|
|
# Add some whitespace padding.
|
|
curr = CGWrapper(curr, pre='\n',post='\n')
|
|
|
|
# Add the namespace.
|
|
curr = CGNamespace(namespace, curr)
|
|
|
|
# Add the typedef
|
|
typedef = '\ntypedef %s::%s %s;\n\n' % (namespace, enumName, enumName)
|
|
curr = CGList([curr, CGGeneric(declare=typedef)])
|
|
|
|
# Save the result.
|
|
self.node = curr
|
|
|
|
def declare(self):
|
|
return self.node.declare()
|
|
def define(self):
|
|
assert False # Only for headers.
|
|
|
|
class CGDictionary(CGThing):
|
|
def __init__(self, dictionary, descriptorProvider):
|
|
self.dictionary = dictionary
|
|
self.descriptorProvider = descriptorProvider
|
|
self.workers = descriptorProvider.workers
|
|
self.needToInitIds = not self.workers and len(dictionary.members) > 0
|
|
self.memberInfo = [
|
|
(member,
|
|
getJSToNativeConversionTemplate(member.type,
|
|
descriptorProvider,
|
|
isMember="Dictionary",
|
|
isOptional=(not member.defaultValue),
|
|
defaultValue=member.defaultValue))
|
|
for member in dictionary.members ]
|
|
|
|
def declare(self):
|
|
d = self.dictionary
|
|
if d.parent:
|
|
inheritance = ": public %s " % self.makeClassName(d.parent)
|
|
elif not self.workers:
|
|
inheritance = ": public MainThreadDictionaryBase "
|
|
else:
|
|
inheritance = ""
|
|
memberDecls = [" %s %s;" %
|
|
(self.getMemberType(m),
|
|
self.makeMemberName(m[0].identifier.name))
|
|
for m in self.memberInfo]
|
|
|
|
return (string.Template(
|
|
"struct ${selfName} ${inheritance}{\n"
|
|
" ${selfName}() {}\n"
|
|
" bool Init(JSContext* cx, const JS::Value& val);\n"
|
|
" bool ToObject(JSContext* cx, JS::Handle<JSObject*> parentObject, JS::Value *vp) const;\n"
|
|
"\n" +
|
|
(" bool Init(const nsAString& aJSON)\n"
|
|
" {\n"
|
|
" Maybe<JSAutoRequest> ar;\n"
|
|
" Maybe<JSAutoCompartment> ac;\n"
|
|
" Maybe< JS::Rooted<JS::Value> > json;\n"
|
|
" JSContext* cx = ParseJSON(aJSON, ar, ac, json);\n"
|
|
" NS_ENSURE_TRUE(cx, false);\n"
|
|
" return Init(cx, json.ref());\n"
|
|
" }\n" if not self.workers else "") +
|
|
"\n" +
|
|
"\n".join(memberDecls) + "\n"
|
|
"private:\n"
|
|
" // Disallow copy-construction\n"
|
|
" ${selfName}(const ${selfName}&) MOZ_DELETE;\n" +
|
|
# NOTE: jsids are per-runtime, so don't use them in workers
|
|
(" static bool InitIds(JSContext* cx);\n"
|
|
" static bool initedIds;\n" if self.needToInitIds else "") +
|
|
"\n".join(" static jsid " +
|
|
self.makeIdName(m.identifier.name) + ";" for
|
|
m in d.members) + "\n"
|
|
"};\n"
|
|
"struct ${selfName}Initializer : public ${selfName} {\n"
|
|
" ${selfName}Initializer() {\n"
|
|
" // Safe to pass a null context if we pass a null value\n"
|
|
" Init(nullptr, JS::NullValue());\n"
|
|
" }\n"
|
|
"};").substitute( { "selfName": self.makeClassName(d),
|
|
"inheritance": inheritance }))
|
|
|
|
def define(self):
|
|
d = self.dictionary
|
|
if d.parent:
|
|
initParent = ("// Per spec, we init the parent's members first\n"
|
|
"if (!%s::Init(cx, val)) {\n"
|
|
" return false;\n"
|
|
"}\n" % self.makeClassName(d.parent))
|
|
toObjectParent = ("// Per spec, we define the parent's members first\n"
|
|
"if (!%s::ToObject(cx, parentObject, vp)) {\n"
|
|
" return false;\n"
|
|
"}\n" % self.makeClassName(d.parent))
|
|
ensureObject = "JS::Rooted<JSObject*> obj(cx, &vp->toObject());\n"
|
|
else:
|
|
initParent = ""
|
|
toObjectParent = ""
|
|
ensureObject = ("JS::Rooted<JSObject*> obj(cx, JS_NewObject(cx, nullptr, nullptr, nullptr));\n"
|
|
"if (!obj) {\n"
|
|
" return false;\n"
|
|
"}\n"
|
|
"*vp = JS::ObjectValue(*obj);\n")
|
|
|
|
memberInits = [CGIndenter(self.getMemberConversion(m)).define()
|
|
for m in self.memberInfo]
|
|
idinit = [CGGeneric('!InternJSString(cx, %s, "%s")' %
|
|
(m.identifier.name + "_id", m.identifier.name))
|
|
for m in d.members]
|
|
idinit = CGList(idinit, " ||\n")
|
|
idinit = CGWrapper(idinit, pre="if (",
|
|
post=(") {\n"
|
|
" return false;\n"
|
|
"}"),
|
|
reindent=True)
|
|
memberDefines = [CGIndenter(self.getMemberDefinition(m)).define()
|
|
for m in self.memberInfo]
|
|
|
|
return string.Template(
|
|
# NOTE: jsids are per-runtime, so don't use them in workers
|
|
("bool ${selfName}::initedIds = false;\n" +
|
|
"\n".join("jsid ${selfName}::%s = JSID_VOID;" %
|
|
self.makeIdName(m.identifier.name)
|
|
for m in d.members) + "\n"
|
|
"\n"
|
|
"bool\n"
|
|
"${selfName}::InitIds(JSContext* cx)\n"
|
|
"{\n"
|
|
" MOZ_ASSERT(!initedIds);\n"
|
|
"${idInit}\n"
|
|
" initedIds = true;\n"
|
|
" return true;\n"
|
|
"}\n"
|
|
"\n" if self.needToInitIds else "") +
|
|
"bool\n"
|
|
"${selfName}::Init(JSContext* cx, const JS::Value& val)\n"
|
|
"{\n"
|
|
" // Passing a null JSContext is OK only if we're initing from null,\n"
|
|
" // Since in that case we will not have to do any property gets\n"
|
|
" MOZ_ASSERT_IF(!cx, val.isNull());\n" +
|
|
# NOTE: jsids are per-runtime, so don't use them in workers
|
|
(" if (cx && !initedIds && !InitIds(cx)) {\n"
|
|
" return false;\n"
|
|
" }\n" if self.needToInitIds else "") +
|
|
"${initParent}" +
|
|
(" JSBool found;\n"
|
|
" JS::Rooted<JS::Value> temp(cx);\n"
|
|
" bool isNull = val.isNullOrUndefined();\n" if len(memberInits) > 0 else "") +
|
|
" if (!IsConvertibleToDictionary(cx, val)) {\n"
|
|
" return ThrowErrorMessage(cx, MSG_NOT_DICTIONARY);\n"
|
|
" }\n"
|
|
"\n"
|
|
"${initMembers}\n"
|
|
" return true;\n"
|
|
"}\n"
|
|
"\n"
|
|
"bool\n"
|
|
"${selfName}::ToObject(JSContext* cx, JS::Handle<JSObject*> parentObject, JS::Value *vp) const\n"
|
|
"{\n" +
|
|
# NOTE: jsids are per-runtime, so don't use them in workers
|
|
(" if (!initedIds && !InitIds(cx)) {\n"
|
|
" return false;\n"
|
|
" }\n" if self.needToInitIds else "") +
|
|
"${toObjectParent}"
|
|
"${ensureObject}"
|
|
"\n"
|
|
"${defineMembers}\n"
|
|
"\n"
|
|
" return true;\n"
|
|
"}").substitute({
|
|
"selfName": self.makeClassName(d),
|
|
"initParent": CGIndenter(CGGeneric(initParent)).define(),
|
|
"initMembers": "\n\n".join(memberInits),
|
|
"idInit": CGIndenter(idinit).define(),
|
|
"isMainThread": toStringBool(not self.workers),
|
|
"toObjectParent": CGIndenter(CGGeneric(toObjectParent)).define(),
|
|
"ensureObject": CGIndenter(CGGeneric(ensureObject)).define(),
|
|
"defineMembers": "\n\n".join(memberDefines)
|
|
})
|
|
|
|
def deps(self):
|
|
return self.dictionary.getDeps()
|
|
|
|
@staticmethod
|
|
def makeDictionaryName(dictionary, workers):
|
|
suffix = "Workers" if workers else ""
|
|
return dictionary.identifier.name + suffix
|
|
|
|
def makeClassName(self, dictionary):
|
|
return self.makeDictionaryName(dictionary, self.workers)
|
|
|
|
@staticmethod
|
|
def makeMemberName(name):
|
|
return "m" + name[0].upper() + name[1:]
|
|
|
|
def getMemberType(self, memberInfo):
|
|
(member, (templateBody, declType,
|
|
holderType, dealWithOptional)) = memberInfo
|
|
# We can't handle having a holderType here
|
|
assert holderType is None
|
|
if dealWithOptional:
|
|
declType = CGTemplatedType("Optional", declType)
|
|
return declType.define()
|
|
|
|
def getMemberConversion(self, memberInfo):
|
|
(member, (templateBody, declType,
|
|
holderType, dealWithOptional)) = memberInfo
|
|
replacements = { "val": "temp",
|
|
"valPtr": "temp.address()",
|
|
"declName": self.makeMemberName(member.identifier.name),
|
|
# We need a holder name for external interfaces, but
|
|
# it's scoped down to the conversion so we can just use
|
|
# anything we want.
|
|
"holderName": "holder" }
|
|
# We can't handle having a holderType here
|
|
assert holderType is None
|
|
if dealWithOptional:
|
|
replacements["declName"] = "(" + replacements["declName"] + ".Value())"
|
|
if member.defaultValue:
|
|
replacements["haveValue"] = "found"
|
|
|
|
# NOTE: jsids are per-runtime, so don't use them in workers
|
|
if self.workers:
|
|
propName = member.identifier.name
|
|
propCheck = ('JS_HasProperty(cx, &val.toObject(), "%s", &found)' %
|
|
propName)
|
|
propGet = ('JS_GetProperty(cx, &val.toObject(), "%s", temp.address())' %
|
|
propName)
|
|
else:
|
|
propId = self.makeIdName(member.identifier.name);
|
|
propCheck = ("JS_HasPropertyById(cx, &val.toObject(), %s, &found)" %
|
|
propId)
|
|
propGet = ("JS_GetPropertyById(cx, &val.toObject(), %s, temp.address())" %
|
|
propId)
|
|
|
|
conversionReplacements = {
|
|
"prop": self.makeMemberName(member.identifier.name),
|
|
"convert": string.Template(templateBody).substitute(replacements),
|
|
"propCheck": propCheck,
|
|
"propGet": propGet
|
|
}
|
|
conversion = ("if (isNull) {\n"
|
|
" found = false;\n"
|
|
"} else if (!${propCheck}) {\n"
|
|
" return false;\n"
|
|
"}\n")
|
|
if member.defaultValue:
|
|
conversion += (
|
|
"if (found) {\n"
|
|
" if (!${propGet}) {\n"
|
|
" return false;\n"
|
|
" }\n"
|
|
"}\n"
|
|
"${convert}")
|
|
else:
|
|
conversion += (
|
|
"if (found) {\n"
|
|
" ${prop}.Construct();\n"
|
|
" if (!${propGet}) {\n"
|
|
" return false;\n"
|
|
" }\n"
|
|
"${convert}\n"
|
|
"}")
|
|
conversionReplacements["convert"] = CGIndenter(
|
|
CGGeneric(conversionReplacements["convert"])).define()
|
|
|
|
return CGGeneric(
|
|
string.Template(conversion).substitute(conversionReplacements)
|
|
)
|
|
|
|
def getMemberDefinition(self, memberInfo):
|
|
member = memberInfo[0]
|
|
declType = memberInfo[1][1]
|
|
memberLoc = self.makeMemberName(member.identifier.name)
|
|
if member.defaultValue:
|
|
memberData = memberLoc
|
|
else:
|
|
# The data is inside the Optional<>
|
|
memberData = "%s.Value()" % memberLoc
|
|
|
|
if self.workers:
|
|
propDef = (
|
|
'JS_DefineProperty(cx, obj, "%s", temp, nullptr, nullptr, JSPROP_ENUMERATE)' %
|
|
member.identifier.name)
|
|
else:
|
|
propDef = (
|
|
'JS_DefinePropertyById(cx, obj, %s, temp, nullptr, nullptr, JSPROP_ENUMERATE)' %
|
|
self.makeIdName(member.identifier.name))
|
|
|
|
innerTemplate = wrapForType(
|
|
member.type, self.descriptorProvider,
|
|
{
|
|
'result' : "currentValue",
|
|
'successCode' : ("if (!%s) {\n"
|
|
" return false;\n"
|
|
"}" % propDef),
|
|
'jsvalRef': "temp",
|
|
'jsvalPtr': "&temp",
|
|
'isCreator': False,
|
|
'obj': "parentObject"
|
|
}, isMember=True)
|
|
conversion = CGGeneric(innerTemplate)
|
|
conversion = CGWrapper(conversion,
|
|
pre=("JS::Value temp;\n"
|
|
"const %s& currentValue = %s;\n" %
|
|
(declType.define(), memberData)
|
|
))
|
|
|
|
if not member.defaultValue:
|
|
# Only do the conversion if we have a value
|
|
conversion = CGIfWrapper(conversion, "%s.WasPassed()" % memberLoc)
|
|
else:
|
|
# Make sure we have a scope for our stuff
|
|
conversion = CGWrapper(CGIndenter(conversion),
|
|
pre=("{\n"
|
|
" // scope for 'temp' and 'currentValue'\n"),
|
|
post="\n}")
|
|
return conversion
|
|
|
|
@staticmethod
|
|
def makeIdName(name):
|
|
return name + "_id"
|
|
|
|
@staticmethod
|
|
def getDictionaryDependenciesFromType(type):
|
|
if type.isDictionary():
|
|
return set([type.unroll().inner])
|
|
if type.isSequence() or type.isArray():
|
|
return CGDictionary.getDictionaryDependenciesFromType(type.unroll())
|
|
return set()
|
|
|
|
@staticmethod
|
|
def getDictionaryDependencies(dictionary):
|
|
deps = set();
|
|
if dictionary.parent:
|
|
deps.add(dictionary.parent)
|
|
for member in dictionary.members:
|
|
deps |= CGDictionary.getDictionaryDependenciesFromType(member.type)
|
|
return deps
|
|
|
|
|
|
class CGRegisterProtos(CGAbstractMethod):
|
|
def __init__(self, config):
|
|
CGAbstractMethod.__init__(self, None, 'Register', 'void',
|
|
[Argument('nsScriptNameSpaceManager*', 'aNameSpaceManager')])
|
|
self.config = config
|
|
|
|
def _defineMacro(self):
|
|
return """
|
|
#define REGISTER_PROTO(_dom_class, _pref_check) \\
|
|
aNameSpaceManager->RegisterDefineDOMInterface(NS_LITERAL_STRING(#_dom_class), _dom_class##Binding::DefineDOMInterface, _pref_check);
|
|
#define REGISTER_CONSTRUCTOR(_dom_constructor, _dom_class, _pref_check) \\
|
|
aNameSpaceManager->RegisterDefineDOMInterface(NS_LITERAL_STRING(#_dom_constructor), _dom_class##Binding::DefineDOMInterface, _pref_check);
|
|
|
|
"""
|
|
def _undefineMacro(self):
|
|
return """
|
|
#undef REGISTER_CONSTRUCTOR
|
|
#undef REGISTER_PROTO"""
|
|
def _registerProtos(self):
|
|
def getPrefCheck(desc):
|
|
if (desc.interface.getExtendedAttribute("PrefControlled") is None and
|
|
desc.interface.getExtendedAttribute("Pref") is None):
|
|
return "nullptr"
|
|
return "%sBinding::PrefEnabled" % desc.name
|
|
lines = []
|
|
for desc in self.config.getDescriptors(hasInterfaceObject=True,
|
|
isExternal=False,
|
|
workers=False,
|
|
register=True):
|
|
lines.append("REGISTER_PROTO(%s, %s);" % (desc.name, getPrefCheck(desc)))
|
|
lines.extend("REGISTER_CONSTRUCTOR(%s, %s, %s);" % (n.identifier.name, desc.name, getPrefCheck(desc))
|
|
for n in desc.interface.namedConstructors)
|
|
return '\n'.join(lines) + '\n'
|
|
def definition_body(self):
|
|
return self._defineMacro() + self._registerProtos() + self._undefineMacro()
|
|
|
|
def dependencySortObjects(objects, dependencyGetter, nameGetter):
|
|
"""
|
|
Sort IDL objects with dependencies on each other such that if A
|
|
depends on B then B will come before A. This is needed for
|
|
declaring C++ classes in the right order, for example. Objects
|
|
that have no dependencies are just sorted by name.
|
|
|
|
objects should be something that can produce a set of objects
|
|
(e.g. a set, iterator, list, etc).
|
|
|
|
dependencyGetter is something that, given an object, should return
|
|
the set of objects it depends on.
|
|
"""
|
|
# XXXbz this will fail if we have two webidl files F1 and F2 such that F1
|
|
# declares an object which depends on an object in F2, and F2 declares an
|
|
# object (possibly a different one!) that depends on an object in F1. The
|
|
# good news is that I expect this to never happen.
|
|
sortedObjects = []
|
|
objects = set(objects)
|
|
while len(objects) != 0:
|
|
# Find the dictionaries that don't depend on anything else
|
|
# anymore and move them over.
|
|
toMove = [o for o in objects if
|
|
len(dependencyGetter(o) & objects) == 0]
|
|
if len(toMove) == 0:
|
|
raise TypeError("Loop in dependency graph\n" +
|
|
"\n".join(o.location for o in objects))
|
|
objects = objects - set(toMove)
|
|
sortedObjects.extend(sorted(toMove, key=nameGetter))
|
|
return sortedObjects
|
|
|
|
|
|
class ForwardDeclarationBuilder:
|
|
"""
|
|
Create a canonical representation of a set of namespaced forward
|
|
declarations.
|
|
"""
|
|
def __init__(self):
|
|
"""
|
|
The set of declarations is represented as a tree of nested namespaces.
|
|
Each tree node has a set of declarations |decls| and a dict |children|.
|
|
Each declaration is a pair consisting of the class name and a boolean
|
|
that is true iff the class is really a struct. |children| maps the
|
|
names of inner namespaces to the declarations in that namespace.
|
|
"""
|
|
self.decls = set([])
|
|
self.children = {}
|
|
|
|
def _listAdd(self, namespaces, name, isStruct=False):
|
|
"""
|
|
Add a forward declaration, where |namespaces| is a list of namespaces.
|
|
|name| should not contain any other namespaces.
|
|
"""
|
|
if namespaces:
|
|
child = self.children.setdefault(namespaces[0], ForwardDeclarationBuilder())
|
|
child._listAdd(namespaces[1:], name, isStruct)
|
|
else:
|
|
assert not '::' in name
|
|
self.decls.add((name, isStruct))
|
|
|
|
def addInMozillaDom(self, name, isStruct=False):
|
|
"""
|
|
Add a forward declaration to the mozilla::dom:: namespace. |name| should not
|
|
contain any other namespaces.
|
|
"""
|
|
self._listAdd(["mozilla", "dom"], name, isStruct)
|
|
|
|
def add(self, nativeType, isStruct=False):
|
|
"""
|
|
Add a forward declaration, where |nativeType| is a string containing
|
|
the type and its namespaces, in the usual C++ way.
|
|
"""
|
|
components = nativeType.split('::')
|
|
self._listAdd(components[:-1], components[-1], isStruct)
|
|
|
|
def _build(self, atTopLevel):
|
|
"""
|
|
Return a codegenerator for the forward declarations.
|
|
"""
|
|
decls = []
|
|
if self.decls:
|
|
decls.append(CGList([CGClassForwardDeclare(cname, isStruct)
|
|
for (cname, isStruct) in sorted(self.decls)]))
|
|
for namespace, child in sorted(self.children.iteritems()):
|
|
decls.append(CGNamespace(namespace, child._build(atTopLevel=False), declareOnly=True))
|
|
|
|
cg = CGList(decls, joiner='\n')
|
|
if not atTopLevel and len(decls) + len(self.decls) > 1:
|
|
cg = CGWrapper(cg, pre='\n', post='\n')
|
|
return cg
|
|
|
|
def build(self):
|
|
return self._build(atTopLevel=True)
|
|
|
|
|
|
class CGForwardDeclarations(CGWrapper):
|
|
"""
|
|
Code generate the forward declarations for a header file.
|
|
"""
|
|
def __init__(self, config, descriptors, mainCallbacks, workerCallbacks,
|
|
callbackInterfaces):
|
|
builder = ForwardDeclarationBuilder()
|
|
|
|
def forwardDeclareForType(t, workerness='both'):
|
|
t = t.unroll()
|
|
if t.isGeckoInterface():
|
|
name = t.inner.identifier.name
|
|
# Find and add the non-worker implementation, if any.
|
|
if workerness != 'workeronly':
|
|
try:
|
|
desc = config.getDescriptor(name, False)
|
|
builder.add(desc.nativeType)
|
|
except NoSuchDescriptorError:
|
|
pass
|
|
# Find and add the worker implementation, if any.
|
|
if workerness != 'mainthreadonly':
|
|
try:
|
|
desc = config.getDescriptor(name, True)
|
|
builder.add(desc.nativeType)
|
|
except NoSuchDescriptorError:
|
|
pass
|
|
elif t.isCallback():
|
|
builder.addInMozillaDom(str(t))
|
|
elif t.isDictionary():
|
|
builder.addInMozillaDom(t.inner.identifier.name, isStruct=True)
|
|
elif t.isCallbackInterface():
|
|
builder.addInMozillaDom(t.inner.identifier.name)
|
|
elif t.isUnion():
|
|
builder.addInMozillaDom(str(t))
|
|
# Don't need to do anything for void, primitive, string, any or object.
|
|
# There may be some other cases we are missing.
|
|
|
|
# Needed for at least PrototypeTraits, PrototypeIDMap, and Wrap.
|
|
for d in descriptors:
|
|
builder.add(d.nativeType)
|
|
|
|
for callback in mainCallbacks:
|
|
forwardDeclareForType(callback)
|
|
for t in getTypesFromCallback(callback):
|
|
forwardDeclareForType(t, workerness='mainthreadonly')
|
|
|
|
for callback in workerCallbacks:
|
|
forwardDeclareForType(callback)
|
|
for t in getTypesFromCallback(callback):
|
|
forwardDeclareForType(t, workerness='workeronly')
|
|
|
|
for d in callbackInterfaces:
|
|
builder.add(d.nativeType)
|
|
for t in getTypesFromDescriptor(d):
|
|
forwardDeclareForType(t)
|
|
|
|
# Dictionaries add a header for each type they contain, so no
|
|
# need for forward declarations. However, they may refer to
|
|
# declarations made later in this file, which is why we
|
|
# forward declare everything that is declared in this file.
|
|
|
|
CGWrapper.__init__(self, builder.build())
|
|
|
|
|
|
class CGBindingRoot(CGThing):
|
|
"""
|
|
Root codegen class for binding generation. Instantiate the class, and call
|
|
declare or define to generate header or cpp code (respectively).
|
|
"""
|
|
def __init__(self, config, prefix, webIDLFile):
|
|
descriptors = config.getDescriptors(webIDLFile=webIDLFile,
|
|
hasInterfaceOrInterfacePrototypeObject=True,
|
|
skipGen=False)
|
|
mainDictionaries = config.getDictionaries(webIDLFile=webIDLFile,
|
|
workers=False)
|
|
workerDictionaries = config.getDictionaries(webIDLFile=webIDLFile,
|
|
workers=True)
|
|
mainCallbacks = config.getCallbacks(webIDLFile=webIDLFile,
|
|
workers=False)
|
|
workerCallbacks = config.getCallbacks(webIDLFile=webIDLFile,
|
|
workers=True)
|
|
callbackDescriptors = config.getDescriptors(webIDLFile=webIDLFile,
|
|
isCallback=True)
|
|
jsImplemented = config.getDescriptors(webIDLFile=webIDLFile,
|
|
isJSImplemented=True)
|
|
|
|
descriptorsWithPrototype = filter(lambda d: d.interface.hasInterfacePrototypeObject(),
|
|
descriptors)
|
|
traitsClasses = [CGPrototypeTraitsClass(d) for d in descriptorsWithPrototype]
|
|
|
|
# We must have a 1:1 mapping here, skip for prototypes which
|
|
# share an implementation with other prototypes.
|
|
traitsClasses.extend([CGPrototypeIDMapClass(d) for d in descriptorsWithPrototype
|
|
if d.unsharedImplementation])
|
|
|
|
# Wrap all of that in our namespaces.
|
|
if len(traitsClasses) > 0:
|
|
traitsClasses = CGNamespace.build(['mozilla', 'dom'],
|
|
CGWrapper(CGList(traitsClasses),
|
|
declarePre='\n'),
|
|
declareOnly=True)
|
|
traitsClasses = CGWrapper(traitsClasses, declarePost='\n')
|
|
else:
|
|
traitsClasses = None
|
|
|
|
# Do codegen for all the enums
|
|
def makeEnum(e):
|
|
return CGNamespace.build([e.identifier.name + "Values"],
|
|
CGEnum(e))
|
|
def makeEnumTypedef(e):
|
|
return CGGeneric(declare=("typedef %sValues::valuelist %s;\n" %
|
|
(e.identifier.name, e.identifier.name)))
|
|
cgthings = [ fun(e) for e in config.getEnums(webIDLFile)
|
|
for fun in [makeEnum, makeEnumTypedef] ]
|
|
|
|
# Do codegen for all the dictionaries. We have to be a bit careful
|
|
# here, because we have to generate these in order from least derived
|
|
# to most derived so that class inheritance works out. We also have to
|
|
# generate members before the dictionary that contains them.
|
|
cgthings.extend([CGDictionary(d, config.getDescriptorProvider(True))
|
|
for d in
|
|
dependencySortObjects(workerDictionaries,
|
|
CGDictionary.getDictionaryDependencies,
|
|
lambda d: d.identifier.name)])
|
|
cgthings.extend([CGDictionary(d, config.getDescriptorProvider(False))
|
|
for d in
|
|
dependencySortObjects(mainDictionaries,
|
|
CGDictionary.getDictionaryDependencies,
|
|
lambda d: d.identifier.name)])
|
|
|
|
# Do codegen for all the callbacks. Only do non-worker codegen for now,
|
|
# since we don't have a sane setup yet for invoking callbacks in workers
|
|
# and managing their lifetimes.
|
|
cgthings.extend(CGCallbackFunction(c, config.getDescriptorProvider(False))
|
|
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([CGCallbackInterface(x) for x in callbackDescriptors])
|
|
|
|
# Do codegen for JS implemented classes
|
|
def getParentDescriptor(desc):
|
|
if not desc.interface.parent:
|
|
return set()
|
|
return { desc.getDescriptor(desc.interface.parent.identifier.name) }
|
|
for x in dependencySortObjects(jsImplemented, getParentDescriptor,
|
|
lambda d: d.interface.identifier.name):
|
|
cgthings.append(CGCallbackInterface(x))
|
|
cgthings.append(CGJSImplClass(x))
|
|
|
|
# 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(['mozilla', 'dom'],
|
|
CGWrapper(curr, pre="\n"))
|
|
|
|
curr = CGList([CGForwardDeclarations(config, descriptors,
|
|
mainCallbacks, workerCallbacks,
|
|
callbackDescriptors + jsImplemented),
|
|
CGWrapper(CGGeneric("using namespace mozilla::dom;"),
|
|
defineOnly=True),
|
|
traitsClasses, curr],
|
|
"\n")
|
|
|
|
# Add header includes.
|
|
curr = CGHeaders(descriptors,
|
|
mainDictionaries + workerDictionaries,
|
|
mainCallbacks + workerCallbacks,
|
|
callbackDescriptors,
|
|
['mozilla/dom/BindingDeclarations.h',
|
|
'mozilla/ErrorResult.h',
|
|
'mozilla/dom/DOMJSClass.h',
|
|
'mozilla/dom/DOMJSProxyHandler.h'],
|
|
['mozilla/dom/BindingUtils.h',
|
|
'mozilla/dom/NonRefcountedDOMObject.h',
|
|
'mozilla/dom/Nullable.h',
|
|
'PrimitiveConversions.h',
|
|
'XPCQuickStubs.h',
|
|
'XPCWrapper.h',
|
|
'nsDOMQS.h',
|
|
'AccessCheck.h',
|
|
'WorkerPrivate.h',
|
|
'nsContentUtils.h',
|
|
'mozilla/Preferences.h',
|
|
# Have to include nsDOMQS.h to get fast arg unwrapping
|
|
# for old-binding things with castability.
|
|
'nsDOMQS.h'
|
|
],
|
|
curr,
|
|
config,
|
|
jsImplemented)
|
|
|
|
# Add include guards.
|
|
curr = CGIncludeGuard(prefix, curr)
|
|
|
|
# Add the auto-generated comment.
|
|
curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
|
|
|
|
# Store the final result.
|
|
self.root = curr
|
|
|
|
def declare(self):
|
|
return stripTrailingWhitespace(self.root.declare())
|
|
def define(self):
|
|
return stripTrailingWhitespace(self.root.define())
|
|
|
|
def deps(self):
|
|
return self.root.deps()
|
|
|
|
class CGNativeMember(ClassMethod):
|
|
def __init__(self, descriptor, member, name, signature, extendedAttrs,
|
|
breakAfter=True, passCxAsNeeded=True, visibility="public",
|
|
jsObjectsArePtr=False, variadicIsSequence=False):
|
|
"""
|
|
If jsObjectsArePtr is true, typed arrays and "object" will be
|
|
passed as JSObject*
|
|
"""
|
|
self.descriptor = descriptor
|
|
self.member = member
|
|
self.extendedAttrs = extendedAttrs
|
|
self.resultAlreadyAddRefed = isResultAlreadyAddRefed(self.descriptor,
|
|
self.extendedAttrs)
|
|
self.passCxAsNeeded = passCxAsNeeded
|
|
self.jsObjectsArePtr = jsObjectsArePtr
|
|
self.variadicIsSequence = variadicIsSequence
|
|
breakAfterSelf = "\n" if breakAfter else ""
|
|
ClassMethod.__init__(self, name,
|
|
self.getReturnType(signature[0], False),
|
|
self.getArgs(signature[0], signature[1]),
|
|
static=member.isStatic(),
|
|
# Mark our getters, which are attrs that
|
|
# have a non-void return type, as const.
|
|
const=(not member.isStatic() and member.isAttr() and
|
|
not signature[0].isVoid()),
|
|
breakAfterReturnDecl=" ",
|
|
breakAfterSelf=breakAfterSelf,
|
|
visibility=visibility)
|
|
|
|
def getReturnType(self, type, isMember):
|
|
return self.getRetvalInfo(type, isMember)[0]
|
|
|
|
def getRetvalInfo(self, type, isMember):
|
|
"""
|
|
Returns a tuple:
|
|
|
|
The first element is the type declaration for the retval
|
|
|
|
The second element is a default value that can be used on error returns.
|
|
For cases whose behavior depends on isMember, the second element will be
|
|
None if isMember is true.
|
|
|
|
The third element is a template for actually returning a value stored in
|
|
"${declName}" and "${holderName}". This means actually returning it if
|
|
we're not outparam, else assigning to the "retval" outparam. If
|
|
isMember is true, this can be None, since in that case the caller will
|
|
never examine this value.
|
|
"""
|
|
if type.isVoid():
|
|
return "void", "", ""
|
|
if type.isPrimitive() and type.tag() in builtinNames:
|
|
result = CGGeneric(builtinNames[type.tag()])
|
|
defaultReturnArg = "0"
|
|
if type.nullable():
|
|
result = CGTemplatedType("Nullable", result)
|
|
defaultReturnArg = ""
|
|
return (result.define(),
|
|
"%s(%s)" % (result.define(), defaultReturnArg),
|
|
"return ${declName};")
|
|
if type.isString():
|
|
if isMember:
|
|
# No need for a third element in the isMember case
|
|
return "nsString", None, None
|
|
# Outparam
|
|
return "void", "", "retval = ${declName};"
|
|
if type.isEnum():
|
|
enumName = type.unroll().inner.identifier.name
|
|
if type.nullable():
|
|
enumName = CGTemplatedType("Nullable",
|
|
CGGeneric(enumName)).define()
|
|
defaultValue = "%s()" % enumName
|
|
else:
|
|
defaultValue = "%s(0)" % enumName
|
|
return enumName, defaultValue, "return ${declName};"
|
|
if type.isGeckoInterface():
|
|
iface = type.unroll().inner;
|
|
nativeType = self.descriptor.getDescriptor(
|
|
iface.identifier.name).nativeType
|
|
# Now trim off unnecessary namespaces
|
|
nativeType = nativeType.split("::")
|
|
if nativeType[0] == "mozilla":
|
|
nativeType.pop(0)
|
|
if nativeType[0] == "dom":
|
|
nativeType.pop(0)
|
|
result = CGGeneric("::".join(nativeType))
|
|
if self.resultAlreadyAddRefed:
|
|
if isMember:
|
|
holder = "nsRefPtr"
|
|
else:
|
|
holder = "already_AddRefed"
|
|
if memberIsCreator(self.member):
|
|
warning = ""
|
|
else:
|
|
warning = "// Mark this as resultNotAddRefed to return raw pointers\n"
|
|
result = CGWrapper(result,
|
|
pre=("%s%s<" % (warning, holder)),
|
|
post=">")
|
|
else:
|
|
result = CGWrapper(result, post="*")
|
|
if iface.isExternal():
|
|
# The holder is an nsRefPtr. If we're nullable and end up null,
|
|
# the holder will be null anyway, so it's safe to just always
|
|
# return it here.
|
|
returnCode = "return ${holderName}.forget();"
|
|
elif iface.isCallback():
|
|
# The decl is an OwningNonNull or nsRefPtr, depending
|
|
# on whether we're nullable.
|
|
returnCode = "return ${declName}.forget();"
|
|
elif type.nullable():
|
|
# Decl is a raw pointer
|
|
returnCode = ("NS_IF_ADDREF(${declName});\n"
|
|
"return ${declName};")
|
|
else:
|
|
# Decl is a non-null raw pointer.
|
|
returnCode = ("NS_ADDREF(${declName});\n"
|
|
"return ${declName};")
|
|
return result.define(), "nullptr", returnCode
|
|
if type.isCallback():
|
|
return ("already_AddRefed<%s>" % type.unroll().identifier.name,
|
|
"nullptr", "return ${declName}.forget();")
|
|
if type.isAny():
|
|
return "JS::Value", "JS::UndefinedValue()", "return ${declName};"
|
|
if type.isObject():
|
|
if type.nullable():
|
|
returnCode = "return ${declName};"
|
|
else:
|
|
returnCode = "return ${declName}.Ptr();"
|
|
return "JSObject*", "nullptr", returnCode
|
|
if type.isSpiderMonkeyInterface():
|
|
if type.nullable():
|
|
returnCode = "return ${declName} ? ${declName}->Obj() : nullptr;"
|
|
else:
|
|
returnCode = ("return static_cast<%s&>(${declName}).Obj();" % type.name)
|
|
return "JSObject*", "nullptr", returnCode
|
|
if type.isSequence():
|
|
# If we want to handle sequence-of-sequences return values, we're
|
|
# going to need to fix example codegen to not produce nsTArray<void>
|
|
# for the relevant argument...
|
|
assert not isMember
|
|
# Outparam.
|
|
if type.nullable():
|
|
returnCode = ("if (${declName}.IsNull()) {\n"
|
|
" retval.SetNull();\n"
|
|
"} else {\n"
|
|
" retval.SetValue().SwapElements(${declName}.Value());\n"
|
|
"}")
|
|
else:
|
|
returnCode = "retval.SwapElements(${declName});"
|
|
return "void", "", returnCode
|
|
raise TypeError("Don't know how to declare return value for %s" %
|
|
type)
|
|
|
|
def getArgs(self, returnType, argList):
|
|
args = [self.getArg(arg) for arg in argList]
|
|
# Now the outparams
|
|
if returnType.isString():
|
|
args.append(Argument("nsString&", "retval"))
|
|
elif returnType.isSequence():
|
|
nullable = returnType.nullable()
|
|
if nullable:
|
|
returnType = returnType.inner
|
|
# And now the actual underlying type
|
|
elementDecl = self.getReturnType(returnType.inner, True)
|
|
type = CGTemplatedType("nsTArray", CGGeneric(elementDecl))
|
|
if nullable:
|
|
type = CGTemplatedType("Nullable", type)
|
|
args.append(Argument("%s&" % type.define(), "retval"))
|
|
# And the ErrorResult
|
|
if not 'infallible' in self.extendedAttrs:
|
|
# Use aRv so it won't conflict with local vars named "rv"
|
|
args.append(Argument("ErrorResult&", "aRv"))
|
|
# The legacycaller thisval
|
|
if self.member.isMethod() and self.member.isLegacycaller():
|
|
# If it has an identifier, we can't deal with it yet
|
|
assert self.member.isIdentifierLess()
|
|
args.insert(0, Argument("JS::Value", "aThisVal"))
|
|
# And jscontext bits.
|
|
if (self.passCxAsNeeded and
|
|
needCx(returnType, argList, self.extendedAttrs,
|
|
self.descriptor)):
|
|
args.insert(0, Argument("JSContext*", "cx"))
|
|
# And if we're static, a global
|
|
if self.member.isStatic():
|
|
globalObjectType = "GlobalObject"
|
|
if self.descriptor.workers:
|
|
globalObjectType = "Worker" + globalObjectType
|
|
args.insert(0, Argument("const %s&" % globalObjectType, "global"))
|
|
return args
|
|
|
|
def doGetArgType(self, type, optional, isMember):
|
|
"""
|
|
The main work of getArgType. Returns a string type decl, whether this
|
|
is a const ref, as well as whether the type should be wrapped in
|
|
Nullable as needed.
|
|
"""
|
|
if type.isArray():
|
|
raise TypeError("Can't handle array arguments yet")
|
|
|
|
if type.isSequence():
|
|
nullable = type.nullable()
|
|
if nullable:
|
|
type = type.inner
|
|
elementType = type.inner
|
|
argType = self.getArgType(elementType, False, False, True)[0]
|
|
decl = CGTemplatedType("Sequence", argType)
|
|
return decl.define(), True, True
|
|
|
|
if type.isUnion():
|
|
if type.nullable():
|
|
type = type.inner
|
|
return str(type), True, True
|
|
|
|
if (type.isGeckoInterface() and
|
|
(not type.isCallbackInterface() or
|
|
type.unroll().inner.identifier.name == "EventListener")):
|
|
iface = type.unroll().inner
|
|
argIsPointer = type.nullable() or iface.isExternal()
|
|
forceOwningType = iface.isCallback() or isMember
|
|
if argIsPointer:
|
|
if (optional or isMember) and forceOwningType:
|
|
typeDecl = "nsRefPtr<%s>"
|
|
else:
|
|
typeDecl = "%s*"
|
|
else:
|
|
if optional or isMember:
|
|
if forceOwningType:
|
|
typeDecl = "OwningNonNull<%s>"
|
|
else:
|
|
typeDecl = "NonNull<%s>"
|
|
else:
|
|
typeDecl = "%s&"
|
|
return ((typeDecl %
|
|
self.descriptor.getDescriptor(iface.identifier.name).nativeType),
|
|
False, False)
|
|
|
|
if type.isSpiderMonkeyInterface():
|
|
assert not isMember
|
|
if self.jsObjectsArePtr:
|
|
typeDecl = "JSObject*"
|
|
else:
|
|
if type.nullable():
|
|
typeDecl = "%s*"
|
|
else:
|
|
typeDecl = "%s"
|
|
if not optional:
|
|
typeDecl += "&"
|
|
typeDecl = typeDecl % type.name
|
|
return typeDecl, False, False
|
|
|
|
if type.isString():
|
|
if isMember:
|
|
declType = "nsString"
|
|
else:
|
|
declType = "nsAString"
|
|
return declType, True, False
|
|
|
|
if type.isEnum():
|
|
return type.unroll().inner.identifier.name, False, True
|
|
|
|
if type.isCallback() or type.isCallbackInterface():
|
|
forceOwningType = optional or isMember
|
|
if type.nullable():
|
|
if forceOwningType:
|
|
declType = "nsRefPtr<%s>"
|
|
else:
|
|
declType = "%s*"
|
|
else:
|
|
if forceOwningType:
|
|
declType = "OwningNonNull<%s>"
|
|
else:
|
|
declType = "%s&"
|
|
if type.isCallback():
|
|
name = type.unroll().identifier.name
|
|
else:
|
|
name = type.unroll().inner.identifier.name
|
|
return declType % name, False, False
|
|
|
|
if type.isAny():
|
|
return "JS::Value", False, False
|
|
|
|
if type.isObject():
|
|
if type.nullable() or self.jsObjectsArePtr:
|
|
declType = "%s*"
|
|
elif optional:
|
|
declType = "NonNull<%s>"
|
|
else:
|
|
declType = "%s&"
|
|
return (declType % "JSObject"), False, False
|
|
|
|
if type.isDictionary():
|
|
typeName = CGDictionary.makeDictionaryName(type.inner,
|
|
self.descriptor.workers)
|
|
return typeName, True, True
|
|
|
|
assert type.isPrimitive()
|
|
|
|
return builtinNames[type.tag()], False, True
|
|
|
|
def getArgType(self, type, optional, variadic, isMember):
|
|
"""
|
|
Get the type of an argument declaration. Returns the type CGThing, and
|
|
whether this should be a const ref.
|
|
"""
|
|
(decl, ref, handleNullable) = self.doGetArgType(type, optional,
|
|
isMember or variadic)
|
|
decl = CGGeneric(decl)
|
|
if handleNullable and type.nullable():
|
|
decl = CGTemplatedType("Nullable", decl)
|
|
ref = True
|
|
if variadic:
|
|
arrayType = "Sequence" if self.variadicIsSequence else "nsTArray"
|
|
decl = CGTemplatedType(arrayType, decl)
|
|
ref = True
|
|
elif optional:
|
|
# Note: All variadic args claim to be optional, but we can just use
|
|
# empty arrays to represent them not being present.
|
|
decl = CGTemplatedType("Optional", decl)
|
|
ref = True
|
|
return (decl, ref)
|
|
|
|
def getArg(self, arg):
|
|
"""
|
|
Get the full argument declaration for an argument
|
|
"""
|
|
(decl, ref) = self.getArgType(arg.type,
|
|
arg.optional and not arg.defaultValue,
|
|
arg.variadic,
|
|
False)
|
|
if ref:
|
|
decl = CGWrapper(decl, pre="const ", post="&")
|
|
|
|
return Argument(decl.define(), arg.identifier.name)
|
|
|
|
|
|
class CGExampleMethod(CGNativeMember):
|
|
def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
|
|
CGNativeMember.__init__(self, descriptor, method,
|
|
CGSpecializedMethod.makeNativeName(descriptor,
|
|
method),
|
|
signature,
|
|
descriptor.getExtendedAttributes(method),
|
|
breakAfter=breakAfter,
|
|
variadicIsSequence=True)
|
|
def define(self, cgClass):
|
|
return ''
|
|
|
|
class CGExampleGetter(CGNativeMember):
|
|
def __init__(self, descriptor, attr):
|
|
CGNativeMember.__init__(self, descriptor, attr,
|
|
CGSpecializedGetter.makeNativeName(descriptor,
|
|
attr),
|
|
(attr.type, []),
|
|
descriptor.getExtendedAttributes(attr,
|
|
getter=True))
|
|
def define(self, cgClass):
|
|
return ''
|
|
|
|
class CGExampleSetter(CGNativeMember):
|
|
def __init__(self, descriptor, attr):
|
|
CGNativeMember.__init__(self, descriptor, attr,
|
|
CGSpecializedSetter.makeNativeName(descriptor,
|
|
attr),
|
|
(BuiltinTypes[IDLBuiltinType.Types.void],
|
|
[FakeArgument(attr.type, attr)]),
|
|
descriptor.getExtendedAttributes(attr,
|
|
setter=True))
|
|
def define(self, cgClass):
|
|
return ''
|
|
|
|
class CGBindingImplClass(CGClass):
|
|
"""
|
|
Common codegen for generating a C++ implementation of a WebIDL interface
|
|
"""
|
|
def __init__(self, descriptor, cgMethod, cgGetter, cgSetter):
|
|
"""
|
|
cgMethod, cgGetter and cgSetter are classes used to codegen methods,
|
|
getters and setters.
|
|
"""
|
|
self.descriptor = descriptor
|
|
self._deps = descriptor.interface.getDeps()
|
|
|
|
iface = descriptor.interface
|
|
|
|
self.methodDecls = []
|
|
def appendMethod(m, isConstructor=False):
|
|
sigs = m.signatures()
|
|
for s in sigs[:-1]:
|
|
# Don't put an empty line after overloads, until we
|
|
# get to the last one.
|
|
self.methodDecls.append(cgMethod(descriptor, m, s,
|
|
isConstructor,
|
|
breakAfter=False))
|
|
self.methodDecls.append(cgMethod(descriptor, m, sigs[-1],
|
|
isConstructor))
|
|
|
|
if iface.ctor():
|
|
appendMethod(iface.ctor(), isConstructor=True)
|
|
for n in iface.namedConstructors:
|
|
appendMethod(n, isConstructor=True)
|
|
for m in iface.members:
|
|
if m.isMethod():
|
|
if m.isIdentifierLess():
|
|
continue
|
|
appendMethod(m)
|
|
elif m.isAttr():
|
|
self.methodDecls.append(cgGetter(descriptor, m))
|
|
if not m.readonly:
|
|
self.methodDecls.append(cgSetter(descriptor, m))
|
|
|
|
# Now do the special operations
|
|
def appendSpecialOperation(name, op):
|
|
if op is None:
|
|
return
|
|
if name == "IndexedCreator" or name == "NamedCreator":
|
|
# These are identical to the setters
|
|
return
|
|
assert len(op.signatures()) == 1
|
|
(returnType, args) = op.signatures()[0]
|
|
# Make a copy of the args, since we plan to modify them.
|
|
args = list(args)
|
|
if op.isGetter() or op.isDeleter():
|
|
# This is a total hack. The '&' belongs with the
|
|
# type, not the name! But it works, and is simpler
|
|
# than trying to somehow make this pretty.
|
|
args.append(FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean],
|
|
op, name="&found"))
|
|
if name == "Stringifier":
|
|
if op.isIdentifierLess():
|
|
# XXXbz I wish we were consistent about our renaming here.
|
|
name = "Stringify"
|
|
else:
|
|
# We already added this method
|
|
return
|
|
if name == "LegacyCaller":
|
|
if op.isIdentifierLess():
|
|
# XXXbz I wish we were consistent about our renaming here.
|
|
name = "LegacyCall"
|
|
else:
|
|
# We already added this method
|
|
return
|
|
self.methodDecls.append(
|
|
CGNativeMember(descriptor, op,
|
|
name,
|
|
(returnType, args),
|
|
descriptor.getExtendedAttributes(op)))
|
|
# Sort things by name so we get stable ordering in the output.
|
|
ops = descriptor.operations.items()
|
|
ops.sort(key=lambda x: x[0])
|
|
for (name, op) in ops:
|
|
appendSpecialOperation(name, op)
|
|
# If we support indexed properties, then we need a Length()
|
|
# method so we know which indices are supported.
|
|
if descriptor.supportsIndexedProperties():
|
|
# But we don't need it if we already have an infallible
|
|
# "length" attribute, which we often do.
|
|
haveLengthAttr = any(
|
|
m for m in iface.members if m.isAttr() and
|
|
CGSpecializedGetter.makeNativeName(descriptor, m) == "Length")
|
|
if not haveLengthAttr:
|
|
self.methodDecls.append(
|
|
CGNativeMember(descriptor, FakeMember(),
|
|
"Length",
|
|
(BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
|
|
[]),
|
|
{ "infallible": True }))
|
|
# And if we support named properties we need to be able to
|
|
# enumerate the supported names.
|
|
if descriptor.supportsNamedProperties():
|
|
self.methodDecls.append(
|
|
CGNativeMember(
|
|
descriptor, FakeMember(),
|
|
"GetSupportedNames",
|
|
(IDLSequenceType(None,
|
|
BuiltinTypes[IDLBuiltinType.Types.domstring]),
|
|
[]),
|
|
{ "infallible": True }))
|
|
|
|
wrapArgs = [Argument('JSContext*', 'aCx'),
|
|
Argument('JS::Handle<JSObject*>', 'aScope')]
|
|
self.methodDecls.insert(0,
|
|
ClassMethod("WrapObject", "JSObject*",
|
|
wrapArgs, virtual=descriptor.wrapperCache,
|
|
breakAfterReturnDecl=" ",
|
|
override=descriptor.wrapperCache,
|
|
body=self.getWrapObjectBody()))
|
|
self.methodDecls.insert(0,
|
|
ClassMethod("GetParentObject",
|
|
self.getGetParentObjectReturnType(),
|
|
[], const=True,
|
|
breakAfterReturnDecl=" ",
|
|
body=self.getGetParentObjectBody()))
|
|
|
|
# Invoke CGClass.__init__ in any subclasses afterwards to do the actual codegen.
|
|
|
|
def getWrapObjectBody(self):
|
|
return None
|
|
|
|
def getGetParentObjectReturnType(self):
|
|
return ("// TODO: return something sensible here, and change the return type\n"
|
|
"%s*" % self.descriptor.nativeType.split('::')[-1])
|
|
|
|
def getGetParentObjectBody(self):
|
|
return None
|
|
|
|
def deps(self):
|
|
return self._deps
|
|
|
|
|
|
class CGExampleClass(CGBindingImplClass):
|
|
"""
|
|
Codegen for the actual example class implementation for this descriptor
|
|
"""
|
|
def __init__(self, descriptor):
|
|
CGBindingImplClass.__init__(self, descriptor, CGExampleMethod, CGExampleGetter, CGExampleSetter)
|
|
|
|
extradeclarations=(
|
|
"public:\n"
|
|
" NS_DECL_CYCLE_COLLECTING_ISUPPORTS\n"
|
|
" NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(%s)\n"
|
|
"\n" % descriptor.nativeType.split('::')[-1])
|
|
|
|
CGClass.__init__(self, descriptor.nativeType.split('::')[-1],
|
|
bases=[ClassBase("nsISupports /* Change nativeOwnership in the binding configuration if you don't want this */"),
|
|
ClassBase("nsWrapperCache /* Change wrapperCache in the binding configuration if you don't want this */")],
|
|
constructors=[ClassConstructor([],
|
|
visibility="public")],
|
|
destructor=ClassDestructor(visibility="public"),
|
|
methods=self.methodDecls,
|
|
decorators="MOZ_FINAL",
|
|
extradeclarations=extradeclarations)
|
|
|
|
def define(self):
|
|
# Just override CGClass and do our own thing
|
|
classImpl = """
|
|
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(${nativeType})
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(${nativeType})
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(${nativeType})
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${nativeType})
|
|
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
${nativeType}::${nativeType}()
|
|
{
|
|
SetIsDOMBinding();
|
|
}
|
|
|
|
${nativeType}::~${nativeType}()
|
|
{
|
|
}
|
|
|
|
JSObject*
|
|
${nativeType}::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
|
|
{
|
|
return ${ifaceName}Binding::Wrap(aCx, aScope, this);
|
|
}
|
|
|
|
"""
|
|
return string.Template(classImpl).substitute(
|
|
{ "ifaceName": self.descriptor.name,
|
|
"nativeType": self.descriptor.nativeType.split('::')[-1] }
|
|
)
|
|
|
|
|
|
class CGExampleRoot(CGThing):
|
|
"""
|
|
Root codegen class for example implementation generation. Instantiate the
|
|
class and call declare or define to generate header or cpp code,
|
|
respectively.
|
|
"""
|
|
def __init__(self, config, interfaceName):
|
|
# Let's assume we're not doing workers stuff
|
|
descriptor = config.getDescriptor(interfaceName, False)
|
|
|
|
self.root = CGWrapper(CGExampleClass(descriptor),
|
|
pre="\n", post="\n")
|
|
|
|
self.root = CGNamespace.build(["mozilla", "dom"], self.root);
|
|
|
|
self.root = CGList([CGClassForwardDeclare("JSContext", isStruct=True),
|
|
self.root], "\n")
|
|
|
|
# Throw in our #includes
|
|
self.root = CGHeaders([], [], [], [],
|
|
[ "nsWrapperCache.h",
|
|
"nsCycleCollectionParticipant.h",
|
|
"mozilla/Attributes.h",
|
|
"mozilla/ErrorResult.h" ],
|
|
[ "%s.h" % interfaceName,
|
|
"mozilla/dom/%sBinding.h" % interfaceName,
|
|
"nsContentUtils.h" ], self.root);
|
|
|
|
# In the header, #pragma once before everything
|
|
self.root = CGWrapper(self.root, declarePre="#pragma once\n\n")
|
|
|
|
# And our license block comes before everything else
|
|
self.root = CGWrapper(self.root, pre="""/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
|
/* 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/. */
|
|
|
|
""")
|
|
|
|
def declare(self):
|
|
return self.root.declare()
|
|
|
|
def define(self):
|
|
return self.root.define()
|
|
|
|
|
|
def jsImplName(name):
|
|
return name + "JSImpl"
|
|
|
|
class CGJSImplMethod(CGNativeMember):
|
|
def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
|
|
CGNativeMember.__init__(self, descriptor, method,
|
|
CGSpecializedMethod.makeNativeName(descriptor,
|
|
method),
|
|
signature,
|
|
descriptor.getExtendedAttributes(method),
|
|
breakAfter=breakAfter,
|
|
variadicIsSequence=True,
|
|
passCxAsNeeded=False)
|
|
self.signature = signature
|
|
if isConstructor:
|
|
self.body = self.getConstructorImpl()
|
|
else:
|
|
self.body = self.getImpl()
|
|
|
|
def getImpl(self):
|
|
callbackArgs = [arg.name for arg in self.getArgs(self.signature[0], self.signature[1])]
|
|
return 'return mImpl->%s(%s);' % (self.name, ", ".join(callbackArgs))
|
|
|
|
def getConstructorImpl(self):
|
|
assert self.descriptor.interface.isJSImplemented()
|
|
if self.name != 'Constructor':
|
|
raise TypeError("Named constructors are not supported for JS implemented WebIDL. See bug 851287.")
|
|
if len(self.signature[1]) != 0:
|
|
raise TypeError("Constructors with arguments are unsupported. See bug 851178.")
|
|
|
|
return string.Template(
|
|
""" // Get the window to use as a parent and for initialization.
|
|
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(global.Get());
|
|
if (!window) {
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
return nullptr;
|
|
}
|
|
// Get the XPCOM component containing the JS implementation.
|
|
nsCOMPtr<nsISupports> implISupports = do_CreateInstance("${contractId}");
|
|
MOZ_ASSERT(implISupports, "Failed to get JS implementation instance from contract ID.");
|
|
if (!implISupports) {
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
return nullptr;
|
|
}
|
|
// Initialize the object, if it implements nsIDOMGlobalPropertyInitializer.
|
|
nsCOMPtr<nsIDOMGlobalPropertyInitializer> gpi = do_QueryInterface(implISupports);
|
|
if (gpi) {
|
|
JS::Value initReturn = JSVAL_VOID;
|
|
nsresult rv = gpi->Init(window, &initReturn);
|
|
if (NS_FAILED(rv)) {
|
|
aRv.Throw(rv);
|
|
return nullptr;
|
|
}
|
|
MOZ_ASSERT(initReturn.isUndefined(),
|
|
"Expected nsIDOMGlobalPropertyInitializer to return undefined");
|
|
}
|
|
// Extract the JS implementation from the XPCOM object.
|
|
nsCOMPtr<nsIXPConnectWrappedJS> implWrapped = do_QueryInterface(implISupports);
|
|
MOZ_ASSERT(implWrapped, "Failed to get wrapped JS from XPCOM component.");
|
|
if (!implWrapped) {
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
return nullptr;
|
|
}
|
|
JSObject* jsImplObj;
|
|
if (NS_FAILED(implWrapped->GetJSObject(&jsImplObj))) {
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
return nullptr;
|
|
}
|
|
// Build the C++ implementation.
|
|
nsRefPtr<${implClass}> impl = new ${implClass}(jsImplObj, window);
|
|
return impl.forget();""").substitute({"implClass" : self.descriptor.name,
|
|
"contractId" : self.descriptor.interface.getJSImplementation()
|
|
})
|
|
|
|
# We're always fallible
|
|
def callbackGetterName(attr):
|
|
return "Get" + MakeNativeName(attr.identifier.name)
|
|
|
|
def callbackSetterName(attr):
|
|
return "Set" + MakeNativeName(attr.identifier.name)
|
|
|
|
class CGJSImplGetter(CGNativeMember):
|
|
def __init__(self, descriptor, attr):
|
|
CGNativeMember.__init__(self, descriptor, attr,
|
|
CGSpecializedGetter.makeNativeName(descriptor,
|
|
attr),
|
|
(attr.type, []),
|
|
descriptor.getExtendedAttributes(attr,
|
|
getter=True),
|
|
passCxAsNeeded=False)
|
|
self.body = self.getImpl()
|
|
|
|
def getImpl(self):
|
|
callbackArgs = [arg.name for arg in self.getArgs(self.member.type, [])]
|
|
return 'return mImpl->%s(%s);' % (callbackGetterName(self.member), ", ".join(callbackArgs))
|
|
|
|
class CGJSImplSetter(CGNativeMember):
|
|
def __init__(self, descriptor, attr):
|
|
CGNativeMember.__init__(self, descriptor, attr,
|
|
CGSpecializedSetter.makeNativeName(descriptor,
|
|
attr),
|
|
(BuiltinTypes[IDLBuiltinType.Types.void],
|
|
[FakeArgument(attr.type, attr)]),
|
|
descriptor.getExtendedAttributes(attr,
|
|
setter=True),
|
|
passCxAsNeeded=False)
|
|
self.body = self.getImpl()
|
|
|
|
def getImpl(self):
|
|
callbackArgs = [arg.name for arg in self.getArgs(BuiltinTypes[IDLBuiltinType.Types.void],
|
|
[FakeArgument(self.member.type, self.member)])]
|
|
return 'mImpl->%s(%s);' % (callbackSetterName(self.member), ", ".join(callbackArgs))
|
|
|
|
class CGJSImplClass(CGBindingImplClass):
|
|
def __init__(self, descriptor):
|
|
CGBindingImplClass.__init__(self, descriptor, CGJSImplMethod, CGJSImplGetter, CGJSImplSetter)
|
|
|
|
if descriptor.interface.parent:
|
|
parentClass = descriptor.getDescriptor(
|
|
descriptor.interface.parent.identifier.name).jsImplParent
|
|
baseClasses = [ClassBase(parentClass)]
|
|
isupportsDecl = "NS_DECL_ISUPPORTS_INHERITED"
|
|
ccDecl = ("NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(%s, %s)" %
|
|
(descriptor.name, parentClass))
|
|
constructorBody = (
|
|
"// Make sure we're an nsWrapperCache already\n"
|
|
"MOZ_ASSERT(static_cast<nsWrapperCache*>(this));\n"
|
|
"// And that our ancestor has called SetIsDOMBinding()\n"
|
|
"MOZ_ASSERT(IsDOMBinding());")
|
|
extradefinitions= string.Template(
|
|
"NS_IMPL_CYCLE_COLLECTION_INHERITED_2(${ifaceName}, ${parentClass}, mImpl, mParent)\n"
|
|
"NS_IMPL_ADDREF_INHERITED(${ifaceName}, ${parentClass})\n"
|
|
"NS_IMPL_RELEASE_INHERITED(${ifaceName}, ${parentClass})\n"
|
|
"NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(${ifaceName})\n"
|
|
"NS_INTERFACE_MAP_END_INHERITING(${parentClass})\n").substitute(
|
|
{ "ifaceName": self.descriptor.name,
|
|
"parentClass": parentClass })
|
|
else:
|
|
baseClasses = [ClassBase("nsISupports"),
|
|
ClassBase("nsWrapperCache")]
|
|
isupportsDecl = "NS_DECL_CYCLE_COLLECTING_ISUPPORTS"
|
|
ccDecl = ("NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(%s)" %
|
|
descriptor.name)
|
|
constructorBody = "SetIsDOMBinding();"
|
|
extradefinitions= string.Template(
|
|
"NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_2(${ifaceName}, mImpl, mParent)\n"
|
|
"NS_IMPL_CYCLE_COLLECTING_ADDREF(${ifaceName})\n"
|
|
"NS_IMPL_CYCLE_COLLECTING_RELEASE(${ifaceName})\n"
|
|
"NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(${ifaceName})\n"
|
|
" NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY\n"
|
|
" NS_INTERFACE_MAP_ENTRY(nsISupports)\n"
|
|
"NS_INTERFACE_MAP_END\n").substitute({ "ifaceName": self.descriptor.name })
|
|
|
|
extradeclarations=(
|
|
"public:\n"
|
|
" %s\n"
|
|
" %s\n"
|
|
"\n"
|
|
"private:\n"
|
|
" nsRefPtr<%s> mImpl;\n"
|
|
" nsCOMPtr<nsISupports> mParent;\n"
|
|
"\n" % (isupportsDecl, ccDecl, jsImplName(descriptor.name)))
|
|
|
|
if descriptor.interface.hasChildInterfaces():
|
|
decorators = ""
|
|
# We need a public virtual destructor our subclasses can use
|
|
destructor = ClassDestructor(virtual=True, visibility="public")
|
|
else:
|
|
decorators = "MOZ_FINAL"
|
|
destructor = None
|
|
|
|
baseConstructors=["mImpl(new %s(aJSImplObject))" % jsImplName(descriptor.name),
|
|
"mParent(aParent)"]
|
|
parentInterface = descriptor.interface.parent
|
|
while parentInterface:
|
|
if parentInterface.isJSImplemented():
|
|
baseConstructors.insert(
|
|
0, "%s(aJSImplObject, aParent)" % parentClass )
|
|
break
|
|
parentInterface = parentInterface.parent
|
|
if not parentInterface and descriptor.interface.parent:
|
|
# We only have C++ ancestors, so only pass along the window
|
|
baseConstructors.insert(0,
|
|
"%s(aParent)" % parentClass)
|
|
|
|
constructor = ClassConstructor(
|
|
[Argument("JSObject*", "aJSImplObject"),
|
|
Argument("nsPIDOMWindow*", "aParent")],
|
|
visibility="public",
|
|
baseConstructors=baseConstructors,
|
|
body=constructorBody)
|
|
|
|
CGClass.__init__(self, descriptor.name,
|
|
bases=baseClasses,
|
|
constructors=[constructor],
|
|
destructor=destructor,
|
|
methods=self.methodDecls,
|
|
decorators=decorators,
|
|
extradeclarations=extradeclarations,
|
|
extradefinitions=extradefinitions)
|
|
|
|
def getWrapObjectBody(self):
|
|
return ("JS::Rooted<JSObject*> obj(aCx, %sBinding::Wrap(aCx, aScope, this));\n"
|
|
"if (!obj) {\n"
|
|
" return nullptr;\n"
|
|
"}\n"
|
|
"\n"
|
|
"// Now define it on our chrome object\n"
|
|
"JSAutoCompartment ac(aCx, mImpl->Callback());\n"
|
|
"if (!JS_WrapObject(aCx, obj.address())) {\n"
|
|
" return nullptr;\n"
|
|
"}\n"
|
|
'if (!JS_DefineProperty(aCx, mImpl->Callback(), "__DOM_IMPL__", JS::ObjectValue(*obj), nullptr, nullptr, 0)) {\n'
|
|
" return nullptr;\n"
|
|
"}\n"
|
|
"return obj;" % self.descriptor.name)
|
|
|
|
def getGetParentObjectReturnType(self):
|
|
return "nsISupports*"
|
|
|
|
def getGetParentObjectBody(self):
|
|
return "return mParent;"
|
|
|
|
class CGCallback(CGClass):
|
|
def __init__(self, idlObject, descriptorProvider, baseName, methods,
|
|
getters=[], setters=[]):
|
|
self.baseName = baseName
|
|
self._deps = idlObject.getDeps()
|
|
name = idlObject.identifier.name
|
|
if descriptorProvider.workers:
|
|
name += "Workers"
|
|
if isinstance(descriptorProvider, Descriptor) and descriptorProvider.interface.isJSImplemented():
|
|
name = jsImplName(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)
|
|
|
|
def getConstructors(self):
|
|
return [ClassConstructor(
|
|
[Argument("JSObject*", "aCallback")],
|
|
bodyInHeader=True,
|
|
visibility="public",
|
|
explicit=True,
|
|
baseConstructors=[
|
|
"%s(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 == "JSContext*"
|
|
assert args[1].name == "aThisObj" and args[1].argType == "JS::Handle<JSObject*>"
|
|
args = args[2:]
|
|
# Record the names of all the arguments, so we can use them when we call
|
|
# the private method.
|
|
argnames = [arg.name for arg in args]
|
|
argnamesWithThis = ["s.GetContext()", "thisObjJS"] + argnames
|
|
argnamesWithoutThis = ["s.GetContext()", "JS::NullPtr()"] + 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",
|
|
"eReportExceptions"))
|
|
# And now insert our template argument.
|
|
argsWithoutThis = list(args)
|
|
args.insert(0, Argument("const T&", "thisObj"))
|
|
|
|
setupCall = ("CallSetup s(mCallback, aRv, aExceptionHandling);\n"
|
|
"if (!s.GetContext()) {\n"
|
|
" aRv.Throw(NS_ERROR_UNEXPECTED);\n"
|
|
" return${errorReturn};\n"
|
|
"}\n")
|
|
|
|
bodyWithThis = string.Template(
|
|
setupCall+
|
|
"JS::Rooted<JSObject*> thisObjJS(s.GetContext(),\n"
|
|
" WrapCallThisObject(s.GetContext(), CallbackPreserveColor(), thisObj));\n"
|
|
"if (!thisObjJS) {\n"
|
|
" aRv.Throw(NS_ERROR_FAILURE);\n"
|
|
" return${errorReturn};\n"
|
|
"}\n"
|
|
"return ${methodName}(${callArgs});").substitute({
|
|
"errorReturn" : method.getDefaultRetval(),
|
|
"callArgs" : ", ".join(argnamesWithThis),
|
|
"methodName": method.name,
|
|
})
|
|
bodyWithoutThis = string.Template(
|
|
setupCall +
|
|
"return ${methodName}(${callArgs});").substitute({
|
|
"errorReturn" : method.getDefaultRetval(),
|
|
"callArgs" : ", ".join(argnamesWithoutThis),
|
|
"methodName": method.name,
|
|
})
|
|
return [ClassMethod(method.name, method.returnType, args,
|
|
bodyInHeader=True,
|
|
templateArgs=["typename T"],
|
|
body=bodyWithThis),
|
|
ClassMethod(method.name, method.returnType, argsWithoutThis,
|
|
bodyInHeader=True,
|
|
body=bodyWithoutThis),
|
|
method]
|
|
|
|
def deps(self):
|
|
return self._deps
|
|
|
|
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) + [
|
|
ClassConstructor(
|
|
[Argument("CallbackFunction*", "aOther")],
|
|
bodyInHeader=True,
|
|
visibility="public",
|
|
explicit=True,
|
|
baseConstructors=[
|
|
"CallbackFunction(aOther)"
|
|
])]
|
|
|
|
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()]
|
|
methods = [CallbackOperation(m, sig, descriptor) for m in methods
|
|
for sig in m.signatures()]
|
|
CGCallback.__init__(self, iface, descriptor, "CallbackInterface",
|
|
methods, getters=getters, setters=setters)
|
|
|
|
class FakeMember():
|
|
def __init__(self):
|
|
self.treatUndefinedAs = self.treatNullAs = "Default"
|
|
def isStatic(self):
|
|
return False
|
|
def isAttr(self):
|
|
return False
|
|
def isMethod(self):
|
|
return False
|
|
def getExtendedAttribute(self, name):
|
|
# Claim to be a [Creator] so we can avoid the "mark this
|
|
# resultNotAddRefed" comments CGNativeMember codegen would
|
|
# otherwise stick in.
|
|
if name == "Creator":
|
|
return True
|
|
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.Length()" % (self.argCount,
|
|
lastArg.identifier.name))
|
|
else:
|
|
self.argCountStr = "%d" % self.argCount
|
|
self.needThisHandling = needThisHandling
|
|
# If needThisHandling, we generate ourselves as private and the caller
|
|
# will handle generating public versions that handle the "this" stuff.
|
|
visibility = "private" if needThisHandling else "public"
|
|
# 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={},
|
|
passCxAsNeeded=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=("aRv.Throw(NS_ERROR_UNEXPECTED);\n"
|
|
"return%s;" % self.getDefaultRetval())
|
|
self.body = self.getImpl()
|
|
|
|
def getImpl(self):
|
|
replacements = {
|
|
"declRval": self.getRvalDecl(),
|
|
"errorReturn" : self.getDefaultRetval(),
|
|
"returnResult": self.getResultConversion(),
|
|
"convertArgs": self.getArgConversions(),
|
|
"doCall": self.getCall(),
|
|
"setupCall": self.getCallSetup(),
|
|
}
|
|
if self.argCount > 0:
|
|
replacements["argCount"] = self.argCountStr
|
|
replacements["argvDecl"] = string.Template(
|
|
"JS::AutoValueVector argv(cx);\n"
|
|
"if (!argv.resize(${argCount})) {\n"
|
|
" aRv.Throw(NS_ERROR_OUT_OF_MEMORY);\n"
|
|
" return${errorReturn};\n"
|
|
"}\n"
|
|
).substitute(replacements)
|
|
else:
|
|
# Avoid weird 0-sized arrays
|
|
replacements["argvDecl"] = ""
|
|
|
|
return string.Template(
|
|
# Newlines and semicolons are in the values
|
|
"${setupCall}"
|
|
"${declRval}"
|
|
"${argvDecl}"
|
|
"${convertArgs}"
|
|
"${doCall}"
|
|
"${returnResult}").substitute(replacements)
|
|
|
|
def getResultConversion(self):
|
|
replacements = {
|
|
"val": "rval",
|
|
"valPtr": "rval.address()",
|
|
"holderName" : "rvalHolder",
|
|
"declName" : "rvalDecl",
|
|
# We actually want to pass in a null scope object here, because
|
|
# wrapping things into our current compartment (that of mCallback)
|
|
# is what we want.
|
|
"obj": "nullptr"
|
|
}
|
|
|
|
convertType = instantiateJSToNativeConversionTemplate(
|
|
getJSToNativeConversionTemplate(self.retvalType,
|
|
self.descriptor,
|
|
exceptionCode=self.exceptionCode,
|
|
isCallbackReturnValue=True),
|
|
replacements)
|
|
assignRetval = string.Template(
|
|
self.getRetvalInfo(self.retvalType,
|
|
False)[2]).substitute(replacements)
|
|
return convertType.define() + "\n" + assignRetval
|
|
|
|
def getArgConversions(self):
|
|
# Just reget the arglist from self.originalSig, because our superclasses
|
|
# just have way to many members they like to clobber, so I can't find a
|
|
# safe member name to store it in.
|
|
argConversions = [self.getArgConversion(i, arg) for (i, arg)
|
|
in enumerate(self.originalSig[1])]
|
|
# Do them back to front, so our argc modifications will work
|
|
# correctly, because we examine trailing arguments first.
|
|
argConversions.reverse();
|
|
# Wrap each one in a scope so that any locals it has don't leak out, and
|
|
# also so that we can just "break;" for our successCode.
|
|
argConversions = [CGWrapper(CGIndenter(CGGeneric(c)),
|
|
pre="do {\n",
|
|
post="\n} while (0);")
|
|
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 += ".Value()"
|
|
if arg.type.isString():
|
|
# XPConnect string-to-JS conversion wants to mutate the string. So
|
|
# let's give it a string it can mutate
|
|
# XXXbz if we try to do a sequence of strings, this will kinda fail.
|
|
result = "mutableStr"
|
|
prepend = "nsString mutableStr(%s);\n" % argval
|
|
else:
|
|
result = argval
|
|
prepend = ""
|
|
|
|
conversion = prepend + wrapForType(
|
|
arg.type, self.descriptor,
|
|
{
|
|
'result' : result,
|
|
'successCode' : "continue;" if arg.variadic else "break;",
|
|
'jsvalRef' : "argv[%s]" % jsvalIndex,
|
|
'jsvalPtr' : "&argv[%s]" % jsvalIndex,
|
|
# XXXbz we don't have anything better to use for 'obj',
|
|
# really... It's OK to use CallbackPreserveColor because
|
|
# CallSetup already handled the unmark-gray bits for us.
|
|
'obj' : 'CallbackPreserveColor()',
|
|
'isCreator': False,
|
|
'exceptionCode' : self.exceptionCode
|
|
})
|
|
if arg.variadic:
|
|
conversion = string.Template(
|
|
"for (uint32_t idx = 0; idx < ${arg}.Length(); ++idx) {\n" +
|
|
CGIndenter(CGGeneric(conversion)).define() + "\n"
|
|
"}\n"
|
|
"break;").substitute({ "arg": arg.identifier.name })
|
|
elif arg.optional and not arg.defaultValue:
|
|
conversion = (
|
|
CGIfWrapper(CGGeneric(conversion),
|
|
"%s.WasPassed()" % arg.identifier.name).define() +
|
|
" else if (argc == %d) {\n"
|
|
" // This is our current trailing argument; reduce argc\n"
|
|
" --argc;\n"
|
|
"} else {\n"
|
|
" argv[%d] = JS::UndefinedValue();\n"
|
|
"}" % (i+1, i))
|
|
return conversion
|
|
|
|
def getDefaultRetval(self):
|
|
default = self.getRetvalInfo(self.retvalType, False)[1]
|
|
if len(default) != 0:
|
|
default = " " + default
|
|
return default
|
|
|
|
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.
|
|
return args + [Argument("ExceptionHandling", "aExceptionHandling",
|
|
"eReportExceptions")]
|
|
# We want to allow the caller to pass in a "this" object, as
|
|
# well as a JSContext.
|
|
return [Argument("JSContext*", "cx"),
|
|
Argument("JS::Handle<JSObject*>", "aThisObj")] + args
|
|
|
|
def getCallSetup(self):
|
|
if self.needThisHandling:
|
|
# It's been done for us already
|
|
return ""
|
|
return string.Template(
|
|
"CallSetup s(mCallback, aRv, aExceptionHandling);\n"
|
|
"JSContext* cx = s.GetContext();\n"
|
|
"if (!cx) {\n"
|
|
" aRv.Throw(NS_ERROR_UNEXPECTED);\n"
|
|
" return${errorReturn};\n"
|
|
"}\n").substitute({
|
|
"errorReturn" : self.getDefaultRetval(),
|
|
})
|
|
|
|
def getArgcDecl(self):
|
|
return CGGeneric("unsigned argc = %s;" % self.argCountStr);
|
|
|
|
@staticmethod
|
|
def ensureASCIIName(idlObject):
|
|
type = "attribute" if idlObject.isAttr() else "operation"
|
|
if re.match("[^\x20-\x7E]", idlObject.identifier.name):
|
|
raise SyntaxError('Callback %s name "%s" contains non-ASCII '
|
|
"characters. We can't handle that. %s" %
|
|
(type, idlObject.identifier.name,
|
|
idlObject.location))
|
|
if re.match('"', idlObject.identifier.name):
|
|
raise SyntaxError("Callback %s name '%s' contains "
|
|
"double-quote character. We can't handle "
|
|
"that. %s" %
|
|
(type, idlObject.identifier.name,
|
|
idlObject.location))
|
|
|
|
class CallbackMethod(CallbackMember):
|
|
def __init__(self, sig, name, descriptorProvider, needThisHandling):
|
|
CallbackMember.__init__(self, sig, name, descriptorProvider,
|
|
needThisHandling)
|
|
def getRvalDecl(self):
|
|
return "JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());\n"
|
|
|
|
def getCall(self):
|
|
replacements = {
|
|
"errorReturn" : self.getDefaultRetval(),
|
|
"thisObj": self.getThisObj(),
|
|
"getCallable": self.getCallableDecl()
|
|
}
|
|
if self.argCount > 0:
|
|
replacements["argv"] = "argv.begin()"
|
|
replacements["argc"] = "argc"
|
|
else:
|
|
replacements["argv"] = "nullptr"
|
|
replacements["argc"] = "0"
|
|
return string.Template("${getCallable}"
|
|
"if (!JS_CallFunctionValue(cx, ${thisObj}, callable,\n"
|
|
" ${argc}, ${argv}, rval.address())) {\n"
|
|
" aRv.Throw(NS_ERROR_UNEXPECTED);\n"
|
|
" return${errorReturn};\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 "JS::Rooted<JS::Value> callable(cx, JS::ObjectValue(*mCallback));\n"
|
|
|
|
class CallbackOperation(CallbackMethod):
|
|
def __init__(self, method, signature, descriptor):
|
|
self.singleOperation = descriptor.interface.isSingleOperationInterface()
|
|
self.ensureASCIIName(method)
|
|
self.methodName = method.identifier.name
|
|
CallbackMethod.__init__(self, signature,
|
|
MakeNativeName(self.methodName),
|
|
descriptor,
|
|
self.singleOperation)
|
|
|
|
def getThisObj(self):
|
|
if not self.singleOperation:
|
|
return "mCallback"
|
|
# This relies on getCallableDecl declaring a boolean
|
|
# isCallable in the case when we're a single-operation
|
|
# interface.
|
|
return "isCallable ? aThisObj : mCallback"
|
|
|
|
def getCallableDecl(self):
|
|
replacements = {
|
|
"errorReturn" : self.getDefaultRetval(),
|
|
"methodName": self.methodName
|
|
}
|
|
getCallableFromProp = string.Template(
|
|
'if (!GetCallableProperty(cx, "${methodName}", &callable)) {\n'
|
|
' aRv.Throw(NS_ERROR_UNEXPECTED);\n'
|
|
' return${errorReturn};\n'
|
|
'}\n').substitute(replacements)
|
|
if not self.singleOperation:
|
|
return 'JS::Rooted<JS::Value> callable(cx);\n' + getCallableFromProp
|
|
return (
|
|
'bool isCallable = JS_ObjectIsCallable(cx, mCallback);\n'
|
|
'JS::Rooted<JS::Value> callable(cx);\n'
|
|
'if (isCallable) {\n'
|
|
' callable = JS::ObjectValue(*mCallback);\n'
|
|
'} else {\n'
|
|
'%s'
|
|
'}\n' % CGIndenter(CGGeneric(getCallableFromProp)).define())
|
|
|
|
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 = {
|
|
"errorReturn" : self.getDefaultRetval(),
|
|
"attrName": self.attrName
|
|
}
|
|
return string.Template(
|
|
'if (!JS_GetProperty(cx, mCallback, "${attrName}", rval.address())) {\n'
|
|
' aRv.Throw(NS_ERROR_UNEXPECTED);\n'
|
|
' return${errorReturn};\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 = {
|
|
"errorReturn" : self.getDefaultRetval(),
|
|
"attrName": self.attrName,
|
|
"argv": "argv.begin()",
|
|
}
|
|
return string.Template(
|
|
'MOZ_ASSERT(argv.length() == 1);\n'
|
|
'if (!JS_SetProperty(cx, mCallback, "${attrName}", ${argv})) {\n'
|
|
' aRv.Throw(NS_ERROR_UNEXPECTED);\n'
|
|
' return${errorReturn};\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(hasInterfacePrototypeObject=True)]
|
|
idEnum = CGNamespacedEnum('id', 'ID', ['_ID_Start'] + protos,
|
|
[0, '_ID_Start'])
|
|
idEnum = CGList([idEnum])
|
|
|
|
# This is only used by DOM worker code, once there are no more consumers
|
|
# of INTERFACE_CHAIN_* this code should be removed.
|
|
def ifaceChainMacro(ifaceCount):
|
|
supplied = [CGGeneric(declare="_iface_" + str(i + 1)) for i in range(ifaceCount)]
|
|
remaining = [CGGeneric(declare="prototypes::id::_ID_Count")] * (config.maxProtoChainLength - ifaceCount)
|
|
macro = CGWrapper(CGList(supplied, ", "),
|
|
pre="#define INTERFACE_CHAIN_" + str(ifaceCount) + "(",
|
|
post=") \\\n")
|
|
macroContent = CGIndenter(CGList(supplied + remaining, ", \\\n"))
|
|
macroContent = CGIndenter(CGWrapper(macroContent, pre="{ \\\n",
|
|
post=" \\\n}"))
|
|
return CGWrapper(CGList([macro, macroContent]), post="\n\n")
|
|
|
|
idEnum.append(ifaceChainMacro(1))
|
|
|
|
# Wrap all of that in our namespaces.
|
|
idEnum = CGNamespace.build(['mozilla', 'dom', 'prototypes'],
|
|
CGWrapper(idEnum, pre='\n'))
|
|
idEnum = CGWrapper(idEnum, post='\n')
|
|
|
|
curr = CGList([idEnum])
|
|
|
|
# Let things know the maximum length of the prototype chain.
|
|
maxMacro = CGGeneric(declare="#define MAX_PROTOTYPE_CHAIN_LENGTH " + str(config.maxProtoChainLength))
|
|
curr.append(CGWrapper(maxMacro, post='\n\n'))
|
|
|
|
# Constructor ID enum.
|
|
constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True)]
|
|
idEnum = CGNamespacedEnum('id', 'ID', ['_ID_Start'] + constructors,
|
|
['prototypes::id::_ID_Count', '_ID_Start'])
|
|
|
|
# Wrap all of that in our namespaces.
|
|
idEnum = CGNamespace.build(['mozilla', 'dom', 'constructors'],
|
|
CGWrapper(idEnum, pre='\n'))
|
|
idEnum = CGWrapper(idEnum, post='\n')
|
|
|
|
curr.append(idEnum)
|
|
|
|
traitsDecl = CGGeneric(declare="""
|
|
template <prototypes::ID PrototypeID>
|
|
struct PrototypeTraits;
|
|
|
|
template <class ConcreteClass>
|
|
struct PrototypeIDMap;
|
|
""")
|
|
|
|
traitsDecl = CGNamespace.build(['mozilla', 'dom'],
|
|
CGWrapper(traitsDecl, post='\n'))
|
|
|
|
curr.append(traitsDecl)
|
|
|
|
# Add include guards.
|
|
curr = CGIncludeGuard('PrototypeList', curr)
|
|
|
|
# Add the auto-generated comment.
|
|
curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
|
|
|
|
# Done.
|
|
return curr
|
|
|
|
@staticmethod
|
|
def RegisterBindings(config):
|
|
|
|
# TODO - Generate the methods we want
|
|
curr = CGRegisterProtos(config)
|
|
|
|
# Wrap all of that in our namespaces.
|
|
curr = CGNamespace.build(['mozilla', 'dom'],
|
|
CGWrapper(curr, post='\n'))
|
|
curr = CGWrapper(curr, post='\n')
|
|
|
|
# Add the includes
|
|
defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface)
|
|
for desc in config.getDescriptors(hasInterfaceObject=True,
|
|
workers=False,
|
|
register=True)]
|
|
defineIncludes.append('nsScriptNameSpaceManager.h')
|
|
curr = CGHeaders([], [], [], [], [], defineIncludes, curr)
|
|
|
|
# Add include guards.
|
|
curr = CGIncludeGuard('RegisterBindings', curr)
|
|
|
|
# Done.
|
|
return curr
|
|
|
|
@staticmethod
|
|
def UnionTypes(config):
|
|
|
|
(includes, implincludes,
|
|
declarations, unions) = UnionTypes(config.getDescriptors(),
|
|
config.getDictionaries(),
|
|
config.getCallbacks(),
|
|
config)
|
|
includes.add("mozilla/dom/BindingUtils.h")
|
|
|
|
# Wrap all of that in our namespaces.
|
|
curr = CGNamespace.build(['mozilla', 'dom'], unions)
|
|
|
|
curr = CGWrapper(curr, post='\n')
|
|
|
|
namespaces = []
|
|
stack = [CGList([])]
|
|
for (clazz, isStruct) in SortedTuples(declarations):
|
|
elements = clazz.split("::")
|
|
clazz = CGClassForwardDeclare(elements.pop(), isStruct=isStruct)
|
|
i = 0
|
|
if len(elements) > 0:
|
|
common = min(len(namespaces), len(elements))
|
|
while i < common and namespaces[i] == elements[i]:
|
|
i += 1
|
|
|
|
# pop all the namespaces that should be closed
|
|
namespaces = namespaces[:i]
|
|
|
|
# add all the namespaces that should be opened
|
|
for j, namespace in enumerate(elements[i:]):
|
|
namespaces.append(namespace)
|
|
# every CGNamespace that we add holds a CGList
|
|
list = CGList([])
|
|
# add the new namespace to the list on top of the stack
|
|
stack[i + j].append(CGNamespace(namespace, list))
|
|
# set the top of the namespace stack to the list of the new
|
|
# namespace
|
|
stack[i + j + 1:] = [list]
|
|
|
|
stack[len(elements)].append(clazz)
|
|
|
|
curr = CGList([stack[0], curr], "\n")
|
|
|
|
curr = CGHeaders([], [], [], [], includes, implincludes, curr)
|
|
|
|
# Add include guards.
|
|
curr = CGIncludeGuard('UnionTypes', curr)
|
|
|
|
# Done.
|
|
return curr
|
|
|
|
@staticmethod
|
|
def UnionConversions(config):
|
|
|
|
(headers, unions) = UnionConversions(config.getDescriptors(),
|
|
config.getDictionaries(),
|
|
config.getCallbacks(),
|
|
config)
|
|
|
|
# Wrap all of that in our namespaces.
|
|
curr = CGNamespace.build(['mozilla', 'dom'], unions)
|
|
|
|
curr = CGWrapper(curr, post='\n')
|
|
|
|
headers.update(["nsDebug.h", "mozilla/dom/UnionTypes.h", "nsDOMQS.h", "XPCWrapper.h"])
|
|
curr = CGHeaders([], [], [], [], headers, [], curr)
|
|
|
|
# Add include guards.
|
|
curr = CGIncludeGuard('UnionConversions', curr)
|
|
|
|
# Done.
|
|
return curr
|