From 668254e2b2a7b4f1d6da703275b89f3753096f71 Mon Sep 17 00:00:00 2001 From: Gaming4JC Date: Sat, 8 Jun 2019 17:11:49 -0400 Subject: 903389 - Fix uses of ClassMethodIsNative. --- js/src/builtin/RegExp.cpp | 2 +- .../tests/asm.js/import-function-toPrimitive.js | 26 +++++++++++++ .../basic/hasnativemethodpure-optimization.js | 21 +++++++++++ js/src/jsobj.cpp | 24 ++---------- js/src/jsobjinlines.h | 44 +++++++--------------- js/src/jsstr.cpp | 8 +++- js/src/vm/NativeObject.h | 13 ------- js/src/wasm/AsmJS.cpp | 6 +-- 8 files changed, 73 insertions(+), 71 deletions(-) create mode 100644 js/src/jit-test/tests/asm.js/import-function-toPrimitive.js create mode 100644 js/src/jit-test/tests/basic/hasnativemethodpure-optimization.js (limited to 'js') diff --git a/js/src/builtin/RegExp.cpp b/js/src/builtin/RegExp.cpp index b20f41c53..d71cee75e 100644 --- a/js/src/builtin/RegExp.cpp +++ b/js/src/builtin/RegExp.cpp @@ -1800,7 +1800,7 @@ js::intrinsic_GetStringDataProperty(JSContext* cx, unsigned argc, Value* vp) return false; RootedValue v(cx); - if (HasDataProperty(cx, nobj, AtomToId(atom), v.address()) && v.isString()) + if (GetPropertyPure(cx, nobj, AtomToId(atom), v.address()) && v.isString()) args.rval().set(v); else args.rval().setUndefined(); diff --git a/js/src/jit-test/tests/asm.js/import-function-toPrimitive.js b/js/src/jit-test/tests/asm.js/import-function-toPrimitive.js new file mode 100644 index 000000000..aa529b465 --- /dev/null +++ b/js/src/jit-test/tests/asm.js/import-function-toPrimitive.js @@ -0,0 +1,26 @@ +var counter = 0; + +function f(stdlib, foreign) +{ + "use asm"; + var a = +foreign.a; + var b = +foreign.b; + function g() {} + return g; +} + +var foreign = + { + a: function() {}, + b: /value that doesn't coerce purely/, + }; + +foreign.a[Symbol.toPrimitive] = + function() + { + counter++; + return 0; + }; + +f(null, foreign); +assertEq(counter, 1); diff --git a/js/src/jit-test/tests/basic/hasnativemethodpure-optimization.js b/js/src/jit-test/tests/basic/hasnativemethodpure-optimization.js new file mode 100644 index 000000000..2f5e99186 --- /dev/null +++ b/js/src/jit-test/tests/basic/hasnativemethodpure-optimization.js @@ -0,0 +1,21 @@ +load(libdir + "asserts.js"); + +let string = Object.defineProperty(new String("123"), "valueOf", { + get: function() { throw "get-valueOf"; } +}); +assertThrowsValue(() => "" + string, "get-valueOf"); + +string = Object.defineProperty(new String("123"), "toString", { + get: function() { throw "get-toString"; } +}); +assertThrowsValue(() => string.toLowerCase(), "get-toString"); + +string = Object.defineProperty(new String("123"), Symbol.toPrimitive, { + get: function() { throw "get-toPrimitive"; } +}); +assertThrowsValue(() => string.toLowerCase(), "get-toPrimitive"); + +let number = Object.defineProperty(new Number(123), "valueOf", { + get: function() { throw "get-valueOf"; } +}); +assertThrowsValue(() => +number, "get-valueOf"); \ No newline at end of file diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 2364f707e..418e08dad 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -2852,24 +2852,6 @@ js::GetObjectClassName(JSContext* cx, HandleObject obj) /* * */ -bool -js::HasDataProperty(JSContext* cx, NativeObject* obj, jsid id, Value* vp) -{ - if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) { - *vp = obj->getDenseElement(JSID_TO_INT(id)); - return true; - } - - if (Shape* shape = obj->lookup(cx, id)) { - if (shape->hasDefaultGetter() && shape->hasSlot()) { - *vp = obj->getSlot(shape->slot()); - return true; - } - } - - return false; -} - extern bool PropertySpecNameToId(JSContext* cx, const char* name, MutableHandleId id, js::PinningBehavior pin = js::DoNotPinAtom); @@ -2985,7 +2967,7 @@ JS::OrdinaryToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHan /* Optimize (new String(...)).toString(). */ if (clasp == &StringObject::class_) { StringObject* nobj = &obj->as(); - if (ClassMethodIsNative(cx, nobj, &StringObject::class_, id, str_toString)) { + if (HasNativeMethodPure(nobj, cx->names().toString, str_toString, cx)) { vp.setString(nobj->unbox()); return true; } @@ -3007,7 +2989,7 @@ JS::OrdinaryToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHan /* Optimize new String(...).valueOf(). */ if (clasp == &StringObject::class_) { StringObject* nobj = &obj->as(); - if (ClassMethodIsNative(cx, nobj, &StringObject::class_, id, str_toString)) { + if (HasNativeMethodPure(nobj, cx->names().valueOf, str_toString, cx)) { vp.setString(nobj->unbox()); return true; } @@ -3016,7 +2998,7 @@ JS::OrdinaryToPrimitive(JSContext* cx, HandleObject obj, JSType hint, MutableHan /* Optimize new Number(...).valueOf(). */ if (clasp == &NumberObject::class_) { NumberObject* nobj = &obj->as(); - if (ClassMethodIsNative(cx, nobj, &NumberObject::class_, id, num_valueOf)) { + if (HasNativeMethodPure(nobj, cx->names().valueOf, num_valueOf, cx)) { vp.setNumber(nobj->unbox()); return true; } diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index b1d817bca..7028310ce 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -557,48 +557,30 @@ IsNativeFunction(const js::Value& v, JSNative native) return IsFunctionObject(v, &fun) && fun->maybeNative() == native; } -/* - * When we have an object of a builtin class, we don't quite know what its - * valueOf/toString methods are, since these methods may have been overwritten - * or shadowed. However, we can still do better than the general case by - * hard-coding the necessary properties for us to find the native we expect. - * - * TODO: a per-thread shape-based cache would be faster and simpler. - */ +// Return whether looking up a method on 'obj' definitely resolves to the +// original specified native function. The method may conservatively return +// 'false' in the case of proxies or other non-native objects. static MOZ_ALWAYS_INLINE bool -ClassMethodIsNative(JSContext* cx, NativeObject* obj, const Class* clasp, jsid methodid, JSNative native) +HasNativeMethodPure(JSObject* obj, PropertyName* name, JSNative native, JSContext* cx) { - MOZ_ASSERT(obj->getClass() == clasp); - Value v; - if (!HasDataProperty(cx, obj, methodid, &v)) { - JSObject* proto = obj->staticPrototype(); - if (!proto || proto->getClass() != clasp || !HasDataProperty(cx, &proto->as(), methodid, &v)) - return false; - } + if (!GetPropertyPure(cx, obj, NameToId(name), &v)) + return false; return IsNativeFunction(v, native); } -// Return whether looking up 'valueOf' on 'obj' definitely resolves to the -// original Object.prototype.valueOf. The method may conservatively return -// 'false' in the case of proxies or other non-native objects. +// Return whether 'obj' definitely has no @@toPrimitive method. static MOZ_ALWAYS_INLINE bool -HasObjectValueOf(JSObject* obj, JSContext* cx) +HasNoToPrimitiveMethodPure(JSObject* obj, JSContext* cx) { - if (obj->is() || !obj->isNative()) + jsid id = SYMBOL_TO_JSID(cx->wellKnownSymbols().toPrimitive); + JSObject* pobj; + Shape* shape; + if (!LookupPropertyPure(cx, obj, id, &pobj, &shape)) return false; - jsid valueOf = NameToId(cx->names().valueOf); - - Value v; - while (!HasDataProperty(cx, &obj->as(), valueOf, &v)) { - obj = obj->staticPrototype(); - if (!obj || obj->is() || !obj->isNative()) - return false; - } - - return IsNativeFunction(v, obj_valueOf); + return !shape; } /* ES6 draft rev 28 (2014 Oct 14) 7.1.14 */ diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index d7db5129d..3fecb463d 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -461,9 +461,13 @@ ToStringForStringFunction(JSContext* cx, HandleValue thisv) RootedObject obj(cx, &thisv.toObject()); if (obj->is()) { StringObject* nobj = &obj->as(); - Rooted id(cx, NameToId(cx->names().toString)); - if (ClassMethodIsNative(cx, nobj, &StringObject::class_, id, str_toString)) + // We have to make sure that the ToPrimitive call from ToString + // would be unobservable. + if (HasNoToPrimitiveMethodPure(nobj, cx) && + HasNativeMethodPure(nobj, cx->names().toString, str_toString, cx)) + { return nobj->unbox(); + } } } else if (thisv.isNullOrUndefined()) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO, diff --git a/js/src/vm/NativeObject.h b/js/src/vm/NativeObject.h index d9d8b8aec..832a701dd 100644 --- a/js/src/vm/NativeObject.h +++ b/js/src/vm/NativeObject.h @@ -1470,19 +1470,6 @@ NativeGetExistingProperty(JSContext* cx, HandleObject receiver, HandleNativeObje /* * */ -/* - * If obj has an already-resolved data property for id, return true and - * store the property value in *vp. - */ -extern bool -HasDataProperty(JSContext* cx, NativeObject* obj, jsid id, Value* vp); - -inline bool -HasDataProperty(JSContext* cx, NativeObject* obj, PropertyName* name, Value* vp) -{ - return HasDataProperty(cx, obj, NameToId(name), vp); -} - extern bool GetPropertyForNameLookup(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp); diff --git a/js/src/wasm/AsmJS.cpp b/js/src/wasm/AsmJS.cpp index 6483d6ec3..f906d4bf1 100644 --- a/js/src/wasm/AsmJS.cpp +++ b/js/src/wasm/AsmJS.cpp @@ -7478,10 +7478,10 @@ HasPureCoercion(JSContext* cx, HandleValue v) // coercions are not observable and coercion via ToNumber/ToInt32 // definitely produces NaN/0. We should remove this special case later once // most apps have been built with newer Emscripten. - jsid toString = NameToId(cx->names().toString); if (v.toObject().is() && - HasObjectValueOf(&v.toObject(), cx) && - ClassMethodIsNative(cx, &v.toObject().as(), &JSFunction::class_, toString, fun_toString)) + HasNoToPrimitiveMethodPure(&v.toObject(), cx) && + HasNativeMethodPure(&v.toObject(), cx->names().valueOf, obj_valueOf, cx) && + HasNativeMethodPure(&v.toObject(), cx->names().toString, fun_toString, cx)) { return true; } -- cgit v1.2.3