From cd1afc6984882eab69e4730b04b184264a73fd2c Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Wed, 11 Dec 2013 16:04:07 -0800 Subject: [PATCH] Bug 948227 - Add code to warn when the __proto__ setter is called that it's very slow and shouldn't be used. (Don't use it yet, tho, because it requires the second part of bug 948583 to land first. Once that lands, fully enabling this just requires some uncommenting.) r=efaust --HG-- extra : rebase_source : 87d320edcc99b6ef7df98c2470c78d4085aa4f99 --- js/public/Class.h | 2 +- js/src/builtin/Object.cpp | 4 ---- js/src/js.msg | 2 +- js/src/vm/GlobalObject.cpp | 17 ++++++++++++----- js/src/vm/GlobalObject.h | 25 +++++++++++++++++++++++-- 5 files changed, 37 insertions(+), 13 deletions(-) diff --git a/js/public/Class.h b/js/public/Class.h index 320ba0ad0cfd..e5ca52fdba47 100644 --- a/js/public/Class.h +++ b/js/public/Class.h @@ -574,7 +574,7 @@ struct JSClass { // with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was // previously allowed, but is now an ES5 violation and thus unsupported. // -#define JSCLASS_GLOBAL_SLOT_COUNT (3 + JSProto_LIMIT * 3 + 29) +#define JSCLASS_GLOBAL_SLOT_COUNT (3 + JSProto_LIMIT * 3 + 30) #define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n) \ (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n))) #define JSCLASS_GLOBAL_FLAGS \ diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index c77625685982..86d41859f5a8 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -556,10 +556,8 @@ obj_watch(JSContext *cx, unsigned argc, Value *vp) if (!obj) return false; -#if 0 /* pending addressing Firebug's use of this method */ if (!GlobalObject::warnOnceAboutWatch(cx, obj)) return false; -#endif if (args.length() <= 1) { js_ReportMissingArg(cx, args.calleev(), 1); @@ -595,10 +593,8 @@ obj_unwatch(JSContext *cx, unsigned argc, Value *vp) if (!obj) return false; -#if 0 /* pending addressing Firebug's use of this method */ if (!GlobalObject::warnOnceAboutWatch(cx, obj)) return false; -#endif RootedId id(cx); if (args.length() != 0) { diff --git a/js/src/js.msg b/js/src/js.msg index 1292439a59c3..fcdf09d3f2b8 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -260,7 +260,7 @@ MSG_DEF(JSMSG_USER_DEFINED_ERROR, 206, 0, JSEXN_ERR, "JS_ReportError was cal MSG_DEF(JSMSG_WRONG_CONSTRUCTOR, 207, 1, JSEXN_TYPEERR, "wrong constructor called for {0}") MSG_DEF(JSMSG_BAD_GENERATOR_RETURN, 208, 1, JSEXN_TYPEERR, "generator function {0} returns a value") MSG_DEF(JSMSG_BAD_ANON_GENERATOR_RETURN, 209, 0, JSEXN_TYPEERR, "anonymous generator function returns a value") -MSG_DEF(JSMSG_UNUSED210, 210, 0, JSEXN_NONE, "") +MSG_DEF(JSMSG_PROTO_SETTING_SLOW, 210, 0, JSEXN_TYPEERR, "mutating the [[Prototype]] of an object will cause your code to run very slowly; instead create the object with the correct initial [[Prototype]] value using Object.create") MSG_DEF(JSMSG_IN_AFTER_FOR_NAME, 211, 0, JSEXN_SYNTAXERR, "missing 'in' or 'of' after for") MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE, 212, 2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value") MSG_DEF(JSMSG_UNUSED213, 213, 0, JSEXN_NONE, "") diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp index ef6e7a22d3bd..7c3bd5ce085d 100644 --- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -148,6 +148,14 @@ static bool ProtoSetter(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); + + // Do this here, rather than in |ProtoSetterImpl|, so even likely-buggy + // use of the __proto__ setter on unacceptable values, where no subsequent + // use occurs on an acceptable value, will trigger a warning. + RootedObject callee(cx, &args.callee()); + if (!GlobalObject::warnOnceAboutPrototypeMutation(cx, callee)) + return false; + return CallNonGenericMethod(cx, TestProtoThis, ProtoSetterImpl, args); } @@ -501,18 +509,17 @@ GlobalObject::isRuntimeCodeGenEnabled(JSContext *cx, Handle globa } /* static */ bool -GlobalObject::warnOnceAboutWatch(JSContext *cx, HandleObject obj) +GlobalObject::warnOnceAbout(JSContext *cx, HandleObject obj, uint32_t slot, unsigned errorNumber) { Rooted global(cx, &obj->global()); - HeapSlot &v = global->getSlotRef(WARNED_WATCH_DEPRECATED); + HeapSlot &v = global->getSlotRef(slot); if (v.isUndefined()) { - // Warn only once per global object. if (!JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, js_GetErrorMessage, nullptr, - JSMSG_OBJECT_WATCH_DEPRECATED)) + errorNumber)) { return false; } - v.init(global, HeapSlot::Slot, WARNED_WATCH_DEPRECATED, BooleanValue(true)); + v.init(global, HeapSlot::Slot, slot, BooleanValue(true)); } return true; } diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h index 18feb5d1d1ee..4864e7f11a6d 100644 --- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -105,7 +105,8 @@ class GlobalObject : public JSObject static const unsigned DATE_TIME_FORMAT_PROTO = NUMBER_FORMAT_PROTO + 1; static const unsigned REGEXP_STATICS = DATE_TIME_FORMAT_PROTO + 1; static const unsigned WARNED_WATCH_DEPRECATED = REGEXP_STATICS + 1; - static const unsigned RUNTIME_CODEGEN_ENABLED = WARNED_WATCH_DEPRECATED + 1; + static const unsigned WARNED_PROTO_SETTING_SLOW = WARNED_WATCH_DEPRECATED + 1; + static const unsigned RUNTIME_CODEGEN_ENABLED = WARNED_PROTO_SETTING_SLOW + 1; static const unsigned DEBUGGERS = RUNTIME_CODEGEN_ENABLED + 1; static const unsigned INTRINSICS = DEBUGGERS + 1; static const unsigned FLOAT32X4_TYPE_OBJECT = INTRINSICS + 1; @@ -151,6 +152,13 @@ class GlobalObject : public JSObject setSlot(INTRINSICS, ObjectValue(*obj)); } + // Emit the specified warning if the given slot in |obj|'s global isn't + // true, then set the slot to true. Thus calling this method warns once + // for each global object it's called on, and every other call does + // nothing. + static bool + warnOnceAbout(JSContext *cx, HandleObject obj, uint32_t slot, unsigned errorNumber); + public: Value getConstructor(JSProtoKey key) const { JS_ASSERT(key <= JSProto_LIMIT); @@ -605,7 +613,20 @@ class GlobalObject : public JSObject // Warn about use of the deprecated watch/unwatch functions in the global // in which |obj| was created, if no prior warning was given. - static bool warnOnceAboutWatch(JSContext *cx, HandleObject obj); + static bool warnOnceAboutWatch(JSContext *cx, HandleObject obj) { + // Temporarily disabled until we've provided a watch/unwatch workaround for + // debuggers like Firebug (bug 934669). + //return warnOnceAbout(cx, obj, WARNED_WATCH_DEPRECATED, JSMSG_OBJECT_WATCH_DEPRECATED); + return true; + } + + // Warn about use of the given __proto__ setter to attempt to mutate an + // object's [[Prototype]], if no prior warning was given. + static bool warnOnceAboutPrototypeMutation(JSContext *cx, HandleObject protoSetter) { + // Temporarily disabled until the second half of bug 948583 lands. + //return warnOnceAbout(cx, protoSetter, WARNED_PROTO_SETTING_SLOW, JSMSG_PROTO_SETTING_SLOW); + return true; + } static bool getOrCreateEval(JSContext *cx, Handle global, MutableHandleObject eval);