зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1185106 - Part 2: Implement ShellPromise. (r=till)
This commit is contained in:
Родитель
bc4f9d9099
Коммит
9963b230dc
|
@ -8,6 +8,8 @@
|
|||
|
||||
#include "jscntxt.h"
|
||||
|
||||
#include "builtin/SelfHostingDefines.h"
|
||||
|
||||
#include "jsobjinlines.h"
|
||||
|
||||
using namespace js;
|
||||
|
@ -26,19 +28,61 @@ static const JSFunctionSpec promise_static_methods[] = {
|
|||
JS_FS_END
|
||||
};
|
||||
|
||||
static bool
|
||||
namespace js {
|
||||
|
||||
bool
|
||||
PromiseConstructor(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
JSObject* obj = NewBuiltinClassInstance(cx, &ShellPromiseObject::class_);
|
||||
if (!obj)
|
||||
if (args.length() == 0) {
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
|
||||
"ShellPromise.constructor", "0", "s");
|
||||
return false;
|
||||
// TODO: store the resolve and reject callbacks.
|
||||
args.rval().setObject(*obj);
|
||||
}
|
||||
|
||||
HandleValue fn = args.get(0);
|
||||
|
||||
if (!IsCallable(fn)) {
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_CALLABLE,
|
||||
"Argument 1 of ShellPromise.constructor");
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject promise(cx, NewBuiltinClassInstance(cx, &ShellPromiseObject::class_));
|
||||
if (!promise)
|
||||
return false;
|
||||
|
||||
JS_SetReservedSlot(promise, PROMISE_STATE_SLOT, NumberValue(PROMISE_STATE_PENDING));
|
||||
JS_SetReservedSlot(promise, PROMISE_VALUE_SLOT, NullValue());
|
||||
JS_SetReservedSlot(promise, PROMISE_DEFERREDS_SLOT, NullValue());
|
||||
|
||||
RootedValue initRval(cx);
|
||||
JS::AutoValueVector argValues(cx);
|
||||
argValues.append(ObjectValue(*promise));
|
||||
argValues.append(fn);
|
||||
HandleValueArray arr(argValues);
|
||||
|
||||
JSAtom* promiseInitAtom;
|
||||
if (!(promiseInitAtom = Atomize(cx, "Promise_init", 12)))
|
||||
return false;
|
||||
|
||||
RootedPropertyName name(cx, promiseInitAtom->asPropertyName());
|
||||
RootedValue selfHostedFun(cx);
|
||||
|
||||
if (!GlobalObject::getIntrinsicValue(cx, cx->global(), name, &selfHostedFun))
|
||||
return false;
|
||||
|
||||
if (!JS_CallFunctionValue(cx, promise, selfHostedFun, arr, &initRval))
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*promise);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static JSObject*
|
||||
CreatePromisePrototype(JSContext* cx, JSProtoKey key)
|
||||
{
|
||||
|
@ -47,7 +91,7 @@ CreatePromisePrototype(JSContext* cx, JSProtoKey key)
|
|||
|
||||
const Class ShellPromiseObject::class_ = {
|
||||
"ShellPromise",
|
||||
JSCLASS_HAS_CACHED_PROTO(JSProto_ShellPromise),
|
||||
JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_ShellPromise),
|
||||
nullptr, /* addProperty */
|
||||
nullptr, /* delProperty */
|
||||
nullptr, /* getProperty */
|
||||
|
|
|
@ -16,10 +16,14 @@ class AutoSetNewObjectMetadata;
|
|||
class ShellPromiseObject : public NativeObject
|
||||
{
|
||||
public:
|
||||
static const unsigned RESERVED_SLOTS = 3;
|
||||
static const Class class_;
|
||||
static const Class protoClass_;
|
||||
};
|
||||
|
||||
bool
|
||||
PromiseConstructor(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif /* builtin_Promise_h */
|
||||
|
|
|
@ -2,26 +2,240 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
function Promise_all(iterable) {
|
||||
global.print("Hi, I'm Promise_all. Please implement me.");
|
||||
/**
|
||||
* This implementation is by no means complete and is using a polyfill.
|
||||
* In particular, it doesn't fully comply to ES6 Promises spec.
|
||||
* The list of incompatibilities may not be complete and includes:
|
||||
*
|
||||
* - no [Symbol.species] implementation
|
||||
* - implementation is not really async at all, but all is executed
|
||||
* in correct order
|
||||
* - Promise.race is not implemented (not necessary for async/await)
|
||||
* - Promise.all implementation currently only handles arrays, no other
|
||||
* iterables.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This polyfill implements the Promise specification in the following way:
|
||||
* At first, Promise is initialized with the intrinsic Promise constructor,
|
||||
* with its initialization completed by calling Promise_init. The goal is to
|
||||
* completely resolve/reject the promise. There are certain helper functions:
|
||||
* - resolveOneStep() executes "one step" of the promise - which means
|
||||
* getting either the final value or next promise towards complete execution.
|
||||
* - resolveCompletely() executes the entire promise, which merely means
|
||||
* resolving one step at a time, until the final step no longer resolves
|
||||
* to a promise (which means either resolving to a value or rejecting).
|
||||
*
|
||||
* Once resolution is finished, resolveCompletely() is called in order to
|
||||
* update state of the promise. It also spawns callbacks that may have been
|
||||
* deferred with a then() - they are NOT normally taken into consideration,
|
||||
* because resolveCompletely() just runs one path.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Below is a simple simulation of an event loop. Events enqueued using
|
||||
* this setTimeout implementation must be manually triggered by calling
|
||||
* runEvents().
|
||||
*/
|
||||
var eventQueue = new List();
|
||||
|
||||
function runEvents() {
|
||||
while (eventQueue.length > 0) {
|
||||
var evt = callFunction(std_Array_pop, eventQueue);
|
||||
evt();
|
||||
}
|
||||
}
|
||||
|
||||
function Promise_race(iterable) {
|
||||
global.print("Hi, I'm Promise_race. Please implement me.");
|
||||
function setTimeout(cb, interval) {
|
||||
if (!IsCallable(cb))
|
||||
ThrowTypeError(JSMSG_NOT_CALLABLE, "Argument 0");
|
||||
if (interval !== 0)
|
||||
ThrowTypeError(JSMSG_SETTIMEOUT_INTERVAL_NONZERO);
|
||||
callFunction(std_Array_push, eventQueue, cb);
|
||||
}
|
||||
|
||||
function Promise_reject(iterable) {
|
||||
global.print("Hi, I'm Promise_reject. Please implement me.");
|
||||
function Handler(onFulfilled, onRejected, resolve, reject) {
|
||||
this.onFulfilled = IsCallable(onFulfilled) ? onFulfilled : null;
|
||||
this.onRejected = IsCallable(onRejected) ? onRejected : null;
|
||||
this.resolve = resolve;
|
||||
this.reject = reject;
|
||||
}
|
||||
|
||||
function Promise_resolve(iterable) {
|
||||
global.print("Hi, I'm Promise_resolve. Please implement me.");
|
||||
MakeConstructible(Handler, std_Object_create(null));
|
||||
|
||||
function Promise_setState(promise, state) {
|
||||
UnsafeSetReservedSlot(promise, PROMISE_STATE_SLOT, state);
|
||||
}
|
||||
|
||||
function Promise_getState(promise) {
|
||||
return UnsafeGetReservedSlot(promise, PROMISE_STATE_SLOT);
|
||||
}
|
||||
|
||||
function Promise_setDeferreds(promise, deferreds) {
|
||||
UnsafeSetReservedSlot(promise, PROMISE_DEFERREDS_SLOT, deferreds);
|
||||
}
|
||||
|
||||
function Promise_getDeferreds(promise) {
|
||||
return UnsafeGetReservedSlot(promise, PROMISE_DEFERREDS_SLOT);
|
||||
}
|
||||
|
||||
function Promise_setValue(promise, value) {
|
||||
UnsafeSetReservedSlot(promise, PROMISE_VALUE_SLOT, value);
|
||||
}
|
||||
|
||||
function Promise_getValue(promise) {
|
||||
return UnsafeGetReservedSlot(promise, PROMISE_VALUE_SLOT);
|
||||
}
|
||||
|
||||
function Promise_init(promise, fn) {
|
||||
Promise_setDeferreds(promise, new List());
|
||||
resolveOneStep(fn,
|
||||
function(value) { resolveCompletely(promise, value); },
|
||||
function(reason) { reject(promise, reason); });
|
||||
}
|
||||
|
||||
function Promise_isThenable(valueOrPromise) {
|
||||
if (valueOrPromise && (typeof valueOrPromise === 'object' || IsCallable(valueOrPromise))) {
|
||||
var then = valueOrPromise.then;
|
||||
if (IsCallable(then))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function Promise_all(promises) {
|
||||
var length = promises.length;
|
||||
var results = [];
|
||||
return NewPromise(function (resolve, reject) {
|
||||
if (length === 0)
|
||||
return resolve([]);
|
||||
var remaining = length;
|
||||
function resolveChain(index, valueOrPromise) {
|
||||
try {
|
||||
if (Promise_isThenable(valueOrPromise)) {
|
||||
callFunction(valueOrPromise.then, valueOrPromise,
|
||||
function (valueOrPromise) { resolveChain(index, valueOrPromise); },
|
||||
reject);
|
||||
} else {
|
||||
_DefineDataProperty(results, index, valueOrPromise);
|
||||
if (--remaining === 0)
|
||||
resolve(results);
|
||||
}
|
||||
} catch (ex) {
|
||||
reject(ex);
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < length; i++)
|
||||
resolveChain(i, promises[i]);
|
||||
});
|
||||
}
|
||||
|
||||
function Promise_race(values) {
|
||||
ThrowTypeError(JSMSG_NOT_IMPLEMENTED, "Promise.race");
|
||||
}
|
||||
|
||||
function Promise_reject(value) {
|
||||
return NewPromise(function (resolve, reject) {
|
||||
reject(value);
|
||||
});
|
||||
}
|
||||
|
||||
function Promise_resolve(value) {
|
||||
if (value && typeof value === 'object' && IsPromise(value))
|
||||
return value;
|
||||
|
||||
return NewPromise(function (resolve) {
|
||||
resolve(value);
|
||||
});
|
||||
}
|
||||
|
||||
function Promise_catch(onRejected) {
|
||||
global.print("Hi, I'm Promise_catch. Please implement me.");
|
||||
return callFunction(Promise_then, this, undefined, onRejected);
|
||||
}
|
||||
|
||||
function asap(cb) {
|
||||
setTimeout(cb, 0);
|
||||
}
|
||||
|
||||
function deferOrExecute(promise, deferred) {
|
||||
if (Promise_getState(promise) === PROMISE_STATE_PENDING) {
|
||||
Promise_getDeferreds(promise).push(deferred);
|
||||
return;
|
||||
}
|
||||
|
||||
asap(function() {
|
||||
var cb = Promise_getState(promise) === PROMISE_STATE_RESOLVED ?
|
||||
deferred.onFulfilled : deferred.onRejected;
|
||||
if (cb === null) {
|
||||
var value = Promise_getValue(promise);
|
||||
(Promise_getState(promise) === PROMISE_STATE_RESOLVED ? deferred.resolve : deferred.reject)(value);
|
||||
return;
|
||||
}
|
||||
var returnValue;
|
||||
try {
|
||||
returnValue = cb(Promise_getValue(promise));
|
||||
} catch (e) {
|
||||
deferred.reject(e);
|
||||
return;
|
||||
}
|
||||
deferred.resolve(returnValue);
|
||||
});
|
||||
}
|
||||
|
||||
function resolveOneStep(fn, onFulfilled, onRejected) {
|
||||
var done = false;
|
||||
var callOnce = function(cb) {
|
||||
return function(value) {
|
||||
if (done) return;
|
||||
done = true;
|
||||
cb(value);
|
||||
};
|
||||
};
|
||||
|
||||
try {
|
||||
fn(callOnce(onFulfilled), callOnce(onRejected));
|
||||
} catch (ex) {
|
||||
callOnce(onRejected)(ex);
|
||||
}
|
||||
}
|
||||
|
||||
function resolveCompletely(promise, valueOrPromise) {
|
||||
try {
|
||||
// FIXME this is probably not a type error
|
||||
if (valueOrPromise === promise)
|
||||
ThrowTypeError(JSMSG_PROMISE_RESOLVED_WITH_ITSELF);
|
||||
|
||||
if (Promise_isThenable(valueOrPromise)) {
|
||||
resolveOneStep(function(resolve, reject) { valueOrPromise.then(resolve, reject); },
|
||||
function(value) { resolveCompletely(promise, value); },
|
||||
function(value) { reject(promise, value); });
|
||||
}
|
||||
else
|
||||
callFunction(resolvingFinished, promise, PROMISE_STATE_RESOLVED, valueOrPromise);
|
||||
} catch (ex) {
|
||||
callFunction(reject, promise, ex);
|
||||
}
|
||||
}
|
||||
|
||||
function reject(promise, reason) {
|
||||
callFunction(resolvingFinished, promise, PROMISE_STATE_REJECTED, reason);
|
||||
}
|
||||
|
||||
function resolvingFinished(state, newValue) {
|
||||
Promise_setState(this, state);
|
||||
Promise_setValue(this, newValue);
|
||||
var deferreds = Promise_getDeferreds(this);
|
||||
for (var i = 0, len = deferreds.length; i < len; i++)
|
||||
deferOrExecute(this, deferreds[i]);
|
||||
Promise_setDeferreds(this, null);
|
||||
}
|
||||
|
||||
function Promise_then(onFulfilled, onRejected) {
|
||||
global.print("Hi, I'm Promise_then. Please implement me.");
|
||||
var promise = this;
|
||||
var newPromise = NewPromise(function(resolve, reject) {
|
||||
deferOrExecute(promise,
|
||||
new Handler(onFulfilled, onRejected, resolve, reject));
|
||||
});
|
||||
runEvents();
|
||||
return newPromise;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,15 @@
|
|||
// Stores the private WeakMap slot used for WeakSets
|
||||
#define WEAKSET_MAP_SLOT 0
|
||||
|
||||
// Slots for use with promises implementation.
|
||||
#define PROMISE_STATE_SLOT 0
|
||||
#define PROMISE_VALUE_SLOT 1
|
||||
#define PROMISE_DEFERREDS_SLOT 2
|
||||
|
||||
#define PROMISE_STATE_PENDING 0
|
||||
#define PROMISE_STATE_RESOLVED 1
|
||||
#define PROMISE_STATE_REJECTED 2
|
||||
|
||||
#define ITERATOR_SLOT_TARGET 0
|
||||
// Used for collection iterators.
|
||||
#define ITERATOR_SLOT_RANGE 1
|
||||
|
|
|
@ -106,6 +106,7 @@ MSG_DEF(JSMSG_TERMINATED, 1, JSEXN_ERR, "Script terminated by timeo
|
|||
MSG_DEF(JSMSG_PROTO_NOT_OBJORNULL, 1, JSEXN_TYPEERR, "{0}.prototype is not an object or null")
|
||||
MSG_DEF(JSMSG_CANT_CALL_CLASS_CONSTRUCTOR, 0, JSEXN_TYPEERR, "class constructors must be invoked with |new|")
|
||||
MSG_DEF(JSMSG_DISABLED_DERIVED_CLASS, 1, JSEXN_INTERNALERR, "{0} temporarily disallowed in derived class constructors")
|
||||
MSG_DEF(JSMSG_NOT_CALLABLE, 1, JSEXN_TYPEERR, "{0} is not callable")
|
||||
|
||||
// JSON
|
||||
MSG_DEF(JSMSG_JSON_BAD_PARSE, 3, JSEXN_SYNTAXERR, "JSON.parse: {0} at line {1} column {2} of the JSON data")
|
||||
|
@ -508,6 +509,10 @@ MSG_DEF(JSMSG_NO_INDEXED_SETTER, 2, JSEXN_TYPEERR, "{0} doesn't have an
|
|||
// Super
|
||||
MSG_DEF(JSMSG_CANT_DELETE_SUPER, 0, JSEXN_REFERENCEERR, "invalid delete involving 'super'")
|
||||
|
||||
// Promise
|
||||
MSG_DEF(JSMSG_PROMISE_RESOLVED_WITH_ITSELF, 0, JSEXN_TYPEERR, "A promise cannot be resolved with itself")
|
||||
MSG_DEF(JSMSG_SETTIMEOUT_INTERVAL_NONZERO, 0, JSEXN_TYPEERR, "Intervals other than 0 are not supported")
|
||||
|
||||
// Modules
|
||||
MSG_DEF(JSMSG_BAD_DEFAULT_EXPORT, 0, JSEXN_SYNTAXERR, "default export cannot be provided by export *")
|
||||
MSG_DEF(JSMSG_MISSING_INDIRECT_EXPORT, 0, JSEXN_SYNTAXERR, "indirect export not found")
|
||||
|
|
|
@ -1537,6 +1537,68 @@ PrintErr(JSContext* cx, unsigned argc, Value* vp)
|
|||
return PrintInternal(cx, args, gErrFile);
|
||||
}
|
||||
|
||||
static bool
|
||||
SetTimeout(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (args.length() < 2) {
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
|
||||
"setTimeout", args.length() == 0 ? "0" : "1", "s");
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedValue rval(cx);
|
||||
JS::AutoValueVector argValues(cx);
|
||||
argValues.append(args.get(0));
|
||||
argValues.append(args.get(1));
|
||||
HandleValueArray arr(argValues);
|
||||
|
||||
JSAtom* setTimeoutAtom;
|
||||
if (!(setTimeoutAtom = Atomize(cx, "setTimeout", 10)))
|
||||
return false;
|
||||
|
||||
RootedPropertyName name(cx, setTimeoutAtom->asPropertyName());
|
||||
RootedValue selfHostedFun(cx);
|
||||
|
||||
if (!GlobalObject::getIntrinsicValue(cx, cx->global(), name, &selfHostedFun))
|
||||
return false;
|
||||
|
||||
RootedObject undef(cx);
|
||||
if (!JS_CallFunctionValue(cx, undef, selfHostedFun, arr, &rval))
|
||||
return false;
|
||||
|
||||
args.rval().set(rval);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
RunEvents(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
RootedValue rval(cx);
|
||||
JS::AutoValueVector argValues(cx);
|
||||
HandleValueArray arr(argValues);
|
||||
|
||||
JSAtom* runEvents;
|
||||
if (!(runEvents = Atomize(cx, "runEvents", 9)))
|
||||
return false;
|
||||
|
||||
RootedPropertyName name(cx, runEvents->asPropertyName());
|
||||
RootedValue selfHostedFun(cx);
|
||||
|
||||
if (!GlobalObject::getIntrinsicValue(cx, cx->global(), name, &selfHostedFun))
|
||||
return false;
|
||||
|
||||
RootedObject undef(cx);
|
||||
if (!JS_CallFunctionValue(cx, undef, selfHostedFun, arr, &rval))
|
||||
return false;
|
||||
|
||||
args.rval().set(rval);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
Help(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
|
@ -4598,6 +4660,15 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
|
|||
"dateNow()",
|
||||
" Return the current time with sub-ms precision."),
|
||||
|
||||
JS_FN_HELP("setTimeout", SetTimeout, 2, 2,
|
||||
"setTimeout(fn, timeout)",
|
||||
" Execute a function after a specified timeout. Currently only 0 is supported."),
|
||||
|
||||
JS_FN_HELP("runEvents", RunEvents, 2, 2,
|
||||
"runEvents()",
|
||||
" Run events that were scheduled using setTimeout() calls.\n"
|
||||
" This call is required, because there is no real event loop."),
|
||||
|
||||
JS_FN_HELP("help", Help, 0, 0,
|
||||
"help([name ...])",
|
||||
" Display usage and help messages."),
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* Because there is no way to "wait" for a callback in this testing system,
|
||||
* there was a need to make one big promise out of all test cases.
|
||||
*/
|
||||
|
||||
var Promise = ShellPromise;
|
||||
|
||||
Promise.all([
|
||||
|
||||
assertEventuallyEq(new Promise(resolve => resolve(2)), 2),
|
||||
|
||||
assertEventuallyThrows(new Promise((_, reject) => reject(new Error())), Error),
|
||||
|
||||
assertEventuallyThrows(new Promise(() => { throw new Error(); }), Error),
|
||||
|
||||
assertEventuallyEq(new Promise(resolve => resolve())
|
||||
.then(() => 3), 3),
|
||||
|
||||
assertEventuallyEq(new Promise(resolve => resolve())
|
||||
.then(() => new Promise(r => r(3))), 3),
|
||||
|
||||
assertEventuallyEq(new Promise((_, reject) => reject(new Error()))
|
||||
.catch(() => new Promise(r => r(3))), 3),
|
||||
|
||||
assertEventuallyThrows(new Promise(resolve => resolve())
|
||||
.then(() => { throw new Error(); }), Error),
|
||||
|
||||
assertEventuallyEq(new Promise((_, reject) => reject(new Error()))
|
||||
.catch(() => 4), 4),
|
||||
|
||||
assertEventuallyEq(Promise.resolve(5), 5),
|
||||
|
||||
assertEventuallyThrows(Promise.reject(new Error()), Error),
|
||||
|
||||
assertEventuallyDeepEq(Promise.all([]), []),
|
||||
|
||||
assertEventuallyDeepEq(Promise.all(Array(10).fill()
|
||||
.map((_, id) => new Promise(resolve => resolve(id)))),
|
||||
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
|
||||
|
||||
])
|
||||
.then(() => {
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
var Promise = ShellPromise;
|
||||
|
||||
var oldThen = Promise.prototype.then;
|
||||
|
||||
// Changing then() should not break catch()
|
||||
Promise.prototype.then = function() { throw new Error(); };
|
||||
|
||||
new Promise(a => { throw new Error(); })
|
||||
.catch(() => {
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* These functions are inspired by chai-as-promised library for promise testing
|
||||
* in JS applications. They check if promises eventually resolve to a given value
|
||||
* or are rejected with a specified error type.
|
||||
*/
|
||||
|
||||
if (typeof assertEventuallyEq === 'undefined') {
|
||||
assertEventuallyEq = function(promise, expected) {
|
||||
return promise.then(actual => assertEq(actual, expected));
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof assertEventuallyThrows === 'undefined') {
|
||||
assertEventuallyThrows = function(promise, expectedErrorType) {
|
||||
return promise.catch(actualE => assertEq(actualE instanceof expectedErrorType, true));
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof assertEventuallyDeepEq === 'undefined') {
|
||||
assertEventuallyDeepEq = function(promise, expected) {
|
||||
return promise.then(actual => assertDeepEq(actual, expected));
|
||||
};
|
||||
}
|
|
@ -23,6 +23,7 @@
|
|||
#include "builtin/MapObject.h"
|
||||
#include "builtin/ModuleObject.h"
|
||||
#include "builtin/Object.h"
|
||||
#include "builtin/Promise.h"
|
||||
#include "builtin/Reflect.h"
|
||||
#include "builtin/SelfHostingDefines.h"
|
||||
#include "builtin/SIMD.h"
|
||||
|
@ -439,6 +440,54 @@ intrinsic_GetIteratorPrototype(JSContext* cx, unsigned argc, Value* vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
intrinsic_NewPromise(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
return js::PromiseConstructor(cx, argc, vp);
|
||||
}
|
||||
|
||||
static bool
|
||||
intrinsic_IsPromise(JSContext* cx, unsigned argc, Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 1);
|
||||
MOZ_ASSERT(args[0].isObject());
|
||||
bool isPromise = args[0].toObject().getClass() == &ShellPromiseObject::class_;
|
||||
|
||||
args.rval().setBoolean(isPromise);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
intrinsic_SetFunName(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 2);
|
||||
MOZ_ASSERT(args[0].isObject() && args[0].toObject().is<JSFunction>());
|
||||
MOZ_ASSERT(args[1].isString());
|
||||
JSAtom* atom = AtomizeString(cx, args[1].toString());
|
||||
if (atom == nullptr)
|
||||
return false;
|
||||
RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
|
||||
fun->setFlags(fun->flags() & ~JSFunction::HAS_GUESSED_ATOM);
|
||||
fun->initAtom(atom);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
intrinsic_GetFunName(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 1);
|
||||
MOZ_ASSERT(args[0].isObject() && args[0].toObject().is<JSFunction>());
|
||||
PropertyName* name = args[0].toObject().as<JSFunction>().name();
|
||||
if (!name)
|
||||
args.rval().setUndefined();
|
||||
else
|
||||
args.rval().setString(name);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
intrinsic_NewArrayIterator(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
|
@ -1192,6 +1241,20 @@ intrinsic_IsWeakSet(JSContext* cx, unsigned argc, Value* vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
intrinsic_SetFunctionExtendedSlot(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
MOZ_ASSERT(args.length() == 3);
|
||||
MOZ_ASSERT(args[0].isObject());
|
||||
MOZ_ASSERT(args[0].toObject().is<JSFunction>());
|
||||
MOZ_ASSERT(args[1].isInt32());
|
||||
|
||||
args[0].toObject().as<JSFunction>().setExtendedSlot(args[1].toPrivateUint32(), args[2]);
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default locale as a well-formed, but not necessarily canonicalized,
|
||||
* BCP-47 language tag.
|
||||
|
@ -1431,6 +1494,7 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
|||
JS_FN("_ConstructorForTypedArray", intrinsic_ConstructorForTypedArray, 1,0),
|
||||
JS_FN("DecompileArg", intrinsic_DecompileArg, 2,0),
|
||||
JS_FN("RuntimeDefaultLocale", intrinsic_RuntimeDefaultLocale, 0,0),
|
||||
JS_FN("SetFunctionExtendedSlot", intrinsic_SetFunctionExtendedSlot, 3,0),
|
||||
JS_FN("LocalTZA", intrinsic_LocalTZA, 0,0),
|
||||
|
||||
JS_INLINABLE_FN("_IsConstructing", intrinsic_IsConstructing, 0,0,
|
||||
|
@ -1518,7 +1582,11 @@ static const JSFunctionSpec intrinsic_functions[] = {
|
|||
CallNonGenericSelfhostedMethod<Is<StarGeneratorObject>>, 2, 0),
|
||||
|
||||
JS_FN("IsWeakSet", intrinsic_IsWeakSet, 1,0),
|
||||
JS_FN("NewPromise", intrinsic_NewPromise, 1,0),
|
||||
JS_FN("IsPromise", intrinsic_IsPromise, 1,0),
|
||||
|
||||
JS_FN("SetFunName", intrinsic_SetFunName, 2,0),
|
||||
JS_FN("GetFunName", intrinsic_GetFunName, 1,0),
|
||||
// See builtin/TypedObject.h for descriptors of the typedobj functions.
|
||||
JS_FN("NewOpaqueTypedObject", js::NewOpaqueTypedObject, 1, 0),
|
||||
JS_FN("NewDerivedTypedObject", js::NewDerivedTypedObject, 3, 0),
|
||||
|
|
|
@ -29,11 +29,11 @@ namespace js {
|
|||
*
|
||||
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
|
||||
*/
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 310;
|
||||
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 311;
|
||||
static const uint32_t XDR_BYTECODE_VERSION =
|
||||
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
|
||||
|
||||
static_assert(JSErr_Limit == 415,
|
||||
static_assert(JSErr_Limit == 418,
|
||||
"GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
|
||||
"removed MSG_DEFs from js.msg, you should increment "
|
||||
"XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
|
||||
|
|
Загрузка…
Ссылка в новой задаче