[JAEGER] Re-added Dave Mandelin's polymorphic inline caches for GETPROP (bug 572310).

This commit is contained in:
David Anderson 2010-06-19 21:58:55 -07:00
parent ff9eadf410
commit 884b494c29
80 changed files with 1824 additions and 38 deletions

View File

@ -313,6 +313,7 @@ CPPSRCS += Assertions.cpp \
FastOps.cpp \
StubCompiler.cpp \
MonoIC.cpp \
PolyIC.cpp \
ImmutableSync.cpp \
InvokeHelpers.cpp \
$(NULL)

View File

@ -56,6 +56,7 @@ public:
JITCode& code = codeBlock->getJITCode();
m_start = code.start();
m_size = code.size();
mprot = true;
if (mprot)
ExecutableAllocator::makeWritable(m_start, m_size);

View File

@ -587,6 +587,9 @@ JSThreadData::purge(JSContext *cx)
if (cx->runtime->gcRegenShapes)
traceMonitor.needFlush = JS_TRUE;
#endif
#ifdef JS_METHODJIT
jmData.purge(cx);
#endif
/* Destroy eval'ed scripts. */
js_DestroyScriptsToGC(cx, this);

View File

@ -236,6 +236,7 @@ namespace mjit {
bool addScript(JSScript *script);
void removeScript(JSScript *script);
void purge(JSContext *cx);
};
}

View File

@ -189,6 +189,10 @@ struct JSScript {
(char*)jcode < (char*)ncode + jitLength;
}
# endif
inline uint32 numPICs() {
return *(uint32*)((uint8 *)pics - sizeof(uint32));
}
#endif
#if 0 /* def JS_TRACER */
js::TraceTreeCache *trees; /* trace tree info. */

View File

@ -117,6 +117,11 @@ class BaseAssembler : public JSC::MacroAssembler
load32(ptr, reg);
}
void loadShape(RegisterID obj, RegisterID shape) {
loadPtr(Address(obj, offsetof(JSObject, map)), shape);
load32(Address(shape, offsetof(JSObjectMap, shape)), shape);
}
/*
* Finds and returns the address of a known object and slot.
*/
@ -222,15 +227,15 @@ class BaseAssembler : public JSC::MacroAssembler
Call call(void *fun) {
#if defined(_MSC_VER) && defined(_M_X64)
masm.subPtr(JSC::MacroAssembler::Imm32(32),
JSC::MacroAssembler::stackPointerRegister);
subPtr(JSC::MacroAssembler::Imm32(32),
JSC::MacroAssembler::stackPointerRegister);
#endif
Call cl = JSC::MacroAssembler::call();
#if defined(_MSC_VER) && defined(_M_X64)
masm.addPtr(JSC::MacroAssembler::Imm32(32),
JSC::MacroAssembler::stackPointerRegister);
addPtr(JSC::MacroAssembler::Imm32(32),
JSC::MacroAssembler::stackPointerRegister);
#endif
callPatches.append(CallPatch(differenceBetween(startLabel, cl), fun));

View File

@ -76,7 +76,8 @@ static const JSC::MacroAssembler::RegisterID JSReturnReg_Data = JSC::X86Register
mjit::Compiler::Compiler(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeChain)
: cx(cx), script(script), scopeChain(scopeChain), globalObj(scopeChain->getGlobal()), fun(fun),
analysis(cx, script), jumpMap(NULL), frame(cx, script, masm),
branchPatches(ContextAllocPolicy(cx)), mics(ContextAllocPolicy(cx)), stubcc(cx, *this, frame, script)
branchPatches(ContextAllocPolicy(cx)), mics(ContextAllocPolicy(cx)),
pics(ContextAllocPolicy(cx)), stubcc(cx, *this, frame, script)
{
}
@ -238,7 +239,7 @@ mjit::Compiler::finishThisUp()
memcpy(result + masm.size(), stubcc.buffer(), stubcc.size());
/* Build the pc -> ncode mapping. */
void **nmap = (void **)cx->calloc(sizeof(void *) * script->length + 1);
void **nmap = (void **)cx->calloc(sizeof(void *) * (script->length + 1));
if (!nmap) {
execPool->release();
return Compile_Error;
@ -277,6 +278,34 @@ mjit::Compiler::finishThisUp()
script->mics[i].dataWrite = mics[i].dataWrite;
}
if (pics.length()) {
uint8 *cursor = (uint8 *)cx->calloc(sizeof(ic::PICInfo) * pics.length() + sizeof(uint32));
if (!cursor) {
execPool->release();
return Compile_Error;
}
*(uint32*)cursor = pics.length();
cursor += sizeof(uint32);
script->pics = (ic::PICInfo *)cursor;
}
for (size_t i = 0; i < pics.length(); i++) {
script->pics[i].kind = pics[i].kind;
script->pics[i].shapeRegHasBaseShape = true;
script->pics[i].fastPathStart = fullCode.locationOf(pics[i].hotPathBegin);
script->pics[i].storeBack = fullCode.locationOf(pics[i].storeBack);
script->pics[i].slowPathStart = stubCode.locationOf(pics[i].slowPathStart);
script->pics[i].callReturn = uint8((uint8*)stubCode.locationOf(pics[i].callReturn).executableAddress() -
(uint8*)script->pics[i].slowPathStart.executableAddress());
script->pics[i].shapeReg = pics[i].shapeReg;
script->pics[i].objReg = pics[i].objReg;
script->pics[i].objRemat = pics[i].objRemat.offset;
script->pics[i].atomIndex = pics[i].atomIndex;
script->pics[i].shapeGuard = masm.distanceOf(pics[i].shapeGuard) -
masm.distanceOf(pics[i].hotPathBegin);
new (&script->pics[i].execPools) ic::PICInfo::ExecPoolVector(SystemAllocPolicy());
}
/* Link fast and slow paths together. */
stubcc.fixCrossJumps(result, masm.size(), masm.size() + stubcc.size());
@ -674,23 +703,23 @@ mjit::Compiler::generateMethod()
BEGIN_CASE(JSOP_GETTHISPROP)
/* Push thisv onto stack. */
jsop_this();
jsop_getprop_slow();
jsop_getprop(fullAtomIndex(PC));
END_CASE(JSOP_GETTHISPROP);
BEGIN_CASE(JSOP_GETARGPROP)
/* Push arg onto stack. */
jsop_getarg(GET_SLOTNO(PC));
jsop_getprop_slow();
jsop_getprop(fullAtomIndex(&PC[ARGNO_LEN]));
END_CASE(JSOP_GETARGPROP)
BEGIN_CASE(JSOP_GETLOCALPROP)
frame.pushLocal(GET_SLOTNO(PC));
jsop_getprop_slow();
jsop_getprop(fullAtomIndex(&PC[SLOTNO_LEN]));
END_CASE(JSOP_GETLOCALPROP)
BEGIN_CASE(JSOP_GETPROP)
BEGIN_CASE(JSOP_GETXPROP)
jsop_getprop_slow();
jsop_getprop(fullAtomIndex(PC));
END_CASE(JSOP_GETPROP)
BEGIN_CASE(JSOP_LENGTH)
@ -1792,6 +1821,87 @@ mjit::Compiler::jsop_getprop_slow()
frame.pushSynced();
}
#if ENABLE_PIC
void
mjit::Compiler::jsop_getprop(uint32 atomIndex)
{
FrameEntry *top = frame.peek(-1);
/* If the incoming type is not an object, take a slow path. */
if (top->isTypeKnown() &&
(top->getTypeTag() != JSVAL_MASK32_FUNOBJ &&
top->getTypeTag() != JSVAL_MASK32_NONFUNOBJ)) {
jsop_getprop_slow();
return;
}
PICGenInfo pic(ic::PICInfo::GET);
pic.hotPathBegin = masm.label();
/* Guard that the type is an object. */
Jump typeMismatch;
bool typeMismatchSet = false;
if (!top->isTypeKnown()) {
RegisterID reg = frame.tempRegForType(top);
RegisterID type = frame.allocReg();
masm.move(reg, type);
masm.and32(Imm32(JSVAL_MASK32_OBJECT), type);
Jump j = masm.branch32(Assembler::BelowOrEqual, type, Imm32(JSVAL_MASK32_CLEAR));
stubcc.linkExit(j);
stubcc.leave();
stubcc.call(stubs::GetProp);
typeMismatch = stubcc.masm.jump();
frame.freeReg(type);
typeMismatchSet = true;
}
RegisterID shapeReg = frame.allocReg();
RegisterID objReg = frame.copyDataIntoReg(top);
pic.shapeReg = shapeReg;
pic.atomIndex = atomIndex;
pic.objRemat = frame.dataRematInfo(top);
/* Guard on shape. */
masm.loadPtr(Address(objReg, offsetof(JSObject, map)), shapeReg);
masm.load32(Address(shapeReg, offsetof(JSObjectMap, shape)), shapeReg);
pic.shapeGuard = masm.label();
Jump j = masm.branch32(Assembler::NotEqual, shapeReg,
Imm32(int32(JSObjectMap::INVALID_SHAPE)));
pic.slowPathStart = stubcc.masm.label();
stubcc.linkExit(j);
stubcc.leave();
stubcc.masm.move(Imm32(pics.length()), Registers::ArgReg1);
pic.callReturn = stubcc.call(ic::GetProp);
/* Load dslots. */
masm.loadPtr(Address(objReg, offsetof(JSObject, dslots)), objReg);
/* Copy the slot value to the expression stack. */
Address slot(objReg, 1 << 24);
frame.pop();
masm.loadTypeTag(slot, shapeReg);
masm.loadData32(slot, objReg);
pic.objReg = objReg;
frame.pushRegs(shapeReg, objReg);
pic.storeBack = masm.label();
if (typeMismatchSet)
typeMismatch.linkTo(stubcc.masm.label(), &stubcc.masm);
stubcc.rejoin(1);
pics.append(pic);
}
#else
void
mjit::Compiler::jsop_getprop(uint32 atomIndex)
{
jsop_getprop_slow();
}
#endif
void
mjit::Compiler::jsop_getarg(uint32 index)
{

View File

@ -47,6 +47,7 @@
#include "CodeGenIncludes.h"
#include "StubCompiler.h"
#include "MonoIC.h"
#include "PolyIC.h"
namespace js {
namespace mjit {
@ -85,6 +86,21 @@ class Compiler
bool dataWrite;
};
struct PICGenInfo {
PICGenInfo(ic::PICInfo::Kind kind) : kind(kind)
{ }
ic::PICInfo::Kind kind;
Label hotPathBegin;
Label storeBack;
Label slowPathStart;
RegisterID shapeReg;
RegisterID objReg;
Label shapeGuard;
uint32 atomIndex;
StateRemat objRemat;
Call callReturn;
};
struct Uses {
Uses(uint32 nuses)
: nuses(nuses)
@ -111,6 +127,7 @@ class Compiler
FrameState frame;
js::Vector<BranchPatch, 64> branchPatches;
js::Vector<MICGenInfo, 64> mics;
js::Vector<PICGenInfo, 64> pics;
StubCompiler stubcc;
Label invokeLabel;
@ -169,6 +186,7 @@ class Compiler
void jsop_setelem_slow();
void jsop_getelem_slow();
void jsop_unbrand();
void jsop_getprop(uint32 atomIndex);
/* Fast opcodes. */
void jsop_bitop(JSOp op);

View File

@ -596,6 +596,23 @@ FrameState::addEscaping(uint32 local)
escaping[local] = 1;
}
inline StateRemat
FrameState::dataRematInfo(const FrameEntry *fe) const
{
if (fe->isCopy())
fe = fe->copyOf();
StateRemat remat;
if (fe->data.inRegister()) {
remat.reg = fe->data.reg();
remat.inReg = true;
} else {
JS_ASSERT(fe->data.synced());
remat.offset = addressOf(fe).offset;
remat.inReg = false;
}
return remat;
}
} /* namspace mjit */
} /* namspace js */

