Support variadic interface arguments (fixes #8159)

We use a RootedVec value in codegen, of which we use the `r()` method to
pass `&[&T]` to the interface methods.
This commit is contained in:
Anthony Ramine 2015-10-25 22:43:13 +01:00
parent e66a361e08
commit acb13dc899
7 changed files with 105 additions and 63 deletions

View file

@ -102,29 +102,26 @@ numericTags = [
] ]
class CastableObjectUnwrapper(): def unwrapCastableObject(descriptor, source, codeOnFailure, conversionFunction):
""" """
A class for unwrapping an object named by the "source" argument A function for unwrapping an object named by the "source" argument
based on the passed-in descriptor. Stringifies to a Rust expression of based on the passed-in descriptor. Returns the string of the Rust expression of
the appropriate type. the appropriate type.
codeOnFailure is the code to run if unwrapping fails. codeOnFailure is the code to run if unwrapping fails.
""" """
def __init__(self, descriptor, source, codeOnFailure, handletype): args = {
self.substitution = { "failureCode": CGIndenter(CGGeneric(codeOnFailure), 8).define(),
"source": source, "function": conversionFunction,
"codeOnFailure": CGIndenter(CGGeneric(codeOnFailure), 8).define(), "source": source,
"handletype": handletype, }
} return """\
match %(function)s(%(source)s) {
def __str__(self):
return string.Template("""\
match root_from_handle${handletype}(${source}) {
Ok(val) => val, Ok(val) => val,
Err(()) => { Err(()) => {
${codeOnFailure} %(failureCode)s
} }
}""").substitute(self.substitution) }""" % args
# 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
@ -757,12 +754,13 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
return handleOptional(template, declType, handleDefaultNull("None")) return handleOptional(template, declType, handleDefaultNull("None"))
if isMember: conversionFunction = "root_from_handlevalue"
descriptorType = descriptor.memberType descriptorType = descriptor.returnType
if isMember == "Variadic":
conversionFunction = "native_from_handlevalue"
descriptorType = descriptor.nativeType
elif isArgument: elif isArgument:
descriptorType = descriptor.argumentType descriptorType = descriptor.argumentType
else:
descriptorType = descriptor.nativeType
templateBody = "" templateBody = ""
if descriptor.interface.isConsequential(): if descriptor.interface.isConsequential():
@ -782,8 +780,8 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
else: else:
unwrapFailureCode = failureCode unwrapFailureCode = failureCode
templateBody = str( templateBody = unwrapCastableObject(
CastableObjectUnwrapper(descriptor, "${val}", unwrapFailureCode, "value")) descriptor, "${val}", unwrapFailureCode, conversionFunction)
declType = CGGeneric(descriptorType) declType = CGGeneric(descriptorType)
if type.nullable(): if type.nullable():
@ -1187,31 +1185,26 @@ class CGArgumentConverter(CGThing):
variadicConversion = { variadicConversion = {
"val": string.Template("${args}.get(variadicArg)").substitute(replacer), "val": string.Template("${args}.get(variadicArg)").substitute(replacer),
} }
innerConverter = instantiateJSToNativeConversionTemplate( innerConverter = [instantiateJSToNativeConversionTemplate(
template, variadicConversion, declType, "slot") template, variadicConversion, declType, "slot")]
seqType = CGTemplatedType("Vec", declType) arg = "arg%d" % index
vec = "Vec::with_capacity((%(argc)s - %(index)s) as usize)" % {'argc': argc, 'index': index}
variadicConversion = string.Template( if argument.type.isGeckoInterface():
"let mut vector: ${seqType} = Vec::with_capacity((${argc} - ${index}) as usize);\n" code = "let mut %s = RootedVec::new();\n*%s = %s;\n" % (arg, arg, vec)
"for variadicArg in ${index}..${argc} {\n" innerConverter.append(CGGeneric("%s.push(JS::from_ref(&*slot));" % arg))
"${inner}\n" else:
" vector.push(slot);\n" code = "let mut %s = %s;\n" % (arg, vec)
"}\n" innerConverter.append(CGGeneric("%s.push(slot);" % arg))
"vector" inner = CGIndenter(CGList(innerConverter, "\n"), 4).define()
).substitute({
"index": index,
"argc": argc,
"seqType": seqType.define(),
"inner": CGIndenter(innerConverter, 4).define(),
})
variadicConversion = CGIfElseWrapper(condition, code += """\
CGGeneric(variadicConversion), for variadicArg in %(index)s..%(argc)s {
CGGeneric("Vec::new()")).define() %(inner)s
}""" % {'argc': argc, 'index': index, 'inner': inner}
self.converter = instantiateJSToNativeConversionTemplate( self.converter = CGGeneric(code)
variadicConversion, replacementVariables, seqType, "arg%d" % index)
def define(self): def define(self):
return self.converter.define() return self.converter.define()
@ -1693,7 +1686,7 @@ class CGImports(CGWrapper):
class CGIfWrapper(CGWrapper): class CGIfWrapper(CGWrapper):
def __init__(self, child, condition): def __init__(self, condition, child):
pre = CGWrapper(CGGeneric(condition), pre="if ", post=" {\n", pre = CGWrapper(CGGeneric(condition), pre="if ", post=" {\n",
reindent=True) reindent=True)
CGWrapper.__init__(self, CGIndenter(child), pre=pre.define(), CGWrapper.__init__(self, CGIndenter(child), pre=pre.define(),
@ -1923,7 +1916,7 @@ class CGList(CGThing):
class CGIfElseWrapper(CGList): class CGIfElseWrapper(CGList):
def __init__(self, condition, ifTrue, ifFalse): def __init__(self, condition, ifTrue, ifFalse):
kids = [CGIfWrapper(ifTrue, condition), kids = [CGIfWrapper(condition, ifTrue),
CGWrapper(CGIndenter(ifFalse), pre=" else {\n", post="\n}")] CGWrapper(CGIndenter(ifFalse), pre=" else {\n", post="\n}")]
CGList.__init__(self, kids) CGList.__init__(self, kids)
@ -3476,7 +3469,7 @@ def getUnionTypeTemplateVars(type, descriptorProvider):
if type.isGeckoInterface(): if type.isGeckoInterface():
name = type.inner.identifier.name name = type.inner.identifier.name
typeName = descriptorProvider.getDescriptor(name).nativeType typeName = descriptorProvider.getDescriptor(name).returnType
elif type.isEnum(): elif type.isEnum():
name = type.inner.identifier.name name = type.inner.identifier.name
typeName = name typeName = name
@ -3629,7 +3622,7 @@ class CGUnionConversionStruct(CGThing):
if hasObjectTypes: if hasObjectTypes:
assert interfaceObject assert interfaceObject
templateBody = CGList([interfaceObject], "\n") templateBody = CGList([interfaceObject], "\n")
conversions.append(CGIfWrapper(templateBody, "value.get().is_object()")) conversions.append(CGIfWrapper("value.get().is_object()", templateBody))
otherMemberTypes = [ otherMemberTypes = [
t for t in memberTypes if t.isPrimitive() or t.isString() or t.isEnum() t for t in memberTypes if t.isPrimitive() or t.isString() or t.isEnum()
@ -4082,7 +4075,7 @@ class CGProxySpecialOperation(CGPerSignatureCall):
return "" return ""
wrap = CGGeneric(wrapForType(**self.templateValues)) wrap = CGGeneric(wrapForType(**self.templateValues))
wrap = CGIfWrapper(wrap, "found") wrap = CGIfWrapper("found", wrap)
return "\n" + wrap.define() return "\n" + wrap.define()
@ -5216,7 +5209,7 @@ class CGBindingRoot(CGThing):
'dom::bindings::callback::wrap_call_this_object', 'dom::bindings::callback::wrap_call_this_object',
'dom::bindings::conversions::{ConversionBehavior, DOM_OBJECT_SLOT, IDLInterface}', 'dom::bindings::conversions::{ConversionBehavior, DOM_OBJECT_SLOT, IDLInterface}',
'dom::bindings::conversions::{FromJSValConvertible, StringificationBehavior}', 'dom::bindings::conversions::{FromJSValConvertible, StringificationBehavior}',
'dom::bindings::conversions::{ToJSValConvertible, jsid_to_str}', 'dom::bindings::conversions::{ToJSValConvertible, jsid_to_str, native_from_handlevalue}',
'dom::bindings::conversions::{private_from_object, root_from_object}', 'dom::bindings::conversions::{private_from_object, root_from_object}',
'dom::bindings::conversions::{root_from_handleobject, root_from_handlevalue}', 'dom::bindings::conversions::{root_from_handleobject, root_from_handlevalue}',
'dom::bindings::codegen::{PrototypeList, RegisterBindings, UnionTypes}', 'dom::bindings::codegen::{PrototypeList, RegisterBindings, UnionTypes}',
@ -5231,6 +5224,7 @@ class CGBindingRoot(CGThing):
'dom::bindings::num::Finite', 'dom::bindings::num::Finite',
'dom::bindings::str::ByteString', 'dom::bindings::str::ByteString',
'dom::bindings::str::USVString', 'dom::bindings::str::USVString',
'dom::bindings::trace::RootedVec',
'mem::heap_size_of_raw_self_and_children', 'mem::heap_size_of_raw_self_and_children',
'libc', 'libc',
'util::str::DOMString', 'util::str::DOMString',
@ -5262,7 +5256,10 @@ def argument_type(descriptorProvider, ty, optional=False, defaultValue=None, var
declType = info.declType declType = info.declType
if variadic: if variadic:
declType = CGWrapper(declType, pre="Vec<", post=">") if ty.isGeckoInterface():
declType = CGWrapper(declType, pre="&[", post="]")
else:
declType = CGWrapper(declType, pre="Vec<", post=">")
elif optional and not defaultValue: elif optional and not defaultValue:
declType = CGWrapper(declType, pre="Option<", post=">") declType = CGWrapper(declType, pre="Option<", post=">")
@ -5636,8 +5633,8 @@ class CallbackMember(CGNativeMember):
).substitute({"arg": arg.identifier.name}) ).substitute({"arg": arg.identifier.name})
elif arg.optional and not arg.defaultValue: elif arg.optional and not arg.defaultValue:
conversion = ( conversion = (
CGIfWrapper(CGGeneric(conversion), CGIfWrapper("%s.is_some()" % arg.identifier.name,
"%s.is_some()" % arg.identifier.name).define() + CGGeneric(conversion)).define() +
" else if argc == %d {\n" " else if argc == %d {\n"
" // This is our current trailing argument; reduce argc\n" " // This is our current trailing argument; reduce argc\n"
" argc -= 1;\n" " argc -= 1;\n"

View file

@ -157,14 +157,12 @@ class Descriptor(DescriptorProvider):
ty = "%sBinding::%s" % (ifaceName, ifaceName) ty = "%sBinding::%s" % (ifaceName, ifaceName)
self.returnType = "Rc<%s>" % ty self.returnType = "Rc<%s>" % ty
self.argumentType = "???" self.argumentType = "???"
self.memberType = "???"
self.nativeType = ty self.nativeType = ty
else: else:
self.needsRooting = True self.needsRooting = True
self.returnType = "Root<%s>" % ifaceName self.returnType = "Root<%s>" % ifaceName
self.argumentType = "&%s" % ifaceName self.argumentType = "&%s" % ifaceName
self.memberType = "Root<%s>" % ifaceName self.nativeType = "*const %s" % ifaceName
self.nativeType = "Root<%s>" % ifaceName
self.concreteType = ifaceName self.concreteType = ifaceName
self.register = desc.get('register', True) self.register = desc.get('register', True)

View file

@ -728,6 +728,14 @@ pub unsafe fn private_from_proto_check<F>(mut obj: *mut JSObject, proto_check: F
} }
} }
fn native_from_object<T>(obj: *mut JSObject) -> Result<*const T, ()>
where T: Reflectable + IDLInterface
{
unsafe {
private_from_proto_check(obj, T::derives).map(|ptr| ptr as *const T)
}
}
/// Get a `Root<T>` for the given DOM object, unwrapping any wrapper /// Get a `Root<T>` for the given DOM object, unwrapping any wrapper
/// around it first, and checking if the object is of the correct type. /// around it first, and checking if the object is of the correct type.
/// ///
@ -737,11 +745,14 @@ pub unsafe fn private_from_proto_check<F>(mut obj: *mut JSObject, proto_check: F
pub fn root_from_object<T>(obj: *mut JSObject) -> Result<Root<T>, ()> pub fn root_from_object<T>(obj: *mut JSObject) -> Result<Root<T>, ()>
where T: Reflectable + IDLInterface where T: Reflectable + IDLInterface
{ {
unsafe { native_from_object(obj).map(|ptr| unsafe { Root::from_ref(&*ptr) })
private_from_proto_check(obj, T::derives).map(|ptr| { }
Root::from_ref(&*(ptr as *const T))
}) /// Get a `*const T` for a DOM object accessible from a `HandleValue`.
} pub fn native_from_handlevalue<T>(v: HandleValue) -> Result<*const T, ()>
where T: Reflectable + IDLInterface
{
native_from_object(v.get().to_object())
} }
/// Get a `Root<T>` for a DOM object accessible from a `HandleValue`. /// Get a `Root<T>` for a DOM object accessible from a `HandleValue`.

View file

@ -6,13 +6,14 @@
use dom::bindings::codegen::Bindings::EventListenerBinding::EventListener; use dom::bindings::codegen::Bindings::EventListenerBinding::EventListener;
use dom::bindings::codegen::Bindings::FunctionBinding::Function; use dom::bindings::codegen::Bindings::FunctionBinding::Function;
use dom::bindings::codegen::Bindings::TestBindingBinding::{TestBindingMethods, TestEnum}; use dom::bindings::codegen::Bindings::TestBindingBinding::{self, TestBindingMethods, TestEnum};
use dom::bindings::codegen::UnionTypes::{BlobOrString, EventOrString}; use dom::bindings::codegen::UnionTypes::{BlobOrString, EventOrString};
use dom::bindings::codegen::UnionTypes::{EventOrUSVString, HTMLElementOrLong}; use dom::bindings::codegen::UnionTypes::{EventOrUSVString, HTMLElementOrLong};
use dom::bindings::error::Fallible;
use dom::bindings::global::{GlobalRef, global_root_from_reflector}; use dom::bindings::global::{GlobalRef, global_root_from_reflector};
use dom::bindings::js::Root; use dom::bindings::js::Root;
use dom::bindings::num::Finite; use dom::bindings::num::Finite;
use dom::bindings::reflector::Reflector; use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::str::{ByteString, USVString}; use dom::bindings::str::{ByteString, USVString};
use dom::blob::Blob; use dom::blob::Blob;
use js::jsapi::{HandleValue, JSContext, JSObject}; use js::jsapi::{HandleValue, JSContext, JSObject};
@ -27,6 +28,23 @@ pub struct TestBinding {
reflector_: Reflector, reflector_: Reflector,
} }
impl TestBinding {
fn new_inherited() -> TestBinding {
TestBinding {
reflector_: Reflector::new(),
}
}
pub fn new(global: GlobalRef) -> Root<TestBinding> {
reflect_dom_object(box TestBinding::new_inherited(),
global, TestBindingBinding::Wrap)
}
pub fn Constructor(global: GlobalRef) -> Fallible<Root<TestBinding>> {
Ok(TestBinding::new(global))
}
}
impl TestBindingMethods for TestBinding { impl TestBindingMethods for TestBinding {
fn BooleanAttribute(&self) -> bool { false } fn BooleanAttribute(&self) -> bool { false }
fn SetBooleanAttribute(&self, _: bool) {} fn SetBooleanAttribute(&self, _: bool) {}
@ -360,7 +378,7 @@ impl TestBindingMethods for TestBinding {
fn PassVariadicUsvstring(&self, _: Vec<USVString>) {} fn PassVariadicUsvstring(&self, _: Vec<USVString>) {}
fn PassVariadicByteString(&self, _: Vec<ByteString>) {} fn PassVariadicByteString(&self, _: Vec<ByteString>) {}
fn PassVariadicEnum(&self, _: Vec<TestEnum>) {} fn PassVariadicEnum(&self, _: Vec<TestEnum>) {}
// fn PassVariadicInterface(self, _: Vec<&Blob>) {} fn PassVariadicInterface(&self, _: &[&Blob]) {}
fn PassVariadicUnion(&self, _: Vec<HTMLElementOrLong>) {} fn PassVariadicUnion(&self, _: Vec<HTMLElementOrLong>) {}
fn PassVariadicUnion2(&self, _: Vec<EventOrString>) {} fn PassVariadicUnion2(&self, _: Vec<EventOrString>) {}
fn PassVariadicUnion3(&self, _: Vec<BlobOrString>) {} fn PassVariadicUnion3(&self, _: Vec<BlobOrString>) {}

View file

@ -68,6 +68,7 @@ dictionary TestDictionaryDefaults {
object? nullableObjectValue = null; object? nullableObjectValue = null;
}; };
[Constructor]
interface TestBinding { interface TestBinding {
attribute boolean booleanAttribute; attribute boolean booleanAttribute;
attribute byte byteAttribute; attribute byte byteAttribute;
@ -340,7 +341,7 @@ interface TestBinding {
void passVariadicUsvstring(USVString... args); void passVariadicUsvstring(USVString... args);
void passVariadicByteString(ByteString... args); void passVariadicByteString(ByteString... args);
void passVariadicEnum(TestEnum... args); void passVariadicEnum(TestEnum... args);
// void passVariadicInterface(Blob... args); void passVariadicInterface(Blob... args);
void passVariadicUnion((HTMLElement or long)... args); void passVariadicUnion((HTMLElement or long)... args);
void passVariadicUnion2((Event or DOMString)... args); void passVariadicUnion2((Event or DOMString)... args);
void passVariadicUnion3((Blob or DOMString)... args); void passVariadicUnion3((Blob or DOMString)... args);

View file

@ -5041,6 +5041,12 @@
"url": "/_mozilla/mozilla/union.html" "url": "/_mozilla/mozilla/union.html"
} }
], ],
"mozilla/variadic-interface.html": [
{
"path": "mozilla/variadic-interface.html",
"url": "/_mozilla/mozilla/variadic-interface.html"
}
],
"mozilla/webgl_context_creation_error.html": [ "mozilla/webgl_context_creation_error.html": [
{ {
"path": "mozilla/webgl_context_creation_error.html", "path": "mozilla/webgl_context_creation_error.html",

View file

@ -0,0 +1,11 @@
<!doctype html>
<meta charset=utf-8>
<title></title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script>
test(function() {
var t = new TestBinding;
t.passVariadicInterface(new Blob, new Blob);
}, "Variadic interface arguments work.");
</script>