Generate working constant JSClass data.

This commit is contained in:
Josh Matthews 2013-02-14 08:08:53 -05:00
parent c79153be7b
commit 96d9c6402d
3 changed files with 916 additions and 44 deletions

View file

@ -43,9 +43,9 @@ def generate_binding_rs(config, outputprefix, webidlfile):
filename = outputprefix + ".rs" filename = outputprefix + ".rs"
root = CGBindingRoot(config, outputprefix, webidlfile) root = CGBindingRoot(config, outputprefix, webidlfile)
#root2 = CGBindingRoot(config, outputprefix, webidlfile) root2 = CGBindingRoot(config, outputprefix, webidlfile)
#if replaceFileIfChanged(filename, root.declare() + root2.define()): if replaceFileIfChanged(filename, root.declare() + root2.define()):
if replaceFileIfChanged(filename, root.define()): #if replaceFileIfChanged(filename, root.declare()):
print "Generating binding implementation: %s" % (filename) print "Generating binding implementation: %s" % (filename)
def main(): def main():

View file

@ -430,35 +430,305 @@ def getRetvalDeclarationForType(returnType, descriptorProvider,
raise TypeError("Don't know how to declare return value for %s" % raise TypeError("Don't know how to declare return value for %s" %
returnType) returnType)
class PropertyArrays(): def isChromeOnly(m):
def __init__(self, descriptor): return m.getExtendedAttribute("ChromeOnly")
#self.staticMethods = MethodDefiner(descriptor, "StaticMethods", True)
#self.methods = MethodDefiner(descriptor, "Methods", False)
#self.attrs = AttrDefiner(descriptor, "Attributes")
#self.consts = ConstDefiner(descriptor, "Constants")
pass
@staticmethod class PropertyDefiner:
def arrayNames(): """
return [ "staticMethods", "methods", "attrs", "consts" ] A common superclass for defining things on prototype objects.
@staticmethod
def xrayRelevantArrayNames():
return [ "methods", "attrs", "consts" ]
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
# self.prefCacheData will store an array of (prefname, bool*)
# pairs for our bool var caches. generateArray will fill it
# in as needed.
self.prefCacheData = []
def hasChromeOnly(self): def hasChromeOnly(self):
return reduce(lambda b, a: b or getattr(self, a).hasChromeOnly(), return len(self.chrome) > len(self.regular)
self.arrayNames(), False) def hasNonChromeOnly(self):
def variableNames(self, chrome): return len(self.regular) > 0
names = {} def variableName(self, chrome):
for array in self.arrayNames(): if chrome and self.hasChromeOnly():
names[array] = getattr(self, array).variableName(chrome) return "sChrome" + self.name
return names if self.hasNonChromeOnly():
return "s" + self.name
return "ptr::null()"
def usedForXrays(self, chrome):
# We only need Xrays for methods, attributes and constants. And we only
# need them for the non-chrome ones if we have no chromeonly things.
# Otherwise (we have chromeonly attributes) we need Xrays for the chrome
# methods/attributes/constants. Finally, in workers there are no Xrays.
return ((self.name is "Methods" or self.name is "Attributes" or
self.name is "Constants") and
chrome == self.hasChromeOnly() and
not self.descriptor.workers)
def __str__(self): def __str__(self):
define = "" # We only need to generate id arrays for things that will end
for array in self.arrayNames(): # up used via ResolveProperty or EnumerateProperties.
define += str(getattr(self, array)) str = self.generateArray(self.regular, self.variableName(False),
return define self.usedForXrays(False))
if self.hasChromeOnly():
str += self.generateArray(self.chrome, self.variableName(True),
self.usedForXrays(True))
return str
@staticmethod
def getControllingPref(interfaceMember):
prefName = interfaceMember.getExtendedAttribute("Pref")
if prefName is None:
return None
# It's a list of strings
assert(len(prefName) is 1)
assert(prefName[0] is not None)
return prefName[0]
def generatePrefableArray(self, array, name, specTemplate, specTerminator,
specType, getPref, getDataTuple, doIdArrays):
"""
This method generates our various arrays.
array is an array of interface members as passed to generateArray
name is the name as passed to generateArray
specTemplate is a template for each entry of the spec array
specTerminator is a terminator for the spec array (inserted every time
our controlling pref changes and at the end of the array)
specType is the actual typename of our spec
getPref is a callback function that takes an array entry and returns
the corresponding pref value.
getDataTuple is a callback function that takes an array entry and
returns a tuple suitable for substitution into specTemplate.
"""
# We want to generate a single list of specs, but with specTerminator
# inserted at every point where the pref name controlling the member
# changes. That will make sure the order of the properties as exposed
# on the interface and interface prototype objects does not change when
# pref control is added to members while still allowing us to define all
# the members in the smallest number of JSAPI calls.
assert(len(array) is not 0)
lastPref = getPref(array[0]) # So we won't put a specTerminator
# at the very front of the list.
specs = []
prefableSpecs = []
if doIdArrays:
prefableIds = []
prefableTemplate = ' { true, &%s[%d] }'
prefCacheTemplate = '&%s[%d].enabled'
def switchToPref(props, pref):
# Remember the info about where our pref-controlled
# booleans live.
if pref is not None:
props.prefCacheData.append(
(pref, prefCacheTemplate % (name, len(prefableSpecs)))
)
# Set up pointers to the new sets of specs and ids
# inside prefableSpecs and prefableIds
prefableSpecs.append(prefableTemplate %
(name + "_specs", len(specs)))
switchToPref(self, lastPref)
for member in array:
curPref = getPref(member)
if lastPref != curPref:
# Terminate previous list
specs.append(specTerminator)
# And switch to our new pref
switchToPref(self, curPref)
lastPref = curPref
# And the actual spec
specs.append(specTemplate % getDataTuple(member))
specs.append(specTerminator)
prefableSpecs.append(" { false, NULL }");
arrays = (("const %s_specs: [%s * %i] = [\n" +
',\n'.join(specs) + "\n" +
"];\n\n") % (name, specType, len(specs)))
#+
#"static Prefable<%s> %s[] = [\n" +
#',\n'.join(prefableSpecs) + "\n" +
#"];\n\n")
#if doIdArrays:
# arrays += ("const %s_ids: [jsid * %i] = [" % (name, len(specs))) + ", ".join(["JSID_VOID"] * len(specs)) + "];\n\n"
return arrays
class MethodDefiner(PropertyDefiner):
"""
A class for defining methods on a prototype object.
"""
def __init__(self, descriptor, name, static):
PropertyDefiner.__init__(self, descriptor, name)
# FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822
# We should be able to check for special operations without an
# identifier. For now we check if the name starts with __
methods = [m for m in descriptor.interface.members if
m.isMethod() and m.isStatic() == static and
not m.isIdentifierLess()]
self.chrome = [{"name": m.identifier.name,
"length": methodLength(m),
"flags": "JSPROP_ENUMERATE",
"pref": PropertyDefiner.getControllingPref(m) }
for m in methods]
self.regular = [{"name": m.identifier.name,
"length": methodLength(m),
"flags": "JSPROP_ENUMERATE",
"pref": PropertyDefiner.getControllingPref(m) }
for m in methods if not isChromeOnly(m)]
# FIXME Check for an existing iterator on the interface first.
if any(m.isGetter() and m.isIndexed() for m in methods):
self.chrome.append({"name": 'iterator',
"methodInfo": False,
"nativeName": "JS_ArrayIterator",
"length": 0,
"flags": "JSPROP_ENUMERATE",
"pref": None })
self.regular.append({"name": 'iterator',
"methodInfo": False,
"nativeName": "JS_ArrayIterator",
"length": 0,
"flags": "JSPROP_ENUMERATE",
"pref": None })
#if not descriptor.interface.parent and not static and not descriptor.workers:
# self.chrome.append({"name": 'QueryInterface',
# "methodInfo": False,
# "length": 1,
# "flags": "0",
# "pref": None })
if static:
if not descriptor.interface.hasInterfaceObject():
# static methods go on the interface object
assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
else:
if not descriptor.interface.hasInterfacePrototypeObject():
# non-static methods go on the interface prototype object
assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
def generateArray(self, array, name, doIdArrays):
if len(array) == 0:
return ""
def pref(m):
return m["pref"]
def specData(m):
if m.get("methodInfo", True):
jitinfo = ("&%s_methodinfo" % m["name"])
accessor = "genericMethod"
else:
jitinfo = "0 as *JSJitInfo"
accessor = m.get("nativeName", m["name"])
return (m["name"], accessor, jitinfo, m["length"], m["flags"])
def stringDecl(m):
return "const %s_name: [u8 * %i] = %s;\n" % (m["name"], len(m["name"]) + 1,
str_to_const_array(m["name"]))
decls = ''.join([stringDecl(m) for m in array])
return decls + self.generatePrefableArray(
array, name,
' {name: &%s_name as *u8 as *libc::c_char, call: {op: %s, info: %s}, nargs: %s, flags: %s}',
' {name: 0 as *libc::c_char, call: {op: 0 as JSNative, info: 0 as *JSJitInfo}, nargs: 0, flags: 0}',
'JSFunctionSpec',
pref, specData, doIdArrays)
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)]
def generateArray(self, array, name, doIdArrays):
if len(array) == 0:
return ""
def flags(attr):
return "JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS"
def getter(attr):
native = ("genericLenientGetter" if attr.hasLenientThis()
else "genericGetter")
return ("{op: %(native)s, info: &%(name)s_getterinfo as *JSJitInfo}"
% {"name" : attr.identifier.name,
"native" : native})
def setter(attr):
if attr.readonly:
return "{op: 0 as *u8, info: 0 as *JSJitInfo}"
native = ("genericLenientSetter" if attr.hasLenientThis()
else "genericSetter")
return ("{op: %(native)s, info: &%(name)s_setterinfo as *JSJitInfo}"
% {"name" : attr.identifier.name,
"native" : native})
def specData(attr):
return (attr.identifier.name, flags(attr), getter(attr),
setter(attr))
def stringDecl(attr):
name = attr.identifier.name
return "const %s_name: [u8 * %i] = %s;\n" % (name, len(name) + 1,
str_to_const_array(name))
decls = ''.join([stringDecl(m) for m in array])
return decls + self.generatePrefableArray(
array, name,
' { name: &%s_name as *u8 as *libc::c_char, tinyid: 0, flags: ((%s) & 0xFF) as u8, getter: %s, setter: %s }',
' { name: 0 as *libc::c_char, tinyid: 0, flags: 0, getter: {op: 0 as *u8, info: 0 as *JSJitInfo}, setter: {op: 0 as *u8, info: 0 as *JSJitInfo} }',
'JSPropertySpec',
PropertyDefiner.getControllingPref, specData, doIdArrays)
class ConstDefiner(PropertyDefiner):
"""
A class for definining constants on the interface object
"""
def __init__(self, descriptor, name):
PropertyDefiner.__init__(self, descriptor, name)
self.name = name
self.chrome = [m for m in descriptor.interface.members if m.isConst()]
self.regular = [m for m in self.chrome if not isChromeOnly(m)]
def generateArray(self, array, name, doIdArrays):
if len(array) == 0:
return ""
def specData(const):
return (const.identifier.name,
convertConstIDLValueToJSVal(const.value))
def stringDecl(const):
name = const.identifier.name
return "const %s_name: [u8 * %i] = %s;\n" % (name, len(name) + 1,
str_to_const_array(name))
decls = ''.join([stringDecl(m) for m in array])
return decls + self.generatePrefableArray(
array, name,
' { &%s_name as *u8 as *libc::c_char, %s }',
' { 0, JSVAL_VOID }',
'ConstantSpec',
PropertyDefiner.getControllingPref, specData, doIdArrays)
class CGThing(): class CGThing():
""" """
@ -473,6 +743,33 @@ class CGThing():
"""Produce code for a cpp file.""" """Produce code for a cpp file."""
assert(False) # Override me! 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):
if self.descriptor.workers:
return ""
#return "extern const NativePropertyHooks NativeHooks;\n"
return ""
def define(self):
if self.descriptor.workers:
return ""
if self.descriptor.concrete and self.descriptor.proxy:
resolveOwnProperty = "ResolveOwnProperty"
enumerateOwnProperties = "EnumerateOwnProperties"
else:
enumerateOwnProperties = resolveOwnProperty = "0 as *u8"
parent = self.descriptor.interface.parent
parentHooks = ("&" + toBindingNamespace(parent.identifier.name) + "::NativeHooks"
if parent else '0 as *NativePropertyHooks')
return """
const NativeHooks: NativePropertyHooks = NativePropertyHooks { resolve_own_property: %s, resolve_property: /*ResolveProperty*/ 0 as *u8, enumerate_own_properties: %s, enumerate_properties: /*EnumerateProperties*/ 0 as *u8, proto_hooks: %s };
""" % (resolveOwnProperty, enumerateOwnProperties, parentHooks)
# We'll want to insert the indent at the beginnings of lines, but we # 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 # don't want to indent empty lines. So only indent lines that have a
# non-newline character on them. # non-newline character on them.
@ -572,6 +869,141 @@ class CGNamespace(CGWrapper):
inner = CGNamespace.build(namespaces[1:], child, declareOnly=declareOnly) inner = CGNamespace.build(namespaces[1:], child, declareOnly=declareOnly)
return CGNamespace(namespaces[0], inner, declareOnly=declareOnly) return CGNamespace(namespaces[0], inner, declareOnly=declareOnly)
def DOMClass(descriptor):
protoList = ['prototypes::id::' + proto for proto in descriptor.prototypeChain]
# Pad out the list to the right length with _ID_Count so we
# guarantee that all the lists are the same length. _ID_Count
# is never the ID of any prototype, so it's safe to use as
# padding.
protoList.extend(['prototypes::id::_ID_Count'] * (descriptor.config.maxProtoChainLength - len(protoList)))
prototypeChainString = ', '.join(protoList)
nativeHooks = "0 as *NativePropertyHooks" if descriptor.workers else "&NativeHooks as *NativePropertyHooks"
return """DOMClass {
interface_chain: [ %s ] ,
unused: %s, native_hooks: %s
}""" % (prototypeChainString, "false", #toStringBool(descriptor.nativeIsISupports),
nativeHooks)
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"
return ""
def define(self):
traceHook = TRACE_HOOK_NAME if self.descriptor.customTrace else '0 as *u8'
return """
const Class_name: [u8 * %i] = %s;
const Class: DOMJSClass = DOMJSClass {
base: JSClass { name: &Class_name as *u8 as *libc::c_char,
flags: JSCLASS_IS_DOMJSCLASS | ((1 & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT), //JSCLASS_HAS_RESERVED_SLOTS(1),
addProperty: %s, /* addProperty */
delProperty: crust::JS_PropertyStub, /* delProperty */
getProperty: crust::JS_PropertyStub, /* getProperty */
setProperty: crust::JS_StrictPropertyStub, /* setProperty */
enumerate: crust::JS_EnumerateStub,
resolve: crust::JS_ResolveStub,
convert: crust::JS_ConvertStub,
finalize: %s, /* finalize */
checkAccess: 0 as *u8, /* checkAccess */
call: 0 as *u8, /* call */
hasInstance: 0 as *u8, /* hasInstance */
construct: 0 as *u8, /* construct */
trace: %s, /* trace */
reserved: (0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 05
0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 10
0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 15
0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 20
0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 25
0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 30
0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 35
0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void) // 40
},
dom_class: %s
};
""" % (len(self.descriptor.interface.identifier.name) + 1,
str_to_const_array(self.descriptor.interface.identifier.name),
#ADDPROPERTY_HOOK_NAME if self.descriptor.concrete and not self.descriptor.workers and self.descriptor.wrapperCache else 'crust::JS_PropertyStub',
'crust::JS_PropertyStub',
FINALIZE_HOOK_NAME, traceHook,
CGIndenter(CGGeneric(DOMClass(self.descriptor))).define())
def str_to_const_array(s):
return "[" + (", ".join(map(lambda x: "'" + x + "' as u8", list(s)) + ['0 as u8'])) + "]"
class CGPrototypeJSClass(CGThing):
def __init__(self, descriptor):
CGThing.__init__(self)
self.descriptor = descriptor
def declare(self):
# We're purely for internal consumption
return ""
def define(self):
return """const PrototypeClassName__: [u8 * %s] = %s;
const PrototypeClass: JSClass = JSClass {
name: &PrototypeClassName__ as *u8 as *libc::c_char,
flags: (1 & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT, //JSCLASS_HAS_RESERVED_SLOTS(1)
addProperty: crust::JS_PropertyStub, /* addProperty */
delProperty: crust::JS_PropertyStub, /* delProperty */
getProperty: crust::JS_PropertyStub, /* getProperty */
setProperty: crust::JS_StrictPropertyStub, /* setProperty */
enumerate: crust::JS_EnumerateStub,
resolve: crust::JS_ResolveStub,
convert: crust::JS_ConvertStub,
finalize: 0 as *u8, /* finalize */
checkAccess: 0 as *u8, /* checkAccess */
call: 0 as *u8, /* call */
hasInstance: 0 as *u8, /* hasInstance */
construct: 0 as *u8, /* construct */
trace: 0 as *u8, /* trace */
reserved: (0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 05
0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 10
0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 15
0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 20
0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 25
0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 30
0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, // 35
0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void, 0 as *libc::c_void) // 40
};
""" % (len(self.descriptor.interface.identifier.name + "Prototype") + 1,
str_to_const_array(self.descriptor.interface.identifier.name + "Prototype"))
class CGInterfaceObjectJSClass(CGThing):
def __init__(self, descriptor):
CGThing.__init__(self)
self.descriptor = descriptor
def declare(self):
# We're purely for internal consumption
return ""
def define(self):
if not self.descriptor.hasInstanceInterface:
return ""
ctorname = "0 as *u8" if not self.descriptor.interface.ctor() else CONSTRUCT_HOOK_NAME
hasinstance = HASINSTANCE_HOOK_NAME
return """
const InterfaceObjectClass: JSClass = {
%s, 0,
crust::JS_PropertyStub, /* addProperty */
crust::JS_PropertyStub, /* delProperty */
crust::JS_PropertyStub, /* getProperty */
crust::JS_StrictPropertyStub, /* setProperty */
crust::JS_EnumerateStub,
crust::JS_ResolveStub,
crust::JS_ConvertStub,
0 as *u8, /* finalize */
0 as *u8, /* checkAccess */
%s, /* call */
%s, /* hasInstance */
%s, /* construct */
0 as *u8, /* trace */
JSCLASS_NO_INTERNAL_MEMBERS
};
""" % (str_to_const_array("Function", ctorname, hasinstance, ctorname))
class CGList(CGThing): class CGList(CGThing):
""" """
Generate code for a list of GCThings. Just concatenates them together, with Generate code for a list of GCThings. Just concatenates them together, with
@ -680,17 +1112,19 @@ class CGAbstractMethod(CGThing):
def declare(self): def declare(self):
if self.inline: if self.inline:
return self._define() return self._define()
return "%sfn %s%s(%s)%s;\n" % (self._decorators(), self.name, self._template(), #return "%sfn %s%s(%s)%s;\n" % (self._decorators(), self.name, self._template(),
self.name, self._argstring(), self._returnType()) # self._argstring(), self._returnType())
return ""
def _define(self): def _define(self):
return self.definition_prologue() + "\n" + self.definition_body() + self.definition_epilogue() return self.definition_prologue() + "\n" + self.definition_body() + self.definition_epilogue()
def define(self): def define(self):
return "" if self.inline else self._define() return "" if self.inline else self._define()
def definition_prologue(self): def definition_prologue(self):
return "%sfn %s%s(%s)%s unsafe {" % (self._decorators(), self.name, self._template(), return "%sfn %s%s(%s)%s {\n unsafe {" % (self._decorators(), self.name, self._template(),
self._argstring(), self._returnType()) self._argstring(), self._returnType())
def definition_epilogue(self): def definition_epilogue(self):
return "\n}\n" return "\n }\n}\n"
def definition_body(self): def definition_body(self):
assert(False) # Override me! assert(False) # Override me!
@ -706,6 +1140,284 @@ class CGAbstractExternMethod(CGAbstractMethod):
# We only have implementation # We only have implementation
return "" return ""
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")
pass
@staticmethod
def arrayNames():
return [ "staticMethods", "methods", "attrs", "consts" ]
@staticmethod
def xrayRelevantArrayNames():
return [ "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'),
Argument('*JSObject', 'aReceiver')]
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, aReceiver)" %
toBindingNamespace(parentProtoName))
needInterfaceObject = self.descriptor.interface.hasInterfaceObject()
needInterfacePrototypeObject = self.descriptor.interface.hasInterfacePrototypeObject()
# if we don't need to create anything, why are we generating this?
assert needInterfaceObject or needInterfacePrototypeObject
idsToInit = []
# There is no need to init any IDs in workers, because worker bindings
# don't have Xrays.
if not self.descriptor.workers:
for var in self.properties.xrayRelevantArrayNames():
props = getattr(self.properties, var)
# We only have non-chrome ids to init if we have no chrome ids.
if props.hasChromeOnly():
idsToInit.append(props.variableName(True))
elif props.hasNonChromeOnly():
idsToInit.append(props.variableName(False))
if len(idsToInit) > 0:
initIds = CGList(
[CGGeneric("!InitIds(aCx, %s, %s_ids)" % (varname, varname)) for
varname in idsToInit], ' ||\n')
if len(idsToInit) > 1:
initIds = CGWrapper(initIds, pre="(", post=")", reindent=True)
initIds = CGList(
[CGGeneric("%s_ids[0] == JSID_VOID &&" % idsToInit[0]), initIds],
"\n")
initIds = CGWrapper(initIds, pre="if ", post=" {", reindent=True)
initIds = CGList(
[initIds,
CGGeneric((" %s_ids[0] = JSID_VOID;\n"
" return ptr::null();") % idsToInit[0]),
CGGeneric("}")],
"\n")
else:
initIds = None
prefCacheData = []
for var in self.properties.arrayNames():
props = getattr(self.properties, var)
prefCacheData.extend(props.prefCacheData)
if len(prefCacheData) is not 0:
prefCacheData = [
CGGeneric('Preferences::AddBoolVarCache(%s, "%s");' % (ptr, pref)) for
(pref, ptr) in prefCacheData]
prefCache = CGWrapper(CGIndenter(CGList(prefCacheData, "\n")),
pre=("static bool sPrefCachesInited = false;\n"
"if (!sPrefCachesInited) {\n"
" sPrefCachesInited = true;\n"),
post="\n}")
else:
prefCache = None
getParentProto = ("let parentProto: *JSObject = %s;\n" +
"if parentProto.is_null() {\n" +
" return ptr::null();\n" +
"}\n") % getParentProto
needInterfaceObjectClass = (needInterfaceObject and
self.descriptor.hasInstanceInterface)
needConstructor = (needInterfaceObject and
not self.descriptor.hasInstanceInterface)
if self.descriptor.interface.ctor():
constructHook = CONSTRUCT_HOOK_NAME
constructArgs = methodLength(self.descriptor.interface.ctor())
else:
constructHook = "ThrowingConstructor"
constructArgs = 0
if self.descriptor.concrete:
if self.descriptor.proxy:
domClass = "&Class"
else:
domClass = "&Class.mClass"
else:
domClass = "ptr::null()"
call = """return dom::CreateInterfaceObjects(aCx, aGlobal, aReceiver, parentProto,
%s, %s, %s, %d,
%s,
%%(methods)s, %%(attrs)s,
%%(consts)s, %%(staticMethods)s,
%s);""" % (
"&PrototypeClass" if needInterfacePrototypeObject else "ptr::null()",
"&InterfaceObjectClass" if needInterfaceObjectClass else "ptr::null()",
constructHook if needConstructor else "ptr::null()",
constructArgs,
domClass,
'"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "ptr::null()")
if self.properties.hasChromeOnly():
if self.descriptor.workers:
accessCheck = "mozilla::dom::workers::GetWorkerPrivateFromContext(aCx)->IsChromeWorker()"
else:
accessCheck = "xpc::AccessCheck::isChrome(js::GetObjectCompartment(aGlobal))"
chrome = CGIfWrapper(CGGeneric(call % self.properties.variableNames(True)),
accessCheck)
chrome = CGWrapper(chrome, pre="\n\n")
else:
chrome = None
functionBody = CGList(
[CGGeneric(getParentProto), initIds, prefCache, chrome,
CGGeneric(call % self.properties.variableNames(False))],
"\n\n")
return CGIndenter(CGWrapper(functionBody, pre="/*", post="*/return ptr::null()")).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'),
Argument('*JSObject', 'aReceiver')]
CGAbstractMethod.__init__(self, descriptor, name,
'*JSObject', args, inline=True)
self.id = idPrefix + "id::" + self.descriptor.name
def definition_body(self):
return """
/* aGlobal and aReceiver are usually the same, but they can be different
too. For example a sandbox often has an xray wrapper for a window as the
prototype of the sandbox's global. In that case aReceiver is the xray
wrapper and aGlobal is the sandbox's global.
*/
/* Make sure our global is sane. Hopefully we can remove this sometime */
if ((*JS_GetClass(aGlobal)).flags & JSCLASS_DOM_GLOBAL) == 0 {
return ptr::null();
}
/* Check to see whether the interface objects are already installed */
let protoOrIfaceArray: *mut *JSObject = cast::transmute(GetProtoOrIfaceArray(aGlobal));
let cachedObject: *JSObject = *protoOrIfaceArray.offset(%s as uint);
if cachedObject.is_null() {
let tmp: *JSObject = CreateInterfaceObjects(aCx, aGlobal, aReceiver);
*protoOrIfaceArray.offset(%s as uint) = tmp;
tmp
} else {
cachedObject
}""" % (self.id, self.id)
class CGGetProtoObjectMethod(CGGetPerInterfaceObject):
"""
A method for getting the interface prototype object.
"""
def __init__(self, descriptor):
CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject",
"prototypes::")
def definition_body(self):
return """
/* Get the interface prototype object for this class. This will create the
object as needed. */""" + CGGetPerInterfaceObject.definition_body(self)
class CGGetConstructorObjectMethod(CGGetPerInterfaceObject):
"""
A method for getting the interface constructor object.
"""
def __init__(self, descriptor):
CGGetPerInterfaceObject.__init__(self, descriptor, "GetConstructorObject",
"constructors::")
def definition_body(self):
return """
/* Get the interface object for this class. This will create the object as
needed. */""" + CGGetPerInterfaceObject.definition_body(self)
def CheckPref(descriptor, globalName, 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 = ""
failureCode = (" %s = false;\n" +
" return %s;") % (varName, retval)
return """
{
XPCWrappedNativeScope* scope =
XPCWrappedNativeScope::FindInJSObjectScope(aCx, %s);
if (!scope) {
%s
}
if (!scope->ExperimentalBindingsEnabled()) {
%s%s
}
}
""" % (globalName, failureCode, wrapperCache, failureCode)
class CGDefineDOMInterfaceMethod(CGAbstractMethod):
"""
A method for resolve hooks to try to lazily define the interface object for
a given interface.
"""
def __init__(self, descriptor):
args = [Argument('*JSContext', 'aCx'), Argument('*JSObject', 'aReceiver'),
Argument('*mut bool', 'aEnabled')]
CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface', 'bool', args)
def declare(self):
if self.descriptor.workers:
return ''
#return CGAbstractMethod.declare(self)
return ""
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 (" let global: *JSObject = JS_GetGlobalForObject(aCx, aReceiver);\n" +
CheckPref(self.descriptor, "global", "*aEnabled", "false") +
"""
*aEnabled = true;
return %s(aCx, global, aReceiver).is_not_null();""" % (getter))
class CGCallGenerator(CGThing): class CGCallGenerator(CGThing):
""" """
A class to generate an actual call to a C++ object. Assumes that the C++ A class to generate an actual call to a C++ object. Assumes that the C++
@ -1073,6 +1785,18 @@ class CGMemberJITInfo(CGThing):
return result return result
raise TypeError("Illegal member type to CGPropertyJITInfo") raise TypeError("Illegal member type to CGPropertyJITInfo")
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(CGAbstractExternMethod): class CGAbstractClassHook(CGAbstractExternMethod):
""" """
Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw
@ -1112,6 +1836,102 @@ let _: %s = cast::reinterpret_cast(&RUST_JSVAL_TO_PRIVATE(val));
#return clearWrapper + release #return clearWrapper + release
return release return release
class CGClassConstructHook(CGAbstractStaticMethod):
"""
JS-visible constructor for our objects
"""
def __init__(self, descriptor):
args = [Argument('*JSContext', 'cx'), Argument('unsigned', 'argc'), Argument('*jsval', '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)));
"""
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.descriptor.binaryNames.get(name, name))
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('JSHandleObject', 'obj'),
Argument('JSMutableHandleValue', 'vp'), 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 (!vp.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;
}
JSObject *objProto = &protov.toObject();
JSObject* instance = &vp.toObject();
JSObject* proto;
if (!JS_GetPrototype(cx, instance, &proto))
return false;
while (proto) {
if (proto == objProto) {
*bp = true;
return true;
}
if (!JS_GetPrototype(cx, proto, &proto))
return false;
}
nsISupports* native =
nsContentUtils::XPConnect()->GetNativeOfWrapper(cx, instance);
nsCOMPtr<%s> qiResult = do_QueryInterface(native);
*bp = !!qiResult;
return true;
""" % (self.descriptor.name, self.descriptor.hasInstanceInterface)
class CGClassFinalizeHook(CGAbstractClassHook): class CGClassFinalizeHook(CGAbstractClassHook):
""" """
A hook for finalize, used to release our native object. A hook for finalize, used to release our native object.
@ -1175,23 +1995,23 @@ class CGDescriptor(CGThing):
pass pass
if descriptor.interface.hasInterfaceObject(): if descriptor.interface.hasInterfaceObject():
#cgThings.append(CGClassConstructHook(descriptor)) cgThings.append(CGClassConstructHook(descriptor))
#cgThings.append(CGClassHasInstanceHook(descriptor)) cgThings.append(CGClassHasInstanceHook(descriptor))
#cgThings.append(CGInterfaceObjectJSClass(descriptor)) cgThings.append(CGInterfaceObjectJSClass(descriptor))
pass pass
if descriptor.interface.hasInterfacePrototypeObject(): if descriptor.interface.hasInterfacePrototypeObject():
#cgThings.append(CGPrototypeJSClass(descriptor)) cgThings.append(CGPrototypeJSClass(descriptor))
pass pass
properties = PropertyArrays(descriptor) properties = PropertyArrays(descriptor)
#cgThings.append(CGGeneric(define=str(properties))) cgThings.append(CGGeneric(define=str(properties)))
#cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties)) cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties))
if descriptor.interface.hasInterfacePrototypeObject(): if descriptor.interface.hasInterfacePrototypeObject():
#cgThings.append(CGGetProtoObjectMethod(descriptor)) cgThings.append(CGGetProtoObjectMethod(descriptor))
pass pass
else: else:
#cgThings.append(CGGetConstructorObjectMethod(descriptor)) cgThings.append(CGGetConstructorObjectMethod(descriptor))
pass pass
# Set up our Xray callbacks as needed. Note that we don't need to do # Set up our Xray callbacks as needed. Note that we don't need to do
@ -1206,7 +2026,7 @@ class CGDescriptor(CGThing):
#cgThings.append(CGEnumerateProperties(descriptor, properties)) #cgThings.append(CGEnumerateProperties(descriptor, properties))
if descriptor.interface.hasInterfaceObject(): if descriptor.interface.hasInterfaceObject():
#cgThings.append(CGDefineDOMInterfaceMethod(descriptor)) cgThings.append(CGDefineDOMInterfaceMethod(descriptor))
if (not descriptor.interface.isExternal() and if (not descriptor.interface.isExternal() and
# Workers stuff is never pref-controlled # Workers stuff is never pref-controlled
not descriptor.workers and not descriptor.workers and
@ -1215,7 +2035,7 @@ class CGDescriptor(CGThing):
pass pass
if descriptor.interface.hasInterfacePrototypeObject(): if descriptor.interface.hasInterfacePrototypeObject():
#cgThings.append(CGNativePropertyHooks(descriptor)) cgThings.append(CGNativePropertyHooks(descriptor))
pass pass
if descriptor.concrete: if descriptor.concrete:
@ -1227,7 +2047,7 @@ class CGDescriptor(CGThing):
#cgThings.append(CGIsMethod(descriptor)) #cgThings.append(CGIsMethod(descriptor))
pass pass
else: else:
#cgThings.append(CGDOMJSClass(descriptor)) cgThings.append(CGDOMJSClass(descriptor))
pass pass
if descriptor.wrapperCache: if descriptor.wrapperCache:

View file

@ -192,3 +192,55 @@ pub fn define_empty_prototype(name: ~str, proto: Option<~str>, compartment: @mut
compartment.stash_global_proto(name, obj); compartment.stash_global_proto(name, obj);
return obj; return obj;
} }
// We use slot 0 for holding the raw object. This is safe for both
// globals and non-globals.
const DOM_OBJECT_SLOT: uint = 0;
// All DOM globals must have a slot at DOM_PROTOTYPE_SLOT. We have to
// start at 1 past JSCLASS_GLOBAL_SLOT_COUNT because XPConnect uses
// that one.
const DOM_PROTOTYPE_SLOT: u32 = js::JSCLASS_GLOBAL_SLOT_COUNT + 1;
// NOTE: This is baked into the Ion JIT as 0 in codegen for LGetDOMProperty and
// LSetDOMProperty. Those constants need to be changed accordingly if this value
// changes.
const JSCLASS_DOM_GLOBAL: u32 = js::JSCLASS_USERBIT1;
struct NativePropertyHooks {
resolve_own_property: *u8,
resolve_property: *u8,
enumerate_own_properties: *u8,
enumerate_properties: *u8,
proto_hooks: *NativePropertyHooks
}
struct DOMClass {
// A list of interfaces that this object implements, in order of decreasing
// derivedness.
interface_chain: [prototypes::id::Prototype * 1 /*prototypes::id::_ID_Count*/],
unused: bool, // DOMObjectIsISupports (always false)
native_hooks: *NativePropertyHooks
}
struct DOMJSClass {
base: JSClass,
dom_class: DOMClass
}
fn GetProtoOrIfaceArray(global: *JSObject) -> **JSObject {
unsafe {
assert ((*JS_GetClass(global)).flags & JSCLASS_DOM_GLOBAL) != 0;
cast::reinterpret_cast(&JS_GetReservedSlot(global, DOM_PROTOTYPE_SLOT))
}
}
mod prototypes {
mod id {
pub enum Prototype {
ClientRect,
_ID_Count
}
}
}