Implement binding support for returning and accepting Promises in WebIDL.

This commit is contained in:
Josh Matthews 2016-06-21 19:35:36 -04:00
parent 73b2963509
commit a1091772ec
12 changed files with 217 additions and 20 deletions

View file

@ -658,7 +658,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
# failureCode will prevent pending exceptions from being set in cases when
# they really should be!
if exceptionCode is None:
exceptionCode = "return false;"
exceptionCode = "return false;\n"
if failureCode is None:
failOrPropagate = "throw_type_error(cx, &error);\n%s" % exceptionCode
@ -797,25 +797,69 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
descriptorType = descriptor.argumentType
templateBody = ""
if descriptor.interface.isConsequential():
raise TypeError("Consequential interface %s being used as an "
"argument" % descriptor.interface.identifier.name)
isPromise = descriptor.interface.identifier.name == "Promise"
if isPromise:
# Per spec, what we're supposed to do is take the original
# Promise.resolve and call it with the original Promise as this
# value to make a Promise out of whatever value we actually have
# here. The question is which global we should use. There are
# a couple cases to consider:
#
# 1) Normal call to API with a Promise argument. This is a case the
# spec covers, and we should be using the current Realm's
# Promise. That means the current compartment.
# 2) Promise return value from a callback or callback interface.
# This is in theory a case the spec covers but in practice it
# really doesn't define behavior here because it doesn't define
# what Realm we're in after the callback returns, which is when
# the argument conversion happens. We will use the current
# compartment, which is the compartment of the callable (which
# may itself be a cross-compartment wrapper itself), which makes
# as much sense as anything else. In practice, such an API would
# once again be providing a Promise to signal completion of an
# operation, which would then not be exposed to anyone other than
# our own implementation code.
templateBody = fill(
"""
{ // Scope for our JSAutoCompartment.
if failureCode is None:
substitutions = {
"sourceDescription": sourceDescription,
"interface": descriptor.interface.identifier.name,
"exceptionCode": exceptionCode,
}
unwrapFailureCode = string.Template(
'throw_type_error(cx, "${sourceDescription} does not '
'implement interface ${interface}.");\n'
'${exceptionCode}').substitute(substitutions)
rooted!(in(cx) let globalObj = CurrentGlobalOrNull(cx));
let promiseGlobal = global_root_from_object_maybe_wrapped(globalObj.handle().get());
let mut valueToResolve = RootedValue::new(cx, $${val}.get());
if !JS_WrapValue(cx, valueToResolve.handle_mut()) {
$*{exceptionCode}
}
match Promise::Resolve(promiseGlobal.r(), cx, valueToResolve.handle()) {
Ok(value) => value,
Err(error) => {
throw_dom_exception(cx, promiseGlobal.r(), error);
$*{exceptionCode}
}
}
}
""",
exceptionCode=exceptionCode)
else:
unwrapFailureCode = failureCode
if descriptor.interface.isConsequential():
raise TypeError("Consequential interface %s being used as an "
"argument" % descriptor.interface.identifier.name)
templateBody = unwrapCastableObject(
descriptor, "${val}", unwrapFailureCode, conversionFunction)
if failureCode is None:
substitutions = {
"sourceDescription": sourceDescription,
"interface": descriptor.interface.identifier.name,
"exceptionCode": exceptionCode,
}
unwrapFailureCode = string.Template(
'throw_type_error(cx, "${sourceDescription} does not '
'implement interface ${interface}.");\n'
'${exceptionCode}').substitute(substitutions)
else:
unwrapFailureCode = failureCode
templateBody = unwrapCastableObject(
descriptor, "${val}", unwrapFailureCode, conversionFunction)
declType = CGGeneric(descriptorType)
if type.nullable():
@ -5372,6 +5416,7 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries
'js::jsapi::AutoIdVector',
'js::jsapi::Call',
'js::jsapi::CallArgs',
'js::jsapi::CurrentGlobalOrNull',
'js::jsapi::FreeOp',
'js::jsapi::GetPropertyKeys',
'js::jsapi::GetWellKnownSymbol',
@ -5442,7 +5487,9 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries
'js::jsapi::MutableHandleValue',
'js::jsapi::ObjectOpResult',
'js::jsapi::PropertyDescriptor',
'js::jsapi::RootedId',
'js::jsapi::RootedObject',
'js::jsapi::RootedString',
'js::jsapi::SymbolCode',
'js::jsapi::jsid',
'js::jsval::JSVal',
@ -5472,6 +5519,7 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries
'dom::bindings::constant::ConstantVal',
'dom::bindings::global::GlobalRef',
'dom::bindings::global::global_root_from_object',
'dom::bindings::global::global_root_from_object_maybe_wrapped',
'dom::bindings::global::global_root_from_reflector',
'dom::bindings::interface::ConstructorClassHook',
'dom::bindings::interface::InterfaceConstructorBehavior',
@ -5490,6 +5538,7 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries
'dom::bindings::js::JS',
'dom::bindings::js::OptionalRootedReference',
'dom::bindings::js::Root',
'dom::bindings::js::RootedRcReference',
'dom::bindings::js::RootedReference',
'dom::bindings::namespace::NamespaceObjectClass',
'dom::bindings::namespace::create_namespace_object',