Bug 1054906 - Implement ES6 Symbol.hasInstance 2/2; r=jandem

--HG--
extra : rebase_source : 862c135973071b1cb8abc3a97ab446b7b137d7a8
This commit is contained in:
Morgan Phillips 2016-06-02 14:30:35 -07:00
Родитель df95ec2891
Коммит 6cd35e0297
6 изменённых файлов: 159 добавлений и 0 удалений

Просмотреть файл

@ -7763,6 +7763,20 @@ TryAttachInstanceOfStub(JSContext* cx, BaselineFrame* frame, ICInstanceOf_Fallba
if (fun->isBoundFunction()) if (fun->isBoundFunction())
return true; return true;
// If the user has supplied their own @@hasInstance method we shouldn't
// clobber it.
if (!js::FunctionHasDefaultHasInstance(fun, cx->wellKnownSymbols()))
return true;
// Refuse to optimize any function whose [[Prototype]] isn't
// Function.prototype.
if (!fun->hasStaticPrototype() || fun->hasUncacheableProto())
return true;
Value funProto = cx->global()->getPrototype(JSProto_Function);
if (funProto.isObject() && fun->staticPrototype() != &funProto.toObject())
return true;
Shape* shape = fun->lookupPure(cx->names().prototype); Shape* shape = fun->lookupPure(cx->names().prototype);
if (!shape || !shape->hasSlot() || !shape->hasDefaultGetter()) if (!shape || !shape->hasSlot() || !shape->hasDefaultGetter())
return true; return true;

Просмотреть файл

@ -13887,10 +13887,36 @@ IonBuilder::jsop_instanceof()
if (!rhsObject || !rhsObject->is<JSFunction>() || rhsObject->isBoundFunction()) if (!rhsObject || !rhsObject->is<JSFunction>() || rhsObject->isBoundFunction())
break; break;
// Refuse to optimize anything whose [[Prototype]] isn't Function.prototype
// since we can't guarantee that it uses the default @@hasInstance method.
if (rhsObject->hasUncacheableProto() || !rhsObject->hasStaticPrototype())
break;
Value funProto = script()->global().getPrototype(JSProto_Function);
if (!funProto.isObject() || rhsObject->staticPrototype() != &funProto.toObject())
break;
// If the user has supplied their own @@hasInstance method we shouldn't
// clobber it.
JSFunction* fun = &rhsObject->as<JSFunction>();
const WellKnownSymbols* symbols = &compartment->runtime()->wellKnownSymbols();
if (!js::FunctionHasDefaultHasInstance(fun, *symbols))
break;
// Ensure that we will bail if the @@hasInstance property or [[Prototype]]
// change.
TypeSet::ObjectKey* rhsKey = TypeSet::ObjectKey::get(rhsObject); TypeSet::ObjectKey* rhsKey = TypeSet::ObjectKey::get(rhsObject);
if (!rhsKey->hasStableClassAndProto(constraints()))
break;
if (rhsKey->unknownProperties()) if (rhsKey->unknownProperties())
break; break;
HeapTypeSetKey hasInstanceObject =
rhsKey->property(SYMBOL_TO_JSID(symbols->hasInstance));
if (hasInstanceObject.isOwnProperty(constraints()))
break;
HeapTypeSetKey protoProperty = HeapTypeSetKey protoProperty =
rhsKey->property(NameToId(names().prototype)); rhsKey->property(NameToId(names().prototype));
JSObject* protoObject = protoProperty.singleton(constraints()); JSObject* protoObject = protoProperty.singleton(constraints());

Просмотреть файл

@ -1156,6 +1156,20 @@ fun_toStringHelper(JSContext* cx, HandleObject obj, unsigned indent)
return FunctionToString(cx, fun, indent != JS_DONT_PRETTY_PRINT); return FunctionToString(cx, fun, indent != JS_DONT_PRETTY_PRINT);
} }
bool
js::FunctionHasDefaultHasInstance(JSFunction* fun, const WellKnownSymbols& symbols)
{
jsid id = SYMBOL_TO_JSID(symbols.hasInstance);
Shape* shape = fun->lookupPure(id);
if (shape) {
if (!shape->hasSlot() || !shape->hasDefaultGetter())
return false;
const Value hasInstance = fun->as<NativeObject>().getSlot(shape->slot());
return IsNativeFunction(hasInstance, js::fun_symbolHasInstance);
}
return true;
}
bool bool
js::fun_toString(JSContext* cx, unsigned argc, Value* vp) js::fun_toString(JSContext* cx, unsigned argc, Value* vp)
{ {

Просмотреть файл

@ -677,6 +677,11 @@ FunctionHasResolveHook(const JSAtomState& atomState, jsid id);
extern bool extern bool
fun_toString(JSContext* cx, unsigned argc, Value* vp); fun_toString(JSContext* cx, unsigned argc, Value* vp);
struct WellKnownSymbols;
extern bool
FunctionHasDefaultHasInstance(JSFunction* fun, const WellKnownSymbols& symbols);
extern bool extern bool
fun_symbolHasInstance(JSContext* cx, unsigned argc, Value* vp); fun_symbolHasInstance(JSContext* cx, unsigned argc, Value* vp);

Просмотреть файл

@ -0,0 +1,96 @@
const OriginalHasInstance = Function.prototype[Symbol.hasInstance];
// Ensure that folding doesn't impact user defined @@hasInstance methods.
{
function Test() {
this.x = 1;
}
Object.defineProperty(Test, Symbol.hasInstance,
{writable: true, value: () => false});
function x(t) {
return t instanceof Test;
}
function y() {
let t = new Test;
let b = true;
for (let i = 0; i < 10; i++) {
b = b && x(t);
}
return b;
}
function z() {
let f = 0;
let t = 0;
for (let i = 0; i < 100; i++)
assertEq(y(), false);
}
z();
}
// Ensure that the jitting does not clobber user defined @@hasInstance methods.
{
function a() {
function b() {};
b.__proto__ = a.prototype;
return b;
};
let c = new a();
let t = 0;
let f = 0;
let e = 0;
for (let i = 0; i < 40000; i++) {
if (i == 20000)
Object.defineProperty(a.prototype, Symbol.hasInstance,
{writable: true, value: () => true});
if (i == 30000)
Object.setPrototypeOf(c, Function.prototype);
if (1 instanceof c) {
t++;
} else {
f++;
}
}
assertEq(t, 10000);
assertEq(f, 30000);
}
{
function a() {};
function b() {};
Object.defineProperty(a, Symbol.hasInstance, {writable: true, value: () => true});
assertEq(b instanceof a, true);
for (let _ of Array(10000))
assertEq(b instanceof a, true);
}
{
function a(){};
function b(){};
function c(){};
function d(){};
function e(){};
Object.defineProperty(a, Symbol.hasInstance, {value: () => true });
Object.defineProperty(b, Symbol.hasInstance, {value: () => true });
Object.defineProperty(c, Symbol.hasInstance, {value: () => true });
Object.defineProperty(d, Symbol.hasInstance, {value: () => true });
let funcs = [a, b, c, d];
for (let f of funcs)
assertEq(e instanceof f, true);
for (let _ of Array(10001)) {
for (let f of funcs)
assertEq(e instanceof f, true);
}
}
if (typeof reportCompare === "function")
reportCompare(true, true);

Просмотреть файл

@ -475,6 +475,10 @@ struct WellKnownSymbols
const ImmutableSymbolPtr& get(JS::SymbolCode code) const { const ImmutableSymbolPtr& get(JS::SymbolCode code) const {
return get(size_t(code)); return get(size_t(code));
} }
WellKnownSymbols() {}
WellKnownSymbols(const WellKnownSymbols&) = delete;
WellKnownSymbols& operator=(const WellKnownSymbols&) = delete;
}; };
#define NAME_OFFSET(name) offsetof(JSAtomState, name) #define NAME_OFFSET(name) offsetof(JSAtomState, name)