Bug 1379222 - Avoid [[Get]] for "prototype" property when calling builtin constructors. r=jandem

--HG--
extra : rebase_source : f2a369851a66b880eb693bdea8b53aafb0c1f00b
This commit is contained in:
André Bargull 2017-07-10 04:55:54 -07:00
parent adabab920b
commit b6817f5d89
19 changed files with 93 additions and 112 deletions

View File

@ -212,8 +212,7 @@ DataViewObject::constructSameCompartment(JSContext* cx, HandleObject bufobj, con
return false;
RootedObject proto(cx);
RootedObject newTarget(cx, &args.newTarget().toObject());
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
return false;
Rooted<ArrayBufferObjectMaybeShared*> buffer(cx, &AsArrayBufferMaybeShared(bufobj));
@ -257,8 +256,7 @@ DataViewObject::constructWrapped(JSContext* cx, HandleObject bufobj, const CallA
// Make sure to get the [[Prototype]] for the created view from this
// compartment.
RootedObject proto(cx);
RootedObject newTarget(cx, &args.newTarget().toObject());
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
return false;
Rooted<GlobalObject*> global(cx, cx->compartment()->maybeGlobal());

View File

@ -989,7 +989,7 @@ Collator(JSContext* cx, const CallArgs& args)
// Steps 2-5 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
RootedObject proto(cx);
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
return false;
if (!proto) {
@ -1557,7 +1557,7 @@ NumberFormat(JSContext* cx, const CallArgs& args, bool construct)
// Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
RootedObject proto(cx);
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
return false;
if (!proto) {
@ -2432,7 +2432,7 @@ DateTimeFormat(JSContext* cx, const CallArgs& args, bool construct, DateTimeForm
// Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
RootedObject proto(cx);
if (args.isConstructing() && !GetPrototypeFromCallableConstructor(cx, args, &proto))
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
return false;
if (!proto) {
@ -3540,7 +3540,7 @@ PluralRules(JSContext* cx, unsigned argc, Value* vp)
// Step 2 (Inlined 9.1.14, OrdinaryCreateFromConstructor).
RootedObject proto(cx);
if (!GetPrototypeFromCallableConstructor(cx, args, &proto))
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
return false;
if (!proto) {

View File

@ -564,8 +564,7 @@ MapObject::construct(JSContext* cx, unsigned argc, Value* vp)
return false;
RootedObject proto(cx);
RootedObject newTarget(cx, &args.newTarget().toObject());
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
return false;
Rooted<MapObject*> obj(cx, MapObject::create(cx, proto));
@ -1168,8 +1167,7 @@ SetObject::construct(JSContext* cx, unsigned argc, Value* vp)
return false;
RootedObject proto(cx);
RootedObject newTarget(cx, &args.newTarget().toObject());
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
return false;
Rooted<SetObject*> obj(cx, SetObject::create(cx, proto));

View File

@ -1291,7 +1291,6 @@ PromiseConstructor(JSContext* cx, unsigned argc, Value* vp)
// Steps 3-10.
RootedObject newTarget(cx, &args.newTarget().toObject());
RootedObject originalNewTarget(cx, newTarget);
bool needsWrapping = false;
// If the constructor is called via an Xray wrapper, then the newTarget
@ -1322,9 +1321,11 @@ PromiseConstructor(JSContext* cx, unsigned argc, Value* vp)
// Promises, with the unprivileged one resolved with the resolution of the
// privileged one.
if (IsWrapper(newTarget)) {
newTarget = CheckedUnwrap(newTarget);
MOZ_ASSERT(newTarget);
MOZ_ASSERT(newTarget != originalNewTarget);
JSObject* unwrappedNewTarget = CheckedUnwrap(newTarget);
MOZ_ASSERT(unwrappedNewTarget);
MOZ_ASSERT(unwrappedNewTarget != newTarget);
newTarget = unwrappedNewTarget;
{
AutoCompartment ac(cx, newTarget);
RootedObject promiseCtor(cx);
@ -1340,10 +1341,15 @@ PromiseConstructor(JSContext* cx, unsigned argc, Value* vp)
}
RootedObject proto(cx);
if (!GetPrototypeFromConstructor(cx, needsWrapping ? newTarget : originalNewTarget, &proto))
return false;
if (needsWrapping && !cx->compartment()->wrap(cx, &proto))
return false;
if (needsWrapping) {
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
return false;
if (!cx->compartment()->wrap(cx, &proto))
return false;
} else {
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
return false;
}
Rooted<PromiseObject*> promise(cx, PromiseObject::create(cx, executor, proto, needsWrapping));
if (!promise)
return false;

View File

@ -399,7 +399,7 @@ js::regexp_construct(JSContext* cx, unsigned argc, Value* vp)
return false;
// We can delay step 3 and step 4a until later, during
// GetPrototypeFromCallableConstructor calls. Accessing the new.target
// GetPrototypeFromBuiltinConstructor calls. Accessing the new.target
// and the callee from the stack is unobservable.
if (!args.isConstructing()) {
// Step 3.b.
@ -447,7 +447,7 @@ js::regexp_construct(JSContext* cx, unsigned argc, Value* vp)
// Step 7.
RootedObject proto(cx);
if (!GetPrototypeFromCallableConstructor(cx, args, &proto))
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
return false;
Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx, GenericObject, proto));
@ -506,7 +506,7 @@ js::regexp_construct(JSContext* cx, unsigned argc, Value* vp)
// Step 7.
RootedObject proto(cx);
if (!GetPrototypeFromCallableConstructor(cx, args, &proto))
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
return false;
Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx, GenericObject, proto));

View File

@ -291,8 +291,11 @@ WeakMap_construct(JSContext* cx, unsigned argc, Value* vp)
if (!ThrowIfNotConstructing(cx, args, "WeakMap"))
return false;
RootedObject newTarget(cx, &args.newTarget().toObject());
RootedObject obj(cx, CreateThis(cx, &WeakMapObject::class_, newTarget));
RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
return false;
RootedObject obj(cx, NewObjectWithClassProto<WeakMapObject>(cx, proto));
if (!obj)
return false;

View File

@ -93,8 +93,7 @@ WeakSetObject::construct(JSContext* cx, unsigned argc, Value* vp)
return false;
RootedObject proto(cx);
RootedObject newTarget(cx, &args.newTarget().toObject());
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
return false;
Rooted<WeakSetObject*> obj(cx, WeakSetObject::create(cx, proto));

View File

@ -3515,7 +3515,7 @@ ArrayConstructorImpl(JSContext* cx, CallArgs& args, bool isConstructor)
{
RootedObject proto(cx);
if (isConstructor) {
if (!GetPrototypeFromCallableConstructor(cx, args, &proto))
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
return false;
} else {
// We're emulating |new Array(n)| with |std_Array(n)| in self-hosted JS,

View File

@ -117,10 +117,8 @@ Boolean(JSContext* cx, unsigned argc, Value* vp)
bool b = args.length() != 0 ? JS::ToBoolean(args[0]) : false;
if (args.isConstructing()) {
RootedObject newTarget (cx, &args.newTarget().toObject());
RootedObject proto(cx);
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
return false;
JSObject* obj = BooleanObject::create(cx, b, proto);

View File

@ -3095,8 +3095,7 @@ NewDateObject(JSContext* cx, const CallArgs& args, ClippedTime t)
MOZ_ASSERT(args.isConstructing());
RootedObject proto(cx);
RootedObject newTarget(cx, &args.newTarget().toObject());
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
return false;
JSObject* obj = NewDateObjectMsec(cx, t, proto);

View File

@ -442,7 +442,7 @@ Error(JSContext* cx, unsigned argc, Value* vp)
// ES6 19.5.1.1 mandates the .prototype lookup happens before the toString
RootedObject proto(cx);
if (!GetPrototypeFromCallableConstructor(cx, args, &proto))
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
return false;
/* Compute the error message, if any. */

View File

@ -1884,7 +1884,7 @@ FunctionConstructor(JSContext* cx, const CallArgs& args, GeneratorKind generator
// Step 24.
RootedObject proto(cx);
if (!isAsync) {
if (!GetPrototypeFromCallableConstructor(cx, args, &proto))
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
return false;
}

View File

@ -496,25 +496,25 @@ Number(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
/* Sample JS_CALLEE before clobbering. */
bool isConstructing = args.isConstructing();
if (args.length() > 0) {
if (!ToNumber(cx, args[0]))
return false;
args.rval().set(args[0]);
} else {
args.rval().setInt32(0);
}
if (!isConstructing)
if (!args.isConstructing()) {
if (args.length() > 0)
args.rval().set(args[0]);
else
args.rval().setInt32(0);
return true;
}
RootedObject newTarget(cx, &args.newTarget().toObject());
RootedObject proto(cx);
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
return false;
JSObject* obj = NumberObject::create(cx, args.rval().toNumber(), proto);
double d = args.length() > 0 ? args[0].toNumber() : 0;
JSObject* obj = NumberObject::create(cx, d, proto);
if (!obj)
return false;
args.rval().setObject(*obj);

View File

@ -1002,17 +1002,6 @@ js::GetPrototypeFromConstructor(JSContext* cx, HandleObject newTarget, MutableHa
return true;
}
bool
js::GetPrototypeFromCallableConstructor(JSContext* cx, const CallArgs& args, MutableHandleObject proto)
{
RootedObject newTarget(cx);
if (args.isConstructing())
newTarget = &args.newTarget().toObject();
else
newTarget = &args.callee();
return GetPrototypeFromConstructor(cx, newTarget, proto);
}
JSObject*
js::CreateThisForFunction(JSContext* cx, HandleObject callee, HandleObject newTarget,
NewObjectKind newKind)

View File

@ -1152,8 +1152,21 @@ NewObjectWithTaggedProtoIsCachable(JSContext* cx, Handle<TaggedProto> proto,
extern bool
GetPrototypeFromConstructor(JSContext* cx, js::HandleObject newTarget, js::MutableHandleObject proto);
extern bool
GetPrototypeFromCallableConstructor(JSContext* cx, const CallArgs& args, js::MutableHandleObject proto);
MOZ_ALWAYS_INLINE bool
GetPrototypeFromBuiltinConstructor(JSContext* cx, const CallArgs& args, js::MutableHandleObject proto)
{
// When proto is set to nullptr, the caller is expected to select the
// correct default built-in prototype for this constructor.
if (!args.isConstructing() || &args.newTarget().toObject() == &args.callee()) {
proto.set(nullptr);
return true;
}
// We're calling this constructor from a derived class, retrieve the
// actual prototype from newTarget.
RootedObject newTarget(cx, &args.newTarget().toObject());
return GetPrototypeFromConstructor(cx, newTarget, proto);
}
// Specialized call for constructing |this| with a known function callee,
// and a known prototype.

View File

@ -3209,8 +3209,7 @@ js::StringConstructor(JSContext* cx, unsigned argc, Value* vp)
if (args.isConstructing()) {
RootedObject proto(cx);
RootedObject newTarget(cx, &args.newTarget().toObject());
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
return false;
StringObject* strobj = StringObject::create(cx, str, proto);

View File

@ -285,8 +285,7 @@ ArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value* vp)
// Step 3 (Inlined 24.1.1.1 AllocateArrayBuffer).
// 24.1.1.1, step 1 (Inlined 9.1.14 OrdinaryCreateFromConstructor).
RootedObject proto(cx);
RootedObject newTarget(cx, &args.newTarget().toObject());
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
return false;
// 24.1.1.1, step 3 (Inlined 6.2.6.1 CreateByteDataBlock, step 2).

View File

@ -273,8 +273,7 @@ SharedArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value*
// Step 3 (Inlined 24.2.1.1 AllocateSharedArrayBuffer).
// 24.2.1.1, step 1 (Inlined 9.1.14 OrdinaryCreateFromConstructor).
RootedObject proto(cx);
RootedObject newTarget(cx, &args.newTarget().toObject());
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
return false;
// 24.2.1.1, step 3 (Inlined 6.2.7.2 CreateSharedByteDataBlock, step 2).

View File

@ -321,20 +321,6 @@ NewArray(JSContext* cx, uint32_t nelements);
namespace {
// We allow nullptr for newTarget for all the creation methods, to allow for
// JSFriendAPI functions that don't care about subclassing
static bool
GetPrototypeForInstance(JSContext* cx, HandleObject newTarget, MutableHandleObject proto)
{
if (newTarget) {
if (!GetPrototypeFromConstructor(cx, newTarget, proto))
return false;
} else {
proto.set(nullptr);
}
return true;
}
enum class SpeciesConstructorOverride {
None,
ArrayBuffer
@ -472,7 +458,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
// the time, though, that [[Prototype]] will not be interesting. If
// it isn't, we can do some more TI optimizations.
RootedObject checkProto(cx);
if (!GetBuiltinPrototype(cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()), &checkProto))
if (proto && !GetBuiltinPrototype(cx, JSCLASS_CACHED_PROTO_KEY(instanceClass()), &checkProto))
return nullptr;
AutoSetNewObjectMetadata metadata(cx);
@ -724,24 +710,30 @@ class TypedArrayObjectTemplate : public TypedArrayObject
if (!ToIndex(cx, args.get(0), JSMSG_BAD_ARRAY_LENGTH, &len))
return nullptr;
return fromLength(cx, len, newTarget);
// 22.2.4.1, step 3 and 22.2.4.2, step 5.
// 22.2.4.2.1 AllocateTypedArray, step 1.
RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
return nullptr;
return fromLength(cx, len, proto);
}
RootedObject dataObj(cx, &args[0].toObject());
// 22.2.4.{3,4,5}, step 4.
// 22.2.4.2.1 AllocateTypedArray, step 1.
RootedObject proto(cx);
if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
return nullptr;
// 22.2.4.3 TypedArray ( typedArray )
// 22.2.4.4 TypedArray ( object )
if (!UncheckedUnwrap(dataObj)->is<ArrayBufferObjectMaybeShared>())
return fromArray(cx, dataObj, newTarget);
return fromArray(cx, dataObj, proto);
// 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] )
// Step 4.
// 22.2.4.2.1 AllocateTypedArray, step 1.
RootedObject proto(cx);
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
return nullptr;
uint64_t byteOffset = 0;
if (args.hasDefined(1)) {
// Step 6.
@ -966,16 +958,11 @@ class TypedArrayObjectTemplate : public TypedArrayObject
// 22.2.4.1 TypedArray ( )
// 22.2.4.2 TypedArray ( length )
static JSObject*
fromLength(JSContext* cx, uint64_t nelements, HandleObject newTarget = nullptr)
fromLength(JSContext* cx, uint64_t nelements, HandleObject proto = nullptr)
{
// 22.2.4.1, step 1 and 22.2.4.2, steps 1-3 (performed in caller).
// 22.2.4.1, step 2 and 22.2.4.2, step 4 (implicit).
// 22.2.4.1, step 3 and 22.2.4.2, step 5.
// 22.2.4.2.1 AllocateTypedArray, step 1.
RootedObject proto(cx);
if (!GetPrototypeForInstance(cx, newTarget, &proto))
return nullptr;
// 22.2.4.1, step 3 and 22.2.4.2, step 5 (call AllocateTypedArray).
if (nelements > UINT32_MAX) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
@ -1001,13 +988,13 @@ class TypedArrayObjectTemplate : public TypedArrayObject
MutableHandle<ArrayBufferObject*> buffer);
static JSObject*
fromArray(JSContext* cx, HandleObject other, HandleObject newTarget = nullptr);
fromArray(JSContext* cx, HandleObject other, HandleObject proto = nullptr);
static JSObject*
fromTypedArray(JSContext* cx, HandleObject other, bool isWrapped, HandleObject newTarget);
fromTypedArray(JSContext* cx, HandleObject other, bool isWrapped, HandleObject proto);
static JSObject*
fromObject(JSContext* cx, HandleObject other, HandleObject newTarget);
fromObject(JSContext* cx, HandleObject other, HandleObject proto);
static const NativeType
getIndex(JSObject* obj, uint32_t index)
@ -1190,17 +1177,17 @@ TypedArrayObjectTemplate<T>::CloneArrayBufferNoCopy(JSContext* cx,
template<typename T>
/* static */ JSObject*
TypedArrayObjectTemplate<T>::fromArray(JSContext* cx, HandleObject other,
HandleObject newTarget /* = nullptr */)
HandleObject proto /* = nullptr */)
{
// Allow nullptr newTarget for FriendAPI methods, which don't care about
// Allow nullptr proto for FriendAPI methods, which don't care about
// subclassing.
if (other->is<TypedArrayObject>())
return fromTypedArray(cx, other, /* wrapped= */ false, newTarget);
return fromTypedArray(cx, other, /* wrapped= */ false, proto);
if (other->is<WrapperObject>() && UncheckedUnwrap(other)->is<TypedArrayObject>())
return fromTypedArray(cx, other, /* wrapped= */ true, newTarget);
return fromTypedArray(cx, other, /* wrapped= */ true, proto);
return fromObject(cx, other, newTarget);
return fromObject(cx, other, proto);
}
// ES2017 draft rev 6390c2f1b34b309895d31d8c0512eac8660a0210
@ -1208,7 +1195,7 @@ TypedArrayObjectTemplate<T>::fromArray(JSContext* cx, HandleObject other,
template<typename T>
/* static */ JSObject*
TypedArrayObjectTemplate<T>::fromTypedArray(JSContext* cx, HandleObject other, bool isWrapped,
HandleObject newTarget)
HandleObject proto)
{
// Step 1.
MOZ_ASSERT_IF(!isWrapped, other->is<TypedArrayObject>());
@ -1216,12 +1203,9 @@ TypedArrayObjectTemplate<T>::fromTypedArray(JSContext* cx, HandleObject other, b
other->is<WrapperObject>() &&
UncheckedUnwrap(other)->is<TypedArrayObject>());
// Step 2 (done in caller).
// Step 2 (Already performed in caller).
// Step 4 (partially).
RootedObject proto(cx);
if (!GetPrototypeForInstance(cx, newTarget, &proto))
return nullptr;
// Step 4 (Allocation deferred until later).
// Step 5.
Rooted<TypedArrayObject*> srcArray(cx);
@ -1335,14 +1319,11 @@ IsOptimizableInit(JSContext* cx, HandleObject iterable, bool* optimized)
// 22.2.4.4 TypedArray ( object )
template<typename T>
/* static */ JSObject*
TypedArrayObjectTemplate<T>::fromObject(JSContext* cx, HandleObject other, HandleObject newTarget)
TypedArrayObjectTemplate<T>::fromObject(JSContext* cx, HandleObject other, HandleObject proto)
{
// Steps 1-2 (Already performed in caller).
// Steps 3-4 (Allocation deferred until later).
RootedObject proto(cx);
if (!GetPrototypeForInstance(cx, newTarget, &proto))
return nullptr;
bool optimized = false;
if (!IsOptimizableInit(cx, other, &optimized))