Compare commits

...

1 Commits

Author SHA1 Message Date
Ms2ger
c160aacecc Implement typed array arguments in codegen.
This does not add support for typed arrays within unions, dictionaries,
sequences or records, or in variadic arguments, indexed or named setters,
callback return values or overloaded methods. If we do end up trying to
use them in any of those cases, the code generator will raise an exception.

Fixes #5014.
2017-01-17 11:14:25 +01:00
3 changed files with 100 additions and 6 deletions

View File

@@ -435,6 +435,7 @@ class CGMethodCall(CGThing):
# large enough that we can examine this argument.
info = getJSToNativeConversionInfo(
type, descriptor, failureCode="break;", isDefinitelyObject=True)
assert not info.needsRooting
template = info.template
declType = info.declType
@@ -558,28 +559,44 @@ def union_native_type(t):
return 'UnionTypes::%s' % name
def typed_array_native_type(t):
"""
Returns the name of the native type for `t`.
"""
assert t.isSpiderMonkeyInterface()
assert t.isArrayBuffer() or t.isArrayBufferView() or t.isTypedArray()
name = t.unroll().name
return 'typedarray::%s' % name
class JSToNativeConversionInfo():
"""
An object representing information about a JS-to-native conversion.
"""
def __init__(self, template, default=None, declType=None):
def __init__(self, template, default=None, declType=None,
needsRooting=False):
"""
template: A string representing the conversion code. This will have
template substitution performed on it as follows:
${val} is a handle to the JS::Value in question
${root} is the name of a `Rooted` on the stack, if `needsRooting` is true.
default: A string or None representing rust code for default value(if any).
declType: A CGThing representing the native C++ type we're converting
to. This is allowed to be None if the conversion code is
supposed to be used as-is.
needsRooting: A boolean indicating whether the caller needs to provide
a `Rooted` on the stack.
"""
assert isinstance(template, str)
assert declType is None or isinstance(declType, CGThing)
self.template = template
self.default = default
self.declType = declType
self.needsRooting = needsRooting
def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
@@ -660,9 +677,9 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
else:
failOrPropagate = failureCode
def handleOptional(template, declType, default):
def handleOptional(template, declType, default, needsRooting=False):
assert (defaultValue is None) == (default is None)
return JSToNativeConversionInfo(template, default, declType)
return JSToNativeConversionInfo(template, default, declType, needsRooting=needsRooting)
# Unfortunately, .capitalize() on a string will lowercase things inside the
# string, which we do not want.
@@ -729,6 +746,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
innerInfo = getJSToNativeConversionInfo(innerContainerType(type),
descriptorProvider,
isMember=isMember)
assert not innerInfo.needsRooting
declType = wrapInNativeContainerType(type, innerInfo.declType)
config = getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs)
@@ -862,7 +880,34 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
return handleOptional(templateBody, declType, handleDefaultNull("None"))
if type.isSpiderMonkeyInterface():
raise TypeError("Can't handle SpiderMonkey interface arguments yet")
assert not isEnforceRange
assert not isClamp
ty = typed_array_native_type(type)
templateBody = fill(
"""
{
let array = ${ty}::from(cx, &mut $${root}, $${val}.get().to_object());
match array {
Ok(array) => array,
Err(()) => {
let error = "Object was not a typed array";
$*{failure}
}
}
}
""",
ty=ty,
failure=failOrPropagate)
declType = CGGeneric(ty)
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"),
needsRooting=True)
if type.isDOMString():
nullBehavior = getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs)
@@ -1246,10 +1291,19 @@ class CGArgumentConverter(CGThing):
else:
assert not default
self.converter = instantiateJSToNativeConversionTemplate(
template, replacementVariables, declType, "arg%d" % index)
self.converter = CGList([], "\n")
if info.needsRooting:
name = "__root%d" % index
root = "let mut {root} = Rooted::new_unrooted();".format(root=name)
replacementVariables["root"] = name
self.converter.append(CGGeneric(root))
self.converter.append(instantiateJSToNativeConversionTemplate(
template, replacementVariables, declType, "arg%d" % index))
else:
assert argument.optional
assert not info.needsRooting
variadicConversion = {
"val": string.Template("${args}.get(variadicArg)").substitute(replacer),
}
@@ -4054,6 +4108,7 @@ def getUnionTypeTemplateVars(type, descriptorProvider):
type, descriptorProvider, failureCode="return Ok(None);",
exceptionCode='return Err(());',
isDefinitelyObject=True)
assert not info.needsRooting
template = info.template
jsConversion = string.Template(template).substitute({
@@ -4641,6 +4696,7 @@ class CGProxySpecialOperation(CGPerSignatureCall):
info = getJSToNativeConversionInfo(
argument.type, descriptor, treatNullAs=argument.treatNullAs,
exceptionCode="return false;")
assert not info.needsRooting
template = info.template
declType = info.declType
@@ -5477,6 +5533,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',
@@ -5505,6 +5562,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',
@@ -5842,6 +5900,7 @@ class CGDictionary(CGThing):
defaultValue=member.defaultValue,
exceptionCode="return Err(());"))
for member in dictionary.members]
assert not any(i.needsRooting for (_, i) in self.memberInfo)
def define(self):
if not self.generatable:
@@ -6461,6 +6520,7 @@ class CallbackMember(CGNativeMember):
isCallbackReturnValue="Callback",
# XXXbz we should try to do better here
sourceDescription="return value")
assert not info.needsRooting
template = info.template
declType = info.declType

