Merge tracemonkey to mozilla-central. a=blockers

This commit is contained in:
Robert Sayre 2011-02-25 10:00:38 -08:00
commit 954ddf8007
35 changed files with 437 additions and 217 deletions

View File

@ -0,0 +1,12 @@
this.name = "outer";
var sb = evalcx('');
sb.name = "inner";
sb.parent = this;
function f() { return this.name; }
assertEq(evalcx('this.f = parent.f;\n' +
'var s = "";\n' +
'for (i = 0; i < 10; ++i)\n' +
' s += f();\n' +
's',
sb),
"innerinnerinnerinnerinnerinnerinnerinnerinnerinner");

View File

@ -0,0 +1,19 @@
this.name = "outer";
var sb = evalcx('');
sb.name = "inner";
sb.parent = this;
function f() { return this.name; }
f.notMuchTodo = '42';
assertEq(evalcx('{\n' +
' let f = parent.f;\n' +
' let name = "block";\n' +
' (function () {\n' +
' eval(f.notMuchTodo);\n' + // reify Block
' var s = "";\n' +
' for (i = 0; i < 10; ++i)\n' +
' s += f();\n' +
' return s;\n' +
' })();\n' +
'}',
sb),
"outerouterouterouterouterouterouterouterouterouter");

View File

@ -0,0 +1,20 @@
this.name = "outer";
var sb = evalcx('');
sb.name = "inner";
sb.parent = this;
function f() { return this.name; }
f.notMuchTodo = '42';
assertEq(evalcx('(function () {\n' +
' arguments = null;\n' + // force heavyweight
' var f = parent.f;\n' +
' var name = "call";\n' +
' return (function () {\n' +
' eval(f.notMuchTodo);\n' + // reify Call, make f() compile to JSOP_CALLNAME
' var s = "";\n' +
' for (i = 0; i < 10; ++i)\n' +
' s += f();\n' +
' return s;\n' +
' })();\n' +
'})()',
sb),
"outerouterouterouterouterouterouterouterouterouter");

View File

@ -0,0 +1,19 @@
this.name = "outer";
var sb = evalcx('');
sb.name = "inner";
sb.parent = this;
this.f = function name(outer) {
if (outer) {
return function () {
return name(false);
}();
}
return this.name;
}
assertEq(evalcx('this.f = parent.f;\n' +
'var s = "";\n' +
'for (i = 0; i < 10; ++i)\n' +
' s += f(true);\n' +
's',
sb),
"outerouterouterouterouterouterouterouterouterouter");

View File

@ -463,12 +463,12 @@ JSCompartment::mark(JSTracer *trc)
{
if (IS_GC_MARKING_TRACER(trc)) {
JSRuntime *rt = trc->context->runtime;
if (rt->gcCurrentCompartment != NULL && rt->gcCurrentCompartment != this)
if (rt->gcCurrentCompartment && rt->gcCurrentCompartment != this)
return;
if (marked)
return;
marked = true;
}

View File

@ -449,7 +449,7 @@ struct JS_FRIEND_API(JSCompartment) {
bool init();
/* Mark cross-compartment pointers. */
/* Mark cross-compartment wrappers. */
void markCrossCompartment(JSTracer *trc);
/* Mark this compartment's local roots. */

View File

@ -778,7 +778,6 @@ js_watch_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
if (!ok)
goto out;
shape = obj->nativeLookup(propid);
JS_ASSERT_IF(!shape, !wp->setter);
if (!shape) {
ok = true;

View File

@ -2668,7 +2668,7 @@ LookupInterpretedFunctionPrototype(JSContext *cx, JSObject *funobj)
return shape;
}
}
} /* namespace js */
static JSBool
ThrowTypeError(JSContext *cx, uintN argc, Value *vp)

View File

