Bug 1318017: Call GetPrototypeFromConstructor for generator/async function and Intl constructors

[Depends on] Bug 755821: Function() should use the parser's argument
parsing code
This commit is contained in:
janekptacijarabaci 2018-03-16 09:04:55 +01:00 committed by Roy Tam
commit e7fdd26fde
11 changed files with 586 additions and 53 deletions

View file

@ -765,42 +765,53 @@ static const JSFunctionSpec collator_methods[] = {
};
/**
* Collator constructor.
* Spec: ECMAScript Internationalization API Specification, 10.1
* 10.1.2 Intl.Collator([ locales [, options]])
*
* ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
*/
static bool
Collator(JSContext* cx, const CallArgs& args, bool construct)
{
RootedObject obj(cx);
// We're following ECMA-402 1st Edition when Collator is called because of
// backward compatibility issues.
// See https://github.com/tc39/ecma402/issues/57
if (!construct) {
// 10.1.2.1 step 3
// ES Intl 1st ed., 10.1.2.1 step 3
JSObject* intl = cx->global()->getOrCreateIntlObject(cx);
if (!intl)
return false;
RootedValue self(cx, args.thisv());
if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) {
// 10.1.2.1 step 4
// ES Intl 1st ed., 10.1.2.1 step 4
obj = ToObject(cx, self);
if (!obj)
return false;
// 10.1.2.1 step 5
// ES Intl 1st ed., 10.1.2.1 step 5
bool extensible;
if (!IsExtensible(cx, obj, &extensible))
return false;
if (!extensible)
return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE);
} else {
// 10.1.2.1 step 3.a
// ES Intl 1st ed., 10.1.2.1 step 3.a
construct = true;
}
}
if (construct) {
// 10.1.3.1 paragraph 2
RootedObject proto(cx, cx->global()->getOrCreateCollatorPrototype(cx));
if (!proto)
// Steps 2-5 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
RootedObject proto(cx);
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
return false;
if (!proto) {
proto = cx->global()->getOrCreateCollatorPrototype(cx);
if (!proto)
return false;
}
obj = NewObjectWithGivenProto(cx, &CollatorClass, proto);
if (!obj)
return false;
@ -808,15 +819,13 @@ Collator(JSContext* cx, const CallArgs& args, bool construct)
obj->as<NativeObject>().setReservedSlot(UCOLLATOR_SLOT, PrivateValue(nullptr));
}
// 10.1.2.1 steps 1 and 2; 10.1.3.1 steps 1 and 2
RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue());
RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue());
// 10.1.2.1 step 6; 10.1.3.1 step 3
// Step 6.
if (!IntlInitialize(cx, obj, cx->names().InitializeCollator, locales, options))
return false;
// 10.1.2.1 steps 3.a and 7
args.rval().setObject(*obj);
return true;
}
@ -833,6 +842,7 @@ js::intl_Collator(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
MOZ_ASSERT(!args.isConstructing());
// intl_Collator is an intrinsic for self-hosted JavaScript, so it cannot
// be used with "new", but it still has to be treated as a constructor.
return Collator(cx, args, true);
@ -1257,42 +1267,53 @@ static const JSFunctionSpec numberFormat_methods[] = {
};
/**
* NumberFormat constructor.
* Spec: ECMAScript Internationalization API Specification, 11.1
* 11.2.1 Intl.NumberFormat([ locales [, options]])
*
* ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
*/
static bool
NumberFormat(JSContext* cx, const CallArgs& args, bool construct)
{
RootedObject obj(cx);
// We're following ECMA-402 1st Edition when NumberFormat is called
// because of backward compatibility issues.
// See https://github.com/tc39/ecma402/issues/57
if (!construct) {
// 11.1.2.1 step 3
// ES Intl 1st ed., 11.1.2.1 step 3
JSObject* intl = cx->global()->getOrCreateIntlObject(cx);
if (!intl)
return false;
RootedValue self(cx, args.thisv());
if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) {
// 11.1.2.1 step 4
// ES Intl 1st ed., 11.1.2.1 step 4
obj = ToObject(cx, self);
if (!obj)
return false;
// 11.1.2.1 step 5
// ES Intl 1st ed., 11.1.2.1 step 5
bool extensible;
if (!IsExtensible(cx, obj, &extensible))
return false;
if (!extensible)
return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE);
} else {
// 11.1.2.1 step 3.a
// ES Intl 1st ed., 11.1.2.1 step 3.a
construct = true;
}
}
if (construct) {
// 11.1.3.1 paragraph 2
RootedObject proto(cx, cx->global()->getOrCreateNumberFormatPrototype(cx));
if (!proto)
// Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
RootedObject proto(cx);
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
return false;
if (!proto) {
proto = cx->global()->getOrCreateNumberFormatPrototype(cx);
if (!proto)
return false;
}
obj = NewObjectWithGivenProto(cx, &NumberFormatClass, proto);
if (!obj)
return false;
@ -1300,15 +1321,13 @@ NumberFormat(JSContext* cx, const CallArgs& args, bool construct)
obj->as<NativeObject>().setReservedSlot(UNUMBER_FORMAT_SLOT, PrivateValue(nullptr));
}
// 11.1.2.1 steps 1 and 2; 11.1.3.1 steps 1 and 2
RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue());
RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue());
// 11.1.2.1 step 6; 11.1.3.1 step 3
// Step 3.
if (!IntlInitialize(cx, obj, cx->names().InitializeNumberFormat, locales, options))
return false;
// 11.1.2.1 steps 3.a and 7
args.rval().setObject(*obj);
return true;
}
@ -1325,6 +1344,7 @@ js::intl_NumberFormat(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
MOZ_ASSERT(!args.isConstructing());
// intl_NumberFormat is an intrinsic for self-hosted JavaScript, so it
// cannot be used with "new", but it still has to be treated as a
// constructor.
@ -1725,42 +1745,53 @@ static const JSFunctionSpec dateTimeFormat_methods[] = {
};
/**
* DateTimeFormat constructor.
* Spec: ECMAScript Internationalization API Specification, 12.1
* 12.2.1 Intl.DateTimeFormat([ locales [, options]])
*
* ES2017 Intl draft rev 94045d234762ad107a3d09bb6f7381a65f1a2f9b
*/
static bool
DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct)
{
RootedObject obj(cx);
// We're following ECMA-402 1st Edition when DateTimeFormat is called
// because of backward compatibility issues.
// See https://github.com/tc39/ecma402/issues/57
if (!construct) {
// 12.1.2.1 step 3
// ES Intl 1st ed., 12.1.2.1 step 3
JSObject* intl = cx->global()->getOrCreateIntlObject(cx);
if (!intl)
return false;
RootedValue self(cx, args.thisv());
if (!self.isUndefined() && (!self.isObject() || self.toObject() != *intl)) {
// 12.1.2.1 step 4
// ES Intl 1st ed., 12.1.2.1 step 4
obj = ToObject(cx, self);
if (!obj)
return false;
// 12.1.2.1 step 5
// ES Intl 1st ed., 12.1.2.1 step 5
bool extensible;
if (!IsExtensible(cx, obj, &extensible))
return false;
if (!extensible)
return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE);
} else {
// 12.1.2.1 step 3.a
// ES Intl 1st ed., 12.1.2.1 step 3.a
construct = true;
}
}
if (construct) {
// 12.1.3.1 paragraph 2
RootedObject proto(cx, cx->global()->getOrCreateDateTimeFormatPrototype(cx));
if (!proto)
// Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
RootedObject proto(cx);
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
return false;
if (!proto) {
proto = cx->global()->getOrCreateDateTimeFormatPrototype(cx);
if (!proto)
return false;
}
obj = NewObjectWithGivenProto(cx, &DateTimeFormatClass, proto);
if (!obj)
return false;
@ -1768,15 +1799,13 @@ DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct)
obj->as<NativeObject>().setReservedSlot(UDATE_FORMAT_SLOT, PrivateValue(nullptr));
}
// 12.1.2.1 steps 1 and 2; 12.1.3.1 steps 1 and 2
RootedValue locales(cx, args.length() > 0 ? args[0] : UndefinedValue());
RootedValue options(cx, args.length() > 1 ? args[1] : UndefinedValue());
// 12.1.2.1 step 6; 12.1.3.1 step 3
// Step 3.
if (!IntlInitialize(cx, obj, cx->names().InitializeDateTimeFormat, locales, options))
return false;
// 12.1.2.1 steps 3.a and 7
args.rval().setObject(*obj);
return true;
}
@ -1793,6 +1822,7 @@ js::intl_DateTimeFormat(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
MOZ_ASSERT(args.length() == 2);
MOZ_ASSERT(!args.isConstructing());
// intl_DateTimeFormat is an intrinsic for self-hosted JavaScript, so it
// cannot be used with "new", but it still has to be treated as a
// constructor.

View file

@ -1682,11 +1682,9 @@ const JSFunctionSpec js::function_methods[] = {
};
static bool
FunctionConstructor(JSContext* cx, unsigned argc, Value* vp, GeneratorKind generatorKind,
FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generatorKind,
FunctionAsyncKind asyncKind)
{
CallArgs args = CallArgsFromVp(argc, vp);
/* Block this call if security callbacks forbid it. */
Rooted<GlobalObject*> global(cx, &args.callee().global());
if (!GlobalObject::isRuntimeCodeGenEnabled(cx, global)) {
@ -1803,16 +1801,23 @@ FunctionConstructor(JSContext* cx, unsigned argc, Value* vp, GeneratorKind gener
* and so would a call to f from another top-level's script or function.
*/
RootedAtom anonymousAtom(cx, cx->names().anonymous);
// ES2017, draft rev 0f10dba4ad18de92d47d421f378233a2eae8f077
// 19.2.1.1.1 Runtime Semantics: CreateDynamicFunction, step 24.
RootedObject proto(cx);
if (isStarGenerator) {
if (!isAsync) {
if (!GetPrototypeFromCallableConstructor(cx, args, &proto))
return false;
}
// 19.2.1.1.1, step 4.d, use %Generator% as the fallback prototype.
// Also use %Generator% for the unwrapped function of async functions.
if (!proto && isStarGenerator) {
// Unwrapped function of async function should use GeneratorFunction,
// while wrapped function isn't generator.
proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, global);
if (!proto)
return false;
} else {
if (!GetPrototypeFromCallableConstructor(cx, args, &proto))
return false;
}
RootedObject globalLexical(cx, &global->lexicalEnvironment());
@ -1921,24 +1926,47 @@ FunctionConstructor(JSContext* cx, unsigned argc, Value* vp, GeneratorKind gener
bool
js::Function(JSContext* cx, unsigned argc, Value* vp)
{
return FunctionConstructor(cx, argc, vp, NotGenerator, SyncFunction);
CallArgs args = CallArgsFromVp(argc, vp);
return FunctionConstructor(cx, args, NotGenerator, SyncFunction);
}
bool
js::Generator(JSContext* cx, unsigned argc, Value* vp)
{
return FunctionConstructor(cx, argc, vp, StarGenerator, SyncFunction);
CallArgs args = CallArgsFromVp(argc, vp);
return FunctionConstructor(cx, args, StarGenerator, SyncFunction);
}
bool
js::AsyncFunctionConstructor(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (!FunctionConstructor(cx, argc, vp, StarGenerator, AsyncFunction))
// Save the callee before its reset in FunctionConstructor().
RootedObject newTarget(cx);
if (args.isConstructing())
newTarget = &args.newTarget().toObject();
else
newTarget = &args.callee();
if (!FunctionConstructor(cx, args, StarGenerator, AsyncFunction))
return false;
// ES2017, draft rev 0f10dba4ad18de92d47d421f378233a2eae8f077
// 19.2.1.1.1 Runtime Semantics: CreateDynamicFunction, step 24.
RootedObject proto(cx);
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
return false;
// 19.2.1.1.1, step 4.d, use %AsyncFunctionPrototype% as the fallback.
if (!proto) {
proto = GlobalObject::getOrCreateAsyncFunctionPrototype(cx, cx->global());
if (!proto)
return false;
}
RootedFunction unwrapped(cx, &args.rval().toObject().as<JSFunction>());
RootedObject wrapped(cx, WrapAsyncFunction(cx, unwrapped));
RootedObject wrapped(cx, WrapAsyncFunctionWithProto(cx, unwrapped, proto));
if (!wrapped)
return false;

View file

@ -0,0 +1,81 @@
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
// Test subclassing %Intl.Collator% works correctly.
class MyCollator extends Intl.Collator {}
var obj = new MyCollator();
assertEq(obj instanceof MyCollator, true);
assertEq(obj instanceof Intl.Collator, true);
assertEq(Object.getPrototypeOf(obj), MyCollator.prototype);
obj = Reflect.construct(MyCollator, []);
assertEq(obj instanceof MyCollator, true);
assertEq(obj instanceof Intl.Collator, true);
assertEq(Object.getPrototypeOf(obj), MyCollator.prototype);
obj = Reflect.construct(MyCollator, [], MyCollator);
assertEq(obj instanceof MyCollator, true);
assertEq(obj instanceof Intl.Collator, true);
assertEq(Object.getPrototypeOf(obj), MyCollator.prototype);
obj = Reflect.construct(MyCollator, [], Intl.Collator);
assertEq(obj instanceof MyCollator, false);
assertEq(obj instanceof Intl.Collator, true);
assertEq(Object.getPrototypeOf(obj), Intl.Collator.prototype);
// Set a different constructor as NewTarget.
obj = Reflect.construct(MyCollator, [], Array);
assertEq(obj instanceof MyCollator, false);
assertEq(obj instanceof Intl.Collator, false);
assertEq(obj instanceof Array, true);
assertEq(Object.getPrototypeOf(obj), Array.prototype);
obj = Reflect.construct(Intl.Collator, [], Array);
assertEq(obj instanceof Intl.Collator, false);
assertEq(obj instanceof Array, true);
assertEq(Object.getPrototypeOf(obj), Array.prototype);
// The prototype defaults to %CollatorPrototype% if null.
function NewTargetNullPrototype() {}
NewTargetNullPrototype.prototype = null;
obj = Reflect.construct(Intl.Collator, [], NewTargetNullPrototype);
assertEq(obj instanceof Intl.Collator, true);
assertEq(Object.getPrototypeOf(obj), Intl.Collator.prototype);
obj = Reflect.construct(MyCollator, [], NewTargetNullPrototype);
assertEq(obj instanceof MyCollator, false);
assertEq(obj instanceof Intl.Collator, true);
assertEq(Object.getPrototypeOf(obj), Intl.Collator.prototype);
// "prototype" property is retrieved exactly once.
var trapLog = [], getLog = [];
var ProxiedConstructor = new Proxy(Intl.Collator, new Proxy({
get(target, propertyKey, receiver) {
getLog.push(propertyKey);
return Reflect.get(target, propertyKey, receiver);
}
}, {
get(target, propertyKey, receiver) {
trapLog.push(propertyKey);
return Reflect.get(target, propertyKey, receiver);
}
}));
obj = Reflect.construct(Intl.Collator, [], ProxiedConstructor);
assertEqArray(trapLog, ["get"]);
assertEqArray(getLog, ["prototype"]);
assertEq(obj instanceof Intl.Collator, true);
assertEq(Object.getPrototypeOf(obj), Intl.Collator.prototype);
if (typeof reportCompare === "function")
reportCompare(0, 0);

View file

@ -0,0 +1,81 @@
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
// Test subclassing %Intl.DateTimeFormat% works correctly.
class MyDateTimeFormat extends Intl.DateTimeFormat {}
var obj = new MyDateTimeFormat();
assertEq(obj instanceof MyDateTimeFormat, true);
assertEq(obj instanceof Intl.DateTimeFormat, true);
assertEq(Object.getPrototypeOf(obj), MyDateTimeFormat.prototype);
obj = Reflect.construct(MyDateTimeFormat, []);
assertEq(obj instanceof MyDateTimeFormat, true);
assertEq(obj instanceof Intl.DateTimeFormat, true);
assertEq(Object.getPrototypeOf(obj), MyDateTimeFormat.prototype);
obj = Reflect.construct(MyDateTimeFormat, [], MyDateTimeFormat);
assertEq(obj instanceof MyDateTimeFormat, true);
assertEq(obj instanceof Intl.DateTimeFormat, true);
assertEq(Object.getPrototypeOf(obj), MyDateTimeFormat.prototype);
obj = Reflect.construct(MyDateTimeFormat, [], Intl.DateTimeFormat);
assertEq(obj instanceof MyDateTimeFormat, false);
assertEq(obj instanceof Intl.DateTimeFormat, true);
assertEq(Object.getPrototypeOf(obj), Intl.DateTimeFormat.prototype);
// Set a different constructor as NewTarget.
obj = Reflect.construct(MyDateTimeFormat, [], Array);
assertEq(obj instanceof MyDateTimeFormat, false);
assertEq(obj instanceof Intl.DateTimeFormat, false);
assertEq(obj instanceof Array, true);
assertEq(Object.getPrototypeOf(obj), Array.prototype);
obj = Reflect.construct(Intl.DateTimeFormat, [], Array);
assertEq(obj instanceof Intl.DateTimeFormat, false);
assertEq(obj instanceof Array, true);
assertEq(Object.getPrototypeOf(obj), Array.prototype);
// The prototype defaults to %DateTimeFormatPrototype% if null.
function NewTargetNullPrototype() {}
NewTargetNullPrototype.prototype = null;
obj = Reflect.construct(Intl.DateTimeFormat, [], NewTargetNullPrototype);
assertEq(obj instanceof Intl.DateTimeFormat, true);
assertEq(Object.getPrototypeOf(obj), Intl.DateTimeFormat.prototype);
obj = Reflect.construct(MyDateTimeFormat, [], NewTargetNullPrototype);
assertEq(obj instanceof MyDateTimeFormat, false);
assertEq(obj instanceof Intl.DateTimeFormat, true);
assertEq(Object.getPrototypeOf(obj), Intl.DateTimeFormat.prototype);
// "prototype" property is retrieved exactly once.
var trapLog = [], getLog = [];
var ProxiedConstructor = new Proxy(Intl.DateTimeFormat, new Proxy({
get(target, propertyKey, receiver) {
getLog.push(propertyKey);
return Reflect.get(target, propertyKey, receiver);
}
}, {
get(target, propertyKey, receiver) {
trapLog.push(propertyKey);
return Reflect.get(target, propertyKey, receiver);
}
}));
obj = Reflect.construct(Intl.DateTimeFormat, [], ProxiedConstructor);
assertEqArray(trapLog, ["get"]);
assertEqArray(getLog, ["prototype"]);
assertEq(obj instanceof Intl.DateTimeFormat, true);
assertEq(Object.getPrototypeOf(obj), Intl.DateTimeFormat.prototype);
if (typeof reportCompare === "function")
reportCompare(0, 0);

View file

@ -0,0 +1,81 @@
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
// Test subclassing %Intl.NumberFormat% works correctly.
class MyNumberFormat extends Intl.NumberFormat {}
var obj = new MyNumberFormat();
assertEq(obj instanceof MyNumberFormat, true);
assertEq(obj instanceof Intl.NumberFormat, true);
assertEq(Object.getPrototypeOf(obj), MyNumberFormat.prototype);
obj = Reflect.construct(MyNumberFormat, []);
assertEq(obj instanceof MyNumberFormat, true);
assertEq(obj instanceof Intl.NumberFormat, true);
assertEq(Object.getPrototypeOf(obj), MyNumberFormat.prototype);
obj = Reflect.construct(MyNumberFormat, [], MyNumberFormat);
assertEq(obj instanceof MyNumberFormat, true);
assertEq(obj instanceof Intl.NumberFormat, true);
assertEq(Object.getPrototypeOf(obj), MyNumberFormat.prototype);
obj = Reflect.construct(MyNumberFormat, [], Intl.NumberFormat);
assertEq(obj instanceof MyNumberFormat, false);
assertEq(obj instanceof Intl.NumberFormat, true);
assertEq(Object.getPrototypeOf(obj), Intl.NumberFormat.prototype);
// Set a different constructor as NewTarget.
obj = Reflect.construct(MyNumberFormat, [], Array);
assertEq(obj instanceof MyNumberFormat, false);
assertEq(obj instanceof Intl.NumberFormat, false);
assertEq(obj instanceof Array, true);
assertEq(Object.getPrototypeOf(obj), Array.prototype);
obj = Reflect.construct(Intl.NumberFormat, [], Array);
assertEq(obj instanceof Intl.NumberFormat, false);
assertEq(obj instanceof Array, true);
assertEq(Object.getPrototypeOf(obj), Array.prototype);
// The prototype defaults to %NumberFormatPrototype% if null.
function NewTargetNullPrototype() {}
NewTargetNullPrototype.prototype = null;
obj = Reflect.construct(Intl.NumberFormat, [], NewTargetNullPrototype);
assertEq(obj instanceof Intl.NumberFormat, true);
assertEq(Object.getPrototypeOf(obj), Intl.NumberFormat.prototype);
obj = Reflect.construct(MyNumberFormat, [], NewTargetNullPrototype);
assertEq(obj instanceof MyNumberFormat, false);
assertEq(obj instanceof Intl.NumberFormat, true);
assertEq(Object.getPrototypeOf(obj), Intl.NumberFormat.prototype);
// "prototype" property is retrieved exactly once.
var trapLog = [], getLog = [];
var ProxiedConstructor = new Proxy(Intl.NumberFormat, new Proxy({
get(target, propertyKey, receiver) {
getLog.push(propertyKey);
return Reflect.get(target, propertyKey, receiver);
}
}, {
get(target, propertyKey, receiver) {
trapLog.push(propertyKey);
return Reflect.get(target, propertyKey, receiver);
}
}));
obj = Reflect.construct(Intl.NumberFormat, [], ProxiedConstructor);
assertEqArray(trapLog, ["get"]);
assertEqArray(getLog, ["prototype"]);
assertEq(obj instanceof Intl.NumberFormat, true);
assertEq(Object.getPrototypeOf(obj), Intl.NumberFormat.prototype);
if (typeof reportCompare === "function")
reportCompare(0, 0);

View file

@ -0,0 +1,79 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
const AsyncFunction = async function(){}.constructor;
// Test subclassing %AsyncFunction% works correctly.
class MyAsync extends AsyncFunction {}
var fn = new MyAsync();
assertEq(fn instanceof MyAsync, true);
assertEq(fn instanceof AsyncFunction, true);
assertEq(Object.getPrototypeOf(fn), MyAsync.prototype);
fn = Reflect.construct(MyAsync, []);
assertEq(fn instanceof MyAsync, true);
assertEq(fn instanceof AsyncFunction, true);
assertEq(Object.getPrototypeOf(fn), MyAsync.prototype);
fn = Reflect.construct(MyAsync, [], MyAsync);
assertEq(fn instanceof MyAsync, true);
assertEq(fn instanceof AsyncFunction, true);
assertEq(Object.getPrototypeOf(fn), MyAsync.prototype);
fn = Reflect.construct(MyAsync, [], AsyncFunction);
assertEq(fn instanceof MyAsync, false);
assertEq(fn instanceof AsyncFunction, true);
assertEq(Object.getPrototypeOf(fn), AsyncFunction.prototype);
// Set a different constructor as NewTarget.
fn = Reflect.construct(MyAsync, [], Array);
assertEq(fn instanceof MyAsync, false);
assertEq(fn instanceof AsyncFunction, false);
assertEq(Object.getPrototypeOf(fn), Array.prototype);
fn = Reflect.construct(AsyncFunction, [], Array);
assertEq(fn instanceof AsyncFunction, false);
assertEq(Object.getPrototypeOf(fn), Array.prototype);
// The prototype defaults to %AsyncFunctionPrototype% if null.
function NewTargetNullPrototype() {}
NewTargetNullPrototype.prototype = null;
fn = Reflect.construct(AsyncFunction, [], NewTargetNullPrototype);
assertEq(fn instanceof AsyncFunction, true);
assertEq(Object.getPrototypeOf(fn), AsyncFunction.prototype);
fn = Reflect.construct(MyAsync, [], NewTargetNullPrototype);
assertEq(fn instanceof MyAsync, false);
assertEq(fn instanceof AsyncFunction, true);
assertEq(Object.getPrototypeOf(fn), AsyncFunction.prototype);
// "prototype" property is retrieved exactly once.
var trapLog = [], getLog = [];
var ProxiedConstructor = new Proxy(AsyncFunction, new Proxy({
get(target, propertyKey, receiver) {
getLog.push(propertyKey);
return Reflect.get(target, propertyKey, receiver);
}
}, {
get(target, propertyKey, receiver) {
trapLog.push(propertyKey);
return Reflect.get(target, propertyKey, receiver);
}
}));
fn = Reflect.construct(AsyncFunction, [], ProxiedConstructor);
assertEqArray(trapLog, ["get"]);
assertEqArray(getLog, ["prototype"]);
assertEq(fn instanceof AsyncFunction, true);
assertEq(Object.getPrototypeOf(fn), AsyncFunction.prototype);
if (typeof reportCompare === "function")
reportCompare(0, 0);

View file

@ -0,0 +1,31 @@
// |reftest| skip-if(!xulRuntime.shell) -- needs drainJobQueue
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
const AsyncFunction = async function(){}.constructor;
class MyAsync extends AsyncFunction {}
// MyGen inherits from %AsyncFunction%.
assertEq(Object.getPrototypeOf(MyAsync), AsyncFunction);
// MyGen.prototype inherits from %AsyncFunctionPrototype%.
assertEq(Object.getPrototypeOf(MyAsync.prototype), AsyncFunction.prototype);
var fn = new MyAsync("return await 'ok';");
// fn inherits from MyAsync.prototype.
assertEq(Object.getPrototypeOf(fn), MyAsync.prototype);
// Ensure the new async function can be executed.
var promise = fn();
// promise inherits from %Promise.prototype%.
assertEq(Object.getPrototypeOf(promise), Promise.prototype);
// Computes the expected result.
assertEventuallyEq(promise, "ok");
if (typeof reportCompare === "function")
reportCompare(0, 0);

View file

@ -0,0 +1,79 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
const GeneratorFunction = function*(){}.constructor;
// Test subclassing %GeneratorFunction% works correctly.
class MyGenerator extends GeneratorFunction {}
var fn = new MyGenerator();
assertEq(fn instanceof MyGenerator, true);
assertEq(fn instanceof GeneratorFunction, true);
assertEq(Object.getPrototypeOf(fn), MyGenerator.prototype);
fn = Reflect.construct(MyGenerator, []);
assertEq(fn instanceof MyGenerator, true);
assertEq(fn instanceof GeneratorFunction, true);
assertEq(Object.getPrototypeOf(fn), MyGenerator.prototype);
fn = Reflect.construct(MyGenerator, [], MyGenerator);
assertEq(fn instanceof MyGenerator, true);
assertEq(fn instanceof GeneratorFunction, true);
assertEq(Object.getPrototypeOf(fn), MyGenerator.prototype);
fn = Reflect.construct(MyGenerator, [], GeneratorFunction);
assertEq(fn instanceof MyGenerator, false);
assertEq(fn instanceof GeneratorFunction, true);
assertEq(Object.getPrototypeOf(fn), GeneratorFunction.prototype);
// Set a different constructor as NewTarget.
fn = Reflect.construct(MyGenerator, [], Array);
assertEq(fn instanceof MyGenerator, false);
assertEq(fn instanceof GeneratorFunction, false);
assertEq(Object.getPrototypeOf(fn), Array.prototype);
fn = Reflect.construct(GeneratorFunction, [], Array);
assertEq(fn instanceof GeneratorFunction, false);
assertEq(Object.getPrototypeOf(fn), Array.prototype);
// The prototype defaults to %GeneratorFunctionPrototype% if null.
function NewTargetNullPrototype() {}
NewTargetNullPrototype.prototype = null;
fn = Reflect.construct(GeneratorFunction, [], NewTargetNullPrototype);
assertEq(fn instanceof GeneratorFunction, true);
assertEq(Object.getPrototypeOf(fn), GeneratorFunction.prototype);
fn = Reflect.construct(MyGenerator, [], NewTargetNullPrototype);
assertEq(fn instanceof MyGenerator, false);
assertEq(fn instanceof GeneratorFunction, true);
assertEq(Object.getPrototypeOf(fn), GeneratorFunction.prototype);
// "prototype" property is retrieved exactly once.
var trapLog = [], getLog = [];
var ProxiedConstructor = new Proxy(GeneratorFunction, new Proxy({
get(target, propertyKey, receiver) {
getLog.push(propertyKey);
return Reflect.get(target, propertyKey, receiver);
}
}, {
get(target, propertyKey, receiver) {
trapLog.push(propertyKey);
return Reflect.get(target, propertyKey, receiver);
}
}));
fn = Reflect.construct(GeneratorFunction, [], ProxiedConstructor);
assertEqArray(trapLog, ["get"]);
assertEqArray(getLog, ["prototype"]);
assertEq(fn instanceof GeneratorFunction, true);
assertEq(Object.getPrototypeOf(fn), GeneratorFunction.prototype);
if (typeof reportCompare === "function")
reportCompare(0, 0);

View file

@ -0,0 +1,33 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
const GeneratorFunction = function*(){}.constructor;
class MyGen extends GeneratorFunction {}
// MyGen inherits from %GeneratorFunction%.
assertEq(Object.getPrototypeOf(MyGen), GeneratorFunction);
// MyGen.prototype inherits from %Generator%.
assertEq(Object.getPrototypeOf(MyGen.prototype), GeneratorFunction.prototype);
var fn = new MyGen("yield* [1, 2, 3]");
// fn inherits from MyGen.prototype.
assertEq(Object.getPrototypeOf(fn), MyGen.prototype);
// fn.prototype inherits from %GeneratorPrototype%.
assertEq(Object.getPrototypeOf(fn.prototype), GeneratorFunction.prototype.prototype);
// Ensure the new generator function can be executed.
var it = fn();
// it inherits from fn.prototype.
assertEq(Object.getPrototypeOf(it), fn.prototype);
// Computes the expected result.
assertEqArray([...it], [1, 2, 3]);
if (typeof reportCompare === "function")
reportCompare(0, 0);

View file

@ -107,18 +107,15 @@ WrappedAsyncFunction(JSContext* cx, unsigned argc, Value* vp)
// the async function's body, replacing `await` with `yield`. `wrapped` is a
// function that is visible to the outside, and handles yielded values.
JSObject*
js::WrapAsyncFunction(JSContext* cx, HandleFunction unwrapped)
js::WrapAsyncFunctionWithProto(JSContext* cx, HandleFunction unwrapped, HandleObject proto)
{
MOZ_ASSERT(unwrapped->isStarGenerator());
MOZ_ASSERT(proto, "We need an explicit prototype to avoid the default"
"%FunctionPrototype% fallback in NewFunctionWithProto().");
// Create a new function with AsyncFunctionPrototype, reusing the name and
// the length of `unwrapped`.
// Step 1.
RootedObject proto(cx, GlobalObject::getOrCreateAsyncFunctionPrototype(cx, cx->global()));
if (!proto)
return nullptr;
RootedAtom funName(cx, unwrapped->name());
uint16_t length;
if (!unwrapped->getLength(cx, &length))
@ -141,6 +138,16 @@ js::WrapAsyncFunction(JSContext* cx, HandleFunction unwrapped)
return wrapped;
}
JSObject*
js::WrapAsyncFunction(JSContext* cx, HandleFunction unwrapped)
{
RootedObject proto(cx, GlobalObject::getOrCreateAsyncFunctionPrototype(cx, cx->global()));
if (!proto)
return nullptr;
return WrapAsyncFunctionWithProto(cx, unwrapped, proto);
}
enum class ResumeKind {
Normal,
Throw

View file

@ -21,6 +21,9 @@ GetUnwrappedAsyncFunction(JSFunction* wrapped);
bool
IsWrappedAsyncFunction(JSFunction* fun);
JSObject*
WrapAsyncFunctionWithProto(JSContext* cx, HandleFunction unwrapped, HandleObject proto);
JSObject*
WrapAsyncFunction(JSContext* cx, HandleFunction unwrapped);