View File

@ -49,6 +49,15 @@
namespace js {
namespace mjit {
struct StateRemat {
typedef JSC::MacroAssembler::RegisterID RegisterID;
union {
RegisterID reg : 5;
uint32 offset : 31;
};
bool inReg : 1;
};
/*
* The FrameState keeps track of values on the frame during compilation.
* The compiler can query FrameState for information about arguments, locals,
@ -430,6 +439,8 @@ class FrameState
Address addressOf(const FrameEntry *fe) const;
inline StateRemat dataRematInfo(const FrameEntry *fe) const;
/*
* This is similar to freeReg(ownRegForData(fe)) - except no movement takes place.
* The fe is simply invalidated as if it were popped. This can be used to free

View File

@ -41,6 +41,8 @@
#include "assembler/jit/ExecutableAllocator.h"
#include "jstracer.h"
#include "BaseAssembler.h"
#include "MonoIC.h"
#include "PolyIC.h"
using namespace js;
using namespace js::mjit;
@ -569,6 +571,22 @@ ThreadData::removeScript(JSScript *script)
picScripts.remove(p);
}
void
ThreadData::purge(JSContext *cx)
{
if (!cx->runtime->gcRegenShapes)
return;
for (ThreadData::ScriptSet::Enum e(picScripts); !e.empty(); e.popFront()) {
JSScript *script = e.front();
ic::PurgePICs(cx, script);
//PurgeMICs(cs, script);
}
picScripts.clear();
}
extern "C" JSBool JaegerTrampoline(JSContext *cx, JSStackFrame *fp, void *code,
uintptr_t inlineCallCount);
@ -629,6 +647,12 @@ mjit::JaegerShot(JSContext *cx)
return ok;
}
template <typename T>
static inline void Destroy(T &t)
{
t.~T();
}
void
mjit::ReleaseScriptCode(JSContext *cx, JSScript *script)
{
@ -641,13 +665,16 @@ mjit::ReleaseScriptCode(JSContext *cx, JSScript *script)
script->jitLength = 0;
#endif
#if defined(ENABLE_PIC) && ENABLE_PIC
#if ENABLE_PIC
if (script->pics) {
delete[] script->pics;
script->pics = NULL;
uint32 npics = script->numPICs();
for (uint32 i = 0; i < npics; i++) {
script->pics[i].releasePools();
Destroy(script->pics[i].execPools);
}
JS_METHODJIT_DATA(cx).removeScript(script);
cx->free((uint8*)script->pics - sizeof(uint32));
}
script->npics = 0;
#endif
}

View File

@ -209,6 +209,9 @@ CanMethodJIT(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeCh
return Compile_Okay;
}
void
PurgeShapeDependencies(JSContext *cx);
} /* namespace mjit */
} /* namespace js */
@ -221,3 +224,4 @@ extern "C" void JaegerThrowpoline();
extern "C" void JaegerFromTracer();
#endif /* jsjaeger_h__ */

425
js/src/methodjit/PolyIC.cpp Normal file
View File

@ -0,0 +1,425 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=99:
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
* May 28, 2008.
*
* The Initial Developer of the Original Code is
* Brendan Eich <brendan@mozilla.org>
*
* Contributor(s):
* David Mandelin <dmandelin@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "PolyIC.h"
#include "StubCalls.h"
#include "CodeGenIncludes.h"
#include "StubCalls-inl.h"
#include "assembler/assembler/LinkBuffer.h"
#include "jsscope.h"
#include "jsnum.h"
#include "jsscopeinlines.h"
using namespace js;
using namespace js::mjit;
#if ENABLE_PIC
/* Rough over-estimate of how much memory we need to unprotect. */
static const uint32 INLINE_PATH_LENGTH = 64;
/* Maximum number of stubs for a given callsite. */
static const uint32 MAX_STUBS = 16;
typedef JSC::FunctionPtr FunctionPtr;
typedef JSC::RepatchBuffer RepatchBuffer;
typedef JSC::CodeBlock CodeBlock;
typedef JSC::CodeLocationLabel CodeLocationLabel;
typedef JSC::JITCode JITCode;
typedef JSC::MacroAssembler::Jump Jump;
typedef JSC::MacroAssembler::RegisterID RegisterID;
typedef JSC::MacroAssembler::Label Label;
typedef JSC::MacroAssembler::Imm32 Imm32;
typedef JSC::MacroAssembler::Address Address;
typedef JSC::ReturnAddressPtr ReturnAddressPtr;
typedef JSC::MacroAssemblerCodePtr MacroAssemblerCodePtr;
struct AutoPropertyDropper
{
JSContext *cx;
JSObject *holder;
JSProperty *prop;
public:
AutoPropertyDropper(JSContext *cx, JSObject *obj, JSProperty *prop)
: cx(cx), holder(obj), prop(prop)
{
JS_ASSERT(prop);
}
~AutoPropertyDropper()
{
holder->dropProperty(cx, prop);
}
};
class PICStubCompiler
{
const char *type;
protected:
VMFrame &f;
JSScript *script;
ic::PICInfo &pic;
public:
PICStubCompiler(const char *type, VMFrame &f, JSScript *script, ic::PICInfo &pic)
: type(type), f(f), script(script), pic(pic)
{ }
bool disable(const char *reason, VoidStub stub)
{
return disable(reason, JS_FUNC_TO_DATA_PTR(void *, stub));
}
bool disable(const char *reason, void *stub)
{
spew("disabled", reason);
JITCode jitCode(pic.slowPathStart.executableAddress(), INLINE_PATH_LENGTH);
CodeBlock codeBlock(jitCode);
RepatchBuffer repatcher(&codeBlock);
ReturnAddressPtr retPtr(pic.slowPathStart.callAtOffset(pic.callReturn).executableAddress());
MacroAssemblerCodePtr target(stub);
repatcher.relinkCallerToTrampoline(retPtr, target);
return true;
}
JSC::ExecutablePool *getExecPool(size_t size)
{
mjit::ThreadData *jd = &JS_METHODJIT_DATA(f.cx);
return jd->execPool->poolForSize(size);
}
protected:
void spew(const char *event, const char *op)
{
JaegerSpew(JSpew_PICs, "%s %s: %s (%s: %d)\n",
type, event, op, script->filename,
js_FramePCToLineNumber(f.cx, f.fp));
}
};
class PICRepatchBuffer : public JSC::RepatchBuffer
{
ic::PICInfo &pic;
public:
PICRepatchBuffer(ic::PICInfo &ic)
: JSC::RepatchBuffer(ic.lastPathStart().executableAddress(),
INLINE_PATH_LENGTH),
pic(ic)
{ }
void relink(int32 offset, JSC::CodeLocationLabel target) {
JSC::RepatchBuffer::relink(pic.lastPathStart().jumpAtOffset(offset), target);
}
};
class GetPropCompiler : public PICStubCompiler
{
JSObject *obj;
JSAtom *atom;
VoidStub stub;
/* Offsets for patching, computed manually as reverse from the storeBack. */
#ifdef JS_CPU_X86
static const int32 DSLOTS_LOAD = -15;
static const int32 TYPE_LOAD = -6;
static const int32 DATA_LOAD = 0;
static const int32 INLINE_SHAPE_OFFSET = 6;
static const int32 INLINE_SHAPE_JUMP = 12;
static const int32 STUB_SHAPE_JUMP = 12;
#endif
public:
GetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, JSAtom *atom,
VoidStub stub)
: PICStubCompiler("getprop", f, script, pic), obj(obj), atom(atom), stub(stub)
{ }
static void reset(ic::PICInfo &pic)
{
RepatchBuffer repatcher(pic.fastPathStart.executableAddress(), INLINE_PATH_LENGTH);
repatcher.repatchLEAToLoadPtr(pic.storeBack.instructionAtOffset(DSLOTS_LOAD));
repatcher.repatch(pic.fastPathStart.dataLabel32AtOffset(pic.shapeGuard + INLINE_SHAPE_OFFSET),
int32(JSScope::INVALID_SHAPE));
repatcher.relink(pic.fastPathStart.jumpAtOffset(pic.shapeGuard + INLINE_SHAPE_JUMP),
pic.slowPathStart);
}
bool patchInline(JSObject *holder, JSScopeProperty *sprop)
{
spew("patch", "inline");
PICRepatchBuffer repatcher(pic);
mjit::ThreadData &jm = JS_METHODJIT_DATA(f.cx);
if (!jm.addScript(script)) {
js_ReportOutOfMemory(f.cx);
return false;
}
int32 offset;
if (sprop->slot < JS_INITIAL_NSLOTS) {
JSC::CodeLocationInstruction istr;
istr = pic.storeBack.instructionAtOffset(DSLOTS_LOAD);
repatcher.repatchLoadPtrToLEA(istr);
//
// We've patched | mov dslots, [obj + DSLOTS_OFFSET]
// To: | lea fslots, [obj + DSLOTS_OFFSET]
//
// Because the offset is wrong, it's necessary to correct it
// below.
//
int32 diff = int32(offsetof(JSObject, fslots)) -
int32(offsetof(JSObject, dslots));
JS_ASSERT(diff != 0);
offset = (int32(sprop->slot) * sizeof(Value)) + diff;
} else {
offset = (sprop->slot - JS_INITIAL_NSLOTS) * sizeof(Value);
}
uint32 shapeOffs = pic.shapeGuard + INLINE_SHAPE_OFFSET;
repatcher.repatch(pic.fastPathStart.dataLabel32AtOffset(shapeOffs),
obj->shape());
repatcher.repatch(pic.storeBack.dataLabel32AtOffset(TYPE_LOAD),
offset + 4);
repatcher.repatch(pic.storeBack.dataLabel32AtOffset(DATA_LOAD),
offset);
pic.inlinePathPatched = true;
return true;
}
bool generateStub(JSObject *holder, JSScopeProperty *sprop)
{
Vector<Jump, 8> shapeMismatches(f.cx);
int lastStubSecondShapeGuard = pic.secondShapeGuard;
Assembler masm;
if (pic.objNeedsRemat) {
if (pic.objRemat >= sizeof(JSStackFrame)) {
masm.loadData32(Address(JSFrameReg, pic.objRemat), pic.objReg);
} else {
masm.move(RegisterID(pic.objRemat), pic.objReg);
}
pic.objNeedsRemat = false;
}
if (!pic.shapeRegHasBaseShape) {
masm.loadShape(pic.objReg, pic.shapeReg);
pic.shapeRegHasBaseShape = true;
}
Label start = masm.label();
Jump shapeGuard = masm.branch32_force32(Assembler::NotEqual, pic.shapeReg,
Imm32(obj->shape()));
if (!shapeMismatches.append(shapeGuard))
return false;
if (obj != holder) {
// Emit code that walks the prototype chain.
JSObject *tempObj = obj;
Address fslot(pic.objReg, offsetof(JSObject, fslots) + JSSLOT_PROTO * sizeof(Value));
do {
tempObj = tempObj->getProto();
JS_ASSERT(tempObj);
JS_ASSERT(tempObj->isNative());
masm.loadData32(fslot, pic.objReg);
pic.shapeRegHasBaseShape = false;
pic.objNeedsRemat = true;
Jump j = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg);
if (!shapeMismatches.append(j))
return false;
} while (tempObj != holder);
// Load the shape out of the holder and check it.
masm.loadShape(pic.objReg, pic.shapeReg);
Jump j = masm.branch32_force32(Assembler::NotEqual, pic.shapeReg,
Imm32(holder->shape()));
if (!shapeMismatches.append(j))
return false;
pic.secondShapeGuard = masm.distanceOf(masm.label()) - masm.distanceOf(start);
} else {
pic.secondShapeGuard = 0;
}
masm.loadSlot(pic.objReg, pic.objReg, sprop->slot, pic.shapeReg, pic.objReg);
Jump done = masm.jump();
JSC::ExecutablePool *ep = getExecPool(masm.size());
if (!ep) {
js_ReportOutOfMemory(f.cx);
return false;
}
// :TODO: this can OOM
JSC::LinkBuffer buffer(&masm, ep);
if (!pic.execPools.append(ep)) {
ep->release();
js_ReportOutOfMemory(f.cx);
return false;
}
// The guard exit jumps to the original slow case.
for (Jump *pj = shapeMismatches.begin(); pj != shapeMismatches.end(); ++pj)
buffer.link(*pj, pic.slowPathStart);
// The final exit jumps to the store-back in the inline stub.
buffer.link(done, pic.storeBack);
CodeLocationLabel cs = buffer.finalizeCodeAddendum();
JaegerSpew(JSpew_PICs, "generated getprop stub at %p\n", cs.executableAddress());
// Patch either the inline fast path or a generated stub. The stub
// omits the prefix of the inline fast path that loads the shape, so
// the offsets are different.
PICRepatchBuffer repatcher(pic);
int shapeGuardJumpOffset;
if (pic.stubsGenerated)
shapeGuardJumpOffset = STUB_SHAPE_JUMP;
else
shapeGuardJumpOffset = pic.shapeGuard + INLINE_SHAPE_JUMP;
repatcher.relink(shapeGuardJumpOffset, cs);
if (lastStubSecondShapeGuard)
repatcher.relink(lastStubSecondShapeGuard, cs);
pic.stubsGenerated++;
pic.lastStubStart = buffer.locationOf(start);
if (pic.stubsGenerated == MAX_STUBS)
disable("max stubs reached");
return true;
}
bool update()
{
if (!pic.hit) {
spew("first hit", "nop");
pic.hit = true;
return true;
}
JSObject *aobj = js_GetProtoIfDenseArray(obj);
if (!aobj->isNative())
return disable("non-native");
JSObject *holder;
JSProperty *prop;
if (!aobj->lookupProperty(f.cx, ATOM_TO_JSID(atom), &holder, &prop))
return false;
if (!prop)
return disable("lookup failed");
AutoPropertyDropper dropper(f.cx, holder, prop);
JSScopeProperty *sprop = (JSScopeProperty *)prop;
if (!sprop->hasDefaultGetterOrIsMethod())
return disable("getter");
if (!SPROP_HAS_VALID_SLOT(sprop, holder->scope()))
return disable("invalid slot");
if (obj == holder && !pic.inlinePathPatched) {
// :FIXME:
// Currently, we need this condition in order to patch the inline
// stub because that patching goes relative to fastPathStart, and
// that only points to the inline stub if no other stubs were
// generated. We should either lift that limitation or simplify by
// removing the inlinePathPatched flag, which is redundant now.
if (pic.stubsGenerated == 0)
return patchInline(holder, sprop);
} else {
JS_ASSERT(pic.stubsGenerated < ic::MAX_PIC_STUBS);
return generateStub(holder, sprop);
}
return true;
}
bool disable(const char *reason)
{
return PICStubCompiler::disable(reason, stub);
}
};
void JS_FASTCALL
ic::GetProp(VMFrame &f, uint32 index)
{
JSScript *script = f.fp->script;
PICInfo &pic = script->pics[index];
JSAtom *atom;
atom = script->getAtom(pic.atomIndex);
JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
if (!obj)
THROW();
if (pic.shouldGenerate()) {
GetPropCompiler cc(f, script, obj, pic, atom, stubs::GetProp);
if (!cc.update()) {
cc.disable("error");
THROW();
}
}
Value v;
if (!obj->getProperty(f.cx, ATOM_TO_JSID(atom), &v))
THROW();
f.regs.sp[-1] = v;
}
#endif
void
ic::PurgePICs(JSContext *cx, JSScript *script)
{
uint32 npics = script->numPICs();
for (uint32 i = 0; i < npics; i++) {
ic::PICInfo &pic = script->pics[i];
if (pic.kind == ic::PICInfo::GET)
GetPropCompiler::reset(pic);
pic.reset();
}
}