@ -2189,9 +2189,12 @@ SweepCompartments(JSContext *cx, JSGCInvocationKind gckind)
while (read < end) {
JSCompartment *compartment = *read++;
/* Unmarked compartments containing marked objects don't get deleted, except LAST_CONTEXT GC is performed. */
if ((!compartment->isMarked() && compartment->arenaListsAreEmpty())
|| gckind == GC_LAST_CONTEXT)
/*
* Unmarked compartments containing marked objects don't get deleted,
* except when LAST_CONTEXT GC is performed.
*/
if ((!compartment->isMarked() && compartment->arenaListsAreEmpty()) ||
gckind == GC_LAST_CONTEXT)
{
JS_ASSERT(compartment->freeLists.isEmpty());
if (callback)
@ -2356,13 +2359,16 @@ MarkAndSweepCompartment(JSContext *cx, JSCompartment *comp, JSGCInvocationKind g
comp->finalizeStringArenaLists(cx);
TIMESTAMP(sweepStringEnd);
#ifdef DEBUG
/* Make sure that we didn't mark a Shape in another compartment. */
/*
* Unmark all shapes. Even a per-compartment GC can mark shapes in other
* compartments, and we need to clear these bits. See bug 635873. This will
* be fixed in bug 569422.
*/
for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
JS_ASSERT_IF(*c != comp, (*c)->propertyTree.checkShapesAllUnmarked(cx));
(*c)->propertyTree.unmarkShapes(cx);
PropertyTree::dumpShapes(cx);
#endif
TIMESTAMP(sweepShapeEnd);
/*
* Destroy arenas after we finished the sweeping so finalizers can safely
@ -2482,6 +2488,7 @@ MarkAndSweep(JSContext *cx, JSGCInvocationKind gckind GCTIMER_PARAM)
(*c)->propertyTree.sweepShapes(cx);
PropertyTree::dumpShapes(cx);
TIMESTAMP(sweepShapeEnd);
SweepCompartments(cx, gckind);
@ -2721,7 +2728,7 @@ GCUntilDone(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM
* NULL to look for violations.
*/
SwitchToCompartment(cx, (JSCompartment *)NULL);
JS_ASSERT(!rt->gcCurrentCompartment);
rt->gcCurrentCompartment = comp;

View File

@ -422,18 +422,19 @@ GCTimer::finish(bool lastGC) {
gcFile = fopen("gcTimer.dat", "a");
fprintf(gcFile, " AppTime, Total, Mark, Sweep, FinObj,");
fprintf(gcFile, " FinStr, Destroy, newChunks, destroyChunks\n");
fprintf(gcFile, " FinStr, SwShapes, Destroy, +Chunks, -Chunks\n");
}
JS_ASSERT(gcFile);
fprintf(gcFile, "%12.1f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, %7.1f, ",
fprintf(gcFile, "%12.1f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, %8.1f, %6.1f, ",
(double)(enter - getFirstEnter()) / 1e6,
(double)(end - enter) / 1e6,
(double)(startSweep - startMark) / 1e6,
(double)(sweepDestroyEnd - startSweep) / 1e6,
(double)(sweepObjectEnd - startSweep) / 1e6,
(double)(sweepStringEnd - sweepObjectEnd) / 1e6,
(double)(sweepDestroyEnd - sweepStringEnd) / 1e6);
fprintf(gcFile, "%10d, %10d \n", newChunkCount,
(double)(sweepShapeEnd - sweepStringEnd) / 1e6,
(double)(sweepDestroyEnd - sweepShapeEnd) / 1e6);
fprintf(gcFile, "%7d, %7d \n", newChunkCount,
destroyChunkCount);
fflush(gcFile);

View File

@ -148,6 +148,7 @@ struct GCTimer {
uint64 startSweep;
uint64 sweepObjectEnd;
uint64 sweepStringEnd;
uint64 sweepShapeEnd;
uint64 sweepDestroyEnd;
uint64 end;

View File

@ -2190,6 +2190,15 @@ ScriptPrologue(JSContext *cx, JSStackFrame *fp)
namespace js {
#ifdef __APPLE__
static JS_NEVER_INLINE bool
NEVER_INLINE_ComputeImplicitThis(JSContext *cx, JSObject *obj, const Value &funval, Value *vp)
{
return ComputeImplicitThis(cx, obj, funval, vp);
}
#define ComputeImplicitThis(cx, obj, funval, vp) NEVER_INLINE_ComputeImplicitThis(cx, obj, funval, vp)
#endif
JS_REQUIRES_STACK JS_NEVER_INLINE bool
Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInterpMode interpMode)
{
@ -2213,8 +2222,8 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte
* expect from looking at the code. (We do omit POPs after SETs;
* unfortunate, but not worth fixing.)
*/
# define LOG_OPCODE(OP) JS_BEGIN_MACRO \
if (JS_UNLIKELY(cx->logfp != NULL) && \
# define LOG_OPCODE(OP) JS_BEGIN_MACRO \
if (JS_UNLIKELY(cx->logfp != NULL) && \
(OP) == *regs.pc) \
js_LogOpcode(cx); \
JS_END_MACRO
@ -2337,14 +2346,17 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte
#define LOAD_DOUBLE(PCOFF, dbl) \
(dbl = script->getConst(GET_FULL_INDEX(PCOFF)).toDouble())
#ifdef JS_METHODJIT
bool useMethodJIT = cx->methodJitEnabled && interpMode == JSINTERP_NORMAL;
#else
bool useMethodJIT = false;
#endif
#ifdef JS_METHODJIT
#define RESET_USE_METHODJIT() \
JS_BEGIN_MACRO \
useMethodJIT = cx->methodJitEnabled && \
interpMode == JSINTERP_NORMAL && \
script->getJITStatus(regs.fp->isConstructing()) != JITScript_Invalid; \
JS_END_MACRO
#define MONITOR_BRANCH_METHODJIT() \
JS_BEGIN_MACRO \
mjit::CompileStatus status = \
@ -2366,10 +2378,12 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte
#else
#define RESET_USE_METHODJIT() ((void) 0)
#define MONITOR_BRANCH_METHODJIT() ((void) 0)
#endif
#ifdef JS_TRACER
#ifdef MOZ_TRACEVIS
@ -2574,6 +2588,8 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInte
CHECK_INTERRUPT_HANDLER();
RESET_USE_METHODJIT();
/* State communicated between non-local jumps: */
JSBool interpReturnOK;
JSAtom *atomNotDefined;
@ -2894,6 +2910,7 @@ BEGIN_CASE(JSOP_STOP)
atoms = FrameAtomBase(cx, regs.fp);
/* Resume execution in the calling frame. */
RESET_USE_METHODJIT();
JS_ASSERT(inlineCallCount);
inlineCallCount--;
if (JS_LIKELY(interpReturnOK)) {
@ -4737,6 +4754,7 @@ BEGIN_CASE(JSOP_FUNCALL)
if (newfun->isHeavyweight() && !js_GetCallObject(cx, regs.fp))
goto error;
RESET_USE_METHODJIT();
inlineCallCount++;
JS_RUNTIME_METER(rt, inlineCalls);
@ -4802,26 +4820,13 @@ BEGIN_CASE(JSOP_SETCALL)
}
END_CASE(JSOP_SETCALL)
#define SLOW_PUSH_THISV(cx, obj) \
JS_BEGIN_MACRO \
Class *clasp; \
JSObject *thisp = obj; \
if (!thisp->getParent() || \
(clasp = thisp->getClass()) == &js_CallClass || \
clasp == &js_BlockClass || \
clasp == &js_DeclEnvClass) { \
/* Push the ImplicitThisValue for the Environment Record */ \
/* associated with obj. See ES5 sections 10.2.1.1.6 and */ \
/* 10.2.1.2.6 (ImplicitThisValue) and section 11.2.3 */ \
/* (Function Calls). */ \
PUSH_UNDEFINED(); \
} else { \
thisp = thisp->thisObject(cx); \
if (!thisp) \
goto error; \
PUSH_OBJECT(*thisp); \
} \
JS_END_MACRO
#define PUSH_IMPLICIT_THIS(cx, obj, funval) \
JS_BEGIN_MACRO \
Value v; \
if (!ComputeImplicitThis(cx, obj, funval, &v)) \
goto error; \
PUSH_COPY(v); \
JS_END_MACRO \
BEGIN_CASE(JSOP_GETGNAME)
BEGIN_CASE(JSOP_CALLGNAME)
@ -4851,20 +4856,9 @@ BEGIN_CASE(JSOP_CALLNAME)
PUSH_COPY(rval);
}
/*
* Push results, the same as below, but with a prop$ hit there
* is no need to test for the unusual and uncacheable case where
* the caller determines |this|.
*/
#if DEBUG
Class *clasp;
JS_ASSERT(!obj->getParent() ||
(clasp = obj->getClass()) == &js_CallClass ||
clasp == &js_BlockClass ||
clasp == &js_DeclEnvClass);
#endif
JS_ASSERT(obj->isGlobal() || IsCacheableNonGlobalScope(obj));
if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME)
PUSH_UNDEFINED();
PUSH_IMPLICIT_THIS(cx, obj, regs.sp[-1]);
len = JSOP_NAME_LENGTH;
DO_NEXT_OP(len);
}
@ -4902,7 +4896,7 @@ BEGIN_CASE(JSOP_CALLNAME)
/* obj must be on the scope chain, thus not a function. */
if (op == JSOP_CALLNAME || op == JSOP_CALLGNAME)
SLOW_PUSH_THISV(cx, obj);
PUSH_IMPLICIT_THIS(cx, obj, rval);
}
END_CASE(JSOP_NAME)
@ -6405,7 +6399,7 @@ BEGIN_CASE(JSOP_XMLNAME)
goto error;
regs.sp[-1] = rval;
if (op == JSOP_CALLXMLNAME)
SLOW_PUSH_THISV(cx, obj);
PUSH_IMPLICIT_THIS(cx, obj, rval);
}
END_CASE(JSOP_XMLNAME)

View File

@ -479,7 +479,7 @@ JSStackFrame::callObj() const
JS_ASSERT(hasCallObj());
JSObject *pobj = &scopeChain();
while (JS_UNLIKELY(pobj->getClass() != &js_CallClass)) {
JS_ASSERT(js_IsCacheableNonGlobalScope(pobj) || pobj->isWith());
JS_ASSERT(js::IsCacheableNonGlobalScope(pobj) || pobj->isWith());
pobj = pobj->getParent();
}
return *pobj;
@ -666,6 +666,83 @@ class PrimitiveBehavior<double> {
} // namespace detail
/*
* Compute the implicit |this| parameter for a call expression where the callee
* is an unqualified name reference.
*
* We can avoid computing |this| eagerly and push the implicit callee-coerced
* |this| value, undefined, according to this decision tree:
*
* 1. If the called value, funval, is not an object, bind |this| to undefined.
*
* 2. The nominal |this|, obj, has one of Block, Call, or DeclEnv class (this
* is what IsCacheableNonGlobalScope tests). Such objects-as-scopes must be
* censored.
*
* 3. obj is a global. There are several sub-cases:
*
* a) obj is a proxy: we try unwrapping it (see jswrapper.cpp) in order to find
* a function object inside. If the proxy is not a wrapper, or else it wraps
* a non-function, then bind |this| to undefined per ES5-strict/Harmony.
*
* [Else fall through with callee pointing to an unwrapped function object.]
*
* b) If callee is a function (after unwrapping if necessary), check whether it
* is interpreted and in strict mode. If so, then bind |this| to undefined
* per ES5 strict.
*
* c) Now check that callee is scoped by the same global object as the object
* in which its unqualified name was bound as a property. ES1-3 bound |this|
* to the name's "Reference base object", which in the context of multiple
* global objects may not be the callee's global. If globals match, bind
* |this| to undefined.
*
* This is a backward compatibility measure; see bug 634590.
*
* 4. Finally, obj is neither a declarative scope object to be censored, nor a
* global where the callee requires no backward-compatible special handling
* or future-proofing based on (explicit or imputed by Harmony status in the
* proxy case) strict mode opt-in. Bind |this| to obj->thisObject().
*
* We set *vp to undefined early to reduce code size and bias this code for the
* common and future-friendly cases.
*/
inline bool
ComputeImplicitThis(JSContext *cx, JSObject *obj, const Value &funval, Value *vp)
{
vp->setUndefined();
if (!funval.isObject())
return true;
if (!obj->isGlobal()) {
if (IsCacheableNonGlobalScope(obj))
return true;
} else {
JSObject *callee = &funval.toObject();
if (callee->isProxy()) {
callee = callee->unwrap();
if (!callee->isFunction())
return true; // treat any non-wrapped-function proxy as strict
}
if (callee->isFunction()) {
JSFunction *fun = callee->getFunctionPrivate();
if (fun->isInterpreted() && fun->inStrictMode())
return true;
}
if (callee->getGlobal() == cx->fp()->scopeChain().getGlobal())
return true;;
}
obj = obj->thisObject(cx);
if (!obj)
return false;
vp->setObject(*obj);
return true;
}
template <typename T>
bool
GetPrimitiveThis(JSContext *cx, Value *vp, T *v)

View File

@ -5052,7 +5052,7 @@ js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult,
parent = obj->getParent();
for (scopeIndex = 0;
parent
? js_IsCacheableNonGlobalScope(obj)
? IsCacheableNonGlobalScope(obj)
: !obj->getOps()->lookupProperty;
++scopeIndex) {
protoIndex =
@ -5162,11 +5162,11 @@ js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id)
* farther checks or lookups. For details see the JSOP_BINDNAME case of
* js_Interpret.
*
* The test order here matters because js_IsCacheableNonGlobalScope
* The test order here matters because IsCacheableNonGlobalScope
* must not be passed a global object (i.e. one with null parent).
*/
for (int scopeIndex = 0;
!obj->getParent() || js_IsCacheableNonGlobalScope(obj);
!obj->getParent() || IsCacheableNonGlobalScope(obj);
scopeIndex++) {
JSObject *pobj;
JSProperty *prop;

View File

@ -1661,16 +1661,19 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
JSObject **objp, JSProperty **propp);
extern JS_FRIEND_DATA(js::Class) js_CallClass;
extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass;
namespace js {
/*
* We cache name lookup results only for the global object or for native
* non-global objects without prototype or with prototype that never mutates,
* see bug 462734 and bug 487039.
*/
inline bool
js_IsCacheableNonGlobalScope(JSObject *obj)
static inline bool
IsCacheableNonGlobalScope(JSObject *obj)
{
extern JS_FRIEND_DATA(js::Class) js_CallClass;
extern JS_FRIEND_DATA(js::Class) js_DeclEnvClass;
JS_ASSERT(obj->getParent());
js::Class *clasp = obj->getClass();
@ -1682,6 +1685,8 @@ js_IsCacheableNonGlobalScope(JSObject *obj)
return cacheable;
}
}
/*
* If cacheResult is false, return JS_NO_PROP_CACHE_FILL on success.
*/

View File

@ -49,6 +49,7 @@
#include "jsobj.h"
#include "jsprobes.h"
#include "jspropertytree.h"
#include "jsproxy.h"
#include "jsscope.h"
#include "jsstaticcheck.h"
#include "jsxml.h"
@ -60,6 +61,7 @@
#include "jsscopeinlines.h"
#include "jsstr.h"
#include "jsfuninlines.h"
#include "jsgcinlines.h"
#include "jsprobes.h"

View File

@ -493,8 +493,10 @@ PropertyCache::purge(JSContext *cx)
}
void
PropertyCache::purgeForScript(JSScript *script)
PropertyCache::purgeForScript(JSContext *cx, JSScript *script)
{
JS_ASSERT(!cx->runtime->gcRunning);
for (PropertyCacheEntry *entry = table; entry < table + SIZE; entry++) {
if (JS_UPTRDIFF(entry->kpc, script->code) < script->length) {
entry->kpc = NULL;

View File

@ -269,7 +269,7 @@ class PropertyCache
const js::Shape *shape, JSBool adding = false);
void purge(JSContext *cx);
void purgeForScript(JSScript *script);
void purgeForScript(JSContext *cx, JSScript *script);
};
} /* namespace js */

View File

@ -623,8 +623,8 @@ js::PropertyTree::sweepShapes(JSContext *cx)
#endif /* DEBUG */
}
bool
js::PropertyTree::checkShapesAllUnmarked(JSContext *cx)
void
js::PropertyTree::unmarkShapes(JSContext *cx)
{
JSArena **ap = &arenaPool.first.next;
while (JSArena *a = *ap) {
@ -635,13 +635,10 @@ js::PropertyTree::checkShapesAllUnmarked(JSContext *cx)
if (JSID_IS_VOID(shape->id))
continue;
if (shape->marked())
return false;
shape->clearMark();
}
ap = &a->next;
}
return true;
}
void

