mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
3010 lines
117 KiB
Python
3010 lines
117 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 os
|
|
import string
|
|
|
|
from WebIDL import *
|
|
|
|
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 = '_Construct'
|
|
HASINSTANCE_HOOK_NAME = '_HasInstance'
|
|
|
|
def replaceFileIfChanged(filename, newContents):
|
|
"""
|
|
Read a copy of the old file, so that we don't touch it if it hasn't changed.
|
|
Returns True if the file was updated, false otherwise.
|
|
"""
|
|
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()
|
|
|
|
def toStringBool(arg):
|
|
return str(not not arg).lower()
|
|
|
|
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!
|
|
|
|
class CGNativePropertyHooks(CGThing):
|
|
"""
|
|
Generate a NativePropertyHooks for a given descriptor
|
|
"""
|
|
def __init__(self, descriptor):
|
|
CGThing.__init__(self)
|
|
self.descriptor = descriptor
|
|
def declare(self):
|
|
return " extern const NativePropertyHooks NativeHooks;\n"
|
|
def define(self):
|
|
parent = self.descriptor.interface.parent
|
|
parentHooks = "&" + parent.identifier.name + "::NativeHooks" if parent else 'NULL'
|
|
return """
|
|
const NativePropertyHooks NativeHooks = { ResolveProperty, EnumerateProperties, %s };
|
|
""" % parentHooks
|
|
|
|
class CGDOMJSClass(CGThing):
|
|
"""
|
|
Generate a DOMJSClass for a given descriptor
|
|
"""
|
|
def __init__(self, descriptor):
|
|
CGThing.__init__(self)
|
|
self.descriptor = descriptor
|
|
def declare(self):
|
|
return " extern DOMJSClass Class;\n"
|
|
def define(self):
|
|
traceHook = TRACE_HOOK_NAME if self.descriptor.customTrace else 'NULL'
|
|
protoList = ['id::' + proto for proto in self.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.
|
|
while len(protoList) < self.descriptor.config.maxProtoChainLength:
|
|
protoList.append('id::_ID_Count')
|
|
prototypeChainString = ', '.join(protoList)
|
|
return """
|
|
DOMJSClass Class = {
|
|
{ "%s",
|
|
JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(1),
|
|
%s, /* addProperty */
|
|
JS_PropertyStub, /* delProperty */
|
|
JS_PropertyStub, /* getProperty */
|
|
JS_StrictPropertyStub, /* setProperty */
|
|
JS_EnumerateStub,
|
|
JS_ResolveStub,
|
|
JS_ConvertStub,
|
|
%s, /* finalize */
|
|
NULL, /* checkAccess */
|
|
NULL, /* call */
|
|
NULL, /* construct */
|
|
NULL, /* hasInstance */
|
|
%s, /* trace */
|
|
JSCLASS_NO_INTERNAL_MEMBERS
|
|
},
|
|
{ %s },
|
|
-1, %s, DOM_OBJECT_SLOT,
|
|
&NativeHooks
|
|
};
|
|
""" % (self.descriptor.interface.identifier.name,
|
|
ADDPROPERTY_HOOK_NAME if self.descriptor.concrete and not self.descriptor.workers else 'JS_PropertyStub',
|
|
FINALIZE_HOOK_NAME, traceHook, prototypeChainString,
|
|
str(self.descriptor.nativeIsISupports).lower())
|
|
|
|
class CGPrototypeJSClass(CGThing):
|
|
def __init__(self, descriptor):
|
|
CGThing.__init__(self)
|
|
self.descriptor = descriptor
|
|
def declare(self):
|
|
# We're purely for internal consumption
|
|
return ""
|
|
def define(self):
|
|
return """
|
|
static JSClass PrototypeClass = {
|
|
"%s Prototype", 0,
|
|
JS_PropertyStub, /* addProperty */
|
|
JS_PropertyStub, /* delProperty */
|
|
JS_PropertyStub, /* getProperty */
|
|
JS_StrictPropertyStub, /* setProperty */
|
|
JS_EnumerateStub,
|
|
JS_ResolveStub,
|
|
JS_ConvertStub,
|
|
NULL, /* finalize */
|
|
NULL, /* checkAccess */
|
|
NULL, /* call */
|
|
NULL, /* construct */
|
|
NULL, /* hasInstance */
|
|
NULL, /* trace */
|
|
JSCLASS_NO_INTERNAL_MEMBERS
|
|
};
|
|
""" % (self.descriptor.interface.identifier.name)
|
|
|
|
class CGInterfaceObjectJSClass(CGThing):
|
|
def __init__(self, descriptor):
|
|
CGThing.__init__(self)
|
|
self.descriptor = descriptor
|
|
def declare(self):
|
|
# We're purely for internal consumption
|
|
return ""
|
|
def define(self):
|
|
ctorname = "NULL" if not self.descriptor.interface.ctor() else CONSTRUCT_HOOK_NAME
|
|
hasinstance = "NULL" if not self.descriptor.hasInstanceInterface else HASINSTANCE_HOOK_NAME
|
|
return """
|
|
static JSClass InterfaceObjectClass = {
|
|
"Function", 0,
|
|
JS_PropertyStub, /* addProperty */
|
|
JS_PropertyStub, /* delProperty */
|
|
JS_PropertyStub, /* getProperty */
|
|
JS_StrictPropertyStub, /* setProperty */
|
|
JS_EnumerateStub,
|
|
JS_ResolveStub,
|
|
JS_ConvertStub,
|
|
NULL, /* finalize */
|
|
NULL, /* checkAccess */
|
|
%s, /* call */
|
|
%s, /* construct */
|
|
%s, /* hasInstance */
|
|
NULL, /* trace */
|
|
JSCLASS_NO_INTERNAL_MEMBERS
|
|
};
|
|
""" % (ctorname, ctorname, hasinstance)
|
|
|
|
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 declare(self):
|
|
return self.joiner.join([child.declare() for child in self.children
|
|
if child is not None])
|
|
def define(self):
|
|
return self.joiner.join([child.define() for child in self.children
|
|
if child is not None])
|
|
|
|
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
|
|
|
|
# We'll want to insert the indent at the beginnings of lines, but we
|
|
# don't want to indent empty lines. So only indent lines that have a
|
|
# non-newline character on them.
|
|
lineStartDetector = re.compile("^(?=[^\n#])", re.MULTILINE)
|
|
class CGIndenter(CGThing):
|
|
"""
|
|
A class that takes another CGThing and generates code that indents that
|
|
CGThing by some number of spaces. The default indent is two spaces.
|
|
"""
|
|
def __init__(self, child, indentLevel=2):
|
|
CGThing.__init__(self)
|
|
self.child = child
|
|
self.indent = " " * indentLevel
|
|
def 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 "":
|
|
return re.sub(lineStartDetector, self.indent, defn)
|
|
else:
|
|
return ""
|
|
|
|
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, 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.reindent = reindent
|
|
def declare(self):
|
|
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
|
|
|
|
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 child
|
|
return CGNamespace(namespaces[0], CGNamespace.build(namespaces[1:],
|
|
child),
|
|
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_bindings_%s_h__' % prefix
|
|
CGWrapper.__init__(self, child,
|
|
declarePre='#ifndef %s\n#define %s\n\n' % (define, define),
|
|
declarePost='\n#endif // %s\n' % define)
|
|
|
|
class CGHeaders(CGWrapper):
|
|
"""
|
|
Generates the appropriate include statements.
|
|
"""
|
|
def __init__(self, descriptors, declareIncludes, defineIncludes, child):
|
|
"""
|
|
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.getInterfaceFilename(d) for d in interfaceDeps)
|
|
|
|
# Grab all the implementation declaration files we need.
|
|
implementationIncludes = set(d.headerFile for d in descriptors)
|
|
|
|
# Now find all the things we'll need as arguments because we
|
|
# need to wrap or unwrap them.
|
|
bindingHeaders = set()
|
|
for d in descriptors:
|
|
members = [m for m in d.interface.members]
|
|
signatures = [s for m in members if m.isMethod() for s in m.signatures()]
|
|
types = []
|
|
for s in signatures:
|
|
assert len(s) == 2
|
|
(returnType, arguments) = s
|
|
types.append(returnType)
|
|
types.extend([a.type for a in arguments])
|
|
|
|
attrs = [a for a in members if a.isAttr()]
|
|
types.extend([a.type for a in attrs])
|
|
|
|
for t in types:
|
|
if t.unroll().isInterface():
|
|
if t.unroll().isArrayBuffer():
|
|
bindingHeaders.add("jsfriendapi.h")
|
|
else:
|
|
typeDesc = d.getDescriptor(t.unroll().inner.identifier.name)
|
|
if typeDesc is not None:
|
|
implementationIncludes.add(typeDesc.headerFile)
|
|
bindingHeaders.add(self.getInterfaceFilename(typeDesc.interface))
|
|
|
|
# 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(declareIncludes),
|
|
definePre=_includeString(sorted(set(defineIncludes) |
|
|
bindingIncludes |
|
|
bindingHeaders |
|
|
implementationIncludes)))
|
|
@staticmethod
|
|
def getInterfaceFilename(interface):
|
|
basename = os.path.basename(interface.filename())
|
|
return 'mozilla/dom/bindings/' + \
|
|
basename.replace('.webidl', 'Binding.h')
|
|
|
|
class Argument():
|
|
"""
|
|
A class for outputting the type and name of an argument
|
|
"""
|
|
def __init__(self, argType, name):
|
|
self.argType = argType
|
|
self.name = name
|
|
def __str__(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.
|
|
|
|
static should be True to generate a static method, which only has
|
|
a definition.
|
|
"""
|
|
def __init__(self, descriptor, name, returnType, args, inline=False, static=False):
|
|
CGThing.__init__(self)
|
|
self.descriptor = descriptor
|
|
self.name = name
|
|
self.returnType = returnType
|
|
self.args = args
|
|
self.inline = inline
|
|
self.static = static
|
|
def _argstring(self):
|
|
return ', '.join([str(a) for a in self.args])
|
|
def _decorators(self):
|
|
decorators = []
|
|
if self.inline:
|
|
decorators.append('inline')
|
|
if self.static:
|
|
decorators.append('static')
|
|
decorators.append(self.returnType)
|
|
return ' '.join(decorators)
|
|
def declare(self):
|
|
if self.inline:
|
|
return self._define()
|
|
return "\n %s %s(%s);\n" % (self._decorators(), self.name, self._argstring())
|
|
def _define(self):
|
|
return self.definition_prologue() + "\n" + self.definition_body() + self.definition_epilogue()
|
|
def define(self):
|
|
return "" if self.inline else self._define()
|
|
def definition_prologue(self):
|
|
maybeNewline = " " if self.inline else "\n"
|
|
return "\n%s%s%s(%s)\n{" % (self._decorators(), maybeNewline,
|
|
self.name, self._argstring())
|
|
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):
|
|
return """
|
|
MOZ_ASSERT(js::GetObjectJSClass(obj) == Class.ToJSClass());
|
|
%s* self = UnwrapDOMObject<%s>(obj, Class.ToJSClass());
|
|
""" % (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('JSObject*', 'obj'),
|
|
Argument('jsid', 'id'), Argument('jsval*', 'vp')]
|
|
CGAbstractClassHook.__init__(self, descriptor, ADDPROPERTY_HOOK_NAME,
|
|
'JSBool', args)
|
|
|
|
def generate_code(self):
|
|
return """
|
|
JSCompartment* compartment = js::GetObjectCompartment(obj);
|
|
xpc::CompartmentPrivate* priv =
|
|
static_cast<xpc::CompartmentPrivate*>(JS_GetCompartmentPrivate(compartment));
|
|
if (!priv->RegisterDOMExpandoObject(obj)) {
|
|
return false;
|
|
}
|
|
self->SetPreservingWrapper(true);
|
|
return true;"""
|
|
|
|
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):
|
|
if self.descriptor.customFinalize:
|
|
return """ if (self) {
|
|
self->%s(%s);
|
|
}""" % (self.name, self.args[0].name)
|
|
if self.descriptor.workers:
|
|
release = "self->Release();"
|
|
else:
|
|
assert self.descriptor.nativeIsISupports
|
|
release = """
|
|
XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance();
|
|
if (rt) {
|
|
rt->DeferredRelease(NativeToSupports(self));
|
|
} else {
|
|
NS_RELEASE(self);
|
|
}"""
|
|
return """
|
|
self->ClearWrapper();
|
|
%s""" % (release)
|
|
|
|
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 CGClassConstructHook(CGAbstractStaticMethod):
|
|
"""
|
|
JS-visible constructor for our objects
|
|
"""
|
|
def __init__(self, descriptor):
|
|
args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'), Argument('JS::Value*', 'vp')]
|
|
CGAbstractStaticMethod.__init__(self, descriptor, CONSTRUCT_HOOK_NAME,
|
|
'JSBool', args)
|
|
self._ctor = self.descriptor.interface.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 = """
|
|
JSObject* obj = JS_GetGlobalForObject(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)));
|
|
"""
|
|
preArgs = ""
|
|
if self.descriptor.workers:
|
|
preArgs = "cx, obj, "
|
|
else:
|
|
preamble += """
|
|
nsISupports* global;
|
|
xpc_qsSelfRef globalRef;
|
|
{
|
|
nsresult rv;
|
|
JS::Value val = OBJECT_TO_JSVAL(obj);
|
|
rv = xpc_qsUnwrapArg<nsISupports>(cx, val, &global, &globalRef.ptr, &val);
|
|
if (NS_FAILED(rv)) {
|
|
return Throw<true>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
|
|
}
|
|
}
|
|
"""
|
|
preArgs = "global, "
|
|
|
|
name = "_" + self._ctor.identifier.name
|
|
nativeName = "_" + MakeNativeName(self._ctor.identifier.name)
|
|
nativeName = self.descriptor.binaryNames.get(name, nativeName)
|
|
callGenerator = CGMethodCall(preArgs, nativeName, True,
|
|
self.descriptor, self._ctor, {})
|
|
return preamble + callGenerator.define();
|
|
|
|
class CGClassHasInstanceHook(CGAbstractStaticMethod):
|
|
def __init__(self, descriptor):
|
|
args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'obj'),
|
|
Argument('const jsval*', 'v'), Argument('JSBool*', 'bp')]
|
|
CGAbstractStaticMethod.__init__(self, descriptor, HASINSTANCE_HOOK_NAME,
|
|
'JSBool', args)
|
|
|
|
def define(self):
|
|
if not self.descriptor.hasInstanceInterface:
|
|
return ""
|
|
return CGAbstractStaticMethod.define(self)
|
|
|
|
def definition_body(self):
|
|
return self.generate_code()
|
|
|
|
def generate_code(self):
|
|
return """ if (!v->isObject()) {
|
|
*bp = false;
|
|
return true;
|
|
}
|
|
|
|
jsval protov;
|
|
if (!JS_GetProperty(cx, obj, "prototype", &protov))
|
|
return false;
|
|
if (!protov.isObject()) {
|
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROTOTYPE,
|
|
"%s");
|
|
return false;
|
|
}
|
|
obj = &protov.toObject();
|
|
|
|
JSObject* instance = &v->toObject();
|
|
JSObject* proto = JS_GetPrototype(instance);
|
|
while (proto) {
|
|
if (proto == obj) {
|
|
*bp = true;
|
|
return true;
|
|
}
|
|
proto = JS_GetPrototype(proto);
|
|
}
|
|
|
|
nsISupports* native =
|
|
nsContentUtils::XPConnect()->GetNativeOfWrapper(cx, instance);
|
|
nsCOMPtr<%s> qiResult = do_QueryInterface(native);
|
|
*bp = !!qiResult;
|
|
return true;
|
|
""" % (self.descriptor.name, self.descriptor.hasInstanceInterface)
|
|
|
|
def isChromeOnly(m):
|
|
return m.getExtendedAttribute("ChromeOnly")
|
|
|
|
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 exposed to chrome and self.regular to the list of things exposed to
|
|
web pages. self.chrome must be a superset of self.regular but also include
|
|
all the ChromeOnly stuff.
|
|
"""
|
|
def __init__(self, descriptor, name):
|
|
self.descriptor = descriptor
|
|
self.name = name
|
|
def hasChromeOnly(self):
|
|
return len(self.chrome) > len(self.regular)
|
|
def hasNonChromeOnly(self):
|
|
return len(self.regular) > 0
|
|
def variableName(self, chrome):
|
|
if chrome and self.hasChromeOnly():
|
|
return "sChrome" + self.name
|
|
if self.hasNonChromeOnly():
|
|
return "s" + self.name
|
|
return "NULL"
|
|
def __str__(self):
|
|
str = self.generateArray(self.regular, self.variableName(False))
|
|
if self.hasChromeOnly():
|
|
str += self.generateArray(self.chrome, self.variableName(True))
|
|
return str
|
|
|
|
class MethodDefiner(PropertyDefiner):
|
|
"""
|
|
A class for defining methods on a prototype object.
|
|
"""
|
|
def __init__(self, descriptor, name, static):
|
|
PropertyDefiner.__init__(self, descriptor, name)
|
|
|
|
# The length of a method is the maximum of the lengths of the
|
|
# argument lists of all its overloads.
|
|
def methodLength(method):
|
|
signatures = method.signatures()
|
|
return max([len(arguments) for (retType, arguments) in signatures])
|
|
|
|
methods = [m for m in descriptor.interface.members if
|
|
m.isMethod() and m.isStatic() == static]
|
|
self.chrome = [{"name": m.identifier.name,
|
|
"length": methodLength(m),
|
|
"flags": "JSPROP_ENUMERATE"} for m in methods]
|
|
self.regular = [{"name": m.identifier.name,
|
|
"length": methodLength(m),
|
|
"flags": "JSPROP_ENUMERATE"}
|
|
for m in methods if not isChromeOnly(m)]
|
|
if not descriptor.interface.parent and not static and not descriptor.workers:
|
|
self.chrome.append({"name": 'QueryInterface',
|
|
"length": 1,
|
|
"flags": "0"})
|
|
self.regular.append({"name": 'QueryInterface',
|
|
"length": 1,
|
|
"flags": "0"})
|
|
|
|
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()
|
|
|
|
@staticmethod
|
|
def generateArray(array, name):
|
|
if len(array) == 0:
|
|
return ""
|
|
|
|
funcdecls = [' JS_FN("%s", %s, %s, %s)' %
|
|
(m["name"], m["name"], m["length"], m["flags"])
|
|
for m in array]
|
|
# And add our JS_FS_END
|
|
funcdecls.append(' JS_FS_END')
|
|
|
|
return ("static JSFunctionSpec %s[] = {\n" +
|
|
',\n'.join(funcdecls) + "\n" +
|
|
"};\n\n" +
|
|
"static jsid %s_ids[%i] = { JSID_VOID };\n\n") % (name, name, len(array))
|
|
|
|
class AttrDefiner(PropertyDefiner):
|
|
def __init__(self, descriptor, name):
|
|
PropertyDefiner.__init__(self, descriptor, name)
|
|
self.name = name
|
|
self.chrome = [m for m in descriptor.interface.members if m.isAttr()]
|
|
self.regular = [m for m in self.chrome if not isChromeOnly(m)]
|
|
|
|
@staticmethod
|
|
def generateArray(array, name):
|
|
if len(array) == 0:
|
|
return ""
|
|
|
|
def flags(attr):
|
|
flags = "JSPROP_SHARED | JSPROP_ENUMERATE"
|
|
if generateNativeAccessors:
|
|
flags = "JSPROP_NATIVE_ACCESSORS | " + flags
|
|
elif attr.readonly:
|
|
return "JSPROP_READONLY | " + flags
|
|
return flags
|
|
|
|
def getter(attr):
|
|
return "get_" + attr.identifier.name
|
|
|
|
def setter(attr):
|
|
if attr.readonly:
|
|
return "NULL"
|
|
return "set_" + attr.identifier.name
|
|
|
|
attrdecls = [' { "%s", 0, %s, (JSPropertyOp)%s, (JSStrictPropertyOp)%s }' %
|
|
(attr.identifier.name, flags(attr), getter(attr),
|
|
setter(attr)) for attr in array]
|
|
attrdecls.append(' { 0, 0, 0, 0, 0 }')
|
|
|
|
return ("static JSPropertySpec %s[] = {\n" +
|
|
',\n'.join(attrdecls) + "\n" +
|
|
"};\n\n" +
|
|
"static jsid %s_ids[%i] = { JSID_VOID };\n\n") % (name, name, len(array))
|
|
|
|
class ConstDefiner(PropertyDefiner):
|
|
"""
|
|
A class for definining constants on the interface object
|
|
"""
|
|
def __init__(self, descriptor, name):
|
|
PropertyDefiner.__init__(self, descriptor, name)
|
|
self.name = name
|
|
self.chrome = [m for m in descriptor.interface.members if m.isConst()]
|
|
self.regular = [m for m in self.chrome if not isChromeOnly(m)]
|
|
|
|
@staticmethod
|
|
def generateArray(array, name):
|
|
if len(array) == 0:
|
|
return ""
|
|
|
|
constdecls = [' { "%s", %s }' %
|
|
(const.identifier.name,
|
|
convertConstIDLValueToJSVal(const.value))
|
|
for const in array]
|
|
constdecls.append(' { 0, JSVAL_VOID }')
|
|
|
|
return ("static ConstantSpec %s[] = {\n" +
|
|
',\n'.join(constdecls) + "\n" +
|
|
"};\n\n" +
|
|
"static jsid %s_ids[%i] = { JSID_VOID };\n\n") % (name, name, len(array))
|
|
|
|
class PropertyArrays():
|
|
def __init__(self, descriptor):
|
|
self.staticMethods = MethodDefiner(descriptor, "StaticMethods", True)
|
|
self.methods = MethodDefiner(descriptor, "Methods", False)
|
|
self.attrs = AttrDefiner(descriptor, "Attributes")
|
|
self.consts = ConstDefiner(descriptor, "Constants")
|
|
|
|
@staticmethod
|
|
def arrayNames():
|
|
return [ "staticMethods", "methods", "attrs", "consts" ]
|
|
|
|
def hasChromeOnly(self):
|
|
return reduce(lambda b, a: b or getattr(self, a).hasChromeOnly(),
|
|
self.arrayNames(), False)
|
|
def variableNames(self, chrome):
|
|
names = {}
|
|
for array in self.arrayNames():
|
|
names[array] = getattr(self, array).variableName(chrome)
|
|
return names
|
|
def __str__(self):
|
|
define = ""
|
|
for array in self.arrayNames():
|
|
define += str(getattr(self, array))
|
|
return define
|
|
|
|
class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
|
|
"""
|
|
Generate the CreateInterfaceObjects method for an interface descriptor.
|
|
|
|
properties should be a PropertyArrays instance.
|
|
"""
|
|
def __init__(self, descriptor, properties):
|
|
args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal')]
|
|
CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'JSObject*', args)
|
|
self.properties = properties
|
|
def definition_body(self):
|
|
protoChain = self.descriptor.prototypeChain
|
|
if len(protoChain) == 1:
|
|
getParentProto = "JS_GetObjectPrototype(aCx, aGlobal)"
|
|
else:
|
|
parentProtoName = self.descriptor.prototypeChain[-2]
|
|
getParentProto = "%s::GetProtoObject(aCx, aGlobal)" % (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 = []
|
|
for var in self.properties.arrayNames():
|
|
props = getattr(self.properties, var)
|
|
if props.hasNonChromeOnly():
|
|
idsToInit.append(props.variableName(False))
|
|
if props.hasChromeOnly() and not self.descriptor.workers:
|
|
idsToInit.append(props.variableName(True))
|
|
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 NULL;") % idsToInit[0]),
|
|
CGGeneric("}")],
|
|
"\n")
|
|
else:
|
|
initIds = None
|
|
|
|
getParentProto = ("JSObject* parentProto = %s;\n"
|
|
"if (!parentProto) {\n"
|
|
" return NULL;\n"
|
|
"}") % getParentProto
|
|
|
|
call = CGGeneric(("return bindings::CreateInterfaceObjects(aCx, aGlobal, parentProto,\n"
|
|
" %s, %s,\n"
|
|
" %%(methods)s, %%(attrs)s, %%(consts)s, %%(staticMethods)s,\n"
|
|
" %s);") % (
|
|
"&PrototypeClass" if needInterfacePrototypeObject else "NULL",
|
|
"&InterfaceObjectClass" if needInterfaceObject else "NULL",
|
|
'"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "NULL"))
|
|
|
|
if self.properties.hasChromeOnly():
|
|
if self.descriptor.workers:
|
|
accessCheck = "mozilla::dom::workers::GetWorkerPrivateFromContext(aCx)->IsChromeWorker()"
|
|
else:
|
|
accessCheck = "xpc::AccessCheck::isChrome(js::GetObjectCompartment(aGlobal))"
|
|
accessCheck = "if (" + accessCheck + ") {\n"
|
|
chrome = CGWrapper(CGGeneric((CGIndenter(call).define() % self.properties.variableNames(True))),
|
|
pre=accessCheck, post="\n}")
|
|
else:
|
|
chrome = None
|
|
|
|
functionBody = CGList(
|
|
[CGGeneric(getParentProto), initIds, chrome,
|
|
CGGeneric(call.define() % self.properties.variableNames(False))],
|
|
"\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** protoOrIfaceArray = GetProtoOrIfaceArray(aGlobal);
|
|
JSObject* cachedObject = protoOrIfaceArray[%s];
|
|
if (!cachedObject) {
|
|
protoOrIfaceArray[%s] = cachedObject = CreateInterfaceObjects(aCx, aGlobal);
|
|
}
|
|
|
|
/* 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):
|
|
args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal')]
|
|
CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject")
|
|
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):
|
|
args = [Argument('JSContext*', 'aCx'), Argument('JSObject*', 'aGlobal')]
|
|
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)
|
|
|
|
def CheckPref(descriptor, scopeName, varName, retval, wrapperCache = None):
|
|
"""
|
|
Check whether bindings should be enabled for this descriptor. If not, set
|
|
varName to false and return retval.
|
|
"""
|
|
if not descriptor.prefable:
|
|
return ""
|
|
if wrapperCache:
|
|
wrapperCache = "%s->ClearIsDOMBinding();\n" % (wrapperCache)
|
|
else:
|
|
wrapperCache = ""
|
|
return """
|
|
if (!%s->ExperimentalBindingsEnabled()) {
|
|
%s %s = false;
|
|
return %s;
|
|
}
|
|
""" % (scopeName, wrapperCache, varName, retval)
|
|
|
|
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('XPCWrappedNativeScope*', 'aScope'),
|
|
Argument('bool*', 'aEnabled')]
|
|
CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface', 'bool', 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 self.descriptor.interface.hasInterfacePrototypeObject():
|
|
# We depend on GetProtoObject defining an interface constructor
|
|
# object as needed.
|
|
getter = "GetProtoObject"
|
|
else:
|
|
getter = "GetConstructorObject"
|
|
|
|
return CheckPref(self.descriptor, "aScope", "*aEnabled", "false") + """
|
|
*aEnabled = true;
|
|
return !!%s(aCx, aScope->GetGlobalJSObject());""" % (getter)
|
|
|
|
class CGNativeToSupportsMethod(CGAbstractStaticMethod):
|
|
"""
|
|
A method to cast our native to an nsISupports. We do it by casting up the
|
|
interface chain in hopes of getting to something that singly-inherits from
|
|
nsISupports.
|
|
"""
|
|
def __init__(self, descriptor):
|
|
args = [Argument(descriptor.nativeType + '*', 'aNative')]
|
|
CGAbstractStaticMethod.__init__(self, descriptor, 'NativeToSupports', 'nsISupports*', args)
|
|
|
|
def definition_body(self):
|
|
cur = CGGeneric("aNative")
|
|
for proto in reversed(self.descriptor.prototypeChain[:-1]):
|
|
d = self.descriptor.getDescriptor(proto)
|
|
cast = "static_cast<%s*>(\n" % d.nativeType;
|
|
cur = CGWrapper(CGIndenter(cur), pre=cast, post=")")
|
|
return CGIndenter(CGWrapper(cur, pre="return ", post=";")).define();
|
|
|
|
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('JSObject*', 'aScope'),
|
|
Argument(descriptor.nativeType + '*', 'aObject'),
|
|
Argument('bool*', 'aTriedToWrap')]
|
|
CGAbstractMethod.__init__(self, descriptor, 'Wrap', 'JSObject*', args)
|
|
|
|
def definition_body(self):
|
|
if self.descriptor.workers:
|
|
return """
|
|
*aTriedToWrap = true;
|
|
return aObject->GetJSObject();"""
|
|
|
|
return """
|
|
*aTriedToWrap = true;
|
|
|
|
JSObject* parent = bindings::WrapNativeParent(aCx, aScope, aObject->GetParentObject());
|
|
if (!parent) {
|
|
return NULL;
|
|
}
|
|
|
|
JSAutoEnterCompartment ac;
|
|
if (js::GetGlobalForObjectCrossCompartment(parent) != aScope) {
|
|
if (!ac.enter(aCx, parent)) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
XPCWrappedNativeScope* scope =
|
|
XPCWrappedNativeScope::FindInJSObjectScope(aCx, parent);
|
|
if (!scope) {
|
|
return NULL;
|
|
}
|
|
%s
|
|
JSObject* proto = GetProtoObject(aCx, scope->GetGlobalJSObject());
|
|
if (!proto) {
|
|
return NULL;
|
|
}
|
|
|
|
JSObject* obj = JS_NewObject(aCx, &Class.mBase, proto, parent);
|
|
if (!obj) {
|
|
return NULL;
|
|
}
|
|
|
|
js::SetReservedSlot(obj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(aObject));
|
|
NS_ADDREF(aObject);
|
|
|
|
aObject->SetWrapper(obj);
|
|
|
|
return obj;""" % (CheckPref(self.descriptor, "scope", "*aTriedToWrap", "NULL", "aObject"))
|
|
|
|
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.float: 'float',
|
|
IDLType.Tags.double: 'double'
|
|
}
|
|
|
|
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):
|
|
assert descriptor.castable
|
|
self.substitution = { "type" : descriptor.nativeType,
|
|
"protoID" : "id::" + descriptor.name,
|
|
"source" : source,
|
|
"target" : target,
|
|
"codeOnFailure" : codeOnFailure }
|
|
|
|
def __str__(self):
|
|
return string.Template(
|
|
""" {
|
|
nsresult rv = UnwrapObject<${protoID}>(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):
|
|
CastableObjectUnwrapper.__init__(self, descriptor, source, target,
|
|
"return Throw<%s>(cx, rv);" %
|
|
toStringBool(not descriptor.workers))
|
|
|
|
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, codeOnFailure=None):
|
|
if codeOnFailure is None:
|
|
codeOnFailure = ("return Throw<%s>(cx, rv);" %
|
|
toStringBool(not descriptor.workers))
|
|
self.descriptor = descriptor
|
|
self.substitution = { "nativeType" : descriptor.nativeType,
|
|
"source" : source,
|
|
"target" : target,
|
|
"codeOnFailure" : codeOnFailure }
|
|
|
|
def __str__(self):
|
|
if self.descriptor.workers:
|
|
return string.Template("""
|
|
${target} = ${source};""").substitute(self.substitution)
|
|
|
|
return string.Template("""
|
|
nsresult rv;
|
|
XPCCallContext ccx(JS_CALLER, cx);
|
|
if (!ccx.IsValid()) {
|
|
rv = NS_ERROR_XPC_BAD_CONVERT_JS;
|
|
${codeOnFailure}
|
|
}
|
|
|
|
const nsIID& iid = NS_GET_IID(${nativeType});
|
|
nsRefPtr<nsXPCWrappedJS> wrappedJS;
|
|
rv = nsXPCWrappedJS::GetNewOrUsed(ccx, ${source}, iid,
|
|
NULL, getter_AddRefs(wrappedJS));
|
|
if (NS_FAILED(rv) || !wrappedJS) {
|
|
${codeOnFailure}
|
|
}
|
|
|
|
${target} = do_QueryObject(wrappedJS.get());
|
|
if (!${target}) {
|
|
${codeOnFailure}
|
|
}""").substitute(self.substitution)
|
|
|
|
def getArgumentConversionTemplate(type, descriptor):
|
|
if type.isSequence() or type.isArray():
|
|
raise TypeError("Can't handle sequence or array arguments yet")
|
|
|
|
if descriptor is not None:
|
|
assert(type.isInterface())
|
|
# This is an interface that we implement as a concrete class
|
|
# or an XPCOM interface.
|
|
argIsPointer = type.nullable() or type.unroll().inner.isExternal()
|
|
if argIsPointer:
|
|
nameSuffix = ""
|
|
else:
|
|
nameSuffix = "_ptr"
|
|
|
|
# If we're going to QI, we want an nsCOMPtr. But note that XPConnect
|
|
# unwrapping may or may not QI, and we don't know whether it will. So
|
|
# we use a raw pointer for the isExternal() case, and if a ref is needed
|
|
# it'll be handled by the xpc_qsSelfRef we put on the stack later.
|
|
if descriptor.castable or type.unroll().inner.isExternal() or descriptor.workers:
|
|
declType = " ${typeName}*"
|
|
else:
|
|
declType = " nsCOMPtr<${typeName}>"
|
|
template = declType + " ${name}%s;\n" % nameSuffix
|
|
|
|
# We have to be very careful here to put anything that might need to
|
|
# hold references across the C++ call in |template| and not
|
|
# |templateBody|, since things in |templateBody| will go out of scope
|
|
# before the call happens.
|
|
templateBody = " if (${argVal}.isObject()) {"
|
|
if descriptor.castable:
|
|
templateBody += str(FailureFatalCastableObjectUnwrapper(
|
|
descriptor,
|
|
"&${argVal}.toObject()",
|
|
"${name}"+nameSuffix)).replace("\n", "\n ") + "\n"
|
|
elif descriptor.interface.isCallback():
|
|
templateBody += str(CallbackObjectUnwrapper(
|
|
descriptor,
|
|
"&${argVal}.toObject()",
|
|
"${name}"+nameSuffix)) + "\n"
|
|
elif descriptor.workers:
|
|
templateBody += """
|
|
${name}%s = &${argVal}.toObject();
|
|
MOZ_ASSERT(${name}%s);
|
|
""" % (nameSuffix, nameSuffix)
|
|
else:
|
|
template += " xpc_qsSelfRef tmpRef_${name};\n"
|
|
template += " jsval tmpVal_${name} = ${argVal};\n"
|
|
templateBody += """
|
|
${typeName}* tmp;
|
|
if (NS_FAILED(xpc_qsUnwrapArg<${typeName}>(cx, ${argVal}, &tmp, &tmpRef_${name}.ptr,
|
|
&tmpVal_${name}))) {
|
|
return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
|
|
}
|
|
MOZ_ASSERT(tmp);
|
|
${name}%s = tmp;
|
|
""" % (toStringBool(not descriptor.workers), nameSuffix)
|
|
|
|
if type.nullable():
|
|
templateBody += (
|
|
" } else if (${argVal}.isNullOrUndefined()) {\n"
|
|
" ${name}%s = NULL;\n" % nameSuffix)
|
|
|
|
templateBody += (
|
|
" } else {\n"
|
|
" return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);\n"
|
|
" }\n" % toStringBool(not descriptor.workers))
|
|
|
|
template += templateBody
|
|
|
|
if not argIsPointer:
|
|
template += " ${typeName} &${name} = *${name}_ptr;\n"
|
|
|
|
return template
|
|
|
|
if type.isArrayBuffer():
|
|
template = (
|
|
" JSObject* ${name};\n"
|
|
" if (${argVal}.isObject() && JS_IsArrayBufferObject(&${argVal}.toObject(), cx)) {\n"
|
|
" ${name} = &${argVal}.toObject();\n"
|
|
" }")
|
|
if type.nullable():
|
|
template += (
|
|
" else if (${argVal}.isNullOrUndefined()) {\n"
|
|
" ${name} = NULL;\n"
|
|
" }")
|
|
|
|
template += (
|
|
# XXXbz We don't know whether we're on workers, so play it safe
|
|
" else {\n"
|
|
" return Throw<false>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);\n"
|
|
" }")
|
|
|
|
return template
|
|
|
|
if type.isInterface():
|
|
raise TypeError("Interface type with no descriptor: " + type)
|
|
|
|
if type.isString():
|
|
# XXXbz Need to figure out string behavior based on extended args? Also, how to
|
|
# detect them?
|
|
|
|
# For nullable strings that are not otherwise annotated, null
|
|
# and undefined become null strings.
|
|
if type.nullable():
|
|
nullBehavior = "eNull"
|
|
undefinedBehavior = "eNull"
|
|
else:
|
|
nullBehavior = "eStringify"
|
|
undefinedBehavior = "eStringify"
|
|
|
|
return (
|
|
" const xpc_qsDOMString ${name}(cx, ${argVal}, ${argPtr},\n"
|
|
" xpc_qsDOMString::%s,\n"
|
|
" xpc_qsDOMString::%s);\n"
|
|
" if (!${name}.IsValid()) {\n"
|
|
" return false;\n"
|
|
" }\n" % (nullBehavior, undefinedBehavior))
|
|
|
|
if type.isEnum():
|
|
if type.nullable():
|
|
raise TypeError("We don't support nullable enumerated arguments "
|
|
"yet")
|
|
enum = type.inner.identifier.name
|
|
return (
|
|
" %(enumtype)s ${name};\n"
|
|
" {\n"
|
|
" bool ok;\n"
|
|
" ${name} = static_cast<%(enumtype)s>(FindEnumStringIndex(cx, ${argVal}, %(values)s, &ok));\n"
|
|
" if (!ok) {\n"
|
|
" return false;\n"
|
|
" }\n"
|
|
" }" % { "enumtype" : enum + "::value",
|
|
"values" : enum + "::strings" })
|
|
|
|
if type.isCallback():
|
|
# XXXbz we're going to assume that callback types are always
|
|
# nullable and always have [TreatNonCallableAsNull] for now.
|
|
return (
|
|
" JSObject* ${name};\n"
|
|
" if (${argVal}.isObject() && JS_ObjectIsCallable(cx, &${argVal}.toObject())) {\n"
|
|
" ${name} = &${argVal}.toObject();\n"
|
|
" } else {\n"
|
|
" ${name} = NULL;\n"
|
|
" }\n")
|
|
|
|
if type.isAny():
|
|
return " JS::Value ${name} = ${argVal};\n"
|
|
|
|
if not type.isPrimitive():
|
|
raise TypeError("Need conversion for argument type '%s'" % type)
|
|
|
|
tag = type.tag()
|
|
replacements = dict()
|
|
if type.nullable():
|
|
replacements["declareArg"] = (
|
|
" Nullable<${typeName}> ${name};\n"
|
|
" if (${argVal}.isNullOrUndefined()) {\n"
|
|
" ${name}.SetNull();\n"
|
|
" } else"
|
|
)
|
|
replacements["finalValueSetter"] = "${name}.SetValue"
|
|
else:
|
|
replacements["declareArg"] = " ${typeName} ${name};\n"
|
|
replacements["finalValueSetter"] = "${name} = "
|
|
|
|
replacements["intermediateCast"] = ""
|
|
|
|
if tag == IDLType.Tags.bool:
|
|
replacements["jstype"] = "JSBool"
|
|
replacements["converter"] = "JS_ValueToBoolean"
|
|
elif tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
|
|
IDLType.Tags.uint16, IDLType.Tags.int32, IDLType.Tags.uint32]:
|
|
# XXXbz need to add support for [EnforceRange] and [Clamp]
|
|
# The output of JS_ValueToECMAInt32 is determined as follows:
|
|
# 1) The value is converted to a double
|
|
# 2) Anything that's not a finite double returns 0
|
|
# 3) The double is rounded towards zero to the nearest integer
|
|
# 4) The resulting integer is reduced mod 2^32. The output of this
|
|
# operation is an integer in the range [0, 2^32).
|
|
# 5) If the resulting number is >= 2^31, 2^32 is subtracted from it.
|
|
#
|
|
# The result of all this is a number in the range [-2^31, 2^31)
|
|
#
|
|
# WebIDL conversions for the 8-bit, 16-bit, and 32-bit integer types
|
|
# are defined in the same way, except that step 4 uses reduction mod
|
|
# 2^8 and 2^16 for the 8-bit and 16-bit types respectively, and step 5
|
|
# is only done for the signed types.
|
|
#
|
|
# C/C++ define integer conversion semantics to unsigned types as taking
|
|
# your input integer mod (1 + largest value representable in the
|
|
# unsigned type). Since 2^32 is zero mod 2^8, 2^16, and 2^32,
|
|
# converting to the unsigned int of the relevant width will correctly
|
|
# perform step 4; in particular, the 2^32 possibly subtracted in step 5
|
|
# will become 0.
|
|
#
|
|
# Once we have step 4 done, we're just going to assume 2s-complement
|
|
# representation and cast directly to the type we really want.
|
|
#
|
|
# So we can cast directly for all unsigned types and for int32_t; for
|
|
# the smaller-width signed types we need to cast through the
|
|
# corresponding unsigned type.
|
|
replacements["jstype"] = "int32_t"
|
|
replacements["converter"] = "JS::ToInt32"
|
|
if tag is IDLType.Tags.int8:
|
|
replacements["intermediateCast"] = "(uint8_t)"
|
|
elif tag is IDLType.Tags.int16:
|
|
replacements["intermediateCast"] = "(uint16_t)"
|
|
else:
|
|
replacements["intermediateCast"] = ""
|
|
elif tag is IDLType.Tags.int64:
|
|
# XXXbz this may not match what WebIDL says to do in terms of reducing
|
|
# mod 2^64. Should we check?
|
|
replacements["jstype"] = "int64_t"
|
|
replacements["converter"] = "xpc::ValueToInt64"
|
|
elif tag is IDLType.Tags.uint64:
|
|
# XXXbz this may not match what WebIDL says to do in terms of reducing
|
|
# mod 2^64. Should we check?
|
|
replacements["jstype"] = "uint64_t"
|
|
replacements["converter"] = "xpc::ValueToUint64"
|
|
elif tag in [IDLType.Tags.float, IDLType.Tags.double]:
|
|
replacements["jstype"] = "double"
|
|
replacements["converter"] = "JS::ToNumber"
|
|
else:
|
|
raise TypeError("Unknown primitive type '%s'" % type);
|
|
|
|
# We substitute the %(name)s things here. Our caller will
|
|
# substitute the ${name} things.
|
|
return (" %(jstype)s ${name}_jstype;\n"
|
|
"%(declareArg)s" # No leading whitespace or newline here, on purpose
|
|
" if (%(converter)s(cx, ${argVal}, &${name}_jstype)) {\n"
|
|
" %(finalValueSetter)s((${typeName})%(intermediateCast)s${name}_jstype);\n"
|
|
" } else {\n"
|
|
" return false;\n"
|
|
" }\n" % replacements)
|
|
|
|
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(%s)" % (value.value)
|
|
if tag in [IDLType.Tags.int64, IDLType.Tags.uint64]:
|
|
return "DOUBLE_TO_JSVAL(%s)" % (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)
|
|
|
|
def convertIDLDefaultValueToJSVal(value):
|
|
if value.type:
|
|
tag = value.type.tag()
|
|
if tag == IDLType.Tags.domstring:
|
|
assert False # Not implemented!
|
|
return convertConstIDLValueToJSVal(value)
|
|
|
|
unindenter = re.compile("^ ", re.MULTILINE)
|
|
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):
|
|
CGThing.__init__(self)
|
|
self.argument = argument
|
|
# XXXbz should optional jsval args get JSVAL_VOID? What about
|
|
# others?
|
|
self.replacementVariables = {
|
|
"index" : index,
|
|
"argc" : argc,
|
|
"argv" : argv,
|
|
"defaultValue" : "JSVAL_VOID",
|
|
"name" : "arg%d" % index
|
|
}
|
|
if argument.optional:
|
|
if argument.defaultValue:
|
|
self.replacementVariables["defaultValue"] = convertIDLDefaultValueToJSVal(argument.defaultValue)
|
|
self.replacementVariables["argVal"] = string.Template(
|
|
"(${index} < ${argc} ? ${argv}[${index}] : ${defaultValue})"
|
|
).substitute(self.replacementVariables)
|
|
self.replacementVariables["argPtr"] = string.Template(
|
|
"(${index} < ${argc} ? &${argv}[${index}] : NULL)"
|
|
).substitute(self.replacementVariables)
|
|
else:
|
|
self.replacementVariables["argVal"] = string.Template(
|
|
"${argv}[${index}]"
|
|
).substitute(self.replacementVariables)
|
|
self.replacementVariables["argPtr"] = (
|
|
"&" + self.replacementVariables["argVal"])
|
|
self.descriptor = None
|
|
if argument.type.isPrimitive():
|
|
self.replacementVariables["typeName"] = builtinNames[argument.type.tag()]
|
|
elif argument.type.isInterface() and not argument.type.isArrayBuffer():
|
|
descriptor = descriptorProvider.getDescriptor(
|
|
argument.type.unroll().inner.identifier.name)
|
|
self.descriptor = descriptor
|
|
self.replacementVariables["typeName"] = descriptor.nativeType
|
|
|
|
def define(self):
|
|
return string.Template(
|
|
re.sub(unindenter,
|
|
"",
|
|
getArgumentConversionTemplate(self.argument.type,
|
|
self.descriptor))
|
|
).substitute(self.replacementVariables)
|
|
|
|
def getWrapTemplateForTypeImpl(type, result, descriptorProvider,
|
|
resultAlreadyAddRefed):
|
|
if type is None or type.isVoid():
|
|
return """
|
|
${jsvalRef} = JSVAL_VOID;
|
|
return true;"""
|
|
|
|
if type.isSequence() or type.isArray():
|
|
raise TypeError("Can't handle sequence or array return values yet")
|
|
|
|
if type.isInterface() and not type.isArrayBuffer():
|
|
descriptor = descriptorProvider.getDescriptor(type.unroll().inner.identifier.name)
|
|
wrappingCode = ("""
|
|
if (!%s) {
|
|
${jsvalRef} = JSVAL_NULL;
|
|
return true;
|
|
}""" % result) if type.nullable() else ""
|
|
if descriptor.castable and not type.unroll().inner.isExternal():
|
|
wrappingCode += """
|
|
if (WrapNewBindingObject(cx, obj, %s, ${jsvalPtr})) {
|
|
return true;
|
|
}""" % result
|
|
# We don't support prefable stuff in workers.
|
|
assert(not descriptor.prefable or not descriptor.workers)
|
|
if not descriptor.prefable:
|
|
# Non-prefable bindings can only fail to wrap as a new-binding object
|
|
# if they already threw an exception. Same thing for
|
|
# non-prefable bindings.
|
|
wrappingCode += """
|
|
MOZ_ASSERT(JS_IsExceptionPending(cx));
|
|
return false;"""
|
|
else:
|
|
# Try old-style wrapping for bindings which might be preffed off.
|
|
wrappingCode += """
|
|
return HandleNewBindingWrappingFailure(cx, obj, %s, ${jsvalPtr});""" % result
|
|
else:
|
|
if descriptor.notflattened:
|
|
getIID = "&NS_GET_IID(%s), " % descriptor.nativeType
|
|
else:
|
|
getIID = ""
|
|
wrappingCode += """
|
|
return WrapObject(cx, obj, %s, %s${jsvalPtr});""" % (result, getIID)
|
|
return wrappingCode
|
|
|
|
if type.isString():
|
|
if type.nullable():
|
|
return """
|
|
return xpc::StringToJsval(cx, %s, ${jsvalPtr});""" % result
|
|
else:
|
|
return """
|
|
return xpc::NonVoidStringToJsval(cx, %s, ${jsvalPtr});""" % result
|
|
|
|
if type.isEnum():
|
|
if type.nullable():
|
|
raise TypeError("We don't support nullable enumerated return types "
|
|
"yet")
|
|
return """
|
|
MOZ_ASSERT(uint32_t(%(result)s) < ArrayLength(%(strings)s));
|
|
JSString* result_str = JS_NewStringCopyN(cx, %(strings)s[uint32_t(%(result)s)].value, %(strings)s[uint32_t(%(result)s)].length);
|
|
if (!result_str) {
|
|
return false;
|
|
}
|
|
${jsvalRef} = JS::StringValue(result_str);
|
|
return true;""" % { "result" : result,
|
|
"strings" : type.inner.identifier.name + "::strings" }
|
|
|
|
if type.isCallback() and not type.isInterface():
|
|
# XXXbz we're going to assume that callback types are always
|
|
# nullable and always have [TreatNonCallableAsNull] for now.
|
|
# See comments in WrapNewBindingObject explaining why we need
|
|
# to wrap here.
|
|
return """
|
|
${jsvalRef} = JS::ObjectOrNullValue(%s);
|
|
return JS_WrapValue(cx, ${jsvalPtr});""" % result
|
|
|
|
if type.tag() == IDLType.Tags.any:
|
|
# See comments in WrapNewBindingObject explaining why we need
|
|
# to wrap here.
|
|
return """
|
|
${jsvalRef} = %s;\n
|
|
return JS_WrapValue(cx, ${jsvalPtr});""" % result
|
|
|
|
if not type.isPrimitive():
|
|
raise TypeError("Need to learn to wrap %s" % type)
|
|
|
|
if type.nullable():
|
|
return """
|
|
if (%s.IsNull()) {
|
|
${jsvalRef} = JSVAL_NULL;
|
|
return true;
|
|
}
|
|
%s""" % (result, getWrapTemplateForTypeImpl(type.inner, "%s.Value()" % result,
|
|
descriptorProvider,
|
|
resultAlreadyAddRefed))
|
|
|
|
tag = type.tag()
|
|
|
|
if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, IDLType.Tags.int16,
|
|
IDLType.Tags.uint16, IDLType.Tags.int32]:
|
|
return """
|
|
${jsvalRef} = INT_TO_JSVAL(int32_t(%s));
|
|
return true;""" % result
|
|
|
|
elif tag in [IDLType.Tags.int64, IDLType.Tags.uint64, IDLType.Tags.float,
|
|
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 """
|
|
return JS_NewNumberValue(cx, double(%s), ${jsvalPtr});""" % result
|
|
|
|
elif tag == IDLType.Tags.uint32:
|
|
return """
|
|
${jsvalRef} = UINT_TO_JSVAL(%s);
|
|
return true;""" % result
|
|
|
|
elif tag == IDLType.Tags.bool:
|
|
return """
|
|
${jsvalRef} = BOOLEAN_TO_JSVAL(%s);
|
|
return true;""" % result
|
|
|
|
else:
|
|
raise TypeError("Need to learn to wrap primitive: %s" % type)
|
|
|
|
def getWrapTemplateForType(type, descriptorProvider, resultAlreadyAddRefed):
|
|
return getWrapTemplateForTypeImpl(type, "result", descriptorProvider,
|
|
resultAlreadyAddRefed)
|
|
|
|
class CGCallGenerator(CGThing):
|
|
"""
|
|
A class to generate an actual call to a C++ object. Assumes that the C++
|
|
object is stored in a variable named "self".
|
|
"""
|
|
def __init__(self, errorReport, argCount, argsPre, returnType,
|
|
resultAlreadyAddRefed, descriptorProvider, nativeMethodName, static):
|
|
CGThing.__init__(self)
|
|
|
|
isFallible = errorReport is not None
|
|
|
|
args = CGList([CGGeneric("arg" + str(i)) for i in range(argCount)], ", ")
|
|
resultOutParam = returnType is not None and returnType.isString()
|
|
# Return values that go in outparams go here
|
|
if resultOutParam:
|
|
args.append(CGGeneric("result"))
|
|
if isFallible:
|
|
args.append(CGGeneric("rv"))
|
|
|
|
if returnType is None or returnType.isVoid():
|
|
# Nothing to declare
|
|
result = None
|
|
elif returnType.isPrimitive() and returnType.tag() in builtinNames:
|
|
result = CGGeneric(builtinNames[returnType.tag()])
|
|
if returnType.nullable():
|
|
result = CGWrapper(result, pre="Nullable<", post=">")
|
|
elif returnType.isString():
|
|
result = CGGeneric("nsString")
|
|
elif returnType.isEnum():
|
|
if returnType.nullable():
|
|
raise TypeError("We don't support nullable enum return values")
|
|
result = CGGeneric(returnType.inner.identifier.name + "::value")
|
|
elif returnType.isInterface() and not returnType.isArrayBuffer():
|
|
result = CGGeneric(descriptorProvider.getDescriptor(
|
|
returnType.unroll().inner.identifier.name).nativeType)
|
|
if resultAlreadyAddRefed:
|
|
result = CGWrapper(result, pre="nsRefPtr<", post=">")
|
|
else:
|
|
result = CGWrapper(result, post="*")
|
|
elif returnType.isCallback():
|
|
# XXXbz we're going to assume that callback types are always
|
|
# nullable for now.
|
|
result = CGGeneric("JSObject*")
|
|
elif returnType.tag() is IDLType.Tags.any:
|
|
result = CGGeneric("JS::Value")
|
|
else:
|
|
raise TypeError("Don't know how to declare return value for %s" %
|
|
returnType)
|
|
|
|
# Build up our actual call
|
|
self.cgRoot = CGList([], "\n")
|
|
|
|
call = CGGeneric(nativeMethodName)
|
|
if static:
|
|
call = CGWrapper(call, pre="%s::" % (descriptorProvider.getDescriptor(
|
|
returnType.unroll().inner.identifier.name).nativeType))
|
|
else:
|
|
call = CGWrapper(call, pre="self->")
|
|
call = CGList([call, CGWrapper(args, pre="(" + argsPre, 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("nsresult rv = NS_OK;"))
|
|
self.cgRoot.append(CGGeneric("if (NS_FAILED(rv)) {"))
|
|
self.cgRoot.append(CGIndenter(CGGeneric(errorReport)))
|
|
self.cgRoot.append(CGGeneric("}"))
|
|
|
|
def define(self):
|
|
return self.cgRoot.define()
|
|
|
|
class CGPerSignatureCall(CGThing):
|
|
"""
|
|
This class handles the guts of generating code for a particular
|
|
call signature. A call signature consists of four things:
|
|
|
|
1) A return type, which can be None to indicate that there is no
|
|
actual return value (e.g. this is an attribute setter) or an
|
|
IDLType if there's an IDL type involved (including |void|).
|
|
2) An argument list, which is allowed to be empty.
|
|
3) A name of a native method to call.
|
|
4) Whether or not this method is static.
|
|
|
|
We also need to know whether this is a method or a getter/setter
|
|
to do error reporting correctly.
|
|
|
|
The idlNode parameter can be either a method or an attr. We can query
|
|
|idlNode.identifier| in both cases, so we can be agnostic between the two.
|
|
"""
|
|
# XXXbz For now each entry in the argument list is either an
|
|
# IDLArgument or a FakeArgument, but longer-term we may want to
|
|
# have ways of flagging things like JSContext* or optional_argc in
|
|
# there.
|
|
|
|
def __init__(self, returnType, argsPre, arguments, nativeMethodName, static,
|
|
descriptor, idlNode, extendedAttributes, argConversionStartsAt=0):
|
|
CGThing.__init__(self)
|
|
self.returnType = returnType
|
|
self.descriptor = descriptor
|
|
self.idlNode = idlNode
|
|
self.extendedAttributes = extendedAttributes
|
|
# Default to already_AddRefed on the main thread, raw pointer in workers
|
|
self.resultAlreadyAddRefed = not descriptor.workers and not 'resultNotAddRefed' in self.extendedAttributes
|
|
self.argsPre = "cx, " if 'implicitJSContext' in self.extendedAttributes else ""
|
|
self.argsPre += argsPre
|
|
self.argCount = len(arguments)
|
|
if self.argCount > argConversionStartsAt:
|
|
# Insert our argv in there
|
|
cgThings = [CGGeneric(self.getArgvDecl())]
|
|
else:
|
|
cgThings = []
|
|
cgThings.extend([CGArgumentConverter(arguments[i], i, self.getArgv(),
|
|
self.getArgc(), self.descriptor) for
|
|
i in range(argConversionStartsAt, self.argCount)])
|
|
|
|
cgThings.append(CGCallGenerator(
|
|
self.getErrorReport() if self.isFallible() else None,
|
|
self.argCount, self.argsPre, returnType,
|
|
self.resultAlreadyAddRefed, 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 isFallible(self):
|
|
return not 'infallible' in self.extendedAttributes
|
|
|
|
def wrap_return_value(self):
|
|
resultTemplateValues = {'jsvalRef': '*vp', 'jsvalPtr': 'vp'}
|
|
return string.Template(
|
|
re.sub(unindenter,
|
|
"",
|
|
getWrapTemplateForType(self.returnType, self.descriptor,
|
|
self.resultAlreadyAddRefed))
|
|
).substitute(resultTemplateValues)
|
|
|
|
def getErrorReport(self):
|
|
return '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() + 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, argsPre, nativeMethodName, static, descriptor, method,
|
|
extendedAttributes):
|
|
CGThing.__init__(self)
|
|
|
|
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 maxSigLength(signatures):
|
|
return max([len(s[1]) for s in signatures])
|
|
|
|
def signaturesForArgCount(i, signatures):
|
|
return filter(
|
|
lambda s: len(s[1]) == i or (len(s[1]) > i and
|
|
s[1][i].optional),
|
|
signatures)
|
|
|
|
def findDistinguishingIndex(argCount, signatures):
|
|
def isValidDistinguishingIndex(idx, signatures):
|
|
for firstSigIndex in range(0, len(signatures)):
|
|
for secondSigIndex in range(0, firstSigIndex):
|
|
firstType = signatures[firstSigIndex][1][idx].type
|
|
secondType = signatures[secondSigIndex][1][idx].type
|
|
if not firstType.isDistinguishableFrom(secondType):
|
|
return False
|
|
return True
|
|
for idx in range(0, argCount):
|
|
if isValidDistinguishingIndex(idx, signatures):
|
|
return idx
|
|
return -1
|
|
|
|
def getPerSignatureCall(signature, argConversionStartsAt=0):
|
|
return CGPerSignatureCall(signature[0], argsPre, signature[1],
|
|
nativeMethodName, static, descriptor,
|
|
method, extendedAttributes,
|
|
argConversionStartsAt)
|
|
|
|
|
|
signatures = method.signatures()
|
|
if len(signatures) == 1:
|
|
# Special case: we can just do a per-signature method call
|
|
# here for our one signature and not worry about switching
|
|
# on anything.
|
|
signature = signatures[0]
|
|
self.cgRoot = CGList([ CGIndenter(getPerSignatureCall(signature)) ])
|
|
requiredArgs = requiredArgCount(signature)
|
|
if requiredArgs > 0:
|
|
self.cgRoot.prepend(
|
|
CGWrapper(
|
|
CGIndenter(
|
|
CGGeneric(
|
|
"if (argc < %d) {\n"
|
|
" return Throw<%s>(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);\n"
|
|
"}" % (requiredArgs,
|
|
toStringBool(not descriptor.workers)))
|
|
),
|
|
pre="\n", post="\n")
|
|
)
|
|
return
|
|
|
|
# Need to find the right overload
|
|
maxSigArgs = maxSigLength(signatures)
|
|
allowedArgCounts = [ i for i in range(0, maxSigArgs+1)
|
|
if len(signaturesForArgCount(i, signatures)) != 0 ]
|
|
|
|
argCountCases = []
|
|
for argCount in allowedArgCounts:
|
|
possibleSignatures = signaturesForArgCount(argCount, signatures)
|
|
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(signaturesForArgCount(argCount+1, signatures)) == 1):
|
|
argCountCases.append(
|
|
CGCase(str(argCount), None, True))
|
|
else:
|
|
argCountCases.append(
|
|
CGCase(str(argCount), getPerSignatureCall(signature)))
|
|
continue
|
|
|
|
distinguishingIndex = findDistinguishingIndex(argCount,
|
|
possibleSignatures)
|
|
if distinguishingIndex == -1:
|
|
raise TypeError(("Signatures with %s arguments for " +
|
|
descriptor.interface.identifier.name + "." +
|
|
method.identifier.name +
|
|
" are not distinguishable") % argCount)
|
|
|
|
for idx in range(0, distinguishingIndex):
|
|
firstSigType = possibleSignatures[0][1][idx].type
|
|
for sigIdx in range(1, len(possibleSignatures)):
|
|
if possibleSignatures[sigIdx][1][idx].type != firstSigType:
|
|
raise TypeError(("Signatures with %d arguments for " +
|
|
descriptor.interface.identifier.name +
|
|
"." + method.identifier.name +
|
|
" have different types at index %d" +
|
|
" which is before distinguishing" +
|
|
" index %d") % (argCount,
|
|
idx,
|
|
distinguishingIndex))
|
|
|
|
# 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
|
|
|
|
# First check for null or undefined
|
|
pickFirstSignature("%s.isNullOrUndefined()" % distinguishingArg,
|
|
lambda s: s[1][distinguishingIndex].type.nullable())
|
|
|
|
# Now check for distinguishingArg being a platform object.
|
|
# We can actually check separately for array buffers and
|
|
# other things.
|
|
# XXXbz Do we need to worry about security
|
|
# wrappers around the array buffer?
|
|
pickFirstSignature("%s.isObject() && JS_IsArrayBufferObject(&%s.toObject(), cx)" %
|
|
(distinguishingArg, distinguishingArg),
|
|
lambda s: (s[1][distinguishingIndex].type.isArrayBuffer() or
|
|
s[1][distinguishingIndex].type.isObject()))
|
|
|
|
interfacesSigs = [
|
|
s for s in possibleSignatures
|
|
if (s[1][distinguishingIndex].type.isObject() or
|
|
(s[1][distinguishingIndex].type.isInterface() and
|
|
not s[1][distinguishingIndex].type.isArrayBuffer() and
|
|
not s[1][distinguishingIndex].type.isCallback())) ]
|
|
# There might be more than one of these; we need to check
|
|
# which ones we unwrap to.
|
|
|
|
if len(interfacesSigs) > 0:
|
|
caseBody.append(CGGeneric("if (%s.isObject() &&\n"
|
|
" IsPlatformObject(cx, &%s.toObject())) {" %
|
|
(distinguishingArg, distinguishingArg)))
|
|
for sig in interfacesSigs:
|
|
caseBody.append(CGIndenter(CGGeneric("do {")));
|
|
type = sig[1][distinguishingIndex].type
|
|
|
|
# XXXbz this duplicates some argument-unwrapping code!
|
|
interfaceDesc = descriptor.getDescriptor(
|
|
type.unroll().inner.identifier.name)
|
|
argIsPointer = (type.nullable() or
|
|
type.unroll().inner.isExternal())
|
|
if argIsPointer:
|
|
nameSuffix = ""
|
|
else:
|
|
nameSuffix = "_ptr"
|
|
if (interfaceDesc.castable or
|
|
type.unroll().inner.isExternal() or
|
|
interfaceDesc.workers):
|
|
declType = " ${typeName}*"
|
|
else:
|
|
declType = " nsCOMPtr<${typeName}>"
|
|
template = declType + " ${name}%s;\n" % nameSuffix
|
|
if interfaceDesc.castable:
|
|
template += str(CastableObjectUnwrapper(
|
|
interfaceDesc,
|
|
"&${argVal}.toObject()",
|
|
"${name}"+nameSuffix,
|
|
"break;")) + "\n"
|
|
elif interfaceDesc.workers:
|
|
template += """
|
|
${name}%s = &${argVal}.toObject();
|
|
MOZ_ASSERT(${name}%s);
|
|
""" % (nameSuffix, nameSuffix)
|
|
else:
|
|
template += " xpc_qsSelfRef tmpRef_${name};\n"
|
|
template += " jsval tmpVal_${name} = ${argVal};\n"
|
|
template += """
|
|
${typeName}* tmp;
|
|
if (NS_FAILED(xpc_qsUnwrapArg<${typeName}>(cx, ${argVal}, &tmp, &tmpRef_${name}.ptr,
|
|
&tmpVal_${name}))) {
|
|
break;
|
|
}
|
|
MOZ_ASSERT(tmp);
|
|
${name}%s = tmp;
|
|
""" % nameSuffix
|
|
|
|
if not argIsPointer:
|
|
template += " ${typeName} &${name} = *${name}_ptr;\n"
|
|
|
|
testCode = string.Template(template).substitute(
|
|
{
|
|
"typeName": interfaceDesc.nativeType,
|
|
"name" : "arg%d" % distinguishingIndex,
|
|
"argVal" : distinguishingArg
|
|
}
|
|
)
|
|
caseBody.append(CGIndenter(CGGeneric(testCode)));
|
|
# If we got this far, we know we unwrapped to the right
|
|
# interface, so just do the call. Start conversion with
|
|
# distinguishingIndex + 1, since we already converted
|
|
# distinguishingIndex.
|
|
caseBody.append(CGIndenter(CGIndenter(
|
|
getPerSignatureCall(sig, distinguishingIndex + 1))))
|
|
caseBody.append(CGIndenter(CGGeneric("} while (0);")))
|
|
|
|
caseBody.append(CGGeneric("}"))
|
|
|
|
# XXXbz Now we're supposed to check for distinguishingArg being
|
|
# an array or a platform object that supports indexed
|
|
# properties... skip that last for now. It's a bit of a pain.
|
|
pickFirstSignature("%s.isObject() && IsArrayLike(cx, &%s.toObject()" %
|
|
(distinguishingArg, distinguishingArg),
|
|
lambda s:
|
|
(s[1][distinguishingIndex].type.isArray() or
|
|
s[1][distinguishingIndex].type.isSequence() or
|
|
s[1][distinguishingIndex].type.isObject()))
|
|
|
|
# Check for Date objects
|
|
# XXXbz Do we need to worry about security wrappers around the Date?
|
|
pickFirstSignature("%s.isObject() && JS_ObjectIsDate(cx, &%s.toObject())" %
|
|
(distinguishingArg, distinguishingArg),
|
|
lambda s: (s[1][distinguishingIndex].type.isDate() or
|
|
s[1][distinguishingIndex].type.isObject()))
|
|
|
|
# Check for vanilla JS objects
|
|
# XXXbz Do we need to worry about security wrappers?
|
|
pickFirstSignature("%s.isObject() && !IsPlatformObject(cx, &%s.toObject())" %
|
|
(distinguishingArg, distinguishingArg),
|
|
lambda s: (s[1][distinguishingIndex].type.isCallback() or
|
|
s[1][distinguishingIndex].type.isDictionary() or
|
|
s[1][distinguishingIndex].type.isObject()))
|
|
|
|
# The remaining cases are mutually exclusive. The
|
|
# pickFirstSignature calls are what change caseBody
|
|
# Check for strings or enums
|
|
if pickFirstSignature(None,
|
|
lambda s: (s[1][distinguishingIndex].type.isString() or
|
|
s[1][distinguishingIndex].type.isEnum())):
|
|
pass
|
|
# Check for primitives
|
|
elif pickFirstSignature(None,
|
|
lambda s: s[1][distinguishingIndex].type.isPrimitive()):
|
|
pass
|
|
# Check for "any"
|
|
elif pickFirstSignature(None,
|
|
lambda s: s[1][distinguishingIndex].type.isAny()):
|
|
pass
|
|
else:
|
|
# Just throw; we have no idea what we're supposed to
|
|
# do with this.
|
|
caseBody.append(CGGeneric("return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);" %
|
|
toStringBool(not descriptor.workers)))
|
|
|
|
argCountCases.append(CGCase(str(argCount),
|
|
CGList(caseBody, "\n")))
|
|
|
|
overloadCGThings = []
|
|
overloadCGThings.append(
|
|
CGGeneric("unsigned argcount = NS_MIN(argc, %du);" %
|
|
maxSigArgs))
|
|
overloadCGThings.append(
|
|
CGSwitch("argcount",
|
|
argCountCases,
|
|
CGGeneric("return Throw<%s>(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);" %
|
|
toStringBool(not descriptor.workers))))
|
|
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 CGGetterSetterCall(CGPerSignatureCall):
|
|
"""
|
|
A class to generate a native object getter or setter call for a
|
|
particular IDL getter or setter.
|
|
"""
|
|
def __init__(self, returnType, arguments, nativeMethodName, descriptor,
|
|
attr, extendedAttributes):
|
|
CGPerSignatureCall.__init__(self, returnType, "", arguments,
|
|
nativeMethodName, False, descriptor, attr,
|
|
extendedAttributes)
|
|
def getArgv(self):
|
|
if generateNativeAccessors:
|
|
return CGPerSignatureCall.getArgv(self)
|
|
return "vp"
|
|
|
|
class CGGetterCall(CGGetterSetterCall):
|
|
"""
|
|
A class to generate a native object getter call for a particular IDL
|
|
getter.
|
|
"""
|
|
def __init__(self, returnType, nativeMethodName, descriptor, attr,
|
|
extendedAttributes):
|
|
CGGetterSetterCall.__init__(self, returnType, [], nativeMethodName,
|
|
descriptor, attr, extendedAttributes)
|
|
def getArgc(self):
|
|
if generateNativeAccessors:
|
|
return CGGetterSetterCall.getArgc()
|
|
return "0"
|
|
def getArgvDecl(self):
|
|
if generateNativeAccessors:
|
|
return CGPerSignatureCall.getArgvDecl(self)
|
|
# We just get our stuff from vp
|
|
return ""
|
|
|
|
class FakeArgument():
|
|
def __init__(self, type):
|
|
self.type = type
|
|
self.optional = False
|
|
|
|
class CGSetterCall(CGGetterSetterCall):
|
|
"""
|
|
A class to generate a native object setter call for a particular IDL
|
|
setter.
|
|
"""
|
|
def __init__(self, argType, nativeMethodName, descriptor, attr,
|
|
extendedAttributes):
|
|
CGGetterSetterCall.__init__(self, None, [FakeArgument(argType)],
|
|
nativeMethodName, descriptor, attr,
|
|
extendedAttributes)
|
|
def wrap_return_value(self):
|
|
if generateNativeAccessors:
|
|
return CGGetterSetterCall.wrap_return_value(self)
|
|
# We have no return value
|
|
return "\nreturn true;"
|
|
def getArgc(self):
|
|
if generateNativeAccessors:
|
|
return CGGetterSetterCall.getArgc(self)
|
|
return "1"
|
|
def getArgvDecl(self):
|
|
if generateNativeAccessors:
|
|
return (CGPerSignatureCall.getArgvDecl(self) +
|
|
"jsval undef = JS::UndefinedValue();\n"
|
|
"if (argc == 0) {\n"
|
|
" argv = &undef;\n"
|
|
" argc = 1;\n"
|
|
"}")
|
|
# We just get our stuff from vp
|
|
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, extendedAttributes):
|
|
self.extendedAttributes = extendedAttributes
|
|
CGAbstractStaticMethod.__init__(self, descriptor, name, "JSBool", args)
|
|
|
|
def definition_body(self):
|
|
unwrapThis = CGGeneric(
|
|
str(FailureFatalCastableObjectUnwrapper(self.descriptor, "obj", "self")))
|
|
return CGList([ self.getThis(), unwrapThis,
|
|
self.generate_code() ], "\n").define()
|
|
|
|
def getThis(self):
|
|
return CGIndenter(
|
|
CGGeneric("JSObject* obj = JS_THIS_OBJECT(cx, vp);\n"
|
|
"if (!obj) {\n"
|
|
" return false;\n"
|
|
"}\n"
|
|
"\n"
|
|
"%s* self;" % self.descriptor.nativeType))
|
|
|
|
def generate_code(self):
|
|
assert(False) # Override me
|
|
|
|
def MakeNativeName(name):
|
|
return name[0].upper() + name[1:]
|
|
|
|
class CGNativeMethod(CGAbstractBindingMethod):
|
|
"""
|
|
A class for generating the C++ code for an IDL method..
|
|
"""
|
|
def __init__(self, descriptor, method):
|
|
self.method = method
|
|
baseName = method.identifier.name
|
|
args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
|
|
Argument('JS::Value*', 'vp')]
|
|
CGAbstractBindingMethod.__init__(self, descriptor, baseName, args,
|
|
descriptor.getExtendedAttributes(method))
|
|
def generate_code(self):
|
|
name = self.method.identifier.name
|
|
nativeName = self.descriptor.binaryNames.get(name, MakeNativeName(name))
|
|
return CGMethodCall("", nativeName, self.method.isStatic(),
|
|
self.descriptor, self.method,
|
|
self.extendedAttributes)
|
|
|
|
class CGNativeGetter(CGAbstractBindingMethod):
|
|
"""
|
|
A class for generating the C++ code for an IDL attribute getter.
|
|
"""
|
|
def __init__(self, descriptor, attr):
|
|
self.attr = attr
|
|
name = 'get_' + attr.identifier.name
|
|
if generateNativeAccessors:
|
|
args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
|
|
Argument('JS::Value*', 'vp')]
|
|
else:
|
|
args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'obj'),
|
|
Argument('jsid', 'id'), Argument('JS::Value*', 'vp')]
|
|
CGAbstractBindingMethod.__init__(self, descriptor, name, args,
|
|
descriptor.getExtendedAttributes(self.attr, getter=True))
|
|
|
|
def getThis(self):
|
|
if generateNativeAccessors:
|
|
return CGAbstractBindingMethod.getThis(self)
|
|
return CGIndenter(
|
|
CGGeneric("%s* self;" % self.descriptor.nativeType))
|
|
|
|
def generate_code(self):
|
|
|
|
nativeMethodName = "Get" + MakeNativeName(self.attr.identifier.name)
|
|
return CGIndenter(CGGetterCall(self.attr.type, nativeMethodName, self.descriptor,
|
|
self.attr, self.extendedAttributes))
|
|
|
|
class CGNativeSetter(CGAbstractBindingMethod):
|
|
"""
|
|
A class for generating the C++ code for an IDL attribute setter.
|
|
"""
|
|
def __init__(self, descriptor, attr):
|
|
self.attr = attr
|
|
baseName = attr.identifier.name
|
|
name = 'set_' + attr.identifier.name
|
|
if generateNativeAccessors:
|
|
args = [Argument('JSContext*', 'cx'), Argument('unsigned', 'argc'),
|
|
Argument('JS::Value*', 'vp')]
|
|
else:
|
|
args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'obj'),
|
|
Argument('jsid', 'id'), Argument('JSBool', 'strict'),
|
|
Argument('JS::Value*', 'vp')]
|
|
CGAbstractBindingMethod.__init__(self, descriptor, name, args,
|
|
descriptor.getExtendedAttributes(self.attr, setter=True))
|
|
|
|
def getThis(self):
|
|
if generateNativeAccessors:
|
|
return CGAbstractBindingMethod.getThis(self)
|
|
return CGIndenter(
|
|
CGGeneric("%s* self;" % self.descriptor.nativeType))
|
|
|
|
def generate_code(self):
|
|
nativeMethodName = "Set" + MakeNativeName(self.attr.identifier.name)
|
|
return CGIndenter(CGSetterCall(self.attr.type, nativeMethodName, self.descriptor,
|
|
self.attr, self.extendedAttributes))
|
|
|
|
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 chars other than [a-z] or '-' for now. Replace '-' with '_'.
|
|
value = value.replace('-', '_')
|
|
if value == "_empty":
|
|
raise SyntaxError('"_empty" is not an IDL enum value we support yet')
|
|
if value == "":
|
|
return "_empty"
|
|
if not re.match("^[a-z_]+$", value):
|
|
raise SyntaxError('Enum value "' + value + '" contains characters '
|
|
'outside [a-z_]')
|
|
return value
|
|
|
|
class CGEnum(CGThing):
|
|
def __init__(self, enum):
|
|
CGThing.__init__(self)
|
|
self.enum = enum
|
|
|
|
def declare(self):
|
|
return """
|
|
enum value {
|
|
%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()]))
|
|
|
|
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):
|
|
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
|
|
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([str(a) for a in self.args])
|
|
if self.bodyInHeader:
|
|
body = ' ' + self.getBody();
|
|
body = body.replace('\n', '\n ').rstrip(' ')
|
|
body = '\n{\n' + body + '\n}'
|
|
else:
|
|
body = ';'
|
|
|
|
return string.Template("""${templateClause}${decorators}${returnType}
|
|
${name}(${args})${const}${body}
|
|
""").substitute({ 'templateClause': templateClause,
|
|
'decorators': self.getDecorators(True),
|
|
'returnType': self.returnType,
|
|
'name': self.name,
|
|
'const': ' const' if self.const 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([str(a) for a in self.args])
|
|
|
|
body = ' ' + self.getBody()
|
|
body = body.replace('\n', '\n ').rstrip(' ')
|
|
|
|
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 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 getBody(self):
|
|
assert self.body is not None
|
|
return self.body
|
|
|
|
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 ''
|
|
return '%s %s::%s = %s;\n' % (self.type, cgClass.getNameString(),
|
|
self.name, self.getBody())
|
|
|
|
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=[], methods=[], typedefs = [],
|
|
enums=[], templateArgs=[], templateSpecialization=[],
|
|
isStruct=False, indent=''):
|
|
CGThing.__init__(self)
|
|
self.name = name
|
|
self.bases = bases
|
|
self.members = members
|
|
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'
|
|
|
|
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 = [str(a) 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 = ''
|
|
|
|
result = result + '%s%s %s%s' \
|
|
% (self.indent, type, self.name, specialization)
|
|
|
|
if self.bases:
|
|
result = result + ' : %s' % ', '.join([d.declare(self) for d in self.bases])
|
|
|
|
result = result + '\n%s{\n' % self.indent
|
|
|
|
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 + ' '
|
|
else:
|
|
result = result + separator + ' '
|
|
declaration = member.declare(cgClass)
|
|
declaration = declaration.replace('\n', '\n ')
|
|
declaration = declaration.rstrip(' ')
|
|
result = result + declaration
|
|
itemCount = itemCount + 1
|
|
lastVisibility = visibility
|
|
return (result, lastVisibility, itemCount)
|
|
|
|
order = [(self.enums, ''), (self.typedefs, ''), (self.members, ''),
|
|
(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 = self.indent + memberString
|
|
memberString = memberString.replace('\n', '\n' + self.indent)
|
|
memberString = memberString.rstrip(' ')
|
|
result = result + memberString
|
|
|
|
result = result + self.indent + '};\n\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.methods, '\n')]
|
|
|
|
result = ''
|
|
itemCount = 0
|
|
for (memberList, separator) in order:
|
|
(memberString, itemCount) = defineMembers(self, memberList,
|
|
itemCount, separator)
|
|
result = result + memberString
|
|
return result
|
|
|
|
class CGResolveProperty(CGAbstractMethod):
|
|
def __init__(self, descriptor, properties):
|
|
args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'wrapper'),
|
|
Argument('jsid', 'id'), Argument('bool', 'set'),
|
|
Argument('JSPropertyDescriptor*', 'desc')]
|
|
CGAbstractMethod.__init__(self, descriptor, "ResolveProperty", "bool", args)
|
|
self.properties = properties
|
|
def definition_body(self):
|
|
str = ""
|
|
|
|
varNames = self.properties.variableNames(True)
|
|
|
|
methods = self.properties.methods
|
|
if methods.hasNonChromeOnly() or methods.hasChromeOnly():
|
|
str += """ for (size_t i = 0; i < ArrayLength(%(methods)s_ids); ++i) {
|
|
if (id == %(methods)s_ids[i]) {
|
|
JSFunction *fun = JS_NewFunctionById(cx, %(methods)s[i].call, %(methods)s[i].nargs, 0, wrapper, id);
|
|
if (!fun)
|
|
return false;
|
|
JSObject *funobj = JS_GetFunctionObject(fun);
|
|
desc->value.setObject(*funobj);
|
|
desc->attrs = %(methods)s[i].flags;
|
|
desc->obj = wrapper;
|
|
desc->setter = nsnull;
|
|
desc->getter = nsnull;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
""" % varNames
|
|
|
|
attrs = self.properties.attrs
|
|
if attrs.hasNonChromeOnly() or attrs.hasChromeOnly():
|
|
str += """ for (size_t i = 0; i < ArrayLength(%(attrs)s_ids); ++i) {
|
|
if (id == %(attrs)s_ids[i]) {
|
|
desc->attrs = %(attrs)s[i].flags;
|
|
desc->obj = wrapper;
|
|
desc->setter = %(attrs)s[i].setter;
|
|
desc->getter = %(attrs)s[i].getter;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
""" % varNames
|
|
|
|
return str + " return true;"
|
|
|
|
class CGEnumerateProperties(CGAbstractMethod):
|
|
def __init__(self, descriptor, properties):
|
|
args = [Argument('JS::AutoIdVector&', 'props')]
|
|
CGAbstractMethod.__init__(self, descriptor, "EnumerateProperties", "bool", args)
|
|
self.properties = properties
|
|
def definition_body(self):
|
|
str = ""
|
|
|
|
varNames = self.properties.variableNames(True)
|
|
|
|
methods = self.properties.methods
|
|
if methods.hasNonChromeOnly() or methods.hasChromeOnly():
|
|
str += """ for (size_t i = 0; i < sizeof(%(methods)s_ids); ++i) {
|
|
if ((%(methods)s[i].flags & JSPROP_ENUMERATE) &&
|
|
!props.append(%(methods)s_ids[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
""" % varNames
|
|
|
|
attrs = self.properties.attrs
|
|
if attrs.hasNonChromeOnly() or attrs.hasChromeOnly():
|
|
str += """ for (size_t i = 0; i < sizeof(%(attrs)s_ids); ++i) {
|
|
if ((%(attrs)s[i].flags & JSPROP_ENUMERATE) &&
|
|
!props.append(%(attrs)s_ids[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
""" % varNames
|
|
|
|
return str + " return true;"
|
|
|
|
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)
|
|
|
|
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)
|
|
|
|
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 stripTrailingWhitespace(text):
|
|
lines = text.splitlines()
|
|
for i in range(len(lines)):
|
|
lines[i] = lines[i].rstrip()
|
|
return '\n'.join(lines)
|
|
|
|
class CGDescriptor(CGThing):
|
|
def __init__(self, descriptor):
|
|
CGThing.__init__(self)
|
|
|
|
assert not descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject()
|
|
|
|
cgThings = []
|
|
if descriptor.interface.hasInterfacePrototypeObject():
|
|
cgThings.extend([CGNativeMethod(descriptor, m) for m in
|
|
descriptor.interface.members if
|
|
m.isMethod() and not m.isStatic()])
|
|
cgThings.extend([CGNativeGetter(descriptor, a) for a in
|
|
descriptor.interface.members if a.isAttr()])
|
|
cgThings.extend([CGNativeSetter(descriptor, a) for a in
|
|
descriptor.interface.members if
|
|
a.isAttr() and not a.readonly])
|
|
|
|
if descriptor.concrete:
|
|
if not descriptor.workers:
|
|
cgThings.append(CGAddPropertyHook(descriptor))
|
|
|
|
# Always have a finalize hook, regardless of whether the class wants a
|
|
# custom hook.
|
|
if descriptor.nativeIsISupports:
|
|
cgThings.append(CGNativeToSupportsMethod(descriptor))
|
|
cgThings.append(CGClassFinalizeHook(descriptor))
|
|
|
|
# Only generate a trace hook if the class wants a custom hook.
|
|
if (descriptor.customTrace):
|
|
cgThings.append(CGClassTraceHook(descriptor))
|
|
|
|
if descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject():
|
|
cgThings.append(CGNativePropertyHooks(descriptor))
|
|
if descriptor.concrete:
|
|
cgThings.append(CGDOMJSClass(descriptor))
|
|
|
|
if descriptor.interface.hasInterfaceObject():
|
|
cgThings.append(CGClassConstructHook(descriptor))
|
|
cgThings.append(CGClassHasInstanceHook(descriptor))
|
|
cgThings.append(CGInterfaceObjectJSClass(descriptor))
|
|
|
|
if descriptor.interface.hasInterfacePrototypeObject():
|
|
cgThings.append(CGPrototypeJSClass(descriptor))
|
|
|
|
properties = PropertyArrays(descriptor)
|
|
cgThings.append(CGGeneric(define=str(properties)))
|
|
cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties))
|
|
if descriptor.interface.hasInterfacePrototypeObject():
|
|
cgThings.append(CGIndenter(CGGetProtoObjectMethod(descriptor)))
|
|
else:
|
|
cgThings.append(CGIndenter(CGGetConstructorObjectMethod(descriptor)))
|
|
|
|
if descriptor.concrete or descriptor.interface.hasInterfacePrototypeObject():
|
|
cgThings.append(CGResolveProperty(descriptor, properties))
|
|
cgThings.append(CGEnumerateProperties(descriptor, properties))
|
|
|
|
if descriptor.interface.hasInterfaceObject():
|
|
cgThings.append(CGDefineDOMInterfaceMethod(descriptor))
|
|
|
|
if descriptor.concrete:
|
|
cgThings.append(CGWrapMethod(descriptor))
|
|
|
|
cgThings = CGList(cgThings)
|
|
cgThings = CGWrapper(cgThings, post='\n')
|
|
self.cgRoot = CGWrapper(CGNamespace(descriptor.name, cgThings),
|
|
post='\n')
|
|
|
|
def declare(self):
|
|
return self.cgRoot.declare()
|
|
def define(self):
|
|
return self.cgRoot.define()
|
|
|
|
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 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) \\
|
|
aNameSpaceManager->RegisterDefineDOMInterface(NS_LITERAL_STRING(#_dom_class), prototypes::_dom_class::DefineDOMInterface);\n\n"""
|
|
def _undefineMacro(self):
|
|
return "\n#undef REGISTER_PROTO"
|
|
def _registerProtos(self):
|
|
lines = ["REGISTER_PROTO(%s);" % desc.name
|
|
for desc in self.config.getDescriptors(hasInterfaceObject=True,
|
|
isExternal=False,
|
|
workers=False)]
|
|
return '\n'.join(lines) + '\n'
|
|
def definition_body(self):
|
|
return self._defineMacro() + self._registerProtos() + self._undefineMacro()
|
|
|
|
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)
|
|
|
|
forwardDeclares = [CGClassForwardDeclare('XPCWrappedNativeScope')]
|
|
|
|
for x in descriptors:
|
|
nativeType = x.nativeType
|
|
components = x.nativeType.split('::')
|
|
declare = CGClassForwardDeclare(components[-1])
|
|
if len(components) > 1:
|
|
declare = CGNamespace.build(components[:-1],
|
|
CGWrapper(declare, declarePre='\n',
|
|
declarePost='\n'),
|
|
declareOnly=True)
|
|
forwardDeclares.append(CGWrapper(declare, declarePost='\n'))
|
|
|
|
forwardDeclares = CGList(forwardDeclares)
|
|
|
|
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 that have more
|
|
# than one concrete class implementation.
|
|
traitsClasses.extend([CGPrototypeIDMapClass(d) for d in descriptorsWithPrototype
|
|
if d.uniqueImplementation])
|
|
|
|
# Wrap all of that in our namespaces.
|
|
if len(traitsClasses) > 0:
|
|
traitsClasses = CGNamespace.build(['mozilla', 'dom', 'bindings'],
|
|
CGWrapper(CGList(traitsClasses),
|
|
declarePre='\n'),
|
|
declareOnly=True)
|
|
traitsClasses = CGWrapper(traitsClasses, declarePost='\n')
|
|
else:
|
|
traitsClasses = CGGeneric()
|
|
|
|
# Do codegen for all the descriptors and enums.
|
|
cgthings = [CGWrapper(CGNamespace.build([e.identifier.name],
|
|
CGEnum(e)),
|
|
post="\n") for e in config.getEnums(webIDLFile)]
|
|
cgthings.extend([CGDescriptor(x) for x in descriptors])
|
|
curr = CGList(cgthings)
|
|
|
|
# Wrap all of that in our namespaces.
|
|
curr = CGNamespace.build(['mozilla', 'dom', 'bindings', 'prototypes'],
|
|
CGWrapper(curr, pre="\n"))
|
|
|
|
curr = CGList([forwardDeclares, traitsClasses, curr])
|
|
|
|
# Add header includes.
|
|
curr = CGHeaders(descriptors,
|
|
['mozilla/dom/bindings/Utils.h',
|
|
'DOMJSClass.h'],
|
|
['mozilla/dom/bindings/Nullable.h',
|
|
'XPCQuickStubs.h',
|
|
'AccessCheck.h',
|
|
'WorkerPrivate.h',
|
|
'nsContentUtils.h'],
|
|
curr)
|
|
|
|
# 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())
|
|
|
|
|
|
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', protos, [0])
|
|
idEnum = CGList([idEnum])
|
|
idEnum.append(CGGeneric(declare="const unsigned MaxProtoChainLength = " +
|
|
str(config.maxProtoChainLength) + ";\n\n"))
|
|
|
|
# Wrap all of that in our namespaces.
|
|
idEnum = CGNamespace.build(['mozilla', 'dom', 'bindings', 'prototypes'],
|
|
CGWrapper(idEnum, pre='\n'))
|
|
idEnum = CGWrapper(idEnum, post='\n')
|
|
|
|
curr = CGList([idEnum])
|
|
|
|
# Constructor ID enum.
|
|
constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True,
|
|
hasInterfacePrototypeObject=False)]
|
|
idEnum = CGNamespacedEnum('id', 'ID', constructors, [0])
|
|
|
|
# Wrap all of that in our namespaces.
|
|
idEnum = CGNamespace.build(['mozilla', 'dom', 'bindings', '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', 'bindings'],
|
|
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 Common(config):
|
|
|
|
# TODO - Generate the methods we want
|
|
curr = CGRegisterProtos(config)
|
|
|
|
# Wrap all of that in our namespaces.
|
|
curr = CGNamespace.build(['mozilla', 'dom', 'bindings'],
|
|
CGWrapper(curr, post='\n'))
|
|
curr = CGWrapper(curr, post='\n')
|
|
|
|
# Add the includes
|
|
defineIncludes = [CGHeaders.getInterfaceFilename(desc.interface)
|
|
for desc in config.getDescriptors(hasInterfaceObject=True,
|
|
workers=False)]
|
|
defineIncludes.append('nsScriptNameSpaceManager.h')
|
|
curr = CGHeaders([], [], defineIncludes, curr)
|
|
|
|
# Add include guards.
|
|
curr = CGIncludeGuard('Common', curr)
|
|
|
|
# Done.
|
|
return curr
|