diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp index e9e94abc3f6b..1097a9704e1d 100644 --- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -1638,6 +1638,94 @@ Detecting(JSContext *cx, JSScript *script, jsbytecode *pc) return false; } +/* + * Finish getting the property `obj[id]` after looking at every object on the + * prototype chain and not finding any such property. + * + * Per the spec, this should just set the result to `undefined` and call it a + * day. However: + * + * 1. We add support for the nonstandard JSClass::getProperty hook. + * + * 2. This function also runs when we're evaluating an expression that's an + * Identifier (that is, unqualified name lookups), so we need to figure out + * if that's what's happening and throw a ReferenceError if so. + * + * 3. We also emit an optional warning for this. (It's not super useful on the + * web, as there are too many false positives, but anecdotally useful in + * Gecko code.) + */ +static bool +GetNonexistentProperty(JSContext *cx, HandleNativeObject obj, HandleId id, + HandleObject receiver, MutableHandleValue vp) +{ + vp.setUndefined(); + + // Non-standard extension: Call the getProperty class hook. It may set vp; + // if it doesn't, fall through to the warning/error checks below. + if (JSPropertyOp getProperty = obj->getClass()->getProperty) { + if (!CallJSPropertyOp(cx, getProperty, obj, id, vp)) + return false; + + if (!vp.isUndefined()) + return true; + } + + // If we are doing a name lookup, this is a ReferenceError. + jsbytecode *pc = nullptr; + RootedScript script(cx, cx->currentScript(&pc)); + if (!pc) + return true; + JSOp op = (JSOp) *pc; + if (op == JSOP_GETXPROP) { + JSAutoByteString printable; + if (js_ValueToPrintable(cx, IdToValue(id), &printable)) + js_ReportIsNotDefined(cx, printable.ptr()); + return false; + } + + // Give a strict warning if foo.bar is evaluated by a script for an object + // foo with no property named 'bar'. + // + // Don't warn if extra warnings not enabled or for random getprop + // operations. + if (!cx->compartment()->options().extraWarnings(cx) || (op != JSOP_GETPROP && op != JSOP_GETELEM)) + return true; + + // Don't warn repeatedly for the same script. + if (!script || script->warnedAboutUndefinedProp()) + return true; + + // Don't warn in self-hosted code (where the further presence of + // JS::RuntimeOptions::werror() would result in impossible-to-avoid + // errors to entirely-innocent client code). + if (script->selfHosted()) + return true; + + // We may just be checking if that object has an iterator. + if (JSID_IS_ATOM(id, cx->names().iteratorIntrinsic)) + return true; + + // Do not warn about tests like (obj[prop] == undefined). + pc += js_CodeSpec[op].length; + if (Detecting(cx, script, pc)) + return true; + + unsigned flags = JSREPORT_WARNING | JSREPORT_STRICT; + script->setWarnedAboutUndefinedProp(); + + // Ok, bad undefined property reference: whine about it. + RootedValue val(cx, IdToValue(id)); + if (!js_ReportValueErrorFlags(cx, flags, JSMSG_UNDEFINED_PROP, + JSDVG_IGNORE_STACK, val, js::NullPtr(), + nullptr, nullptr)) + { + return false; + } + + return true; +} + template static MOZ_ALWAYS_INLINE bool NativeGetPropertyInline(JSContext *cx, @@ -1646,7 +1734,7 @@ NativeGetPropertyInline(JSContext *cx, typename MaybeRooted::HandleType id, typename MaybeRooted::MutableHandleType vp) { - /* This call site is hot -- use the always-inlined LookupPropertyInline(). */ + // This call site is hot -- use the always-inlined LookupPropertyInline(). typename MaybeRooted::RootType obj2(cx); typename MaybeRooted::RootType shape(cx); if (!LookupPropertyInline(cx, obj, id, &obj2, &shape)) @@ -1656,75 +1744,11 @@ NativeGetPropertyInline(JSContext *cx, if (!allowGC) return false; - vp.setUndefined(); - - if (JSPropertyOp getProperty = obj->getClass()->getProperty) { - if (!CallJSPropertyOp(cx, getProperty, - MaybeRooted::toHandle(obj), - MaybeRooted::toHandle(id), - MaybeRooted::toMutableHandle(vp))) - { - return false; - } - } - - /* - * Give a strict warning if foo.bar is evaluated by a script for an - * object foo with no property named 'bar'. - */ - if (vp.isUndefined()) { - jsbytecode *pc = nullptr; - RootedScript script(cx, cx->currentScript(&pc)); - if (!pc) - return true; - JSOp op = (JSOp) *pc; - - if (op == JSOP_GETXPROP) { - /* Undefined property during a name lookup, report an error. */ - JSAutoByteString printable; - if (js_ValueToPrintable(cx, IdToValue(id), &printable)) - js_ReportIsNotDefined(cx, printable.ptr()); - return false; - } - - /* Don't warn if extra warnings not enabled or for random getprop operations. */ - if (!cx->compartment()->options().extraWarnings(cx) || (op != JSOP_GETPROP && op != JSOP_GETELEM)) - return true; - - /* Don't warn repeatedly for the same script. */ - if (!script || script->warnedAboutUndefinedProp()) - return true; - - /* - * Don't warn in self-hosted code (where the further presence of - * JS::RuntimeOptions::werror() would result in impossible-to-avoid - * errors to entirely-innocent client code). - */ - if (script->selfHosted()) - return true; - - /* We may just be checking if that object has an iterator. */ - if (JSID_IS_ATOM(id, cx->names().iteratorIntrinsic)) - return true; - - /* Do not warn about tests like (obj[prop] == undefined). */ - pc += js_CodeSpec[op].length; - if (Detecting(cx, script, pc)) - return true; - - unsigned flags = JSREPORT_WARNING | JSREPORT_STRICT; - script->setWarnedAboutUndefinedProp(); - - /* Ok, bad undefined property reference: whine about it. */ - RootedValue val(cx, IdToValue(id)); - if (!js_ReportValueErrorFlags(cx, flags, JSMSG_UNDEFINED_PROP, - JSDVG_IGNORE_STACK, val, js::NullPtr(), - nullptr, nullptr)) - { - return false; - } - } - return true; + return GetNonexistentProperty(cx, + MaybeRooted::toHandle(obj), + MaybeRooted::toHandle(id), + MaybeRooted::toHandle(receiver), + MaybeRooted::toMutableHandle(vp)); } if (!obj2->isNative()) {