165
js/src/methodjit/PolyIC.h Normal file
View File

@ -0,0 +1,165 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=99:
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
* May 28, 2008.
*
* The Initial Developer of the Original Code is
* Brendan Eich <brendan@mozilla.org>
*
* Contributor(s):
* David Mandelin <dmandelin@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#if !defined jsjaeger_poly_ic_h__ && defined JS_METHODJIT
#define jsjaeger_poly_ic_h__
#include "jscntxt.h"
#include "jstl.h"
#include "jsvector.h"
#include "assembler/assembler/MacroAssembler.h"
#include "assembler/assembler/CodeLocation.h"
#include "methodjit/MethodJIT.h"
#define ENABLE_PIC 1
namespace js {
namespace mjit {
namespace ic {
static const uint32 MAX_PIC_STUBS = 16;
void PurgePICs(JSContext *cx);
struct PICInfo {
typedef JSC::MacroAssembler::RegisterID RegisterID;
// Operation this is a PIC for.
enum Kind {
GET,
CALL,
SET
};
Kind kind : 2;
// State flags.
bool hit : 1; // this PIC has been executed
bool inlinePathPatched : 1; // inline path has been patched
// True if register R holds the base object shape along exits from the
// last stub.
bool shapeRegHasBaseShape : 1;
RegisterID shapeReg : 5; // also the out type reg
RegisterID objReg : 5; // also the out data reg
// True if the last stub has an extra shape load at its start.
bool startsWithShapeLoad : 1;
// Number of stubs generated.
uint32 stubsGenerated : 8;
// Offset from start of fast path to initial shape guard.
int shapeGuard : 8;
// Offset from start of stub to jump target of second shape guard as Nitro
// asm data location. This is 0 if there is only one shape guard in the
// last stub.
int secondShapeGuard : 8;
// Index into the script's atom table.
uint32 atomIndex;
// Remat info for the object reg.
uint32 objRemat : 20;
bool objNeedsRemat : 1;
// Address of inline fast-path.
JSC::CodeLocationLabel fastPathStart;
// Address of store back at the end of the inline fast-path.
JSC::CodeLocationLabel storeBack;
// Return address of slow path call, as an offset from slowPathStart.
uint8 callReturn;
// Offset from callReturn to the start of the slow case.
JSC::CodeLocationLabel slowPathStart;
// Address of the start of the last generated stub, if any.
JSC::CodeLocationLabel lastStubStart;
typedef Vector<JSC::ExecutablePool *, 0, SystemAllocPolicy> ExecPoolVector;
// ExecutablePools that PIC stubs were generated into.
ExecPoolVector execPools;
// Return the start address of the last path in this PIC, which is the
// inline path if no stubs have been generated yet.
JSC::CodeLocationLabel lastPathStart() {
return stubsGenerated > 0 ? lastStubStart : fastPathStart;
}
bool shouldGenerate() {
return stubsGenerated < MAX_PIC_STUBS || !inlinePathPatched;
}
// Release ExecutablePools referred to by this PIC.
void releasePools() {
for (JSC::ExecutablePool **pExecPool = execPools.begin();
pExecPool != execPools.end();
++pExecPool)
{
(*pExecPool)->release();
}
}
// Reset the data members to the state of a fresh PIC before any patching
// or stub generation was done.
void reset() {
hit = false;
inlinePathPatched = false;
objNeedsRemat = false;
shapeRegHasBaseShape = true;
secondShapeGuard = 0;
stubsGenerated = 0;
releasePools();
execPools.clear();
}
};
void PurgePICs(JSContext *cx, JSScript *script);
void JS_FASTCALL GetProp(VMFrame &f, uint32 index);
}
} /* namespace mjit */
} /* namespace js */
#endif /* jsjaeger_poly_ic_h__ */

