mirror of
https://github.com/servo/servo.git
synced 2025-07-24 15:50:21 +01:00
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:
parent
e66a361e08
commit
acb13dc899
7 changed files with 105 additions and 63 deletions
|
@ -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"
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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`.
|
||||||
|
|
|
@ -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>) {}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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",
|
||||||
|
|
11
tests/wpt/mozilla/tests/mozilla/variadic-interface.html
Normal file
11
tests/wpt/mozilla/tests/mozilla/variadic-interface.html
Normal 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>
|
Loading…
Add table
Add a link
Reference in a new issue