Bug 748267 part 2. Implement codegen for dealing with sequence arguments. r=peterv

This commit is contained in:
Boris Zbarsky 2012-05-15 14:23:29 -04:00
parent 47ff20f5dc
commit 2e5443afcb
2 changed files with 133 additions and 40 deletions

View File

@ -138,18 +138,25 @@ inline bool
IsArrayLike(JSContext* cx, JSObject* obj)
{
MOZ_ASSERT(obj);
// For simplicity, check for security wrappers up front
// For simplicity, check for security wrappers up front. In case we
// have a security wrapper, don't forget to enter the compartment of
// the underlying object after unwrapping.
JSAutoEnterCompartment ac;
if (js::IsWrapper(obj)) {
obj = XPCWrapper::Unwrap(cx, obj, false);
if (!obj) {
// Let's say it's not
return false;
}
if (!ac.enter(cx, obj)) {
return false;
}
}
// XXXbz need to detect platform objects (including listbinding
// ones) with indexGetters here!
return JS_IsArrayObject(cx, obj);
return JS_IsArrayObject(cx, obj) || JS_IsTypedArrayObject(obj, cx);
}
inline bool

View File

@ -1178,8 +1178,9 @@ if (!tmp) {
}
${target} = tmp.forget();""").substitute(self.substitution)
def getJSToNativeConversionTemplate(type, descriptor, failureCode=None,
isDefinitelyObject=False):
def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
isDefinitelyObject=False,
isSequenceMember=False):
"""
Get a template for converting a JS value to a native object based on the
given type and descriptor. If failureCode is given, then we're actually
@ -1215,21 +1216,114 @@ def getJSToNativeConversionTemplate(type, descriptor, failureCode=None,
If holderType is not None then ${holderName} must be in scope
before the generated code is entered.
"""
if type.isSequence() or type.isArray():
raise TypeError("Can't handle sequence or array arguments yet")
if descriptor is not None:
assert(type.isInterface())
# A helper function for wrapping up the template body for
# possibly-nullable objecty stuff
def wrapObjectTemplate(templateBody, isDefinitelyObject, type,
codeToSetNull, isWorker):
if not isDefinitelyObject:
# Handle the non-object cases by wrapping up the whole
# thing in an if cascade.
templateBody = (
"if (${val}.isObject()) {\n" +
CGIndenter(CGGeneric(templateBody)).define() + "\n")
if type.nullable():
templateBody += (
"} else if (${val}.isNullOrUndefined()) {\n"
" %s;\n" % codeToSetNull)
templateBody += (
"} else {\n"
" return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);\n"
"}" % toStringBool(not isWorker))
return templateBody
if type.isArray():
raise TypeError("Can't handle array arguments yet")
if type.isSequence():
if isSequenceMember:
raise TypeError("Can't handle sequences of sequences")
if failureCode is not None:
raise TypeError("Can't handle sequences when failureCode is not None")
nullable = type.nullable();
if nullable:
type = type.inner;
elementType = type.inner;
# We don't know anything about the object-ness of the things
# we wrap, so don't pass through isDefinitelyObject
(elementTemplate, elementDeclType,
elementHolderType) = getJSToNativeConversionTemplate(
elementType, descriptorProvider, isSequenceMember=True)
if elementHolderType is not None:
raise TypeError("Shouldn't need holders for sequences")
# Have to make sure to use a fallible array, because it's trivial for
# page JS to create things with very large lengths.
typeName = CGWrapper(elementDeclType, pre="nsTArray< ", post=" >")
if nullable:
typeName = CGWrapper(typeName, pre="Nullable< ", post=" >")
templateBody = ("""JSObject* seq = &${val}.toObject();\n
if (!IsArrayLike(cx, seq)) {
return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);
}
uint32_t length;
// JS_GetArrayLength actually works on all objects
if (!JS_GetArrayLength(cx, seq, &length)) {
return false;
}
// Jump through a hoop to do a fallible allocation but later end up with
// an infallible array.
FallibleTArray< %s > arr;
if (!arr.SetCapacity(length)) {
return Throw<%s>(cx, NS_ERROR_OUT_OF_MEMORY);
}
for (uint32_t i = 0; i < length; ++i) {
jsval temp;
if (!JS_GetElement(cx, seq, i, &temp)) {
return false;
}
""" % (toStringBool(descriptorProvider.workers),
elementDeclType.define(),
toStringBool(descriptorProvider.workers)))
templateBody += CGIndenter(CGGeneric(
string.Template(elementTemplate).substitute(
{
"val" : "temp",
"declName" : "*arr.AppendElement()"
}
))).define()
templateBody += """
}
// And the other half of the hoop-jump"""
if nullable:
templateBody += """
${declName}.SetValue().SwapElements(arr);
"""
else:
templateBody += """
${declName}.SwapElements(arr);
"""
templateBody = wrapObjectTemplate(templateBody, isDefinitelyObject,
type, "${declName}.SetNull()",
descriptorProvider.workers)
return (templateBody, typeName, None)
if type.isInterface() and not type.isArrayBuffer():
descriptor = descriptorProvider.getDescriptor(
type.unroll().inner.identifier.name)
# This is an interface that we implement as a concrete class
# or an XPCOM interface.
# Allow null pointers for nullable types and old-binding classes
argIsPointer = type.nullable() or type.unroll().inner.isExternal()
# Non-worker callbacks have to hold a strong ref to the thing being
# passed down.
# Sequences and non-worker callbacks have to hold a strong ref to the
# thing being passed down.
forceOwningType = (descriptor.interface.isCallback() and
not descriptor.workers)
not descriptor.workers) or isSequenceMember
typeName = descriptor.nativeType
typePtr = typeName + "*"
@ -1284,7 +1378,12 @@ def getJSToNativeConversionTemplate(type, descriptor, failureCode=None,
# to addref when unwrapping or not. So we just pass an
# getter_AddRefs(nsCOMPtr) to XPConnect and if we'll need a release
# it'll put a non-null pointer in there.
holderType = "nsCOMPtr<" + typeName + ">"
if forceOwningType:
# Don't return a holderType in this case; our declName
# will just own stuff.
templateBody += "nsCOMPtr<" + typeName + "> ${holderName};"
else:
holderType = "nsCOMPtr<" + typeName + ">"
templateBody += (
"jsval tmpVal = ${val};\n" +
typePtr + " tmp;\n"
@ -1311,20 +1410,9 @@ def getJSToNativeConversionTemplate(type, descriptor, failureCode=None,
# And store our tmp, before it goes out of scope.
templateBody += "${declName} = tmp;"
if not isDefinitelyObject:
# Handle the non-object cases by wrapping up the whole
# thing in an if cascade.
templateBody = (
"if (${val}.isObject()) {\n" +
CGIndenter(CGGeneric(templateBody)).define() + "\n")
if type.nullable():
templateBody += (
"} else if (${val}.isNullOrUndefined()) {\n"
" ${declName} = NULL;\n")
templateBody += (
"} else {\n"
" return Throw<%s>(cx, NS_ERROR_XPC_BAD_CONVERT_JS);\n"
"}" % toStringBool(not descriptor.workers))
templateBody = wrapObjectTemplate(templateBody, isDefinitelyObject,
type, "${declName} = NULL",
descriptor.workers)
declType = CGGeneric(declType)
if holderType is not None:
@ -1332,6 +1420,8 @@ def getJSToNativeConversionTemplate(type, descriptor, failureCode=None,
return (templateBody, declType, holderType)
if type.isArrayBuffer():
if isSequenceMember:
raise TypeError("Can't handle sequences of arraybuffers")
declType = "JSObject*"
template = (
"if (${val}.isObject() && JS_IsArrayBufferObject(&${val}.toObject(), cx)) {\n"
@ -1351,10 +1441,9 @@ def getJSToNativeConversionTemplate(type, descriptor, failureCode=None,
return (template, CGGeneric(declType), None)
if type.isInterface():
raise TypeError("Interface type with no descriptor: " + type)
if type.isString():
if isSequenceMember:
raise TypeError("Can't handle sequences of strings")
# XXXbz Need to figure out string behavior based on extended args? Also, how to
# detect them?
@ -1392,6 +1481,8 @@ def getJSToNativeConversionTemplate(type, descriptor, failureCode=None,
CGGeneric(enum), None)
if type.isCallback():
if isSequenceMember:
raise TypeError("Can't handle sequences of callbacks")
# XXXbz we're going to assume that callback types are always
# nullable and always have [TreatNonCallableAsNull] for now.
return (
@ -1402,6 +1493,8 @@ def getJSToNativeConversionTemplate(type, descriptor, failureCode=None,
"}", CGGeneric("JSObject*"), None)
if type.isAny():
if isSequenceMember:
raise TypeError("Can't handle sequences of 'any'")
return ("${declName} = ${val};", CGGeneric("JS::Value"), None)
if not type.isPrimitive():
@ -1506,16 +1599,12 @@ class CGArgumentConverter(CGThing):
).substitute(replacer)
self.replacementVariables["valPtr"] = (
"&" + self.replacementVariables["val"])
if argument.type.isInterface() and not argument.type.isArrayBuffer():
self.descriptor = descriptorProvider.getDescriptor(
argument.type.unroll().inner.identifier.name)
else:
self.descriptor = None
self.descriptorProvider = descriptorProvider
def define(self):
return instantiateJSToNativeConversionTemplate(
getJSToNativeConversionTemplate(self.argument.type,
self.descriptor),
self.descriptorProvider),
self.replacementVariables).define()
def getWrapTemplateForType(type, descriptorProvider, result, successCode):
@ -2062,11 +2151,8 @@ class CGMethodCall(CGThing):
caseBody.append(CGIndenter(CGGeneric("do {")));
type = sig[1][distinguishingIndex].type
interfaceDesc = descriptor.getDescriptor(
type.unroll().inner.identifier.name)
testCode = instantiateJSToNativeConversionTemplate(
getJSToNativeConversionTemplate(type, interfaceDesc,
getJSToNativeConversionTemplate(type, descriptor,
failureCode="break;",
isDefinitelyObject=True),
{
@ -2090,7 +2176,7 @@ class CGMethodCall(CGThing):
# 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()" %
pickFirstSignature("%s.isObject() && IsArrayLike(cx, &%s.toObject())" %
(distinguishingArg, distinguishingArg),
lambda s:
(s[1][distinguishingIndex].type.isArray() or