View File

@ -0,0 +1,74 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=99:
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
* May 28, 2008.
*
* The Initial Developer of the Original Code is
* Brendan Eich <brendan@mozilla.org>
*
* Contributor(s):
* David Anderson <danderson@mozilla.com>
* David Mandelin <dmandelin@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef jslogic_h_inl__
#define jslogic_h_inl__
namespace js {
namespace mjit {
#define THROW() \
do { \
void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline); \
f.setReturnAddress(ReturnAddressPtr(FunctionPtr(ptr))); \
return; \
} while (0)
#define THROWV(v) \
do { \
void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline); \
f.setReturnAddress(ReturnAddressPtr(FunctionPtr(ptr))); \
return v; \
} while (0)
static inline JSObject *
ValueToObject(JSContext *cx, Value *vp)
{
if (vp->isObject())
return &vp->asObject();
if (!js_ValueToNonNullObject(cx, *vp, vp))
return NULL;
return &vp->asObject();
}
}}
#endif /* jslogic_h__ */

View File

@ -62,6 +62,7 @@
#include "jsobjinlines.h"
#include "jscntxtinlines.h"
#include "jsatominlines.h"
#include "StubCalls-inl.h"
#include "jsautooplen.h"
@ -69,20 +70,6 @@ using namespace js;
using namespace js::mjit;
using namespace JSC;
#define THROW() \
do { \
void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline); \
f.setReturnAddress(ReturnAddressPtr(FunctionPtr(ptr))); \
return; \
} while (0)
#define THROWV(v) \
do { \
void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline); \
f.setReturnAddress(ReturnAddressPtr(FunctionPtr(ptr))); \
return v; \
} while (0)
void JS_FASTCALL
mjit::stubs::BindName(VMFrame &f)
{
@ -142,16 +129,6 @@ mjit::stubs::DebugHook(VMFrame &f)
} \
JS_END_MACRO
static inline JSObject *
ValueToObject(JSContext *cx, Value *vp)
{
if (vp->isObject())
return &vp->asObject();
if (!js_ValueToNonNullObject(cx, *vp, vp))
return NULL;
return &vp->asObject();
}
#define NATIVE_GET(cx,obj,pobj,sprop,getHow,vp,onerr) \
JS_BEGIN_MACRO \
if (sprop->hasDefaultGetter()) { \

View File

@ -77,6 +77,22 @@ class Assembler : public BaseAssembler
return BaseIndex(address.base, address.index, address.scale, address.offset + TAG_OFFSET);
}
void loadSlot(RegisterID obj, RegisterID clobber, uint32 slot, RegisterID type, RegisterID data) {
JS_ASSERT(type != data);
Address address(obj, offsetof(JSObject, fslots) + slot * sizeof(Value));
if (slot >= JS_INITIAL_NSLOTS) {
loadPtr(Address(obj, offsetof(JSObject, dslots)), clobber);
address = Address(obj, (slot - JS_INITIAL_NSLOTS) * sizeof(Value));
}
if (obj == type) {
loadData32(address, data);
loadTypeTag(address, type);
} else {
loadTypeTag(address, type);
loadData32(address, data);
}
}
void loadTypeTag(Address address, RegisterID reg) {
load32(Address(address.base, address.offset + TAG_OFFSET), reg);
}

