diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index d2d090f83502..ae415bd97da3 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -563,6 +563,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, isMember=False, isArgument=False, isAutoRooted=False, + needsRootingInto=False, invalidEnumValueFatal=True, defaultValue=None, treatNullAs="Default", @@ -870,8 +871,48 @@ def wrapObjectTemplate(templateBody, nullValue, isDefinitelyObject, type, return handleOptional(templateBody, declType, handleDefaultNull("None")) - if type.isSpiderMonkeyInterface(): - raise TypeError("Can't handle SpiderMonkey interface arguments yet") + if type.isTypedArray() or type.isArrayBuffer() or type.isArrayBufferView() or type.isSharedArrayBuffer(): + # Typed arrays can be constructed only with a prior rooted value + assert isArgument or isinstance(needsRootingInto, basestring) + + if failureCode is None: + substitutions = { + "sourceDescription": sourceDescription, + "exceptionCode": exceptionCode, + } + unwrapFailureCode = string.Template( + 'throw_type_error(cx, "${sourceDescription} is not a typed array.");\n' + '${exceptionCode}').substitute(substitutions) + else: + unwrapFailureCode = failureCode + + templateBody = fill( + """ + match typedarray::${ty}::from(cx, &mut ${stackRootName}, + $${val}.get().to_object()) { + Ok(val) => val, + Err(()) => { + $*{failureCode} + } + } + """, + ty=type.name, + stackRootName=needsRootingInto, + failureCode=unwrapFailureCode + "\n", + ) + + declType = CGGeneric("typedarray::%s" % type.name) + if type.nullable(): + templateBody = "Some(%s)" % templateBody + declType = CGWrapper(declType, pre="Option<", post=">") + + templateBody = wrapObjectTemplate(templateBody, "None", + isDefinitelyObject, type, failureCode) + + return handleOptional(templateBody, declType, handleDefaultNull("None")) + + elif type.isSpiderMonkeyInterface(): + raise TypeError("Can't handle SpiderMonkey interface arguments other than typed arrays yet") if type.isDOMString(): nullBehavior = getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs) @@ -1245,6 +1286,7 @@ def __init__(self, argument, index, args, argc, descriptorProvider, isClamp=argument.clamp, isMember="Variadic" if argument.variadic else False, isAutoRooted=type_needs_auto_root(argument.type), + needsRootingInto="arg_root" if type_conversion_needs_root(argument.type) else False, allowTreatNonObjectAsNull=argument.allowTreatNonCallableAsNull()) template = info.template default = info.default @@ -1269,12 +1311,17 @@ def __init__(self, argument, index, args, argc, descriptorProvider, arg = "arg%d" % index - self.converter = instantiateJSToNativeConversionTemplate( - template, replacementVariables, declType, arg) + converter = [instantiateJSToNativeConversionTemplate( + template, replacementVariables, declType, arg)] + + if type_conversion_needs_root(argument.type): + converter.insert(0, CGGeneric("let mut arg_root = Rooted::new_unrooted();")) # The auto rooting is done only after the conversion is performed if type_needs_auto_root(argument.type): - self.converter.append(CGGeneric("auto_root!(in(cx) let %s = %s);" % (arg, arg))) + converter.append(CGGeneric("auto_root!(in(cx) let %s = %s);" % (arg, arg))) + + self.converter = CGList(converter, "\n") else: assert argument.optional @@ -5687,6 +5734,7 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries 'js::jsapi::MutableHandleValue', 'js::jsapi::ObjectOpResult', 'js::jsapi::PropertyDescriptor', + 'js::jsapi::Rooted', 'js::jsapi::RootedId', 'js::jsapi::RootedObject', 'js::jsapi::RootedString', @@ -5718,6 +5766,7 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries 'js::rust::define_methods', 'js::rust::define_properties', 'js::rust::get_object_class', + 'js::typedarray', 'dom', 'dom::bindings', 'dom::bindings::codegen::InterfaceObjectMap', @@ -6438,6 +6487,12 @@ def type_needs_tracing(t): assert False, (t, type(t)) +def type_conversion_needs_root(t): + assert isinstance(t, IDLType) + + return t.isSpiderMonkeyInterface() + + def type_needs_auto_root(t): """ Certain IDL types, such as `sequence` or `sequence` need to be diff --git a/components/script/dom/testbinding.rs b/components/script/dom/testbinding.rs index 2dd774275909..1b6d74720008 100644 --- a/components/script/dom/testbinding.rs +++ b/components/script/dom/testbinding.rs @@ -38,6 +38,7 @@ use js::jsapi::{HandleObject, HandleValue, Heap, JSContext, JSObject}; use js::jsapi::{JS_NewPlainObject, JS_NewUint8ClampedArray}; use js::jsval::{JSVal, NullValue}; use js::rust::CustomAutoRooterGuard; +use js::typedarray; use script_traits::MsDuration; use servo_config::prefs::PREFS; use std::borrow::ToOwned; @@ -428,6 +429,9 @@ impl TestBindingMethods for TestBinding { fn PassByteString(&self, _: ByteString) {} fn PassEnum(&self, _: TestEnum) {} fn PassInterface(&self, _: &Blob) {} + fn PassTypedArray(&self, _: typedarray::Int8Array) {} + fn PassTypedArray2(&self, _: typedarray::ArrayBuffer) {} + fn PassTypedArray3(&self, _: typedarray::ArrayBufferView) {} fn PassUnion(&self, _: HTMLElementOrLong) {} fn PassUnion2(&self, _: EventOrString) {} fn PassUnion3(&self, _: BlobOrString) {} diff --git a/components/script/dom/webidls/TestBinding.webidl b/components/script/dom/webidls/TestBinding.webidl index 25292953d60a..24473dbcd844 100644 --- a/components/script/dom/webidls/TestBinding.webidl +++ b/components/script/dom/webidls/TestBinding.webidl @@ -246,6 +246,9 @@ interface TestBinding { void passByteString(ByteString arg); void passEnum(TestEnum arg); void passInterface(Blob arg); + void passTypedArray(Int8Array arg); + void passTypedArray2(ArrayBuffer arg); + void passTypedArray3(ArrayBufferView arg); void passUnion((HTMLElement or long) arg); void passUnion2((Event or DOMString) data); void passUnion3((Blob or DOMString) data);