View File

@ -128,7 +128,7 @@ class PropertyTree
void orphanChildren(js::Shape *shape);
void sweepShapes(JSContext *cx);
bool checkShapesAllUnmarked(JSContext *cx);
void unmarkShapes(JSContext *cx);
static void dumpShapes(JSContext *cx);
#ifdef DEBUG

View File

@ -1483,11 +1483,6 @@ PrintPropertyMethod(JSTracer *trc, char *buf, size_t bufsize)
void
Shape::trace(JSTracer *trc) const
{
#ifdef DEBUG
JSRuntime *rt = trc->context->runtime;
JS_ASSERT_IF(rt->gcCurrentCompartment, compartment == rt->gcCurrentCompartment);
#endif
if (IS_GC_MARKING_TRACER(trc))
mark();

View File

@ -1617,15 +1617,11 @@ DestroyScript(JSContext *cx, JSScript *script)
/* FIXME: bug 506341; would like to do this only if regenerating shapes. */
if (!cx->runtime->gcRunning) {
JSStackFrame *fp = js_GetTopStackFrame(cx);
if (!(fp && fp->isEvalFrame())) {
JS_PROPERTY_CACHE(cx).purgeForScript(script);
JS_PROPERTY_CACHE(cx).purgeForScript(cx, script);
#ifdef CHECK_SCRIPT_OWNER
JS_ASSERT(script->owner == cx->thread);
JS_ASSERT(script->owner == cx->thread);
#endif
}
}
#ifdef JS_TRACER

View File

@ -6595,7 +6595,7 @@ ScopeChainCheck(JSContext* cx, TreeFragment* f)
*/
JSObject* child = &cx->fp()->scopeChain();
while (JSObject* parent = child->getParent()) {
if (!js_IsCacheableNonGlobalScope(child)) {
if (!IsCacheableNonGlobalScope(child)) {
debug_only_print0(LC_TMTracer,"Blacklist: non-cacheable object on scope chain.\n");
Blacklist((jsbytecode*) f->root->ip);
return false;
@ -7102,7 +7102,12 @@ RecordLoopEdge(JSContext* cx, TraceMonitor* tm, uintN& inlineCallCount)
tm->recorder->assertInsideLoop();
jsbytecode* pc = cx->regs->pc;
if (pc == tm->recorder->tree->ip) {
tm->recorder->closeLoop();
AbortableRecordingStatus status = tm->recorder->closeLoop();
if (status != ARECORD_COMPLETED) {
if (tm->recorder)
AbortRecording(cx, "closeLoop failed");
return MONITOR_NOT_RECORDING;
}
} else {
MonitorResult r = TraceRecorder::recordLoopEdge(cx, tm->recorder, inlineCallCount);
JS_ASSERT((r == MONITOR_RECORDING) == (tm->recorder != NULL));
@ -15182,7 +15187,7 @@ TraceRecorder::traverseScopeChain(JSObject *obj, LIns *obj_ins, JSObject *target
/* There was a call object, or should be a call object now. */
for (;;) {
if (obj != globalObj) {
if (!js_IsCacheableNonGlobalScope(obj))
if (!IsCacheableNonGlobalScope(obj))
RETURN_STOP("scope chain lookup crosses non-cacheable object");
// We must guard on the shape of all call objects for heavyweight functions

View File

@ -1187,7 +1187,7 @@ class ScopeNameCompiler : public PICStubCompiler
JS_ASSERT_IF(pic.kind == ic::PICInfo::XNAME, getprop.obj == tobj);
while (tobj && tobj != getprop.holder) {
if (!js_IsCacheableNonGlobalScope(tobj))
if (!IsCacheableNonGlobalScope(tobj))
return disable("non-cacheable scope chain object");
JS_ASSERT(tobj->isNative());
@ -1539,7 +1539,7 @@ class BindNameCompiler : public PICStubCompiler
JSObject *tobj = scopeChain;
Address parent(pic.objReg, offsetof(JSObject, parent));
while (tobj && tobj != obj) {
if (!js_IsCacheableNonGlobalScope(tobj))
if (!IsCacheableNonGlobalScope(tobj))
return disable("non-cacheable obj in scope chain");
masm.loadPtr(parent, pic.objReg);
Jump nullTest = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg);

View File

@ -333,86 +333,56 @@ NameOp(VMFrame &f, JSObject *obj, bool callname = false)
JS_PROPERTY_CACHE(cx).test(cx, f.regs.pc, obj, obj2, entry, atom);
if (!atom) {
if (entry->vword.isFunObj()) {
f.regs.sp++;
f.regs.sp[-1].setObject(entry->vword.toFunObj());
rval.setObject(entry->vword.toFunObj());
} else if (entry->vword.isSlot()) {
uintN slot = entry->vword.toSlot();
f.regs.sp++;
f.regs.sp[-1] = obj2->nativeGetSlot(slot);
rval = obj2->nativeGetSlot(slot);
} else {
JS_ASSERT(entry->vword.isShape());
shape = entry->vword.toShape();
NATIVE_GET(cx, obj, obj2, shape, JSGET_METHOD_BARRIER, &rval, return NULL);
f.regs.sp++;
f.regs.sp[-1] = rval;
}
/*
* Push results, the same as below, but with a prop$ hit there
* is no need to test for the unusual and uncacheable case where
* the caller determines |this|.
*/
#if DEBUG
Class *clasp;
JS_ASSERT(!obj->getParent() ||
(clasp = obj->getClass()) == &js_CallClass ||
clasp == &js_BlockClass ||
clasp == &js_DeclEnvClass);
#endif
if (callname) {
f.regs.sp++;
f.regs.sp[-1].setUndefined();
}
return obj;
}
jsid id;
id = ATOM_TO_JSID(atom);
JSProperty *prop;
if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
return NULL;
if (!prop) {
/* Kludge to allow (typeof foo == "undefined") tests. */
JSOp op2 = js_GetOpcode(cx, f.fp()->script(), f.regs.pc + JSOP_NAME_LENGTH);
if (op2 == JSOP_TYPEOF) {
f.regs.sp++;
f.regs.sp[-1].setUndefined();
return obj;
}
ReportAtomNotDefined(cx, atom);
return NULL;
}
/* Take the slow path if prop was not found in a native object. */
if (!obj->isNative() || !obj2->isNative()) {
if (!obj->getProperty(cx, id, &rval))
return NULL;
JS_ASSERT(obj->isGlobal() || IsCacheableNonGlobalScope(obj));
} else {
shape = (Shape *)prop;
JSObject *normalized = obj;
if (normalized->getClass() == &js_WithClass && !shape->hasDefaultGetter())
normalized = js_UnwrapWithObject(cx, normalized);
NATIVE_GET(cx, normalized, obj2, shape, JSGET_METHOD_BARRIER, &rval, return NULL);
jsid id;
id = ATOM_TO_JSID(atom);
JSProperty *prop;
if (!js_FindPropertyHelper(cx, id, true, &obj, &obj2, &prop))
return NULL;
if (!prop) {
/* Kludge to allow (typeof foo == "undefined") tests. */
JSOp op2 = js_GetOpcode(cx, f.fp()->script(), f.regs.pc + JSOP_NAME_LENGTH);
if (op2 == JSOP_TYPEOF) {
f.regs.sp++;
f.regs.sp[-1].setUndefined();
return obj;
}
ReportAtomNotDefined(cx, atom);
return NULL;
}
/* Take the slow path if prop was not found in a native object. */
if (!obj->isNative() || !obj2->isNative()) {
if (!obj->getProperty(cx, id, &rval))
return NULL;
} else {
shape = (Shape *)prop;
JSObject *normalized = obj;
if (normalized->getClass() == &js_WithClass && !shape->hasDefaultGetter())
normalized = js_UnwrapWithObject(cx, normalized);
NATIVE_GET(cx, normalized, obj2, shape, JSGET_METHOD_BARRIER, &rval, return NULL);
}
}
f.regs.sp++;
f.regs.sp[-1] = rval;
*f.regs.sp++ = rval;
if (callname) {
Class *clasp;
JSObject *thisp = obj;
if (!thisp->getParent() ||
(clasp = thisp->getClass()) == &js_CallClass ||
clasp == &js_BlockClass ||
clasp == &js_DeclEnvClass) {
f.regs.sp++;
f.regs.sp[-1].setUndefined();
} else {
thisp = thisp->thisObject(cx);
if (!thisp)
return NULL;
f.regs.sp++;
f.regs.sp[-1].setObject(*thisp);
}
Value thisv;
if (!ComputeImplicitThis(cx, obj, rval, &thisv))
return NULL;
*f.regs.sp++ = thisv;
}
return obj;
}

View File

@ -3639,7 +3639,6 @@ resolver_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **
JS_ALWAYS_TRUE(JS_GetReservedSlot(cx, obj, 0, &v));
return CopyProperty(cx, obj, JSVAL_TO_OBJECT(v), id, flags, objp);
}
static JSNewResolveOp resolver_resolve_check = resolver_resolve;
static JSBool
resolver_enumerate(JSContext *cx, JSObject *obj)
@ -4477,6 +4476,40 @@ StringStats(JSContext *cx, uintN argc, jsval *vp)
return true;
}
enum CompartmentKind { SAME_COMPARTMENT, NEW_COMPARTMENT };
static JSObject *
NewGlobalObject(JSContext *cx, CompartmentKind compartment);
JSBool
NewGlobal(JSContext *cx, uintN argc, jsval *vp)
{
if (argc != 1 || !JSVAL_IS_STRING(JS_ARGV(cx, vp)[0])) {
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "newGlobal");
return false;
}
JSString *str = JSVAL_TO_STRING(JS_ARGV(cx, vp)[0]);
JSBool equalSame = JS_FALSE, equalNew = JS_FALSE;
if (!JS_StringEqualsAscii(cx, str, "same-compartment", &equalSame) ||
!JS_StringEqualsAscii(cx, str, "new-compartment", &equalNew)) {
return false;
}
if (!equalSame && !equalNew) {
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "newGlobal");
return false;
}
JSObject *global = NewGlobalObject(cx, equalSame ? SAME_COMPARTMENT : NEW_COMPARTMENT);
if (!global)
return false;
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(global));
return true;
}
static JSFunctionSpec shell_functions[] = {
JS_FN("version", Version, 0,0),
JS_FN("revertVersion", RevertVersion, 0,0),
@ -4574,6 +4607,7 @@ static JSFunctionSpec shell_functions[] = {
JS_FN("mjitstats", MJitStats, 0,0),
#endif
JS_FN("stringstats", StringStats, 0,0),
JS_FN("newGlobal", NewGlobal, 1,0),
JS_FS_END
};
@ -4599,7 +4633,7 @@ static const char *const shell_help_messages[] = {
"assertEq(actual, expected[, msg])\n"
" Throw if the first two arguments are not the same (both +0 or both -0,\n"
" both NaN, or non-zero and ===)",
"assertJit() Throw if the calling function failed to JIT\n",
"assertJit() Throw if the calling function failed to JIT",
"gc() Run the garbage collector",
#ifdef JS_GCMETER
"gcstats() Print garbage collector statistics",
@ -4644,7 +4678,7 @@ static const char *const shell_help_messages[] = {
"dumpObject() Dump an internal representation of an object",
"notes([fun]) Show source notes for functions",
"tracing([true|false|filename]) Turn bytecode execution tracing on/off.\n"
" With filename, send to file.\n",
" With filename, send to file.",
"stats([string ...]) Dump 'arena', 'atom', 'global' stats",
#endif
#ifdef TEST_CVTARGS
@ -4697,24 +4731,37 @@ static const char *const shell_help_messages[] = {
" Get/Set the limit in seconds for the execution time for the current context.\n"
" A negative value (default) means that the execution time is unlimited.",
"elapsed() Execution time elapsed for the current context.",
"parent(obj) Returns the parent of obj.\n",
"wrap(obj) Wrap an object into a noop wrapper.\n",
"serialize(sd) Serialize sd using JS_WriteStructuredClone. Returns a TypedArray.\n",
"deserialize(a) Deserialize data generated by serialize.\n",
"parent(obj) Returns the parent of obj.",
"wrap(obj) Wrap an object into a noop wrapper.",
"serialize(sd) Serialize sd using JS_WriteStructuredClone. Returns a TypedArray.",
"deserialize(a) Deserialize data generated by serialize.",
#ifdef JS_METHODJIT
"mjitstats() Return stats on mjit memory usage.\n",
"mjitstats() Return stats on mjit memory usage.",
#endif
"stringstats() Return stats on string memory usage.\n"
"stringstats() Return stats on string memory usage.",
"newGlobal(kind) Return a new global object, in the current\n"
" compartment if kind === 'same-compartment' or in a\n"
" new compartment if kind === 'new-compartment'",
/* Keep these last: see the static assertion below. */
#ifdef MOZ_PROFILING
"startProfiling() Start a profiling session.\n"
" Profiler must be running with programatic sampling\n"
"stopProfiling() Stop a running profiling session"
" Profiler must be running with programatic sampling",
"stopProfiling() Stop a running profiling session\n"
#endif
};
#ifdef MOZ_PROFILING
#define PROFILING_FUNCTION_COUNT 2
#else
#define PROFILING_FUNCTION_COUNT 0
#endif
/* Help messages must match shell functions. */
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages) + 1 ==
JS_ARRAY_LENGTH(shell_functions));
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages) - PROFILING_FUNCTION_COUNT ==
JS_ARRAY_LENGTH(shell_functions) - 1 /* JS_FS_END */);
#undef PROFILING_FUNCTION_COUNT
#ifdef DEBUG
static void
@ -4723,12 +4770,13 @@ CheckHelpMessages()
const char *const *m;
const char *lp;
/* Each message must begin with "function_name(" prefix. */
/* Messages begin with "function_name(" prefix and don't end with \n. */
for (m = shell_help_messages; m != JS_ARRAY_END(shell_help_messages); ++m) {
lp = strchr(*m, '(');
JS_ASSERT(lp);
JS_ASSERT(memcmp(shell_functions[m - shell_help_messages].name,
*m, lp - *m) == 0);
JS_ASSERT((*m)[strlen(*m) - 1] != '\n');
}
}
#else
@ -5548,46 +5596,52 @@ DestroyContext(JSContext *cx, bool withGC)
}
static JSObject *
NewGlobalObject(JSContext *cx)
NewGlobalObject(JSContext *cx, CompartmentKind compartment)
{
JSObject *glob = JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL);
JSObject *glob = (compartment == NEW_COMPARTMENT)
? JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL)
: JS_NewGlobalObject(cx, &global_class);
if (!glob)
return NULL;
JSAutoEnterCompartment ac;
if (!ac.enter(cx, glob))
return NULL;
{
JSAutoEnterCompartment ac;
if (!ac.enter(cx, glob))
return NULL;
#ifdef LAZY_STANDARD_CLASSES
JS_SetGlobalObject(cx, glob);
#else
if (!JS_InitStandardClasses(cx, glob))
return NULL;
#ifndef LAZY_STANDARD_CLASSES
if (!JS_InitStandardClasses(cx, glob))
return NULL;
#endif
#ifdef JS_HAS_CTYPES
if (!JS_InitCTypesClass(cx, glob))
return NULL;
if (!JS_InitCTypesClass(cx, glob))
return NULL;
#endif
if (!JS::RegisterPerfMeasurement(cx, glob))
return NULL;
if (!JS_DefineFunctions(cx, glob, shell_functions) ||
!JS_DefineProfilingFunctions(cx, glob)) {
return NULL;
if (!JS::RegisterPerfMeasurement(cx, glob))
return NULL;
if (!JS_DefineFunctions(cx, glob, shell_functions) ||
!JS_DefineProfilingFunctions(cx, glob)) {
return NULL;
}
JSObject *it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0);
if (!it)
return NULL;
if (!JS_DefineProperties(cx, it, its_props))
return NULL;
if (!JS_DefineFunctions(cx, it, its_methods))
return NULL;
if (!JS_DefineProperty(cx, glob, "custom", JSVAL_VOID, its_getter,
its_setter, 0))
return NULL;
if (!JS_DefineProperty(cx, glob, "customRdOnly", JSVAL_VOID, its_getter,
its_setter, JSPROP_READONLY))
return NULL;
}
JSObject *it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0);
if (!it)
return NULL;
if (!JS_DefineProperties(cx, it, its_props))
return NULL;
if (!JS_DefineFunctions(cx, it, its_methods))
return NULL;
if (!JS_DefineProperty(cx, glob, "custom", JSVAL_VOID, its_getter,
its_setter, 0))
return NULL;
if (!JS_DefineProperty(cx, glob, "customRdOnly", JSVAL_VOID, its_getter,
its_setter, JSPROP_READONLY))
if (compartment == NEW_COMPARTMENT && !JS_WrapObject(cx, &glob))
return NULL;
return glob;
@ -5598,7 +5652,7 @@ Shell(JSContext *cx, int argc, char **argv, char **envp)
{
JSAutoRequest ar(cx);
JSObject *glob = NewGlobalObject(cx);
JSObject *glob = NewGlobalObject(cx, NEW_COMPARTMENT);
if (!glob)
return 1;
@ -5606,6 +5660,8 @@ Shell(JSContext *cx, int argc, char **argv, char **envp)
if (!ac.enter(cx, glob))
return 1;
JS_SetGlobalObject(cx, glob);
JSObject *envobj = JS_DefineObject(cx, glob, "environment", &env_class, NULL, 0);
if (!envobj || !JS_SetPrivate(cx, envobj, envp))
return 1;
@ -5642,7 +5698,7 @@ Shell(JSContext *cx, int argc, char **argv, char **envp)
class ShellWorkerHooks : public js::workers::WorkerHooks {
public:
JSObject *newGlobalObject(JSContext *cx) {
return NewGlobalObject(cx);
return NewGlobalObject(cx, NEW_COMPARTMENT);
}
};
ShellWorkerHooks hooks;

View File

@ -15,6 +15,6 @@ script regress-352789.js
script regress-355101.js
script regress-355474-01.js
skip script regress-373678.js # obsolete test
script regress-429249.js
skip script regress-429249.js
script regress-461233.js
script regress-463360.js

View File

@ -32,4 +32,4 @@ script 15.8-1.js
script 15.9.5.js
script 8.6.2.1-1.js
script 9.9-1.js
script trapflatclosure.js
skip script trapflatclosure.js

View File

@ -11,5 +11,5 @@ script regress-320854.js
script regress-327170.js
script regress-368516.js
script regress-385393-03.js
script regress-429248.js
skip script regress-429248.js
script regress-430740.js

View File

@ -164,16 +164,16 @@ skip script regress-418730.js # obsolete test
script regress-420612.js
script regress-420869-01.js
skip script regress-421621.js # obsolete test
script regress-422137.js
skip script regress-422137.js
script regress-422592.js
script regress-424683-01.js
script regress-426711.js
script regress-427196-01.js
script regress-427196-02.js
script regress-427196-03.js
script regress-429264.js
skip script regress-429264.js
script regress-429739.js
script regress-431428.js
skip script regress-431428.js
skip script regress-432075.js # obsolete test
script regress-434837-01.js
fails script regress-435345-01.js

View File

@ -46,4 +46,4 @@ script regress-379925.js
script regress-380506.js
script regress-410571.js
script regress-410649.js
script regress-429252.js
skip script regress-429252.js

View File

@ -89,3 +89,5 @@ script regress-634210-1.js
script regress-634210-2.js
script regress-634210-3.js
script regress-634210-4.js
script regress-635195.js
script regress-636364.js

View File

@ -8,7 +8,11 @@ if (typeof evalcx == 'function') {
var cx = evalcx("");
evalcx("function f() { return this; }", cx);
f = cx.f;
assertEq(f(), cx);
assertEq(f(), this);
evalcx("function g() { 'use strict'; return this; }", cx);
g = cx.g;
assertEq(g(), undefined);
}
reportCompare(0, 0, "");
reportCompare(0, 0, "ok");

View File

@ -0,0 +1,8 @@
// Any copyright is dedicated to the Public Domain.
// http://creativecommons.org/licenses/publicdomain/
var obj = {set x(v) {}};
obj.watch("x", function() { delete obj.x; });
obj.x = "hi"; // don't assert
reportCompare(0, 0, 'ok');

View File

@ -0,0 +1,29 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
* Contributors: Jason Orendorff, Brendan Eich
*/
if (typeof newGlobal == 'function') {
var gsame = newGlobal('same-compartment');
gsame.eval("function f() { return this; }");
f = gsame.f;
assertEq(f(), this);
gsame.eval("function g() { 'use strict'; return this; }");
g = gsame.g;
assertEq(g(), undefined);
var gnew = newGlobal('new-compartment');
gnew.eval("function f() { return this; }");
f = gnew.f;
assertEq(f(), this);
gnew.eval("function g() { 'use strict'; return this; }");
g = gnew.g;
assertEq(g(), undefined);
}
reportCompare(0, 0, "ok");