View File

@ -0,0 +1,43 @@
// |trace-test| error: TypeError
function start() {
MAX_TOTAL_TIME = startTime = new Date
do {
if (rnd(0)) return (a[rnd()])()
lastTime = new Date
} while ( lastTime - startTime < MAX_TOTAL_TIME )
}
function MersenneTwister19937() {
this.init_genrand = function() {
for (mti = 1; mti < 4; mti++) {
Array[mti] = 1
}
};
this.genrand_int32 = function() {
if (mti > 4) {
mti = 0
}
return Array[mti++];
}
} (function() {
fuzzMT = new MersenneTwister19937;
fuzzMT.init_genrand()
rnd = function() {
return Math.floor(fuzzMT.genrand_int32())
}
} ())
function weighted(wa) {
a = []
for (i = 0; i < wa.length; ++i) {
for (var j = 0; j < 8; ++j) {
a.push(wa[i].fun)
}
}
}
statementMakers = weighted([{
fun: function makeMixedTypeArray() { [[, , , , , , , , , , , , , , , , , , ,
, , , , , ""][(a[rnd()])()]]}
}])
start()
/* Don't assert. */

View File

@ -0,0 +1,10 @@
(function () {
for (var q = 0; q < 6; ++q) {
x: (function () {
var m = (function () {})()
})([0, , 0, 0, 0, , 0, 0, 0, , 0, 0, 0, , 0, 0, 0, 0, 0, 0, Number(1)])
}
})()
/* Don't assert. */

