diff options
Diffstat (limited to 'dom/bindings/Codegen.py')
-rw-r--r-- | dom/bindings/Codegen.py | 494 |
1 files changed, 231 insertions, 263 deletions
diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index d7d700a968..6b23e8225c 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -1135,6 +1135,12 @@ class CGHeaders(CGWrapper): declareIncludes.add("mozilla/dom/Date.h") else: bindingHeaders.add("mozilla/dom/Date.h") + elif unrolled.isPromise(): + # See comment in the isInterface() case for why we add + # Promise.h to headerSet, not bindingHeaders. + headerSet.add("mozilla/dom/Promise.h") + # We need ToJSValue to do the Promise to JS conversion. + bindingHeaders.add("mozilla/dom/ToJSValue.h") elif unrolled.isInterface(): if unrolled.isSpiderMonkeyInterface(): bindingHeaders.add("jsfriendapi.h") @@ -1352,7 +1358,11 @@ def UnionTypes(unionTypes, config): headers.add("mozilla/dom/Nullable.h") isSequence = f.isSequence() f = f.unroll() - if f.isInterface(): + if f.isPromise(): + headers.add("mozilla/dom/Promise.h") + # We need ToJSValue to do the Promise to JS conversion. + headers.add("mozilla/dom/ToJSValue.h") + elif f.isInterface(): if f.isSpiderMonkeyInterface(): headers.add("jsfriendapi.h") headers.add("mozilla/dom/TypedArray.h") @@ -1434,7 +1444,11 @@ def UnionConversions(unionTypes, config): def addHeadersForType(f): f = f.unroll() - if f.isInterface(): + if f.isPromise(): + headers.add("mozilla/dom/Promise.h") + # We need ToJSValue to do the Promise to JS conversion. + headers.add("mozilla/dom/ToJSValue.h") + elif f.isInterface(): if f.isSpiderMonkeyInterface(): headers.add("jsfriendapi.h") headers.add("mozilla/dom/TypedArray.h") @@ -3052,23 +3066,6 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod): else: unforgeableHolderSetup = None - if self.descriptor.name == "Promise": - speciesSetup = CGGeneric(fill( - """ - #ifndef SPIDERMONKEY_PROMISE - JS::Rooted<JSObject*> promiseConstructor(aCx, *interfaceCache); - JS::Rooted<jsid> species(aCx, - SYMBOL_TO_JSID(JS::GetWellKnownSymbol(aCx, JS::SymbolCode::species))); - if (!JS_DefinePropertyById(aCx, promiseConstructor, species, JS::UndefinedHandleValue, - JSPROP_SHARED, Promise::PromiseSpecies, nullptr)) { - $*{failureCode} - } - #endif // SPIDERMONKEY_PROMISE - """, - failureCode=failureCode)) - else: - speciesSetup = None - if (self.descriptor.interface.isOnGlobalProtoChain() and needInterfacePrototypeObject): makeProtoPrototypeImmutable = CGGeneric(fill( @@ -3094,7 +3091,7 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod): return CGList( [getParentProto, getConstructorProto, initIds, prefCache, CGGeneric(call), defineAliases, unforgeableHolderSetup, - speciesSetup, makeProtoPrototypeImmutable], + makeProtoPrototypeImmutable], "\n").define() @@ -5244,6 +5241,139 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, holderArgs=holderArgs, dealWithOptional=isOptional and (not nullable or isOwningUnion)) + if type.isPromise(): + assert not type.nullable() + assert defaultValue is None + + # We always have to hold a strong ref to Promise here, because + # Promise::resolve returns an addrefed thing. + argIsPointer = isCallbackReturnValue + if argIsPointer: + declType = CGGeneric("RefPtr<Promise>") + else: + declType = CGGeneric("OwningNonNull<Promise>") + + # 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 + # several 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) Call to API with a Promise argument over Xrays. In practice, + # this sort of thing seems to be used for giving an API + # implementation a way to wait for conclusion of an asyc + # operation, _not_ to expose the Promise to content code. So we + # probably want to allow callers to use such an API in a + # "natural" way, by passing chrome-side promises; indeed, that + # may be all that the caller has to represent their async + # operation. That means we really need to do the + # Promise.resolve() in the caller (chrome) compartment: if we do + # it in the content compartment, we will try to call .then() on + # the chrome promise while in the content compartment, which will + # throw and we'll just get a rejected Promise. Note that this is + # also the reason why a caller who has a chrome Promise + # representing an async operation can't itself convert it to a + # content-side Promise (at least not without some serious + # gyrations). + # 3) Promise return value from a callback or callback interface. + # Per spec, this should use the Realm of the callback object. In + # our case, that's the compartment of the underlying callback, + # not the current compartment (which may be the compartment of + # some cross-compartment wrapper around said callback). + # 4) Return value from a JS-implemented interface. In this case we + # have a problem. Our current compartment is the compartment of + # the JS implementation. But if the JS implementation returned + # a page-side Promise (which is a totally sane thing to do, and + # in fact the right thing to do given that this return value is + # going right to content script) then we don't want to + # Promise.resolve with our current compartment Promise, because + # that will wrap it up in a chrome-side Promise, which is + # decidedly _not_ what's desired here. So in that case we + # should really unwrap the return value and use the global of + # the result. CheckedUnwrap should be good enough for that; if + # it fails, then we're failing unwrap while in a + # system-privileged compartment, so presumably we have a dead + # object wrapper. Just error out. Do NOT fall back to using + # the current compartment instead: that will return a + # system-privileged rejected (because getting .then inside + # resolve() failed) Promise to the caller, which they won't be + # able to touch. That's not helpful. If we error out, on the + # other hand, they will get a content-side rejected promise. + # Same thing if the value returned is not even an object. + if isCallbackReturnValue == "JSImpl": + # Case 4 above. Note that globalObj defaults to the current + # compartment global. Note that we don't use $*{exceptionCode} + # here because that will try to aRv.Throw(NS_ERROR_UNEXPECTED) + # which we don't really want here. + assert exceptionCode == "aRv.Throw(NS_ERROR_UNEXPECTED);\nreturn nullptr;\n" + getPromiseGlobal = fill( + """ + if (!$${val}.isObject()) { + aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING("${sourceDescription}")); + return nullptr; + } + JSObject* unwrappedVal = js::CheckedUnwrap(&$${val}.toObject()); + if (!unwrappedVal) { + // A slight lie, but not much of one, for a dead object wrapper. + aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING("${sourceDescription}")); + return nullptr; + } + globalObj = js::GetGlobalForObjectCrossCompartment(unwrappedVal); + """, + sourceDescription=sourceDescription) + elif isCallbackReturnValue == "Callback": + getPromiseGlobal = dedent( + """ + // We basically want our entry global here. Play it safe + // and use GetEntryGlobal() to get it, with whatever + // principal-clamping it ends up doing. + globalObj = GetEntryGlobal()->GetGlobalJSObject(); + """) + else: + getPromiseGlobal = "" + + templateBody = fill( + """ + { // Scope for our GlobalObject, FastErrorResult, JSAutoCompartment, + // etc. + + JS::Rooted<JSObject*> globalObj(cx, JS::CurrentGlobalOrNull(cx)); + $*{getPromiseGlobal} + JSAutoCompartment ac(cx, globalObj); + GlobalObject promiseGlobal(cx, globalObj); + if (promiseGlobal.Failed()) { + $*{exceptionCode} + } + + JS::Rooted<JS::Value> valueToResolve(cx, $${val}); + if (!JS_WrapValue(cx, &valueToResolve)) { + $*{exceptionCode} + } + binding_detail::FastErrorResult promiseRv; + nsCOMPtr<nsIGlobalObject> global = + do_QueryInterface(promiseGlobal.GetAsSupports()); + if (!global) { + promiseRv.Throw(NS_ERROR_UNEXPECTED); + promiseRv.MaybeSetPendingException(cx); + $*{exceptionCode} + } + $${declName} = Promise::Resolve(global, cx, valueToResolve, + promiseRv); + if (promiseRv.MaybeSetPendingException(cx)) { + $*{exceptionCode} + } + } + """, + getPromiseGlobal=getPromiseGlobal, + exceptionCode=exceptionCode) + + return JSToNativeConversionInfo(templateBody, + declType=declType, + dealWithOptional=isOptional) + if type.isGeckoInterface(): assert not isEnforceRange and not isClamp @@ -5282,12 +5412,8 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, # Also, callback return values always end up addrefing anyway, so there # is no point trying to avoid it here and it makes other things simpler # since we can assume the return value is a strong ref. - # - # Finally, promises need to hold a strong ref because that's what - # Promise.resolve returns. assert not descriptor.interface.isCallback() - isPromise = descriptor.interface.identifier.name == "Promise" - forceOwningType = isMember or isCallbackReturnValue or isPromise + forceOwningType = isMember or isCallbackReturnValue typeName = descriptor.nativeType typePtr = typeName + "*" @@ -5315,143 +5441,8 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, if forceOwningType: templateBody += 'static_assert(IsRefcounted<%s>::value, "We can only store refcounted classes.");' % typeName - 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 - # several 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) Call to API with a Promise argument over Xrays. In practice, - # this sort of thing seems to be used for giving an API - # implementation a way to wait for conclusion of an asyc - # operation, _not_ to expose the Promise to content code. So we - # probably want to allow callers to use such an API in a - # "natural" way, by passing chrome-side promises; indeed, that - # may be all that the caller has to represent their async - # operation. That means we really need to do the - # Promise.resolve() in the caller (chrome) compartment: if we do - # it in the content compartment, we will try to call .then() on - # the chrome promise while in the content compartment, which will - # throw and we'll just get a rejected Promise. Note that this is - # also the reason why a caller who has a chrome Promise - # representing an async operation can't itself convert it to a - # content-side Promise (at least not without some serious - # gyrations). - # 3) 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. - # 4) Return value from a JS-implemented interface. In this case we - # have a problem. Our current compartment is the compartment of - # the JS implementation. But if the JS implementation returned - # a page-side Promise (which is a totally sane thing to do, and - # in fact the right thing to do given that this return value is - # going right to content script) then we don't want to - # Promise.resolve with our current compartment Promise, because - # that will wrap it up in a chrome-side Promise, which is - # decidedly _not_ what's desired here. So in that case we - # should really unwrap the return value and use the global of - # the result. CheckedUnwrap should be good enough for that; if - # it fails, then we're failing unwrap while in a - # system-privileged compartment, so presumably we have a dead - # object wrapper. Just error out. Do NOT fall back to using - # the current compartment instead: that will return a - # system-privileged rejected (because getting .then inside - # resolve() failed) Promise to the caller, which they won't be - # able to touch. That's not helpful. If we error out, on the - # other hand, they will get a content-side rejected promise. - # Same thing if the value returned is not even an object. - if isCallbackReturnValue == "JSImpl": - # Case 4 above. Note that globalObj defaults to the current - # compartment global. Note that we don't use $*{exceptionCode} - # here because that will try to aRv.Throw(NS_ERROR_UNEXPECTED) - # which we don't really want here. - assert exceptionCode == "aRv.Throw(NS_ERROR_UNEXPECTED);\nreturn nullptr;\n" - getPromiseGlobal = fill( - """ - if (!$${val}.isObject()) { - aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING("${sourceDescription}")); - return nullptr; - } - JSObject* unwrappedVal = js::CheckedUnwrap(&$${val}.toObject()); - if (!unwrappedVal) { - // A slight lie, but not much of one, for a dead object wrapper. - aRv.ThrowTypeError<MSG_NOT_OBJECT>(NS_LITERAL_STRING("${sourceDescription}")); - return nullptr; - } - globalObj = js::GetGlobalForObjectCrossCompartment(unwrappedVal); - """, - sourceDescription=sourceDescription) - else: - getPromiseGlobal = "" - - templateBody = fill( - """ - { // Scope for our GlobalObject, FastErrorResult, JSAutoCompartment, - // etc. - - JS::Rooted<JSObject*> globalObj(cx, JS::CurrentGlobalOrNull(cx)); - $*{getPromiseGlobal} - JSAutoCompartment ac(cx, globalObj); - GlobalObject promiseGlobal(cx, globalObj); - if (promiseGlobal.Failed()) { - $*{exceptionCode} - } - - JS::Rooted<JS::Value> valueToResolve(cx, $${val}); - if (!JS_WrapValue(cx, &valueToResolve)) { - $*{exceptionCode} - } - binding_detail::FastErrorResult promiseRv; - #ifdef SPIDERMONKEY_PROMISE - nsCOMPtr<nsIGlobalObject> global = - do_QueryInterface(promiseGlobal.GetAsSupports()); - if (!global) { - promiseRv.Throw(NS_ERROR_UNEXPECTED); - promiseRv.MaybeSetPendingException(cx); - $*{exceptionCode} - } - $${declName} = Promise::Resolve(global, cx, valueToResolve, - promiseRv); - if (promiseRv.MaybeSetPendingException(cx)) { - $*{exceptionCode} - } - #else - JS::Handle<JSObject*> promiseCtor = - PromiseBinding::GetConstructorObjectHandle(cx); - if (!promiseCtor) { - $*{exceptionCode} - } - JS::Rooted<JS::Value> resolveThisv(cx, JS::ObjectValue(*promiseCtor)); - JS::Rooted<JS::Value> resolveResult(cx); - Promise::Resolve(promiseGlobal, resolveThisv, valueToResolve, - &resolveResult, promiseRv); - if (promiseRv.MaybeSetPendingException(cx)) { - $*{exceptionCode} - } - nsresult unwrapRv = UNWRAP_OBJECT(Promise, &resolveResult.toObject(), $${declName}); - if (NS_FAILED(unwrapRv)) { // Quite odd - promiseRv.Throw(unwrapRv); - promiseRv.MaybeSetPendingException(cx); - $*{exceptionCode} - } - #endif // SPIDERMONKEY_PROMISE - } - """, - getPromiseGlobal=getPromiseGlobal, - exceptionCode=exceptionCode) - elif not descriptor.interface.isConsequential() and not descriptor.interface.isExternal(): + if (not descriptor.interface.isConsequential() and + not descriptor.interface.isExternal()): if failureCode is not None: templateBody += str(CastableObjectUnwrapper( descriptor, @@ -5491,24 +5482,12 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, # And store our value in ${declName} templateBody += "${declName} = ${holderName};\n" - if isPromise: - if type.nullable(): - codeToSetNull = "${declName} = nullptr;\n" - templateBody = CGIfElseWrapper( - "${val}.isNullOrUndefined()", - CGGeneric(codeToSetNull), - CGGeneric(templateBody)).define() - if isinstance(defaultValue, IDLNullValue): - templateBody = handleDefault(templateBody, codeToSetNull) - else: - assert defaultValue is None - else: - # Just pass failureCode, not onFailureBadType, here, so we'll report - # the thing as not an object as opposed to not implementing whatever - # our interface is. - templateBody = wrapObjectTemplate(templateBody, type, - "${declName} = nullptr;\n", - failureCode) + # Just pass failureCode, not onFailureBadType, here, so we'll report + # the thing as not an object as opposed to not implementing whatever + # our interface is. + templateBody = wrapObjectTemplate(templateBody, type, + "${declName} = nullptr;\n", + failureCode) declType = CGGeneric(declType) if holderType is not None: @@ -6575,6 +6554,19 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode, return (code, False) + if type.isPromise(): + assert not type.nullable() + # The use of ToJSValue here is a bit annoying because the Promise + # version is not inlined, but we can't put an inline version in either + # ToJSValue.h or BindingUtils.h, because Promise.h includes ToJSValue.h + # and that includes BindingUtils.h, so we'd get an include loop if + # either of those headers included Promise.h. Trying to write the + # conversion by hand here is annoying because we'd have to handle + # the various RefPtr, rawptr, NonNull, etc. cases, which ToJSValue will + # already handle for us, so we just use the function call. + return (wrapAndSetPtr("ToJSValue(cx, %s, ${jsvalHandle})" % result), + False) + if type.isGeckoInterface() and not type.isCallbackInterface(): descriptor = descriptorProvider.getDescriptor(type.unroll().inner.identifier.name) if type.nullable(): @@ -6589,14 +6581,6 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode, wrapMethod = "GetOrCreateDOMReflector" wrapArgs = "cx, %s, ${jsvalHandle}" % result else: - # Hack: the "Promise" interface is OK to return from - # non-newobject things even when it's not wrappercached; that - # happens when using SpiderMonkey promises, and the WrapObject() - # method will just return the existing reflector, which is just - # not stored in a wrappercache. - if (not returnsNewObject and - descriptor.interface.identifier.name != "Promise"): - raise MethodNotNewObjectError(descriptor.interface.identifier.name) wrapMethod = "WrapNewBindingNonWrapperCachedObject" wrapArgs = "cx, ${obj}, %s, ${jsvalHandle}" % result if isConstructorRetval: @@ -6912,14 +6896,17 @@ def getRetvalDeclarationForType(returnType, descriptorProvider, if returnType.nullable(): result = CGTemplatedType("Nullable", result) return result, None, None, None, None - if returnType.isGeckoInterface(): - result = CGGeneric(descriptorProvider.getDescriptor( - returnType.unroll().inner.identifier.name).nativeType) - conversion = None + if returnType.isGeckoInterface() or returnType.isPromise(): + if returnType.isGeckoInterface(): + typeName = descriptorProvider.getDescriptor( + returnType.unroll().inner.identifier.name).nativeType + else: + typeName = "Promise" if isMember: - result = CGGeneric("StrongPtrForMember<%s>::Type" % result.define()) + conversion = None + result = CGGeneric("StrongPtrForMember<%s>::Type" % typeName) else: - conversion = CGGeneric("StrongOrRawPtr<%s>" % result.define()) + conversion = CGGeneric("StrongOrRawPtr<%s>" % typeName) result = CGGeneric("auto") return result, None, None, None, conversion if returnType.isCallback(): @@ -7084,7 +7071,9 @@ class CGCallGenerator(CGThing): if needsConst(a): arg = CGWrapper(arg, pre="Constify(", post=")") # And convert NonNull<T> to T& - if (((a.type.isGeckoInterface() or a.type.isCallback()) and not a.type.nullable()) or + if (((a.type.isGeckoInterface() or a.type.isCallback() or + a.type.isPromise()) and + not a.type.nullable()) or a.type.isDOMString()): arg = CGWrapper(arg, pre="NonNullHelper(", post=")") args.append(arg) @@ -7208,6 +7197,9 @@ class CGCallGenerator(CGThing): def getUnionMemberName(type): + # Promises can't be in unions, because they're not distinguishable + # from anything else. + assert not type.isPromise() if type.isGeckoInterface(): return type.inner.identifier.name if type.isEnum(): @@ -7337,8 +7329,9 @@ def wrapTypeIntoCurrentCompartment(type, value, isMember=True): return CGList(memberWraps, "else ") if len(memberWraps) != 0 else None if (type.isString() or type.isPrimitive() or type.isEnum() or - type.isGeckoInterface() or type.isCallback() or type.isDate()): - # All of these don't need wrapping + type.isGeckoInterface() or type.isCallback() or type.isDate() or + type.isPromise()): + # All of these don't need wrapping. return None raise TypeError("Unknown type; we don't know how to wrap it in constructor " @@ -7476,58 +7469,12 @@ class CGPerSignatureCall(CGThing): if needsCx: argsPre.append("cx") - # Hack for making Promise.prototype.then work well over Xrays. - if (not idlNode.isStatic() and - descriptor.name == "Promise" and - idlNode.isMethod() and - idlNode.identifier.name == "then"): - cgThings.append(CGGeneric(dedent( - """ - JS::Rooted<JSObject*> calleeGlobal(cx, xpc::XrayAwareCalleeGlobal(&args.callee())); - """))) - argsPre.append("calleeGlobal") - needsUnwrap = False argsPost = [] if isConstructor: - if descriptor.name == "Promise": - # Hack for Promise for now: pass in our desired proto so the - # implementation can create the reflector with the right proto. - argsPost.append("desiredProto") - # Also, we do not want to enter the content compartment when the - # Promise constructor is called via Xrays, because we want to - # create our callback functions that we will hand to our caller - # in the Xray compartment. The reason we want to do that is the - # following situation, over Xrays: - # - # contentWindow.Promise.race([Promise.resolve(5)]) - # - # Ideally this would work. Internally, race() does a - # contentWindow.Promise.resolve() on everything in the array. - # Per spec, to support subclassing, - # contentWindow.Promise.resolve has to do: - # - # var resolve, reject; - # var p = new contentWindow.Promise(function(a, b) { - # resolve = a; - # reject = b; - # }); - # resolve(arg); - # return p; - # - # where "arg" is, in this case, the chrome-side return value of - # Promise.resolve(5). But if the "resolve" function in that - # case were created in the content compartment, then calling it - # would wrap "arg" in an opaque wrapper, and that function tries - # to get .then off the argument, which would throw. So we need - # to create the "resolve" function in the chrome compartment, - # and hence want to be running the entire Promise constructor - # (which creates that function) in the chrome compartment in - # this case. So don't set needsUnwrap here. - else: - needsUnwrap = True - needsUnwrappedVar = False - unwrappedVar = "obj" + needsUnwrap = True + needsUnwrappedVar = False + unwrappedVar = "obj" elif descriptor.interface.isJSImplemented(): if not idlNode.isStatic(): needsUnwrap = True @@ -7540,11 +7487,6 @@ class CGPerSignatureCall(CGThing): needsUnwrappedVar = True argsPre.append("unwrappedObj ? *unwrappedObj : obj") - if idlNode.isStatic() and not isConstructor and descriptor.name == "Promise": - # Hack for Promise for now: pass in the "this" value to - # Promise static methods. - argsPre.append("args.thisv()") - if needsUnwrap and needsUnwrappedVar: # We cannot assign into obj because it's a Handle, not a # MutableHandle, so we need a separate Rooted. @@ -7687,7 +7629,8 @@ class CGPerSignatureCall(CGThing): returnsNewObject = memberReturnsNewObject(self.idlNode) if (returnsNewObject and - self.returnType.isGeckoInterface()): + (self.returnType.isGeckoInterface() or + self.returnType.isPromise())): wrapCode += dedent( """ static_assert(!IsPointer<decltype(result)>::value, @@ -9493,6 +9436,8 @@ class CGMemberJITInfo(CGThing): return "JSVAL_TYPE_OBJECT" if t.isRecord(): return "JSVAL_TYPE_OBJECT" + if t.isPromise(): + return "JSVAL_TYPE_OBJECT" if t.isGeckoInterface(): return "JSVAL_TYPE_OBJECT" if t.isString(): @@ -9566,6 +9511,8 @@ class CGMemberJITInfo(CGThing): return "JSJitInfo::ArgType(JSJitInfo::Null | %s)" % CGMemberJITInfo.getJSArgType(t.inner) if t.isSequence(): return "JSJitInfo::Object" + if t.isPromise(): + return "JSJitInfo::Object" if t.isGeckoInterface(): return "JSJitInfo::Object" if t.isString(): @@ -9752,6 +9699,10 @@ def getUnionAccessorSignatureType(type, descriptorProvider): # Flat member types have already unwrapped nullables. assert not type.nullable() + # Promise types can never appear in unions, because Promise is not + # distinguishable from anything. + assert not type.isPromise() + if type.isSequence() or type.isRecord(): if type.isSequence(): wrapperType = "Sequence" @@ -13169,9 +13120,9 @@ class CGDictionary(CGThing): # continues to match the list in test_Object.prototype_props.html if (member.identifier.name in ["constructor", "toSource", "toString", "toLocaleString", "valueOf", - "watch", "unwatch", "hasOwnProperty", "isPrototypeOf", - "propertyIsEnumerable", "__defineGetter__", "__defineSetter__", - "__lookupGetter__", "__lookupSetter__", "__proto__"]): + "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable", + "__defineGetter__", "__defineSetter__", "__lookupGetter__", + "__lookupSetter__", "__proto__"]): raise TypeError("'%s' member of %s dictionary shadows " "a property of Object.prototype, and Xrays to " "Object can't handle that.\n" @@ -13660,6 +13611,8 @@ class ForwardDeclarationBuilder: # Note: Spidermonkey interfaces are typedefs, so can't be # forward-declared + elif t.isPromise(): + self.addInMozillaDom("Promise") elif t.isCallback(): self.addInMozillaDom(t.callback.identifier.name) elif t.isDictionary(): @@ -14114,10 +14067,13 @@ class CGNativeMember(ClassMethod): else: defaultValue = "%s(0)" % enumName return enumName, defaultValue, "return ${declName};\n" - if type.isGeckoInterface(): - iface = type.unroll().inner - result = CGGeneric(self.descriptorProvider.getDescriptor( - iface.identifier.name).prettyNativeType) + if type.isGeckoInterface() or type.isPromise(): + if type.isGeckoInterface(): + iface = type.unroll().inner + result = CGGeneric(self.descriptorProvider.getDescriptor( + iface.identifier.name).prettyNativeType) + else: + result = CGGeneric("Promise") if self.resultAlreadyAddRefed: if isMember: holder = "RefPtr" @@ -14320,11 +14276,18 @@ class CGNativeMember(ClassMethod): # auto-wrapping in Nullable return CGUnionStruct.unionTypeDecl(type, isMember), True, False + if type.isPromise(): + assert not type.nullable() + if optional or isMember: + typeDecl = "OwningNonNull<Promise>" + else: + typeDecl = "Promise&" + return (typeDecl, False, False) + if type.isGeckoInterface() and not type.isCallbackInterface(): iface = type.unroll().inner argIsPointer = type.nullable() or iface.isExternal() - forceOwningType = (iface.isCallback() or isMember or - iface.identifier.name == "Promise") + forceOwningType = (iface.isCallback() or isMember) if argIsPointer: if (optional or isMember) and forceOwningType: typeDecl = "RefPtr<%s>" @@ -16958,7 +16921,10 @@ class CGEventGetter(CGNativeMember): def getMethodBody(self): type = self.member.type memberName = CGDictionary.makeMemberName(self.member.identifier.name) - if (type.isPrimitive() and type.tag() in builtinNames) or type.isEnum() or type.isGeckoInterface(): + if ((type.isPrimitive() and type.tag() in builtinNames) or + type.isEnum() or + type.isPromise() or + type.isGeckoInterface()): return "return " + memberName + ";\n" if type.isDOMString() or type.isByteString() or type.isUSVString(): return "aRetVal = " + memberName + ";\n" @@ -17369,6 +17335,8 @@ class CGEventClass(CGBindingImplClass): nativeType = CGGeneric("nsString") elif type.isByteString(): nativeType = CGGeneric("nsCString") + elif type.isPromise(): + nativeType = CGGeneric("RefPtr<Promise>") elif type.isGeckoInterface(): iface = type.unroll().inner nativeType = self.descriptor.getDescriptor( @@ -17393,7 +17361,7 @@ class CGEventClass(CGBindingImplClass): innerType = type.inner if (not innerType.isPrimitive() and not innerType.isEnum() and not innerType.isDOMString() and not innerType.isByteString() and - not innerType.isGeckoInterface()): + not innerType.isPromise() and not innerType.isGeckoInterface()): raise TypeError("Don't know how to properly manage GC/CC for " "event member of type %s" % type) |