Bug 914314, part 2 - Factor out GetNonexistentProperty. It's nice to separate it from the main path, because everything it does except setting vp to undefined is nonstandard. No change in behavior. r=efaust.

--HG--
extra : rebase_source : 26662847e5fb0a269e54234bab4bbaebc990f558
This commit is contained in:
Jason Orendorff 2014-12-19 14:40:08 -06:00
Родитель 1c93221207
Коммит 514b796363
1 изменённых файлов: 94 добавлений и 70 удалений

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

@ -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 <AllowGC allowGC>
static MOZ_ALWAYS_INLINE bool
NativeGetPropertyInline(JSContext *cx,
@ -1646,7 +1734,7 @@ NativeGetPropertyInline(JSContext *cx,
typename MaybeRooted<jsid, allowGC>::HandleType id,
typename MaybeRooted<Value, allowGC>::MutableHandleType vp)
{
/* This call site is hot -- use the always-inlined LookupPropertyInline(). */
// This call site is hot -- use the always-inlined LookupPropertyInline().
typename MaybeRooted<JSObject*, allowGC>::RootType obj2(cx);
typename MaybeRooted<Shape*, allowGC>::RootType shape(cx);
if (!LookupPropertyInline<allowGC>(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<JSObject*, allowGC>::toHandle(obj),
return GetNonexistentProperty(cx,
MaybeRooted<NativeObject*, allowGC>::toHandle(obj),
MaybeRooted<jsid, allowGC>::toHandle(id),
MaybeRooted<Value, allowGC>::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;
MaybeRooted<JSObject*, allowGC>::toHandle(receiver),
MaybeRooted<Value, allowGC>::toMutableHandle(vp));
}
if (!obj2->isNative()) {