View File

@ -0,0 +1 @@
x = __defineSetter__("x", function(z) function() { z })

View File

@ -0,0 +1,10 @@
(function () {
eval("\
for(var z = 0 ; z < 2 ; ++z) {\
this\
}\
", (<x/>))
})()
/* Don't crash. */

View File

@ -0,0 +1,12 @@
function f(y) {
if (y)
return;
let(x) {
for (;;) {}
}
}
/* Don't assert. */
f(1);

View File

@ -0,0 +1,31 @@
version(180)
function f1(code) {
var c
var t = code.replace(/s/, "")
var f = new Function(code)
var o
e = v = f2(f, c)
}
function f2(f, e) {
try {
a = f()
} catch(r) {
var r = g()
}
}
g1 = [{
text: "(function sum_slicing(array){return array==0?0:a+sum_slicing(array.slice(1))})",
test: function (f) {
f([, 2]) == ""
}
}];
(function () {
for (var i = 0; i < g1.length; ++i) {
var a = g1[i]
var text = a.text
var f = eval(text.replace(/@/, ""))
if (a.test(f)) {}
}
}())
f1("for(let a=0;a<6;a++){print([\"\"].some(function(){false>\"\"}))}")

View File

@ -0,0 +1,3 @@
// |trace-test| error: ReferenceError
x ? o : [] && x

View File

@ -0,0 +1,14 @@
function a() {
function f() {}
this.d = function() {
f
}
} (function() {
var a2, x
a2 = new a;
d = (function(){x * 1})();
})()
/* Don't assert. */

View File