View File

@@ -36,6 +36,7 @@ use dom::url::URL;
use js::jsapi::{HandleObject, HandleValue, JSContext, JSObject, JSAutoCompartment};
use js::jsapi::{JS_NewPlainObject, JS_NewUint8ClampedArray};
use js::jsval::{JSVal, NullValue};
use js::typedarray;
use script_traits::MsDuration;
use servo_config::prefs::PREFS;
use std::borrow::ToOwned;
@@ -626,6 +627,23 @@ impl TestBindingMethods for TestBinding {
unsafe fn PassVariadicAny(&self, _: *mut JSContext, _: Vec<HandleValue>) {}
#[allow(unsafe_code)]
unsafe fn PassVariadicObject(&self, _: *mut JSContext, _: Vec<*mut JSObject>) {}
fn PassArrayBuffer(&self, _: typedarray::ArrayBuffer) {}
fn PassNullableArrayBuffer(&self, _: Option<typedarray::ArrayBuffer>) {}
fn PassOptionalArrayBuffer(&self, _: Option<typedarray::ArrayBuffer>) {}
fn PassOptionalNullableArrayBuffer(&self, _: Option<Option<typedarray::ArrayBuffer>>) {}
fn PassOptionalNullableArrayBufferWithDefaultValue(&self, _: Option<typedarray::ArrayBuffer>) {}
fn PassArrayBufferView(&self, _: typedarray::ArrayBufferView) {}
fn PassInt8Array(&self, _: typedarray::Int8Array) {}
fn PassInt16Array(&self, _: typedarray::Int16Array) {}
fn PassInt32Array(&self, _: typedarray::Int32Array) {}
fn PassUint8Array(&self, _: typedarray::Uint8Array) {}
fn PassUint8ClampedArray(&self, _: typedarray::Uint8ClampedArray) {}
fn PassUint16Array(&self, _: typedarray::Uint16Array) {}
fn PassUint32Array(&self, _: typedarray::Uint32Array) {}
fn PassFloat32Array(&self, _: typedarray::Float32Array) {}
fn PassFloat64Array(&self, _: typedarray::Float64Array) {}
fn BooleanMozPreference(&self, pref_name: DOMString) -> bool {
PREFS.get(pref_name.as_ref()).as_boolean().unwrap_or(false)
}

View File

@@ -425,6 +425,22 @@ interface TestBinding {
void passVariadicAny(any... args);
void passVariadicObject(object... args);
void passArrayBuffer(ArrayBuffer arg);
void passNullableArrayBuffer(ArrayBuffer? arg);
void passOptionalArrayBuffer(optional ArrayBuffer arg);
void passOptionalNullableArrayBuffer(optional ArrayBuffer? arg);
void passOptionalNullableArrayBufferWithDefaultValue(optional ArrayBuffer? arg = null);
void passArrayBufferView(ArrayBufferView arg);
void passInt8Array(Int8Array arg);
void passInt16Array(Int16Array arg);
void passInt32Array(Int32Array arg);
void passUint8Array(Uint8Array arg);
void passUint8ClampedArray(Uint8ClampedArray arg);
void passUint16Array(Uint16Array arg);
void passUint32Array(Uint32Array arg);
void passFloat32Array(Float32Array arg);
void passFloat64Array(Float64Array arg);
void passSequenceSequence(sequence<sequence<long>> seq);
sequence<sequence<long>> returnSequenceSequence();
void passUnionSequenceSequence((long or sequence<sequence<long>>) seq);