@ -0,0 +1,8 @@
(function () {
var a;
eval("for(w in ((function(x,y){b:0})())) ;");
})();
__defineSetter__("l", function() { gc() });
this.watch("l", function(x) { yield #1={} });
l = true;

View File

@ -0,0 +1,8 @@
(function() {
((function f(a) {
if (a > 0) {
f(a - 1)
}
})(6))
})()

View File

@ -0,0 +1,6 @@
(function() {
for (e in ((function() {
yield
})())) return
})()
/* Don't assert. */

View File

@ -0,0 +1,13 @@
(function() {
do {
try {
return
}
catch(x if (c)) {
return
} (x)
} while (x)
})()
/* Don't assert. */

View File

@ -0,0 +1,11 @@
(function () {
try {
return
} catch (x if i) {
return
}
for (z in []);
})()
/* Don't assert */

View File

@ -0,0 +1,18 @@
// |trace-test| allow-oom
(function(){
(x)=<x>></x>
})()
try{
(function(){
((function a(aaaaaa,bbbbbb){
if(aaaaaa.length==bbbbbb){
return eval%bbbbbb.valueOf()
}
cccccc=a(aaaaaa,bbbbbb+1)
return cccccc._=x
})([,,,,,,,,,,,,,,,,,,0],0))
})()
}catch(e){}
/* Don't assert. */

View File

@ -0,0 +1,7 @@
// |trace-test| error: TypeError
for (var a = 0; a < 7; ++a) {
if (a == 1) {
Iterator()
}
}

View File

@ -0,0 +1,9 @@
// |trace-test| error: RangeError
(function() {
for each(let a in [function() {}, Infinity]) {
new Array(a)
}
})()
/* Don't assert/crash. */

View File

@ -0,0 +1,11 @@
// |trace-test| error: SyntaxError
Function("\n\
for (a = 0; a < 3; a++) {\n\
if (a == 0) {} else {\n\
__defineSetter__(\"\",1)\n\
}\n\
}\n\
")()
/* Don't crash/assert. */

View File

@ -0,0 +1,18 @@
(function() {
try {
(eval("\
function() {\
for each(let y in [0]) {\
for (var a = 0; a < 9; ++a) {\
if (a) {\
this.__defineGetter__(\"\",this)\
}\
}\
}\
}\
"))()
} catch(e) {}
})()
/* Don't assert. */

View File

@ -0,0 +1,21 @@
// |trace-test| error: TypeError
(function() {
(function g(m, n) {
if (m = n) {
return eval("x=this")
}
g(m, 1)[[]]
})()
})()
Function("\
for (let b in [0]) {\
for (var k = 0; k < 6; ++k) {\
if (k == 1) {\
print(x)\
}\
}\
}\
")()
/* Don't crash/assert. */

View File

@ -0,0 +1,15 @@
// |trace-test| error: InternalError
(function() {
try {
(Function("__defineGetter__(\"x\",(Function(\"for(z=0;z<6;z++)(x)\")))"))()
} catch(e) {}
})()
((function f(d, aaaaaa) {
if (bbbbbb = aaaaaa) {
x
}
f(bbbbbb, aaaaaa + 1)
})([], 0))
/* Don't assert (32-bit mac only, relies on very specific stack usage). */

View File

@ -0,0 +1,8 @@
(function() {
for (e in [0, 0]) {
if (/x/ < this) {}
}
})()
/* Don't assert. */

View File

@ -0,0 +1,11 @@
function a(code) {
var f = new Function("for each(let x in[false,'',/x/,'',{}]){if(x<x){(({}))}else if(x){}else{}}");
try {
f()
} catch(e) {}
}
a()
a()
/* Don't crash (CLI only). */

View File

@ -0,0 +1,11 @@
(function() {
try { (function() {
for each(let x in [0, /x/, 0, {}]) {
if (x < x) {}
}
})()
} catch(e) {}
})()
/* Don't assert. */

View File

@ -0,0 +1,9 @@
(function() {
for each(let x in [new Boolean(), new Boolean(), /x/, /x/]) {
while (x % x * /x/) {}
}
})()
/* Don't crash. */

View File

@ -0,0 +1,12 @@
// |trace-test| error: TypeError
(function() {
throw (function f(a, b) {
if (a.h == b) {
return eval("((function(){return 1})())!=this")
}
f(b)
})([], 0)
})()
/* Don't assert/crash. */

View File

@ -0,0 +1,4 @@
// |trace-test| error: TypeError
__defineGetter__("x",/a/)
" ".replace(/\s/,"")
x.b

View File

@ -0,0 +1,8 @@
(function() {
for each(let z in [new String(''), new String('q'), new String('')]) {
if (uneval() < z) function(){}
}
})()
/* Don't assert/crash. */

View File

@ -0,0 +1,11 @@
(function() {
let(z) {
for each(b in [{}]) { ({
get __noSuchMethod__() { return Function }
}).w()
}
}
})()
/* Don't crash/assert. */

View File

@ -0,0 +1,7 @@
// |trace-test| error: TypeError
for each(x in [new Number])
x.__proto__ = []
++x[x]
// don't assert

View File

@ -0,0 +1,7 @@
(function() {
for (a = 0; a < 2; a++)
''.watch("", function() {})
})()
/* Don't crash or assert. */

View File

@ -0,0 +1,5 @@
for each(let x in [0, {}, 0, {}]) {
x.valueOf
}
// don't crash

View File

@ -0,0 +1,6 @@
// |trace-test| error: InternalError
for (e in (function x() { [eval()].some(x) } ()));
/* Don't crash or assert. */

View File

@ -0,0 +1,8 @@
// |trace-test| error: TypeError
for (l in [Math.h.h.h.h.h.I.h.h.h.h.h.h.h.I.h.I]) {
t.x
}
/* Don't crash or assert. */

View File

@ -0,0 +1,13 @@
try {
(function() {
(Object.defineProperty(this, "x", ({
set: function() {}
})))
})()
} catch(e) {}
for (var a = 0; a < 4; a++) {
x = 7
}
/* Don't bogus assert. */

View File

@ -0,0 +1,6 @@
const X = 12;
eval("switch (X) { case X: print(); }");
/* Don't assert. */

View File

@ -0,0 +1,7 @@
options("strict", "werror");
var o = {};
// Don't throw here.
if (o.a)
x = true;

View File

@ -0,0 +1,5 @@
/* Test that NaN does not trigger js_InitMathClass & constants while parsing. */
var NaN
var x = 2;

View File

@ -0,0 +1,15 @@
// |trace-test| error: TypeError
x = 2;
function tryItOut(c) {
return eval("(function(){" + c + "})");
}
function doit() {
var f = tryItOut("((( \"\" \n for each (eval in [null, this, null, this, (1/0), new String('q'), new String('q'), null, null, null, new String('q'), new String('q'), new String('q'), null]) if (this)).eval(x = x)));");
f();
}
doit();

View File

@ -0,0 +1,21 @@
expected = '';
function g(code) {
f = Function(code);
gen = f();
gen.next();
try { gen.next(); } catch (ex) { expected = ex.toString() }
}
g("\
yield this.__defineGetter__('x', function(){ return z }); \
let z = new String('hi'); \
");
eval();
gc();
str = x;
assertEq(expected, "[object StopIteration]");
assertEq(str.toString(), "hi");

View File

@ -0,0 +1,60 @@
function()[function() function() function() function() function() function() {}]
foo = [{
text: "(function(){if(d){(1)}})",
s: function() {},
test: function() {
try {
f
} catch(e) {}
}
},
{
text: "(function(){t})",
s: function() {},
test: function() {}
},
{
text: "(function(){if(0){}})",
s: function() {},
test: function() {}
},
{
text: "(function(){if(1){}(2)})",
s: function() {},
test: function() {}
},
{
text: "(function(){g})",
b: function() {},
test: function() {}
},
{
text: "(function(){})",
s: function() {},
test: function() {}
},
{
text: "(function(){1})",
s: function() {},
test: function() {}
}]; (function() {
for (i = 0; i < foo.length; ++i) {
a = foo[i]
text = a.text
eval(text.replace(/@/, ""));
if (a.test()) {}
}
} ());
s = [function() function() function() function() function() function() {}]
[function() function() function() function() {}]
function() { [function() function() {}] }
function() {}
(eval("\
(function(){\
for each(d in[\
0,0,0,0,0,0,0,0,0,0,0,0,0,null,NaN,1,Boolean(false),Boolean(false)\
]){\
[].filter(new Function,gczeal(2))\
}\
})\
"))()

View File

@ -0,0 +1,9 @@
(function() {
for each(let d in [{}, {}, 0]) {
for each(e in [0, 0, 0, 0, 0, 0, 0, 0, 0]) {
d.__defineSetter__("", function() {})
}
}
})()
// don't assert

View File

@ -0,0 +1,15 @@
var o = {
g: function(a) {
return a;
}
};
function f() {
var z;
for (var i = 0; i < 10; ++i) {
z = o.g(i);
assertEq(z, i);
}
}
f();

View File

@ -0,0 +1,12 @@
function f() {
var o = [ 1, 2, 3, 4, 5 ];
for (var i = 6; i < 10; ++i)
o.push(i);
return o;
}
var o = f();
assertEq(o.join(','), [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ].join(','));

View File

@ -0,0 +1,4 @@
(function() {
for (a = 0; a < 2; a++)
''.watch("", function() {})
})()

View File

@ -0,0 +1,3 @@
for each(let x in [0, {}, 0, {}]) {
x.valueOf
}

View File

@ -0,0 +1,3 @@
for each(let w in [[], 0, [], 0]) {
w.unwatch()
}

View File

@ -0,0 +1,28 @@
function A()
{
this.a = 77;
this.b = 88;
}
function B()
{
}
B.prototype = new A;
function C()
{
}
C.prototype = new B;
function f() {
var o = new C;
var z;
for (var i = 0; i < 5; ++i) {
z = o.a;
assertEq(z, 77);
}
}
f();

View File

@ -0,0 +1,16 @@
// length, string
var expected = "3,6,4,3,6,4,3,6,4,3,6,4,";
var actual = '';
function f() {
var ss = [ [1, 2, 3], [1, 2, 3, 4, 5, 6], [1, 2, 3, 4] ];
for (var i = 0; i < 12; ++i) {
actual += ss[i%3].length + ',';
}
}
f();
assertEq(actual, expected);

View File

@ -0,0 +1,22 @@
// length, various types
var expected = "4,5,44,5,44,4,44,4,5,4,5,44,5,44,4,44,4,5,";
var actual = '';
function f() {
var a = [ "abcd", [1, 2, 3, 4, 5], { length: 44 } ];
for (var i = 0; i < 6; ++i) {
// Use 3 PICs so we start out with each type in one PIC.
var i1 = i % 3;
var i2 = (i+1) % 3;
var i3 = (i+2) % 3;
actual += a[i1].length + ',';
actual += a[i2].length + ',';
actual += a[i3].length + ',';
}
}
f();
assertEq(actual, expected);

View File

@ -0,0 +1,16 @@
// length, object
var expected = "777,777,777,777,777,";
var actual = '';
function f() {
var o = { a: 11, length: 777, b: 22 };
for (var i = 0; i < 5; ++i) {
actual += o.length + ',';
}
}
f();
assertEq(actual, expected);

View File

@ -0,0 +1,16 @@
// length, string
var expected = "3,6,4,3,6,4,3,6,4,3,6,4,";
var actual = '';
function f() {
var ss = [ "abc", "foobar", "quux" ];
for (var i = 0; i < 12; ++i) {
actual += ss[i%3].length + ',';
}
}
f();
assertEq(actual, expected);

View File

@ -0,0 +1,24 @@
// getprop, proto, 1 shape
var expected = "11,22,33,11,22,33,11,22,33,11,22,33,11,22,33,";
var actual = '';
var proto = { a: 11, b: 22, c: 33 };
function B() {
}
B.prototype = proto;
function f() {
var o = new B();
for (var i = 0; i < 5; ++i) {
actual += o.a + ',';
actual += o.b + ',';
actual += o.c + ',';
}
}
f();
assertEq(actual, expected);

View File

@ -0,0 +1,32 @@
// getprop, proto, 3 shapes
var expected = "22,202,202,22,202,202,22,202,202,";
var actual = '';
var protoB = { a: 11, b: 22, c: 33 };
function B() {
}
B.prototype = protoB;
var protoC = { a: 101, b: 202, c: 303 };
function C() {
}
C.prototype = protoC;
function f() {
var o1 = new B();
var o2 = new C();
var o3 = new C();
o3.q = 99;
var oa = [ o1, o2, o3 ];
for (var i = 0; i < 9; ++i) {
actual += oa[i%3].b + ',';
}
}
f();
assertEq(actual, expected);

View File

@ -0,0 +1,39 @@
// getprop, proto and self, 3 shapes
var expected = "22,202,99;202,99,22;99,22,202;22,202,99;202,99,22;99,22,202;22,202,99;202,99,22;99,22,202;";
var actual = '';
var protoB = { a: 11, b: 22, c: 33 };
function B() {
}
B.prototype = protoB;
var protoC = { a: 101, b: 202, c: 303 };
function C() {
}
C.prototype = protoC;
function f() {
var o1 = new B();
var o2 = new C();
var o3 = new C();
o3.b = 99;
var oa = [ o1, o2, o3 ];
for (var i = 0; i < 9; ++i) {
// Use 3 PICs so we start out with each type in one PIC.
var i1 = i % 3;
var i2 = (i+1) % 3;
var i3 = (i+2) % 3;
actual += oa[i1].b + ',';
actual += oa[i2].b + ',';
actual += oa[i3].b + ';';
}
}
f();
assertEq(actual, expected);

View File

@ -0,0 +1,18 @@
// getprop, self, 1 shape
var expected = "11,22,33,11,22,33,11,22,33,11,22,33,11,22,33,";
var actual = '';
function f() {
var o = { a: 11, b: 22, c: 33 };
for (var i = 0; i < 5; ++i) {
actual += o.a + ',';
actual += o.b + ',';
actual += o.c + ',';
}
}
f();
assertEq(actual, expected);

View File

@ -0,0 +1,18 @@
// getprop, self, 2 shapes
var expected = "22,303,22,303,22,303,22,303,";
var actual = '';
function f() {
var o1 = { a: 11, b: 22, c: 33 };
var o2 = { x: 101, y: 202, b: 303 };
var oa = [ o1, o2 ];
for (var i = 0; i < 8; ++i) {
actual += oa[i%2].b + ',';
}
}
f();
assertEq(actual, expected);

View File

@ -0,0 +1,19 @@
// getprop, self, 3 shapes
var expected = "22,303,1001,22,303,1001,22,303,";
var actual = '';
function f() {
var o1 = { a: 11, b: 22, c: 33 };
var o2 = { x: 101, y: 202, b: 303 };
var o3 = { b: 1001, x: 2002, y: 3003 };
var oa = [ o1, o2, o3 ];
for (var i = 0; i < 8; ++i) {
actual += oa[i%3].b + ',';
}
}
f();
assertEq(actual, expected);

View File

@ -0,0 +1,32 @@
// getprop, self, 8 shapes
var expected = "0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7,";
var actual = '';
function letter(i) {
return String.fromCharCode(97 + i);
}
function f() {
// Build 8 objects with different shapes and x in different slots.
var oa = [];
for (var i = 0; i < 8; ++i) {
var o = {};
for (var j = 0; j < 8; ++j) {
if (j != i) {
o[letter(j)] = 1000 + i * 10 + j;
} else {
o.x = i;
}
}
oa[i] = o;
}
for (var i = 0; i < 24; ++i) {
actual += oa[i%8].x + ',';
}
}
f();
assertEq(actual, expected);

View File

@ -0,0 +1,10 @@
function f() {
var o = { a: 555 };
for (var j = 0; j < 10; ++j) {
var i = o.a = 100 + j;
assertEq(i, 100 + j);
}
}
f()

View File

@ -0,0 +1,11 @@
function f() {
var o = { a: 5 };
for (var i = 0; i < 5; ++i) {
o.a = i;
}
assertEq(o.a, 4);
}
f();

View File

@ -0,0 +1,14 @@
function f(k) {
var o1 = { a: 5 };
var o2 = { b : 7, a : 9 };
for (var i = 0; i < k; ++i) {
var o = i % 2 ? o2 : o1;
o.a = i;
}
return o1.a + ',' + o2.a;
}
assertEq(f(5), '4,3')
assertEq(f(6), '4,5')

View File

@ -0,0 +1,33 @@
// Try to test that we handle shape regeneration correctly.
// This is a fragile test, but as of this writing, on dmandelin's
// windows box, we have the same shape number with different
// logical shapes in the two assertEq lines.
var o;
var p;
var zz;
var o2;
function f(x) {
return x.a;
}
gczeal(1);
gc();
zz = { q: 11 };
o = { a: 77, b: 88 };
o2 = { c: 11 };
p = { b: 99, a: 11 };
//print('s ' + shapeOf(zz) + ' ' + shapeOf(o) + ' ' + shapeOf(o2) + ' ' + shapeOf(p));
assertEq(f(o), 77);
o = undefined;
gczeal(1);
gc();
//print('s ' + 'x' + ' ' + shapeOf(p));
assertEq(f(p), 11);

View File

@ -0,0 +1,30 @@
// test getthisprop
var expected = "22,22,22,;33,33,33,;";
var actual = '';
function f() {
for (var i = 0; i < 3; ++i) {
actual += this.b + ',';
}
actual += ';';
}
function A() {
this.a = 11;
this.b = 22;
};
A.prototype.f = f;
function B() {
this.b = 33;
this.c = 44;
};
B.prototype.f = f;
new A().f();
new B().f();
assertEq(actual, expected);