Merge tracemonkey to mozilla-central. a=blockers.

This commit is contained in:
Robert Sayre 2010-11-23 10:10:32 -05:00
Родитель d32c70e25c 867324fec9
Коммит 6b1320e19a
78 изменённых файлов: 1663 добавлений и 799 удалений

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

@ -1090,6 +1090,11 @@ namespace JSC {
patchPointerInternal(reinterpret_cast<intptr_t>(from) - sizeof(ARMWord), to);
}
static bool canRelinkJump(void* from, void* to)
{
return true;
}
static void linkCall(void* code, JmpSrc from, void* to)
{
js::JaegerSpew(js::JSpew_Insns,

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

@ -1628,6 +1628,11 @@ public:
ExecutableAllocator::cacheFlush(reinterpret_cast<uint16_t*>(from) - 5, 5 * sizeof(uint16_t));
}
static bool canRelinkJump(void* from, void* to)
{
return true;
}
static void relinkCall(void* from, void* to)
{

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

@ -550,6 +550,11 @@ protected:
AssemblerType::relinkJump(jump.dataLocation(), destination.dataLocation());
}
static bool canRepatchJump(CodeLocationJump jump, CodeLocationLabel destination)
{
return AssemblerType::canRelinkJump(jump.dataLocation(), destination.dataLocation());
}
static void repatchNearCall(CodeLocationNearCall nearCall, CodeLocationLabel destination)
{
AssemblerType::relinkCall(nearCall.dataLocation(), destination.executableAddress());

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

@ -49,15 +49,18 @@ class RepatchBuffer {
typedef MacroAssemblerCodePtr CodePtr;
public:
RepatchBuffer(void *start, size_t size, bool mprot = true)
: m_start(start), m_size(size), mprot(mprot)
RepatchBuffer(const MacroAssemblerCodeRef &ref)
{
ExecutableAllocator::makeWritable(m_start, m_size);
m_start = ref.m_code.executableAddress();
m_size = ref.m_size;
mprot = true;
if (mprot)
ExecutableAllocator::makeWritable(m_start, m_size);
}
RepatchBuffer(CodeBlock* codeBlock)
RepatchBuffer(const JITCode &code)
{
JITCode& code = codeBlock->getJITCode();
m_start = code.start();
m_size = code.size();
mprot = true;
@ -77,6 +80,11 @@ public:
MacroAssembler::repatchJump(jump, destination);
}
bool canRelink(CodeLocationJump jump, CodeLocationLabel destination)
{
return MacroAssembler::canRepatchJump(jump, destination);
}
void relink(CodeLocationCall call, CodeLocationLabel destination)
{
MacroAssembler::repatchCall(call, destination);

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

@ -2140,6 +2140,12 @@ public:
FIXME_INSN_PRINTING;
setRel32(from, to);
}
static bool canRelinkJump(void* from, void* to)
{
intptr_t offset = reinterpret_cast<intptr_t>(to) - reinterpret_cast<intptr_t>(from);
return (offset == static_cast<int32_t>(offset));
}
static void relinkCall(void* from, void* to)
{

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

@ -100,6 +100,8 @@ inline size_t roundUpAllocationSize(size_t request, size_t granularity)
#if ENABLE_ASSEMBLER
//#define DEBUG_STRESS_JSC_ALLOCATOR
namespace JSC {
// These are reference-counted. A new one (from the constructor or create)
@ -217,11 +219,13 @@ public:
ExecutablePool* poolForSize(size_t n)
{
#ifndef DEBUG_STRESS_JSC_ALLOCATOR
// Try to fit in the existing small allocator
if (n < m_smallAllocationPool->available()) {
m_smallAllocationPool->addRef();
return m_smallAllocationPool;
}
#endif
// If the request is large, we just provide a unshared allocator
if (n > JIT_ALLOCATOR_LARGE_ALLOC_SIZE)
@ -364,7 +368,11 @@ inline ExecutablePool::ExecutablePool(size_t n) : m_refCount(1)
m_freePtr = NULL;
return;
}
#ifdef DEBUG_STRESS_JSC_ALLOCATOR
Allocation mem = systemAlloc(size_t(4294967291));
#else
Allocation mem = systemAlloc(allocSize);
#endif
if (!mem.pages) {
m_freePtr = NULL;
return;
@ -384,7 +392,11 @@ inline void* ExecutablePool::poolAllocate(size_t n)
if (allocSize == OVERSIZE_ALLOCATION)
return NULL;
#ifdef DEBUG_STRESS_JSC_ALLOCATOR
Allocation result = systemAlloc(size_t(4294967291));
#else
Allocation result = systemAlloc(allocSize);
#endif
if (!result.pages)
return NULL;

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

@ -48,10 +48,10 @@ class JITCode {
public:
JITCode(void* start, size_t size)
: m_start(start), m_size(size)
{
}
void* start() { return m_start; }
size_t size() { return m_size; }
{ }
JITCode() { }
void* start() const { return m_start; }
size_t size() const { return m_size; }
private:
void* m_start;
size_t m_size;

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

@ -353,6 +353,15 @@ AR_FLAGS='cr $@'
if test "$COMPILE_ENVIRONMENT"; then
# Note:
# In Mozilla, we use the names $target, $host and $build incorrectly, but are
# too far gone to back out now. See Bug 475488:
# - When we say $target, we mean $host, that is, the system on which
# Mozilla will be run.
# - When we say $host, we mean $build, that is, the system on which Mozilla
# is built.
# - $target (in its correct usage) is for compilers who generate code for a
# different platform than $host, so it would not be used by Mozilla.
if test "$target" != "$host"; then
echo "cross compiling from $host to $target"
CROSS_COMPILE=1
@ -915,6 +924,12 @@ fi
fi # COMPILE_ENVIRONMENT
if test "$cross_compiling" = "yes"; then
CROSS_COMPILE=1
else
CROSS_COMPILE=
fi
# Check to see if we are running in a broken QEMU scratchbox.
# We know that anything below 1.0.16 is broken.
AC_CHECK_PROGS(SBCONF, sb-conf ve, "")

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

@ -0,0 +1,5 @@
var array = new Array(4294967295);
for (var j = 0; j < RUNLOOP; ++j) { '' + array.length; }
// Don't assert.

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

@ -0,0 +1,9 @@
var actual = '';
var a = [0, 1];
for (var i in a) {
appendToActual(i);
a.pop();
}
assertEq(actual, "0,");

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

@ -0,0 +1,9 @@
var actual = '';
var a = [0, 1];
for (var i in a) {
appendToActual(i);
delete a[1];
}
assertEq(actual, "0,");

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

@ -0,0 +1,11 @@
var actual = '';
var a = [0, 1];
a.x = 10;
delete a.x;
for (var i in a) {
appendToActual(i);
a.pop();
}
assertEq(actual, "0,");

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

@ -0,0 +1,4 @@
var a = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
a.push(1);
a.splice(0);
a.d = "";

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

@ -0,0 +1,18 @@
// Iterating over a property with an XML id.
function n() {}
function g() {}
eval("\
function a() {}\
function b() {\
for (w in this) {}\
Object.defineProperty(\
this, \
new AttributeName, \
({enumerable: true})\
)\
}\
for (z in [0, 0, 0]) b()\
")
// Test it doesn't assert.

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

@ -0,0 +1,43 @@
var shapes = {};
function stringify(a) {
assertEq(shapes[shapeOf(a)], undefined);
shapes[shapeOf(a)] = 1;
var b = "";
for (var c in a) {
b += c + ":";
if (typeof a[c] == "function")
b += "function,";
else
b += a[c] + ",";
}
return b;
}
function test1() {
return stringify({a: 0, b: 1, a: function() {} });
}
for (var i = 0; i < 3; i++)
assertEq(test1(), "a:function,b:1,");
// This does not cause the object to go to dictionary mode, unlike the above.
function test2() {
return stringify({a: 0, b: 1, a: 2, b: 3});
}
assertEq(test2(), "a:2,b:3,");
function test3() {
return stringify({
aa:0,ab:1,ac:2,ad:3,ae:4,af:5,ag:6,ah:7,ai:8,aj:9,
ba:0,bb:1,bc:2,bd:3,be:4,bf:5,bg:6,bh:7,bi:8,bj:9,
ca:0,cb:1,cc:2,cd:3,ce:4,cf:5,cg:6,ch:7,ci:8,cj:9,
da:0,db:1,dc:2,dd:3,de:4,df:5,dg:6,dh:7,di:8,dj:9,
ea:0,eb:1,ec:2,ed:3,ee:4,ef:5,eg:6,eh:7,ei:8,ej:9,
fa:0,fb:1,fc:2,fd:3,fe:4,ff:5,fg:6,fh:7,fi:8,fj:9,
ga:0,gb:1,gc:2,gd:3,ge:4,gf:5,gg:6,gh:7,gi:8,gj:9,
ha:0,hb:1,hc:2,hd:3,he:4,hf:5,hg:6,hh:7,hi:8,hj:9
});
}
for (var i = 0; i < HOTLOOP + 2; i++)
assertEq(test3(), "aa:0,ab:1,ac:2,ad:3,ae:4,af:5,ag:6,ah:7,ai:8,aj:9,ba:0,bb:1,bc:2,bd:3,be:4,bf:5,bg:6,bh:7,bi:8,bj:9,ca:0,cb:1,cc:2,cd:3,ce:4,cf:5,cg:6,ch:7,ci:8,cj:9,da:0,db:1,dc:2,dd:3,de:4,df:5,dg:6,dh:7,di:8,dj:9,ea:0,eb:1,ec:2,ed:3,ee:4,ef:5,eg:6,eh:7,ei:8,ej:9,fa:0,fb:1,fc:2,fd:3,fe:4,ff:5,fg:6,fh:7,fi:8,fj:9,ga:0,gb:1,gc:2,gd:3,ge:4,gf:5,gg:6,gh:7,gi:8,gj:9,ha:0,hb:1,hc:2,hd:3,he:4,hf:5,hg:6,hh:7,hi:8,hj:9,");

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

@ -0,0 +1,18 @@
function test1() {
return String(#1=[1,2,#1#.length,3,4,delete #1#[0]]);
}
assertEq(test1(), ",2,2,3,4,true");
function test2() {
var x = #1={a:0,b:1,c:delete #1#.a};
var y = "";
for (var z in x) { y += z + ":" + x[z] + ","; }
return y;
}
assertEq(test2(), "b:1,c:true,");
function test3() {
return String(#1=[1,2,#1#.foo = 3,4,5,6]);
}
assertEq(test3(), "1,2,3,4,5,6");

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

@ -0,0 +1,41 @@
var x = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,gc(),
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,gc(),
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,gc(),
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,gc(),
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,gc(),
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,gc(),
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,gc(),
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,gc(),
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,gc(),
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,gc(),
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,gc(),
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,gc(),
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,gc(),
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,gc(),
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,gc(),
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,gc(),
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,gc(),
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,gc(),
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,gc(),
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,gc(),
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,gc(),
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,gc(),
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,gc(),
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,gc(),
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,gc(),
];
assertEq(x.length, 500);
assertEq(x[10], 11);
assertEq(x[90], 11);
function stringify(a) {
var b = "";
for (var c in a) { b += c + ","; }
return b;
}
var y = {a:1,b:2,c:3,d:4,e:gc(),f:6,g:7,h:8,i:9,j:gc(),
k:11,l:12,m:13,n:14,o:gc(),p:16,q:17,r:18,s:19,t:gc()};
assertEq(stringify(y), "a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,");

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

@ -0,0 +1,13 @@
/* Element initializers with unknown index. */
function foo(i) {
var x = [1,2,i == 1 ? 3 : 4,5,6];
var y = "" + x;
if (i == 1)
assertEq(y, "1,2,3,5,6");
else
assertEq(y, "1,2,4,5,6");
}
for (var i = 0; i < HOTLOOP + 2; i++)
foo(i);

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

@ -6,5 +6,7 @@ function caller(obj) {
var x = ({ dana : "zuul" });
return x;
}
trap(caller, 23, "x = 'success'; nop()");
// 0 is the pc of "assertJit()", we want the pc of "return x", 2 lines below.
var pc = line2pc(caller, pc2line(caller, 0) + 2);
trap(caller, pc, "x = 'success'; nop()");
assertEq(caller(this), "success");

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

@ -2,7 +2,7 @@ setDebug(true);
x = "notset";
function main() {
/* The JSOP_STOP in a. */
a = { valueOf: function () { trap(main, 38, "success()"); } };
a = { valueOf: function () { trap(main, 36, "success()"); } };
a + "";
x = "failure";
}

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

@ -2,7 +2,7 @@ setDebug(true);
x = "notset";
function main() {
/* The JSOP_STOP in a. */
a = { valueOf: function () { trap(main, 59, "success()"); } };
a = { valueOf: function () { trap(main, 57, "success()"); } };
b = "";
eval();
a + b;

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

@ -0,0 +1,8 @@
function f() {
var x = 2.6;
var y = 2.1;
return x % y;
}
assertEq(f(), 0.5);

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

@ -0,0 +1,8 @@
function f() {
var i = 1000;
var rest = i % 3;
var div = (i - rest) / 3;
assertEq(div, 333);
}
f();

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

@ -0,0 +1,8 @@
function f() {
var x = 5;
var y = 0;
return x % y;
}
assertEq(f(), NaN);

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

@ -62,7 +62,6 @@ CPPSRCS = \
testGCChunkAlloc.cpp \
testGetPropertyDefault.cpp \
testIntString.cpp \
testIsAboutToBeFinalized.cpp \
testLookup.cpp \
testNewObject.cpp \
testOps.cpp \

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

@ -51,6 +51,9 @@ BEGIN_TEST(testThreads_bug561444)
}
END_TEST(testThreads_bug561444)
const PRUint32 NATIVE_STACK_SIZE = 64 * 1024;
const PRUint32 NATIVE_STACK_HEADROOM = 8 * 1024;
template <class T>
class Repeat {
size_t n;
@ -96,7 +99,7 @@ class Parallel {
size_t i;
for (i = 0; i < n; i++) {
thread[i] = PR_CreateThread(PR_USER_THREAD, threadMain, &p, PR_PRIORITY_NORMAL,
PR_LOCAL_THREAD, PR_JOINABLE_THREAD, 0);
PR_LOCAL_THREAD, PR_JOINABLE_THREAD, NATIVE_STACK_SIZE);
if (thread[i] == NULL) {
p.ok = false;
break;
@ -125,6 +128,7 @@ class eval {
if (!cx)
return false;
JS_SetNativeStackQuota(cx, NATIVE_STACK_SIZE - NATIVE_STACK_HEADROOM);
bool ok = false;
{
JSAutoRequest ar(cx);
@ -152,4 +156,19 @@ BEGIN_TEST(testThreads_bug604782)
}
END_TEST(testThreads_bug604782)
BEGIN_TEST(testThreads_bug609103)
{
const char *code =
"var x = {};\n"
"for (var i = 0; i < 10000; i++)\n"
" x = {next: x};\n";
jsrefcount rc = JS_SuspendRequest(cx);
bool ok = parallel(2, eval(rt, code))();
JS_ResumeRequest(cx, rc);
CHECK(ok);
return true;
}
END_TEST(testThreads_bug609103)
#endif

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

@ -1644,7 +1644,7 @@ JS_SetNativeStackQuota(JSContext *cx, size_t stackSize);
* Set the quota on the number of bytes that stack-like data structures can
* use when the runtime compiles and executes scripts. These structures
* consume heap space, so JS_SetThreadStackLimit does not bound their size.
* The default quota is 32MB which is quite generous.
* The default quota is 128MB which is very generous.
*
* The function must be called before any script compilation or execution API
* calls, i.e. either immediately after JS_NewContext or from JSCONTEXT_NEW
@ -1653,7 +1653,7 @@ JS_SetNativeStackQuota(JSContext *cx, size_t stackSize);
extern JS_PUBLIC_API(void)
JS_SetScriptStackQuota(JSContext *cx, size_t quota);
#define JS_DEFAULT_SCRIPT_STACK_QUOTA ((size_t) 0x2000000)
#define JS_DEFAULT_SCRIPT_STACK_QUOTA ((size_t) 0x8000000)
/************************************************************************/

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

@ -115,9 +115,6 @@ using namespace js::gc;
#define MAXINDEX 4294967295u
#define MAXSTR "4294967295"
/* Small arrays are dense, no matter what. */
#define MIN_SPARSE_INDEX 256
/*
* Use the limit on number of object slots for sanity and consistency (see the
* assertion in JSObject::makeDenseArraySlow).
@ -503,7 +500,7 @@ DeleteArrayElement(JSContext *cx, JSObject *obj, jsdouble index, JSBool strict)
jsuint idx = jsuint(index);
if (idx < obj->getDenseArrayCapacity()) {
obj->setDenseArrayElement(idx, MagicValue(JS_ARRAY_HOLE));
return JS_TRUE;
return js_SuppressDeletedIndexProperties(cx, obj, idx, idx+1);
}
}
return JS_TRUE;
@ -2987,17 +2984,6 @@ JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewPreallocatedArray, CONTEXT, OBJECT, I
0, nanojit::ACCSET_STORE_ANY)
#endif
JSObject* JS_FASTCALL
js_InitializerArray(JSContext* cx, int32 count)
{
gc::FinalizeKind kind = GuessObjectGCKind(count, true);
return NewArrayWithKind(cx, kind);
}
#ifdef JS_TRACER
JS_DEFINE_CALLINFO_2(extern, OBJECT, js_InitializerArray, CONTEXT, INT32, 0,
nanojit::ACCSET_STORE_ANY)
#endif
JSObject *
js_InitArrayClass(JSContext *cx, JSObject *obj)
{

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

@ -144,6 +144,9 @@ js_NewArrayObject(JSContext *cx, jsuint length, const js::Value *vector);
extern JSObject *
js_NewSlowArrayObject(JSContext *cx);
/* Minimum size at which a dense array can be made sparse. */
const uint32 MIN_SPARSE_INDEX = 256;
extern JSBool
js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp);

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

@ -577,7 +577,6 @@ js_dmod(jsdouble a, jsdouble b);
/* Defined in jsarray.cpp. */
JS_DECLARE_CALLINFO(js_NewEmptyArray)
JS_DECLARE_CALLINFO(js_NewPreallocatedArray)
JS_DECLARE_CALLINFO(js_InitializerArray)
JS_DECLARE_CALLINFO(js_ArrayCompPush_tn)
JS_DECLARE_CALLINFO(js_EnsureDenseArrayCapacity)

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

@ -495,7 +495,10 @@ JSThreadData::init()
if (!stackSpace.init())
return false;
#ifdef JS_TRACER
InitJIT(&traceMonitor);
if (!InitJIT(&traceMonitor)) {
finish();
return false;
}
#endif
dtoaState = js_NewDtoaState();
if (!dtoaState) {

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

@ -898,6 +898,10 @@ typedef HashMap<jsbytecode*,
class Oracle;
typedef HashSet<JSScript *,
DefaultHasher<JSScript *>,
SystemAllocPolicy> TracedScriptSet;
/*
* Trace monitor. Every JSThread (if JS_THREADSAFE) or JSRuntime (if not
* JS_THREADSAFE) has an associated trace monitor that keeps track of loop
@ -998,6 +1002,9 @@ struct TraceMonitor {
// before use.
TypeMap* cachedTempTypeMap;
/* Scripts with recorded fragments. */
TracedScriptSet tracedScripts;
#ifdef DEBUG
/* Fields needed for fragment/guard profiling. */
nanojit::Seq<nanojit::Fragment*>* branches;

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

@ -1413,7 +1413,7 @@ JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp,
JS_ASSERT_NOT_ON_TRACE(cx);
if (!CheckDebugMode(cx))
return JS_FALSE;
return false;
JSObject *scobj = JS_GetFrameScopeChain(cx, fp);
if (!scobj)
@ -1421,7 +1421,7 @@ JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp,
js::AutoCompartment ac(cx, scobj);
if (!ac.enter())
return NULL;
return false;
/*
* NB: This function breaks the assumption that the compiler can see all

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

@ -1435,6 +1435,13 @@ EmitTraceOp(JSContext *cx, JSCodeGenerator *cg)
SET_UINT16(pc_, j); \
JS_END_MACRO
#define EMIT_UINT16_IN_PLACE(offset, op, i) \
JS_BEGIN_MACRO \
CG_CODE(cg, offset)[0] = op; \
CG_CODE(cg, offset)[1] = UINT16_HI(i); \
CG_CODE(cg, offset)[2] = UINT16_LO(i); \
JS_END_MACRO
static JSBool
FlushPops(JSContext *cx, JSCodeGenerator *cg, intN *npops)
{
@ -1734,6 +1741,12 @@ LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom,
return JS_TRUE;
}
static inline bool
FitsWithoutBigIndex(uintN index)
{
return index < JS_BIT(16);
}
/*
* Return JSOP_NOP to indicate that index fits 2 bytes and no index segment
* reset instruction is necessary, JSOP_FALSE to indicate an error or either
@ -1753,7 +1766,7 @@ EmitBigIndexPrefix(JSContext *cx, JSCodeGenerator *cg, uintN index)
JS_STATIC_ASSERT(INDEX_LIMIT >=
(JSOP_INDEXBASE3 - JSOP_INDEXBASE1 + 2) << 16);
if (index < JS_BIT(16))
if (FitsWithoutBigIndex(index))
return JSOP_NOP;
indexBase = index >> 16;
if (indexBase <= JSOP_INDEXBASE3 - JSOP_INDEXBASE1 + 1) {
@ -4467,15 +4480,8 @@ EmitFunctionDefNop(JSContext *cx, JSCodeGenerator *cg, uintN index)
static bool
EmitNewInit(JSContext *cx, JSCodeGenerator *cg, JSProtoKey key, JSParseNode *pn, int sharpnum)
{
/*
* Watch for overflow on the initializer size. This isn't problematic because
* (a) we'll be reporting an error for the initializer shortly, and (b)
* the count is only used as a hint for the interpreter and JITs, and does not
* need to be correct.
*/
uint16 count = (pn->pn_count >= JS_BIT(16)) ? JS_BIT(16) - 1 : pn->pn_count;
EMIT_UINT16PAIR_IMM_OP(JSOP_NEWINIT, (uint16) key, count);
if (js_Emit3(cx, cg, JSOP_NEWINIT, (jsbytecode) key, 0) < 0)
return false;
#if JS_HAS_SHARP_VARS
if (cg->hasSharps()) {
if (pn->pn_count != 0)
@ -6771,41 +6777,19 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
* JSOP_NEWINIT and JSOP_INITELEM bytecodes to ignore setters and to
* avoid dup'ing and popping the array as each element is added, as
* JSOP_SETELEM/JSOP_SETPROP would do.
*
* If no sharp variable is defined, the initializer is not for an array
* comprehension, the initializer is not overlarge, and the initializer
* is not in global code (whose stack growth cannot be precisely modeled
* due to the need to reserve space for global variables and regular
* expressions), use JSOP_NEWARRAY to minimize opcodes and to create the
* array using a fast, all-at-once process rather than a slow, element-
* by-element process.
*/
#if JS_HAS_SHARP_VARS
sharpnum = -1;
do_emit_array:
#endif
op = (JS_LIKELY(pn->pn_count < JS_BIT(16)) && cg->inFunction())
? JSOP_NEWARRAY
: JSOP_NEWINIT;
#if JS_HAS_GENERATORS
if (pn->pn_type == TOK_ARRAYCOMP)
op = JSOP_NEWINIT;
#endif
#if JS_HAS_SHARP_VARS
JS_ASSERT_IF(sharpnum >= 0, cg->hasSharps());
if (cg->hasSharps())
op = JSOP_NEWINIT;
#endif
if (op == JSOP_NEWINIT && !EmitNewInit(cx, cg, JSProto_Array, pn, sharpnum))
return JS_FALSE;
#if JS_HAS_GENERATORS
if (pn->pn_type == TOK_ARRAYCOMP) {
uintN saveDepth;
if (!EmitNewInit(cx, cg, JSProto_Array, pn, sharpnum))
return JS_FALSE;
/*
* Pass the new array's stack index to the TOK_ARRAYPUSH case via
* cg->arrayCompDepth, then simply traverse the TOK_FOR node and
@ -6825,9 +6809,25 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
}
#endif /* JS_HAS_GENERATORS */
/*
* Use the slower NEWINIT for arrays in scripts containing sharps, and when
* the array length exceeds MIN_SPARSE_INDEX and can be slowified during GC.
* :FIXME: bug 607825 handle slowify case.
*/
if (cg->hasSharps() || pn->pn_count >= MIN_SPARSE_INDEX) {
if (!EmitNewInit(cx, cg, JSProto_Array, pn, sharpnum))
return JS_FALSE;
} else {
ptrdiff_t off = js_EmitN(cx, cg, JSOP_NEWARRAY, 3);
if (off < 0)
return JS_FALSE;
pc = CG_CODE(cg, off);
SET_UINT24(pc, pn->pn_count);
}
pn2 = pn->pn_head;
for (atomIndex = 0; pn2; atomIndex++, pn2 = pn2->pn_next) {
if (op == JSOP_NEWINIT && !EmitNumberOp(cx, atomIndex, cg))
if (!EmitNumberOp(cx, atomIndex, cg))
return JS_FALSE;
if (pn2->pn_type == TOK_COMMA && pn2->pn_arity == PN_NULLARY) {
if (js_Emit1(cx, cg, JSOP_HOLE) < 0)
@ -6836,7 +6836,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
if (!js_EmitTree(cx, cg, pn2))
return JS_FALSE;
}
if (op == JSOP_NEWINIT && js_Emit1(cx, cg, JSOP_INITELEM) < 0)
if (js_Emit1(cx, cg, JSOP_INITELEM) < 0)
return JS_FALSE;
}
JS_ASSERT(atomIndex == pn->pn_count);
@ -6847,18 +6847,12 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
return JS_FALSE;
}
if (op == JSOP_NEWINIT) {
/*
* Emit an op to finish the array and, secondarily, to aid in sharp
* array cleanup (if JS_HAS_SHARP_VARS) and decompilation.
*/
if (!EmitEndInit(cx, cg, atomIndex))
return JS_FALSE;
break;
}
JS_ASSERT(atomIndex < JS_BIT(16));
EMIT_UINT16_IMM_OP(JSOP_NEWARRAY, atomIndex);
/*
* Emit an op to finish the array and, secondarily, to aid in sharp
* array cleanup (if JS_HAS_SHARP_VARS) and decompilation.
*/
if (!EmitEndInit(cx, cg, atomIndex))
return JS_FALSE;
break;
case TOK_RC: {
@ -6880,9 +6874,22 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
* ignore setters and to avoid dup'ing and popping the object as each
* property is added, as JSOP_SETELEM/JSOP_SETPROP would do.
*/
ptrdiff_t offset = CG_NEXT(cg) - CG_BASE(cg);
if (!EmitNewInit(cx, cg, JSProto_Object, pn, sharpnum))
return JS_FALSE;
/*
* Try to construct the shape of the object as we go, so we can emit a
* JSOP_NEWOBJECT with the final shape instead.
*/
JSObject *obj = NULL;
if (!cg->hasSharps() && cg->compileAndGo()) {
gc::FinalizeKind kind = GuessObjectGCKind(pn->pn_count, false);
obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
if (!obj)
return JS_FALSE;
}
uintN methodInits = 0, slowMethodInits = 0;
for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
/* Emit an index for t[2] for later consumption by JSOP_INITELEM. */
@ -6898,12 +6905,14 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
op = PN_OP(pn2);
if (op == JSOP_GETTER || op == JSOP_SETTER) {
obj = NULL;
if (js_Emit1(cx, cg, op) < 0)
return JS_FALSE;
}
/* Annotate JSOP_INITELEM so we decompile 2:c and not just c. */
if (pn3->pn_type == TOK_NUMBER) {
obj = NULL;
if (js_NewSrcNote(cx, cg, SRC_INITPROP) < 0)
return JS_FALSE;
if (js_Emit1(cx, cg, JSOP_INITELEM) < 0)
@ -6921,24 +6930,58 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
if (lambda)
++methodInits;
if (op == JSOP_INITPROP && lambda && init->pn_funbox->joinable()) {
obj = NULL;
op = JSOP_INITMETHOD;
pn2->pn_op = uint8(op);
} else {
/*
* Disable NEWOBJECT on initializers that set __proto__, which has
* a non-standard setter on objects.
*/
if (pn3->pn_atom == cx->runtime->atomState.protoAtom)
obj = NULL;
op = JSOP_INITPROP;
if (lambda)
++slowMethodInits;
}
if (obj) {
JS_ASSERT(!obj->inDictionaryMode());
JSProperty *prop = NULL;
if (!js_DefineNativeProperty(cx, obj,
ATOM_TO_JSID(pn3->pn_atom), UndefinedValue(), NULL, NULL,
JSPROP_ENUMERATE, 0, 0, &prop, 0)) {
return JS_FALSE;
}
if (obj->inDictionaryMode())
obj = NULL;
}
EMIT_INDEX_OP(op, ALE_INDEX(ale));
}
}
if (cg->funbox && cg->funbox->shouldUnbrand(methodInits, slowMethodInits)) {
obj = NULL;
if (js_Emit1(cx, cg, JSOP_UNBRAND) < 0)
return JS_FALSE;
}
if (!EmitEndInit(cx, cg, pn->pn_count))
return JS_FALSE;
if (obj) {
/*
* The object survived and has a predictable shape. Update the original bytecode,
* as long as we can do so without using a big index prefix/suffix.
*/
JSObjectBox *objbox = cg->parser->newObjectBox(obj);
if (!objbox)
return JS_FALSE;
unsigned index = cg->objectList.index(objbox);
if (FitsWithoutBigIndex(index))
EMIT_UINT16_IN_PLACE(offset, JSOP_NEWOBJECT, uint16(index));
}
break;
}

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

@ -2062,7 +2062,7 @@ fun_finalize(JSContext *cx, JSObject *obj)
* very early.
*/
if (FUN_INTERPRETED(fun) && fun->u.i.script)
js_DestroyScript(cx, fun->u.i.script);
js_DestroyScriptFromGC(cx, fun->u.i.script, NULL);
}
int

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

@ -1751,7 +1751,7 @@ js_DestroyScriptsToGC(JSContext *cx, JSThreadData *data)
while ((script = *listp) != NULL) {
*listp = script->u.nextToGC;
script->u.nextToGC = NULL;
js_DestroyScript(cx, script);
js_DestroyScriptFromGC(cx, script, data);
}
}
}

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

@ -4504,9 +4504,14 @@ BEGIN_CASE(JSOP_GETELEM)
else
goto intern_big_int;
} else {
intern_big_int:
if (!js_InternNonIntElementId(cx, obj, rref, &id))
goto error;
int32_t i;
if (ValueFitsInInt32(rref, &i) && INT_FITS_IN_JSID(i)) {
id = INT_TO_JSID(i);
} else {
intern_big_int:
if (!js_InternNonIntElementId(cx, obj, rref, &id))
goto error;
}
}
if (!obj->getProperty(cx, id, &rval))
@ -5879,43 +5884,56 @@ BEGIN_CASE(JSOP_HOLE)
PUSH_HOLE();
END_CASE(JSOP_HOLE)
BEGIN_CASE(JSOP_NEWARRAY)
{
len = GET_UINT16(regs.pc);
cx->assertValidStackDepth(len);
JSObject *obj = js_NewArrayObject(cx, len, regs.sp - len);
if (!obj)
goto error;
regs.sp -= len - 1;
regs.sp[-1].setObject(*obj);
}
END_CASE(JSOP_NEWARRAY)
BEGIN_CASE(JSOP_NEWINIT)
{
jsint i = GET_UINT16(regs.pc);
jsint count = GET_UINT16(regs.pc + UINT16_LEN);
jsint i = regs.pc[1];
JS_ASSERT(i == JSProto_Array || i == JSProto_Object);
JSObject *obj;
gc::FinalizeKind kind = GuessObjectGCKind(count, i == JSProto_Array);
if (i == JSProto_Array) {
obj = NewArrayWithKind(cx, kind);
if (!obj)
goto error;
obj = js_NewArrayObject(cx, 0, NULL);
} else {
gc::FinalizeKind kind = GuessObjectGCKind(0, false);
obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
if (!obj)
goto error;
}
if (!obj)
goto error;
PUSH_OBJECT(*obj);
CHECK_INTERRUPT_HANDLER();
}
END_CASE(JSOP_NEWINIT)
BEGIN_CASE(JSOP_NEWARRAY)
{
unsigned count = GET_UINT24(regs.pc);
JSObject *obj = js_NewArrayObject(cx, count, NULL);
if (!obj || !obj->ensureDenseArrayElements(cx, count))
goto error;
PUSH_OBJECT(*obj);
CHECK_INTERRUPT_HANDLER();
}
END_CASE(JSOP_NEWARRAY)
BEGIN_CASE(JSOP_NEWOBJECT)
{
JSObject *baseobj;
LOAD_OBJECT(0, baseobj);
JSObject *obj = CopyInitializerObject(cx, baseobj);
if (!obj)
goto error;
PUSH_OBJECT(*obj);
CHECK_INTERRUPT_HANDLER();
}
END_CASE(JSOP_NEWOBJECT)
BEGIN_CASE(JSOP_ENDINIT)
{
/* FIXME remove JSOP_ENDINIT bug 588522 */
@ -5938,10 +5956,6 @@ BEGIN_CASE(JSOP_INITMETHOD)
/*
* Probe the property cache.
*
* We can not assume that the object created by JSOP_NEWINIT is still
* single-threaded as the debugger can access it from other threads.
* So check first.
*
* On a hit, if the cached shape has a non-default setter, it must be
* __proto__. If shape->previous() != obj->lastProperty(), there must be a
* repeated property name. The fast path does not handle these two cases.

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

@ -103,6 +103,8 @@ enum JSFrameFlags
JSFRAME_HAS_PREVPC = 0x400000 /* frame has prevpc_ set */
};
namespace js { namespace mjit { struct JITScript; } }
/*
* A stack frame is a part of a stack segment (see js::StackSegment) which is
* on the per-thread VM stack (see js::StackSpace).
@ -771,6 +773,12 @@ struct JSStackFrame
JS_STATIC_ASSERT(sizeof(JSStackFrame) % sizeof(js::Value) == 0);
}
#ifdef JS_METHODJIT
js::mjit::JITScript *jit() {
return script()->getJIT(isConstructing());
}
#endif
void methodjitStaticAsserts();
#ifdef DEBUG

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

@ -2831,14 +2831,18 @@ JS_DEFINE_TRCINFO_1(js_Object,
nanojit::ACCSET_STORE_ANY)))
JSObject* FASTCALL
js_InitializerObject(JSContext* cx, int32 count)
js_InitializerObject(JSContext* cx, JSObject *proto, JSObject *baseobj)
{
gc::FinalizeKind kind = GuessObjectGCKind(count, false);
return NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
if (!baseobj) {
gc::FinalizeKind kind = GuessObjectGCKind(0, false);
return NewObjectWithClassProto(cx, &js_ObjectClass, proto, kind);
}
return CopyInitializerObject(cx, baseobj);
}
JS_DEFINE_CALLINFO_2(extern, OBJECT, js_InitializerObject, CONTEXT, INT32, 0,
nanojit::ACCSET_STORE_ANY)
JS_DEFINE_CALLINFO_3(extern, OBJECT, js_InitializerObject, CONTEXT, OBJECT, OBJECT,
0, nanojit::ACCSET_STORE_ANY)
JSObject* FASTCALL
js_String_tn(JSContext* cx, JSObject* proto, JSString* str)
@ -3811,6 +3815,8 @@ JSObject::shrinkSlots(JSContext *cx, size_t newcap)
uint32 fill = newcap;
if (newcap < SLOT_CAPACITY_MIN)
newcap = SLOT_CAPACITY_MIN;
if (newcap < numFixedSlots())
newcap = numFixedSlots();
Value *tmpslots = (Value*) cx->realloc(slots, newcap * sizeof(Value));
if (!tmpslots)

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

@ -252,6 +252,12 @@ JSObject::setPrimitiveThis(const js::Value &pthis)
setSlot(JSSLOT_PRIMITIVE_THIS, pthis);
}
inline js::gc::FinalizeKind
GetObjectFinalizeKind(const JSObject *obj)
{
return js::gc::FinalizeKind(obj->arena()->header()->thingKind);
}
inline size_t
JSObject::numFixedSlots() const
{
@ -259,8 +265,7 @@ JSObject::numFixedSlots() const
return JSObject::FUN_CLASS_RESERVED_SLOTS;
if (!hasSlotsArray())
return capacity;
js::gc::FinalizeKind kind = js::gc::FinalizeKind(arena()->header()->thingKind);
return js::gc::GetGCKindSlots(kind);
return js::gc::GetGCKindSlots(GetObjectFinalizeKind(this));
}
inline size_t
@ -1058,6 +1063,26 @@ NewObjectGCKind(JSContext *cx, js::Class *clasp)
return gc::FINALIZE_OBJECT4;
}
/* Make an object with pregenerated shape from a NEWOBJECT bytecode. */
static inline JSObject *
CopyInitializerObject(JSContext *cx, JSObject *baseobj)
{
JS_ASSERT(baseobj->getClass() == &js_ObjectClass);
JS_ASSERT(!baseobj->inDictionaryMode());
gc::FinalizeKind kind = GetObjectFinalizeKind(baseobj);
JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
if (!obj || !obj->ensureSlots(cx, baseobj->numSlots()))
return NULL;
obj->flags = baseobj->flags;
obj->lastProp = baseobj->lastProp;
obj->objShape = baseobj->objShape;
return obj;
}
} /* namespace js */
#endif /* jsobjinlines_h___ */

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

@ -915,7 +915,7 @@ HandleNumber(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len)
return JSONParseError(jp, cx);
}
return PushPrimitive(cx, jp, DoubleValue(val));
return PushPrimitive(cx, jp, NumberValue(val));
}
static JSBool

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

@ -221,8 +221,6 @@ js_GetVariableStackUses(JSOp op, jsbytecode *pc)
return GET_UINT16(pc);
case JSOP_LEAVEBLOCKEXPR:
return GET_UINT16(pc) + 1;
case JSOP_NEWARRAY:
return GET_UINT16(pc);
default:
/* stack: fun, this, [argc arguments] */
JS_ASSERT(op == JSOP_NEW || op == JSOP_CALL || op == JSOP_EVAL ||
@ -1012,19 +1010,12 @@ GetStr(SprintStack *ss, uintN i)
/*
* Gap between stacked strings to allow for insertion of parens and commas
* when auto-parenthesizing expressions and decompiling array initialisers
* (see the JSOP_NEWARRAY case in Decompile).
* when auto-parenthesizing expressions and decompiling array initialisers.
*/
#define PAREN_SLOP (2 + 1)
/*
* These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETNAME,
* JSOP_SETPROP, and JSOP_SETELEM, respectively. They are never stored in
* bytecode, so they don't preempt valid opcodes.
*/
#define JSOP_GETPROP2 JSOP_LIMIT
#define JSOP_GETELEM2 JSOP_LIMIT + 1
JS_STATIC_ASSERT(JSOP_GETELEM2 <= 255);
/* Fake opcodes (see jsopcode.h) must not overflow unsigned 8-bit space. */
JS_STATIC_ASSERT(JSOP_FAKE_LIMIT <= 255);
static void
AddParenSlop(SprintStack *ss)
@ -1105,6 +1096,12 @@ PopStr(SprintStack *ss, JSOp op)
return PopStrPrec(ss, js_CodeSpec[op].prec);
}
static inline bool
IsInitializerOp(unsigned char op)
{
return op == JSOP_NEWINIT || op == JSOP_NEWARRAY || op == JSOP_NEWOBJECT;
}
typedef struct TableEntry {
jsval key;
ptrdiff_t offset;
@ -2117,15 +2114,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
saveop = op;
if (op >= JSOP_LIMIT) {
switch (op) {
case JSOP_GETPROP2:
if (op == JSOP_GETPROP2)
saveop = JSOP_GETPROP;
break;
case JSOP_GETELEM2:
else if (op == JSOP_GETELEM2)
saveop = JSOP_GETELEM;
break;
default:;
}
}
LOCAL_ASSERT(js_CodeSpec[saveop].length == oplen ||
JOF_TYPE(format) == JOF_SLOTATOM);
@ -4450,53 +4442,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
todo = SprintPut(&ss->sprinter, "", 0);
break;
case JSOP_NEWARRAY:
argc = GET_UINT16(pc);
LOCAL_ASSERT(ss->top >= (uintN) argc);
if (argc == 0) {
todo = SprintCString(&ss->sprinter, "[]");
break;
}
argv = (char **) cx->malloc(size_t(argc) * sizeof *argv);
if (!argv)
return NULL;
op = JSOP_SETNAME;
ok = JS_TRUE;
i = argc;
while (i > 0)
argv[--i] = JS_strdup(cx, POP_STR());
todo = SprintCString(&ss->sprinter, "[");
if (todo < 0)
break;
for (i = 0; i < argc; i++) {
if (!argv[i] ||
Sprint(&ss->sprinter, ss_format,
argv[i], (i < argc - 1) ? ", " : "") < 0) {
ok = JS_FALSE;
break;
}
}
for (i = 0; i < argc; i++)
cx->free(argv[i]);
cx->free(argv);
if (!ok)
return NULL;
sn = js_GetSrcNote(jp->script, pc);
if (sn && SN_TYPE(sn) == SRC_CONTINUE && SprintCString(&ss->sprinter, ", ") < 0)
return NULL;
if (SprintCString(&ss->sprinter, "]") < 0)
return NULL;
break;
case JSOP_NEWINIT:
{
i = GET_UINT16(pc);
i = pc[1];
LOCAL_ASSERT(i == JSProto_Array || i == JSProto_Object);
todo = ss->sprinter.offset;
@ -4526,6 +4474,23 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
break;
}
case JSOP_NEWARRAY:
{
todo = ss->sprinter.offset;
++ss->inArrayInit;
if (SprintCString(&ss->sprinter, "[") < 0)
return NULL;
break;
}
case JSOP_NEWOBJECT:
{
todo = ss->sprinter.offset;
if (SprintCString(&ss->sprinter, "{") < 0)
return NULL;
break;
}
case JSOP_ENDINIT:
{
JSBool inArray;
@ -4552,7 +4517,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
const char *maybeComma;
case JSOP_INITELEM:
isFirst = (ss->opcodes[ss->top - 3] == JSOP_NEWINIT);
isFirst = IsInitializerOp(ss->opcodes[ss->top - 3]);
/* Turn off most parens. */
op = JSOP_SETNAME;
@ -4582,7 +4547,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
jschar(ATOM_IS_IDENTIFIER(atom) ? 0 : '\''));
if (!xval)
return NULL;
isFirst = (ss->opcodes[ss->top - 2] == JSOP_NEWINIT);
isFirst = IsInitializerOp(ss->opcodes[ss->top - 2]);
rval = POP_STR();
lval = POP_STR();
/* fall through */

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

@ -61,7 +61,16 @@ typedef enum JSOp {
op = val,
#include "jsopcode.tbl"
#undef OPDEF
JSOP_LIMIT
JSOP_LIMIT,
/*
* These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETNAME,
* JSOP_SETPROP, and JSOP_SETELEM, respectively. They are never stored in
* bytecode, so they don't preempt valid opcodes.
*/
JSOP_GETPROP2 = JSOP_LIMIT,
JSOP_GETELEM2 = JSOP_LIMIT + 1,
JSOP_FAKE_LIMIT = JSOP_GETELEM2
} JSOp;
/*
@ -379,7 +388,7 @@ js_GetVariableBytecodeLength(jsbytecode *pc);
/*
* Find the number of stack slots used by a variadic opcode such as JSOP_CALL
* or JSOP_NEWARRAY (for such ops, JSCodeSpec.nuses is -1).
* (for such ops, JSCodeSpec.nuses is -1).
*/
extern uintN
js_GetVariableStackUses(JSOp op, jsbytecode *pc);

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

@ -245,112 +245,121 @@ OPDEF(JSOP_SETLOCAL, 87,"setlocal", NULL, 3, 1, 1, 3, JOF_LOCAL|
/* Push unsigned 16-bit int constant. */
OPDEF(JSOP_UINT16, 88, "uint16", NULL, 3, 0, 1, 16, JOF_UINT16)
/* Object and array literal support. */
OPDEF(JSOP_NEWINIT, 89, "newinit", NULL, 5, 0, 1, 19, JOF_UINT16PAIR)
OPDEF(JSOP_ENDINIT, 90, "endinit", NULL, 1, 0, 0, 19, JOF_BYTE)
OPDEF(JSOP_INITPROP, 91, "initprop", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_INITELEM, 92, "initelem", NULL, 1, 3, 1, 3, JOF_BYTE |JOF_ELEM|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_DEFSHARP, 93, "defsharp", NULL, 5, 0, 0, 0, JOF_UINT16PAIR|JOF_SHARPSLOT)
OPDEF(JSOP_USESHARP, 94, "usesharp", NULL, 5, 0, 1, 0, JOF_UINT16PAIR|JOF_SHARPSLOT)
/*
* Object and array literal support. NEWINIT takes the kind of initializer
* (JSProto_Array or JSProto_Object). NEWARRAY is an array initializer
* taking the final length, which can be filled in at the start and initialized
* directly. NEWOBJECT is an object initializer taking an object with the final
* shape, which can be set at the start and slots then filled in directly.
* NEWINIT has an extra byte so it can be exchanged with NEWOBJECT during emit.
*/
OPDEF(JSOP_NEWINIT, 89, "newinit", NULL, 3, 0, 1, 19, JOF_UINT8)
OPDEF(JSOP_NEWARRAY, 90, "newarray", NULL, 4, 0, 1, 19, JOF_UINT24)
OPDEF(JSOP_NEWOBJECT, 91, "newobject", NULL, 3, 0, 1, 19, JOF_OBJECT)
OPDEF(JSOP_ENDINIT, 92, "endinit", NULL, 1, 0, 0, 19, JOF_BYTE)
OPDEF(JSOP_INITPROP, 93, "initprop", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_INITELEM, 94, "initelem", NULL, 1, 3, 1, 3, JOF_BYTE|JOF_ELEM|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_DEFSHARP, 95, "defsharp", NULL, 5, 0, 0, 0, JOF_UINT16PAIR|JOF_SHARPSLOT)
OPDEF(JSOP_USESHARP, 96, "usesharp", NULL, 5, 0, 1, 0, JOF_UINT16PAIR|JOF_SHARPSLOT)
/* Fast inc/dec ops for args and locals. */
OPDEF(JSOP_INCARG, 95, "incarg", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_INC|JOF_TMPSLOT3)
OPDEF(JSOP_DECARG, 96, "decarg", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_DEC|JOF_TMPSLOT3)
OPDEF(JSOP_ARGINC, 97, "arginc", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3)
OPDEF(JSOP_ARGDEC, 98, "argdec", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3)
OPDEF(JSOP_INCARG, 97, "incarg", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_INC|JOF_TMPSLOT3)
OPDEF(JSOP_DECARG, 98, "decarg", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_DEC|JOF_TMPSLOT3)
OPDEF(JSOP_ARGINC, 99, "arginc", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3)
OPDEF(JSOP_ARGDEC, 100, "argdec", NULL, 3, 0, 1, 15, JOF_QARG |JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3)
OPDEF(JSOP_INCLOCAL, 99, "inclocal", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_INC|JOF_TMPSLOT3)
OPDEF(JSOP_DECLOCAL, 100,"declocal", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_DEC|JOF_TMPSLOT3)
OPDEF(JSOP_LOCALINC, 101,"localinc", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3)
OPDEF(JSOP_LOCALDEC, 102,"localdec", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3)
OPDEF(JSOP_INCLOCAL, 101,"inclocal", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_INC|JOF_TMPSLOT3)
OPDEF(JSOP_DECLOCAL, 102,"declocal", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_DEC|JOF_TMPSLOT3)
OPDEF(JSOP_LOCALINC, 103,"localinc", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3)
OPDEF(JSOP_LOCALDEC, 104,"localdec", NULL, 3, 0, 1, 15, JOF_LOCAL|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3)
OPDEF(JSOP_IMACOP, 103,"imacop", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_IMACOP, 105,"imacop", NULL, 1, 0, 0, 0, JOF_BYTE)
/* ECMA-compliant for/in ops. */
OPDEF(JSOP_FORNAME, 104,"forname", NULL, 3, 1, 1, 19, JOF_ATOM|JOF_NAME|JOF_FOR|JOF_TMPSLOT3)
OPDEF(JSOP_FORPROP, 105,"forprop", NULL, 3, 2, 1, 18, JOF_ATOM|JOF_PROP|JOF_FOR|JOF_TMPSLOT3)
OPDEF(JSOP_FORELEM, 106,"forelem", NULL, 1, 1, 2, 18, JOF_BYTE |JOF_ELEM|JOF_FOR)
OPDEF(JSOP_POPN, 107,"popn", NULL, 3, -1, 0, 0, JOF_UINT16)
OPDEF(JSOP_FORNAME, 106,"forname", NULL, 3, 1, 1, 19, JOF_ATOM|JOF_NAME|JOF_FOR|JOF_TMPSLOT3)
OPDEF(JSOP_FORPROP, 107,"forprop", NULL, 3, 2, 1, 18, JOF_ATOM|JOF_PROP|JOF_FOR|JOF_TMPSLOT3)
OPDEF(JSOP_FORELEM, 108,"forelem", NULL, 1, 1, 2, 18, JOF_BYTE |JOF_ELEM|JOF_FOR)
OPDEF(JSOP_POPN, 109,"popn", NULL, 3, -1, 0, 0, JOF_UINT16)
/* ECMA-compliant assignment ops. */
OPDEF(JSOP_BINDNAME, 108,"bindname", NULL, 3, 0, 1, 0, JOF_ATOM|JOF_NAME|JOF_SET)
OPDEF(JSOP_SETNAME, 109,"setname", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_BINDNAME, 110,"bindname", NULL, 3, 0, 1, 0, JOF_ATOM|JOF_NAME|JOF_SET)
OPDEF(JSOP_SETNAME, 111,"setname", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING)
/* Exception handling ops. */
OPDEF(JSOP_THROW, 110,js_throw_str, NULL, 1, 1, 0, 0, JOF_BYTE)
OPDEF(JSOP_THROW, 112,js_throw_str, NULL, 1, 1, 0, 0, JOF_BYTE)
/* 'in' and 'instanceof' ops. */
OPDEF(JSOP_IN, 111,js_in_str, js_in_str, 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_INSTANCEOF,112,js_instanceof_str,js_instanceof_str,1,2,1,11,JOF_BYTE|JOF_LEFTASSOC|JOF_TMPSLOT)
OPDEF(JSOP_IN, 113,js_in_str, js_in_str, 1, 2, 1, 11, JOF_BYTE|JOF_LEFTASSOC)
OPDEF(JSOP_INSTANCEOF,114,js_instanceof_str,js_instanceof_str,1,2,1,11,JOF_BYTE|JOF_LEFTASSOC|JOF_TMPSLOT)
/* debugger op */
OPDEF(JSOP_DEBUGGER, 113,"debugger", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_DEBUGGER, 115,"debugger", NULL, 1, 0, 0, 0, JOF_BYTE)
/* gosub/retsub for finally handling */
OPDEF(JSOP_GOSUB, 114,"gosub", NULL, 3, 0, 0, 0, JOF_JUMP)
OPDEF(JSOP_RETSUB, 115,"retsub", NULL, 1, 2, 0, 0, JOF_BYTE)
OPDEF(JSOP_GOSUB, 116,"gosub", NULL, 3, 0, 0, 0, JOF_JUMP)
OPDEF(JSOP_RETSUB, 117,"retsub", NULL, 1, 2, 0, 0, JOF_BYTE)
/* More exception handling ops. */
OPDEF(JSOP_EXCEPTION, 116,"exception", NULL, 1, 0, 1, 0, JOF_BYTE)
OPDEF(JSOP_EXCEPTION, 118,"exception", NULL, 1, 0, 1, 0, JOF_BYTE)
/* Embedded lineno to speedup pc->line mapping. */
OPDEF(JSOP_LINENO, 117,"lineno", NULL, 3, 0, 0, 0, JOF_UINT16)
OPDEF(JSOP_LINENO, 119,"lineno", NULL, 3, 0, 0, 0, JOF_UINT16)
/*
* ECMA-compliant switch statement ops.
* CONDSWITCH is a decompilable NOP; CASE is ===, POP, jump if true, re-push
* lval if false; and DEFAULT is POP lval and GOTO.
*/
OPDEF(JSOP_CONDSWITCH,118,"condswitch", NULL, 1, 0, 0, 0, JOF_BYTE|JOF_PARENHEAD)
OPDEF(JSOP_CASE, 119,"case", NULL, 3, 2, 1, 0, JOF_JUMP)
OPDEF(JSOP_DEFAULT, 120,"default", NULL, 3, 1, 0, 0, JOF_JUMP)
OPDEF(JSOP_CONDSWITCH,120,"condswitch", NULL, 1, 0, 0, 0, JOF_BYTE|JOF_PARENHEAD)
OPDEF(JSOP_CASE, 121,"case", NULL, 3, 2, 1, 0, JOF_JUMP)
OPDEF(JSOP_DEFAULT, 122,"default", NULL, 3, 1, 0, 0, JOF_JUMP)
/*
* ECMA-compliant call to eval op
*/
OPDEF(JSOP_EVAL, 121,"eval", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE)
OPDEF(JSOP_EVAL, 123,"eval", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE)
/*
* ECMA-compliant helper for 'for (x[i] in o)' loops.
*/
OPDEF(JSOP_ENUMELEM, 122,"enumelem", NULL, 1, 3, 0, 3, JOF_BYTE |JOF_SET|JOF_TMPSLOT)
OPDEF(JSOP_ENUMELEM, 124,"enumelem", NULL, 1, 3, 0, 3, JOF_BYTE |JOF_SET|JOF_TMPSLOT)
/*
* Getter and setter prefix bytecodes. These modify the next bytecode, either
* an assignment or a property initializer code, which then defines a property
* getter or setter.
*/
OPDEF(JSOP_GETTER, 123,js_getter_str,NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_SETTER, 124,js_setter_str,NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_GETTER, 125,js_getter_str,NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_SETTER, 126,js_setter_str,NULL, 1, 0, 0, 0, JOF_BYTE)
/*
* Prolog bytecodes for defining function, var, and const names.
*/
OPDEF(JSOP_DEFFUN, 125,"deffun", NULL, 3, 0, 0, 0, JOF_OBJECT|JOF_DECLARING)
OPDEF(JSOP_DEFCONST, 126,"defconst", NULL, 3, 0, 0, 0, JOF_ATOM|JOF_DECLARING)
OPDEF(JSOP_DEFVAR, 127,"defvar", NULL, 3, 0, 0, 0, JOF_ATOM|JOF_DECLARING)
OPDEF(JSOP_DEFFUN, 127,"deffun", NULL, 3, 0, 0, 0, JOF_OBJECT|JOF_DECLARING)
OPDEF(JSOP_DEFCONST, 128,"defconst", NULL, 3, 0, 0, 0, JOF_ATOM|JOF_DECLARING)
OPDEF(JSOP_DEFVAR, 129,"defvar", NULL, 3, 0, 0, 0, JOF_ATOM|JOF_DECLARING)
/* Push a closure for a named or anonymous function expression. */
OPDEF(JSOP_LAMBDA, 128, "lambda", NULL, 3, 0, 1, 19, JOF_OBJECT)
OPDEF(JSOP_LAMBDA, 130, "lambda", NULL, 3, 0, 1, 19, JOF_OBJECT)
/* Used for named function expression self-naming, if lightweight. */
OPDEF(JSOP_CALLEE, 129, "callee", NULL, 1, 0, 1, 19, JOF_BYTE)
OPDEF(JSOP_CALLEE, 131, "callee", NULL, 1, 0, 1, 19, JOF_BYTE)
/*
* Like JSOP_SETLOCAL, but specialized to avoid requiring JSOP_POP immediately
* after to throw away the exception value.
*/
OPDEF(JSOP_SETLOCALPOP, 130, "setlocalpop", NULL, 3, 1, 0, 3, JOF_LOCAL|JOF_NAME|JOF_SET)
OPDEF(JSOP_SETLOCALPOP, 132, "setlocalpop", NULL, 3, 1, 0, 3, JOF_LOCAL|JOF_NAME|JOF_SET)
/* Pick an element from the stack. */
OPDEF(JSOP_PICK, 131, "pick", NULL, 2, 0, 0, 0, JOF_UINT8)
OPDEF(JSOP_PICK, 133, "pick", NULL, 2, 0, 0, 0, JOF_UINT8)
/*
* Exception handling no-op, for more economical byte-coding than SRC_TRYFIN
* srcnote-annotated JSOP_NOPs and to simply stack balance handling.
*/
OPDEF(JSOP_TRY, 132,"try", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_FINALLY, 133,"finally", NULL, 1, 0, 2, 0, JOF_BYTE)
OPDEF(JSOP_TRY, 134,"try", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_FINALLY, 135,"finally", NULL, 1, 0, 2, 0, JOF_BYTE)
/*
* Get a slot from a flat closure function object that contains a snapshot of
@ -358,96 +367,96 @@ OPDEF(JSOP_FINALLY, 133,"finally", NULL, 1, 0, 2, 0, JOF_BYTE)
* in the function's u.i.script->upvars() array. The CALL variant computes the
* callee and this-object in preparation for a JSOP_CALL.
*/
OPDEF(JSOP_GETFCSLOT, 134,"getfcslot", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME)
OPDEF(JSOP_CALLFCSLOT, 135,"callfcslot", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_GETFCSLOT, 136,"getfcslot", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME)
OPDEF(JSOP_CALLFCSLOT, 137,"callfcslot", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP)
/*
* Bytecodes that avoid making an arguments object in most cases:
* JSOP_ARGSUB gets arguments[i] from fp->argv, iff i is in [0, fp->argc-1].
* JSOP_ARGCNT returns fp->argc.
*/
OPDEF(JSOP_ARGSUB, 136,"argsub", NULL, 3, 0, 1, 18, JOF_QARG |JOF_NAME)
OPDEF(JSOP_ARGCNT, 137,"argcnt", NULL, 1, 0, 1, 18, JOF_BYTE)
OPDEF(JSOP_ARGSUB, 138,"argsub", NULL, 3, 0, 1, 18, JOF_QARG |JOF_NAME)
OPDEF(JSOP_ARGCNT, 139,"argcnt", NULL, 1, 0, 1, 18, JOF_BYTE)
/*
* Define a local function object as a local variable.
* The local variable's slot number is the first immediate two-byte operand.
* The function object's atom index is the second immediate operand.
*/
OPDEF(JSOP_DEFLOCALFUN, 138,"deflocalfun",NULL, 5, 0, 0, 0, JOF_SLOTOBJECT|JOF_DECLARING|JOF_TMPSLOT)
OPDEF(JSOP_DEFLOCALFUN, 140,"deflocalfun",NULL, 5, 0, 0, 0, JOF_SLOTOBJECT|JOF_DECLARING|JOF_TMPSLOT)
/* Extended jumps. */
OPDEF(JSOP_GOTOX, 139,"gotox", NULL, 5, 0, 0, 0, JOF_JUMPX)
OPDEF(JSOP_IFEQX, 140,"ifeqx", NULL, 5, 1, 0, 4, JOF_JUMPX|JOF_DETECTING)
OPDEF(JSOP_IFNEX, 141,"ifnex", NULL, 5, 1, 0, 0, JOF_JUMPX|JOF_PARENHEAD)
OPDEF(JSOP_ORX, 142,"orx", NULL, 5, 1, 0, 5, JOF_JUMPX|JOF_DETECTING)
OPDEF(JSOP_ANDX, 143,"andx", NULL, 5, 1, 0, 6, JOF_JUMPX|JOF_DETECTING)
OPDEF(JSOP_GOSUBX, 144,"gosubx", NULL, 5, 0, 0, 0, JOF_JUMPX)
OPDEF(JSOP_CASEX, 145,"casex", NULL, 5, 2, 1, 0, JOF_JUMPX)
OPDEF(JSOP_DEFAULTX, 146,"defaultx", NULL, 5, 1, 0, 0, JOF_JUMPX)
OPDEF(JSOP_TABLESWITCHX, 147,"tableswitchx",NULL, -1, 1, 0, 0, JOF_TABLESWITCHX|JOF_DETECTING|JOF_PARENHEAD)
OPDEF(JSOP_LOOKUPSWITCHX, 148,"lookupswitchx",NULL, -1, 1, 0, 0, JOF_LOOKUPSWITCHX|JOF_DETECTING|JOF_PARENHEAD)
OPDEF(JSOP_GOTOX, 141,"gotox", NULL, 5, 0, 0, 0, JOF_JUMPX)
OPDEF(JSOP_IFEQX, 142,"ifeqx", NULL, 5, 1, 0, 4, JOF_JUMPX|JOF_DETECTING)
OPDEF(JSOP_IFNEX, 143,"ifnex", NULL, 5, 1, 0, 0, JOF_JUMPX|JOF_PARENHEAD)
OPDEF(JSOP_ORX, 144,"orx", NULL, 5, 1, 0, 5, JOF_JUMPX|JOF_DETECTING)
OPDEF(JSOP_ANDX, 145,"andx", NULL, 5, 1, 0, 6, JOF_JUMPX|JOF_DETECTING)
OPDEF(JSOP_GOSUBX, 146,"gosubx", NULL, 5, 0, 0, 0, JOF_JUMPX)
OPDEF(JSOP_CASEX, 147,"casex", NULL, 5, 2, 1, 0, JOF_JUMPX)
OPDEF(JSOP_DEFAULTX, 148,"defaultx", NULL, 5, 1, 0, 0, JOF_JUMPX)
OPDEF(JSOP_TABLESWITCHX, 149,"tableswitchx",NULL, -1, 1, 0, 0, JOF_TABLESWITCHX|JOF_DETECTING|JOF_PARENHEAD)
OPDEF(JSOP_LOOKUPSWITCHX, 150,"lookupswitchx",NULL, -1, 1, 0, 0, JOF_LOOKUPSWITCHX|JOF_DETECTING|JOF_PARENHEAD)
/* Placeholders for a real jump opcode set during backpatch chain fixup. */
OPDEF(JSOP_BACKPATCH, 149,"backpatch",NULL, 3, 0, 0, 0, JOF_JUMP|JOF_BACKPATCH)
OPDEF(JSOP_BACKPATCH_POP, 150,"backpatch_pop",NULL, 3, 1, 0, 0, JOF_JUMP|JOF_BACKPATCH)
OPDEF(JSOP_BACKPATCH, 151,"backpatch",NULL, 3, 0, 0, 0, JOF_JUMP|JOF_BACKPATCH)
OPDEF(JSOP_BACKPATCH_POP, 152,"backpatch_pop",NULL, 3, 1, 0, 0, JOF_JUMP|JOF_BACKPATCH)
/* Set pending exception from the stack, to trigger rethrow. */
OPDEF(JSOP_THROWING, 151,"throwing", NULL, 1, 1, 0, 0, JOF_BYTE)
OPDEF(JSOP_THROWING, 153,"throwing", NULL, 1, 1, 0, 0, JOF_BYTE)
/* Set and get return value pseudo-register in stack frame. */
OPDEF(JSOP_SETRVAL, 152,"setrval", NULL, 1, 1, 0, 2, JOF_BYTE)
OPDEF(JSOP_RETRVAL, 153,"retrval", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_SETRVAL, 154,"setrval", NULL, 1, 1, 0, 2, JOF_BYTE)
OPDEF(JSOP_RETRVAL, 155,"retrval", NULL, 1, 0, 0, 0, JOF_BYTE)
/* Free variable references that must either be found on the global or a ReferenceError */
OPDEF(JSOP_GETGNAME, 154,"getgname", NULL, 3, 0, 1, 19, JOF_ATOM|JOF_NAME|JOF_GNAME)
OPDEF(JSOP_SETGNAME, 155,"setgname", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING|JOF_GNAME)
OPDEF(JSOP_INCGNAME, 156,"incgname", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_INC|JOF_TMPSLOT3|JOF_GNAME)
OPDEF(JSOP_DECGNAME, 157,"decgname", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_DEC|JOF_TMPSLOT3|JOF_GNAME)
OPDEF(JSOP_GNAMEINC, 158,"gnameinc", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3|JOF_GNAME)
OPDEF(JSOP_GNAMEDEC, 159,"gnamedec", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3|JOF_GNAME)
OPDEF(JSOP_GETGNAME, 156,"getgname", NULL, 3, 0, 1, 19, JOF_ATOM|JOF_NAME|JOF_GNAME)
OPDEF(JSOP_SETGNAME, 157,"setgname", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING|JOF_GNAME)
OPDEF(JSOP_INCGNAME, 158,"incgname", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_INC|JOF_TMPSLOT3|JOF_GNAME)
OPDEF(JSOP_DECGNAME, 159,"decgname", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_DEC|JOF_TMPSLOT3|JOF_GNAME)
OPDEF(JSOP_GNAMEINC, 160,"gnameinc", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3|JOF_GNAME)
OPDEF(JSOP_GNAMEDEC, 161,"gnamedec", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3|JOF_GNAME)
/* Regular expression literal requiring special "fork on exec" handling. */
OPDEF(JSOP_REGEXP, 160,"regexp", NULL, 3, 0, 1, 19, JOF_REGEXP)
OPDEF(JSOP_REGEXP, 162,"regexp", NULL, 3, 0, 1, 19, JOF_REGEXP)
/* XML (ECMA-357, a.k.a. "E4X") support. */
OPDEF(JSOP_DEFXMLNS, 161,"defxmlns", NULL, 1, 1, 0, 0, JOF_BYTE)
OPDEF(JSOP_ANYNAME, 162,"anyname", NULL, 1, 0, 1, 19, JOF_BYTE|JOF_XMLNAME)
OPDEF(JSOP_QNAMEPART, 163,"qnamepart", NULL, 3, 0, 1, 19, JOF_ATOM|JOF_XMLNAME)
OPDEF(JSOP_QNAMECONST, 164,"qnameconst", NULL, 3, 1, 1, 19, JOF_ATOM|JOF_XMLNAME)
OPDEF(JSOP_QNAME, 165,"qname", NULL, 1, 2, 1, 0, JOF_BYTE|JOF_XMLNAME)
OPDEF(JSOP_TOATTRNAME, 166,"toattrname", NULL, 1, 1, 1, 19, JOF_BYTE|JOF_XMLNAME)
OPDEF(JSOP_TOATTRVAL, 167,"toattrval", NULL, 1, 1, 1, 19, JOF_BYTE)
OPDEF(JSOP_ADDATTRNAME, 168,"addattrname",NULL, 1, 2, 1, 13, JOF_BYTE)
OPDEF(JSOP_ADDATTRVAL, 169,"addattrval", NULL, 1, 2, 1, 13, JOF_BYTE)
OPDEF(JSOP_BINDXMLNAME, 170,"bindxmlname",NULL, 1, 1, 2, 3, JOF_BYTE|JOF_SET)
OPDEF(JSOP_SETXMLNAME, 171,"setxmlname", NULL, 1, 3, 1, 3, JOF_BYTE|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_XMLNAME, 172,"xmlname", NULL, 1, 1, 1, 19, JOF_BYTE)
OPDEF(JSOP_DESCENDANTS, 173,"descendants",NULL, 1, 2, 1, 18, JOF_BYTE)
OPDEF(JSOP_FILTER, 174,"filter", NULL, 3, 1, 1, 0, JOF_JUMP)
OPDEF(JSOP_ENDFILTER, 175,"endfilter", NULL, 3, 2, 1, 18, JOF_JUMP)
OPDEF(JSOP_TOXML, 176,"toxml", NULL, 1, 1, 1, 19, JOF_BYTE)
OPDEF(JSOP_TOXMLLIST, 177,"toxmllist", NULL, 1, 1, 1, 19, JOF_BYTE)
OPDEF(JSOP_XMLTAGEXPR, 178,"xmltagexpr", NULL, 1, 1, 1, 0, JOF_BYTE)
OPDEF(JSOP_XMLELTEXPR, 179,"xmleltexpr", NULL, 1, 1, 1, 0, JOF_BYTE)
OPDEF(JSOP_NOTRACE, 180,"notrace", NULL, 3, 0, 0, 0, JOF_UINT16)
OPDEF(JSOP_XMLCDATA, 181,"xmlcdata", NULL, 3, 0, 1, 19, JOF_ATOM)
OPDEF(JSOP_XMLCOMMENT, 182,"xmlcomment", NULL, 3, 0, 1, 19, JOF_ATOM)
OPDEF(JSOP_XMLPI, 183,"xmlpi", NULL, 3, 1, 1, 19, JOF_ATOM)
OPDEF(JSOP_CALLPROP, 184,"callprop", NULL, 3, 1, 2, 18, JOF_ATOM|JOF_PROP|JOF_CALLOP|JOF_TMPSLOT3)
OPDEF(JSOP_DEFXMLNS, 163,"defxmlns", NULL, 1, 1, 0, 0, JOF_BYTE)
OPDEF(JSOP_ANYNAME, 164,"anyname", NULL, 1, 0, 1, 19, JOF_BYTE|JOF_XMLNAME)
OPDEF(JSOP_QNAMEPART, 165,"qnamepart", NULL, 3, 0, 1, 19, JOF_ATOM|JOF_XMLNAME)
OPDEF(JSOP_QNAMECONST, 166,"qnameconst", NULL, 3, 1, 1, 19, JOF_ATOM|JOF_XMLNAME)
OPDEF(JSOP_QNAME, 167,"qname", NULL, 1, 2, 1, 0, JOF_BYTE|JOF_XMLNAME)
OPDEF(JSOP_TOATTRNAME, 168,"toattrname", NULL, 1, 1, 1, 19, JOF_BYTE|JOF_XMLNAME)
OPDEF(JSOP_TOATTRVAL, 169,"toattrval", NULL, 1, 1, 1, 19, JOF_BYTE)
OPDEF(JSOP_ADDATTRNAME, 170,"addattrname",NULL, 1, 2, 1, 13, JOF_BYTE)
OPDEF(JSOP_ADDATTRVAL, 171,"addattrval", NULL, 1, 2, 1, 13, JOF_BYTE)
OPDEF(JSOP_BINDXMLNAME, 172,"bindxmlname",NULL, 1, 1, 2, 3, JOF_BYTE|JOF_SET)
OPDEF(JSOP_SETXMLNAME, 173,"setxmlname", NULL, 1, 3, 1, 3, JOF_BYTE|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_XMLNAME, 174,"xmlname", NULL, 1, 1, 1, 19, JOF_BYTE)
OPDEF(JSOP_DESCENDANTS, 175,"descendants",NULL, 1, 2, 1, 18, JOF_BYTE)
OPDEF(JSOP_FILTER, 176,"filter", NULL, 3, 1, 1, 0, JOF_JUMP)
OPDEF(JSOP_ENDFILTER, 177,"endfilter", NULL, 3, 2, 1, 18, JOF_JUMP)
OPDEF(JSOP_TOXML, 178,"toxml", NULL, 1, 1, 1, 19, JOF_BYTE)
OPDEF(JSOP_TOXMLLIST, 179,"toxmllist", NULL, 1, 1, 1, 19, JOF_BYTE)
OPDEF(JSOP_XMLTAGEXPR, 180,"xmltagexpr", NULL, 1, 1, 1, 0, JOF_BYTE)
OPDEF(JSOP_XMLELTEXPR, 181,"xmleltexpr", NULL, 1, 1, 1, 0, JOF_BYTE)
OPDEF(JSOP_NOTRACE, 182,"notrace", NULL, 3, 0, 0, 0, JOF_UINT16)
OPDEF(JSOP_XMLCDATA, 183,"xmlcdata", NULL, 3, 0, 1, 19, JOF_ATOM)
OPDEF(JSOP_XMLCOMMENT, 184,"xmlcomment", NULL, 3, 0, 1, 19, JOF_ATOM)
OPDEF(JSOP_XMLPI, 185,"xmlpi", NULL, 3, 1, 1, 19, JOF_ATOM)
OPDEF(JSOP_CALLPROP, 186,"callprop", NULL, 3, 1, 2, 18, JOF_ATOM|JOF_PROP|JOF_CALLOP|JOF_TMPSLOT3)
/*
* Get a display (free) variable from the closure's reserved slots.
*/
OPDEF(JSOP_GETUPVAR, 185,"getupvar", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME)
OPDEF(JSOP_CALLUPVAR, 186,"callupvar", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_GETUPVAR, 187,"getupvar", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME)
OPDEF(JSOP_CALLUPVAR, 188,"callupvar", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_DELDESC, 187,"deldesc", NULL, 1, 2, 1, 15, JOF_BYTE|JOF_ELEM|JOF_DEL)
OPDEF(JSOP_DELDESC, 189,"deldesc", NULL, 1, 2, 1, 15, JOF_BYTE|JOF_ELEM|JOF_DEL)
/*
* Opcode to hold 24-bit immediate integer operands.
*/
OPDEF(JSOP_UINT24, 188,"uint24", NULL, 4, 0, 1, 16, JOF_UINT24)
OPDEF(JSOP_UINT24, 190,"uint24", NULL, 4, 0, 1, 16, JOF_UINT24)
/*
* Opcodes to allow 24-bit atom or object indexes. Whenever an index exceeds
@ -455,164 +464,155 @@ OPDEF(JSOP_UINT24, 188,"uint24", NULL, 4, 0, 1, 16, JOF_UINT24
* JSOP_INDEXBASE and JSOP_RESETBASE to provide the upper bits of the index.
* See jsemit.c, EmitIndexOp.
*/
OPDEF(JSOP_INDEXBASE, 189,"indexbase", NULL, 2, 0, 0, 0, JOF_UINT8|JOF_INDEXBASE)
OPDEF(JSOP_RESETBASE, 190,"resetbase", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_RESETBASE0, 191,"resetbase0", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_INDEXBASE, 191,"indexbase", NULL, 2, 0, 0, 0, JOF_UINT8|JOF_INDEXBASE)
OPDEF(JSOP_RESETBASE, 192,"resetbase", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_RESETBASE0, 193,"resetbase0", NULL, 1, 0, 0, 0, JOF_BYTE)
/*
* Opcodes to help the decompiler deal with XML.
*/
OPDEF(JSOP_STARTXML, 192,"startxml", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_STARTXMLEXPR, 193,"startxmlexpr",NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_STARTXML, 194,"startxml", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_STARTXMLEXPR, 195,"startxmlexpr",NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_CALLELEM, 194, "callelem", NULL, 1, 2, 2, 18, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC|JOF_CALLOP)
OPDEF(JSOP_CALLELEM, 196, "callelem", NULL, 1, 2, 2, 18, JOF_BYTE |JOF_ELEM|JOF_LEFTASSOC|JOF_CALLOP)
/*
* Stop interpretation, emitted at end of script to save the threaded bytecode
* interpreter an extra branch test on every DO_NEXT_OP (see jsinterp.c).
*/
OPDEF(JSOP_STOP, 195,"stop", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_STOP, 197,"stop", NULL, 1, 0, 0, 0, JOF_BYTE)
/*
* Get an extant property value, throwing ReferenceError if the identified
* property does not exist.
*/
OPDEF(JSOP_GETXPROP, 196,"getxprop", NULL, 3, 1, 1, 18, JOF_ATOM|JOF_PROP)
OPDEF(JSOP_GETXPROP, 198,"getxprop", NULL, 3, 1, 1, 18, JOF_ATOM|JOF_PROP)
OPDEF(JSOP_CALLXMLNAME, 197, "callxmlname", NULL, 1, 1, 2, 19, JOF_BYTE|JOF_CALLOP)
OPDEF(JSOP_CALLXMLNAME, 199, "callxmlname", NULL, 1, 1, 2, 19, JOF_BYTE|JOF_CALLOP)
/*
* Specialized JSOP_TYPEOF to avoid reporting undefined for typeof(0, undef).
*/
OPDEF(JSOP_TYPEOFEXPR, 198,"typeofexpr", NULL, 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING)
OPDEF(JSOP_TYPEOFEXPR, 200,"typeofexpr", NULL, 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING)
/*
* Block-local scope support.
*/
OPDEF(JSOP_ENTERBLOCK, 199,"enterblock", NULL, 3, 0, -1, 0, JOF_OBJECT)
OPDEF(JSOP_LEAVEBLOCK, 200,"leaveblock", NULL, 5, -1, 0, 0, JOF_UINT16)
OPDEF(JSOP_ENTERBLOCK, 201,"enterblock", NULL, 3, 0, -1, 0, JOF_OBJECT)
OPDEF(JSOP_LEAVEBLOCK, 202,"leaveblock", NULL, 5, -1, 0, 0, JOF_UINT16)
/* Jump to target if top of stack value is of primitive type. */
OPDEF(JSOP_IFPRIMTOP, 201,"ifprimtop", NULL, 3, 1, 1, 0, JOF_JUMP|JOF_DETECTING)
OPDEF(JSOP_IFPRIMTOP, 203,"ifprimtop", NULL, 3, 1, 1, 0, JOF_JUMP|JOF_DETECTING)
/* Throws a TypeError if the value at the top of the stack is not primitive. */
OPDEF(JSOP_PRIMTOP, 202,"primtop", NULL, 2, 1, 1, 0, JOF_INT8)
OPDEF(JSOP_PRIMTOP, 204,"primtop", NULL, 2, 1, 1, 0, JOF_INT8)
/*
* Generator and array comprehension support.
*/
OPDEF(JSOP_GENERATOR, 203,"generator", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_YIELD, 204,"yield", NULL, 1, 1, 1, 1, JOF_BYTE)
OPDEF(JSOP_ARRAYPUSH, 205,"arraypush", NULL, 3, 1, 0, 3, JOF_LOCAL)
OPDEF(JSOP_GENERATOR, 205,"generator", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_YIELD, 206,"yield", NULL, 1, 1, 1, 1, JOF_BYTE)
OPDEF(JSOP_ARRAYPUSH, 207,"arraypush", NULL, 3, 1, 0, 3, JOF_LOCAL)
/*
* Get the built-in function::foo namespace and push it.
*/
OPDEF(JSOP_GETFUNNS, 206,"getfunns", NULL, 1, 0, 1, 19, JOF_BYTE)
OPDEF(JSOP_GETFUNNS, 208,"getfunns", NULL, 1, 0, 1, 19, JOF_BYTE)
/*
* Variant of JSOP_ENUMELEM for destructuring const (const [a, b] = ...).
*/
OPDEF(JSOP_ENUMCONSTELEM, 207,"enumconstelem",NULL, 1, 3, 0, 3, JOF_BYTE|JOF_SET)
OPDEF(JSOP_ENUMCONSTELEM, 209,"enumconstelem",NULL, 1, 3, 0, 3, JOF_BYTE|JOF_SET)
/*
* Variant of JSOP_LEAVEBLOCK has a result on the stack above the locals,
* which must be moved down when the block pops.
*/
OPDEF(JSOP_LEAVEBLOCKEXPR,208,"leaveblockexpr",NULL, 5, -1, 1, 3, JOF_UINT16)
OPDEF(JSOP_LEAVEBLOCKEXPR,210,"leaveblockexpr",NULL, 5, -1, 1, 3, JOF_UINT16)
/*
* Optimize common JSOP_{THIS,GET{ARG,LOCAL}} -> JSOP_GETPROP cliches.
*/
OPDEF(JSOP_GETTHISPROP, 209,"getthisprop", NULL, 3, 0, 1, 18, JOF_ATOM|JOF_VARPROP)
OPDEF(JSOP_GETARGPROP, 210,"getargprop", NULL, 5, 0, 1, 18, JOF_SLOTATOM|JOF_VARPROP)
OPDEF(JSOP_GETLOCALPROP, 211,"getlocalprop", NULL, 5, 0, 1, 18, JOF_SLOTATOM|JOF_VARPROP)
OPDEF(JSOP_GETTHISPROP, 211,"getthisprop", NULL, 3, 0, 1, 18, JOF_ATOM|JOF_VARPROP)
OPDEF(JSOP_GETARGPROP, 212,"getargprop", NULL, 5, 0, 1, 18, JOF_SLOTATOM|JOF_VARPROP)
OPDEF(JSOP_GETLOCALPROP, 213,"getlocalprop", NULL, 5, 0, 1, 18, JOF_SLOTATOM|JOF_VARPROP)
/*
* Optimize atom segments 1-3. These must be followed by JSOP_RESETBASE0 after
* the opcode that they prefix.
*/
OPDEF(JSOP_INDEXBASE1, 212,"indexbase1", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE)
OPDEF(JSOP_INDEXBASE2, 213,"indexbase2", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE)
OPDEF(JSOP_INDEXBASE3, 214,"indexbase3", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE)
OPDEF(JSOP_INDEXBASE1, 214,"indexbase1", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE)
OPDEF(JSOP_INDEXBASE2, 215,"indexbase2", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE)
OPDEF(JSOP_INDEXBASE3, 216,"indexbase3", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE)
OPDEF(JSOP_CALLGNAME, 215, "callgname", NULL, 3, 0, 2, 19, JOF_ATOM|JOF_NAME|JOF_CALLOP|JOF_GNAME)
OPDEF(JSOP_CALLLOCAL, 216, "calllocal", NULL, 3, 0, 2, 19, JOF_LOCAL|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_CALLARG, 217, "callarg", NULL, 3, 0, 2, 19, JOF_QARG |JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_BINDGNAME, 218, "bindgname", NULL, 3, 0, 1, 0, JOF_ATOM|JOF_NAME|JOF_SET|JOF_GNAME)
OPDEF(JSOP_CALLGNAME, 217, "callgname", NULL, 3, 0, 2, 19, JOF_ATOM|JOF_NAME|JOF_CALLOP|JOF_GNAME)
OPDEF(JSOP_CALLLOCAL, 218, "calllocal", NULL, 3, 0, 2, 19, JOF_LOCAL|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_CALLARG, 219, "callarg", NULL, 3, 0, 2, 19, JOF_QARG |JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_BINDGNAME, 220, "bindgname", NULL, 3, 0, 1, 0, JOF_ATOM|JOF_NAME|JOF_SET|JOF_GNAME)
/*
* Opcodes to hold 8-bit and 32-bit immediate integer operands.
*/
OPDEF(JSOP_INT8, 219, "int8", NULL, 2, 0, 1, 16, JOF_INT8)
OPDEF(JSOP_INT32, 220, "int32", NULL, 5, 0, 1, 16, JOF_INT32)
OPDEF(JSOP_INT8, 221, "int8", NULL, 2, 0, 1, 16, JOF_INT8)
OPDEF(JSOP_INT32, 222, "int32", NULL, 5, 0, 1, 16, JOF_INT32)
/*
* Get the value of the 'length' property from a stacked object.
*/
OPDEF(JSOP_LENGTH, 221, "length", NULL, 1, 1, 1, 18, JOF_BYTE|JOF_PROP)
/*
* Construct a new dense array whose contents are the values provided on the
* stack, consuming those values and replacing them with the newly-constructed
* array. The topmost value is the last value in the new array, and the
* bottommost value is the first value in the array; the array length is a
* 16-bit immediate operand to the instruction.
*/
OPDEF(JSOP_NEWARRAY, 222, "newarray", NULL, 3, -1, 1, 19, JOF_UINT16)
OPDEF(JSOP_LENGTH, 223, "length", NULL, 1, 1, 1, 18, JOF_BYTE|JOF_PROP)
/*
* Push a JSVAL_HOLE value onto the stack, representing an omitted property in
* an array literal (e.g. property 0 in the array [, 1]). This opcode is used
* with the JSOP_NEWARRAY and JSOP_NEWINIT opcodes.
* with the JSOP_NEWARRAY opcode.
*/
OPDEF(JSOP_HOLE, 223, "hole", NULL, 1, 0, 1, 0, JOF_BYTE)
OPDEF(JSOP_HOLE, 224, "hole", NULL, 1, 0, 1, 0, JOF_BYTE)
/*
* Variants of JSOP_{DEF{,LOCAL}FUN,LAMBDA} optimized for the flat closure case.
*/
OPDEF(JSOP_DEFFUN_FC, 224,"deffun_fc", NULL, 3, 0, 0, 0, JOF_OBJECT|JOF_DECLARING)
OPDEF(JSOP_DEFLOCALFUN_FC,225,"deflocalfun_fc",NULL, 5, 0, 0, 0, JOF_SLOTOBJECT|JOF_DECLARING|JOF_TMPSLOT)
OPDEF(JSOP_LAMBDA_FC, 226,"lambda_fc", NULL, 3, 0, 1, 19, JOF_OBJECT)
OPDEF(JSOP_DEFFUN_FC, 225,"deffun_fc", NULL, 3, 0, 0, 0, JOF_OBJECT|JOF_DECLARING)
OPDEF(JSOP_DEFLOCALFUN_FC,226,"deflocalfun_fc",NULL, 5, 0, 0, 0, JOF_SLOTOBJECT|JOF_DECLARING|JOF_TMPSLOT)
OPDEF(JSOP_LAMBDA_FC, 227,"lambda_fc", NULL, 3, 0, 1, 19, JOF_OBJECT)
/*
* Ensure that the value on the top of the stack is an object. The one
* argument is an error message, defined in js.msg, that takes one parameter
* (the decompilation of the primitive value).
*/
OPDEF(JSOP_OBJTOP, 227,"objtop", NULL, 3, 0, 0, 0, JOF_UINT16)
OPDEF(JSOP_OBJTOP, 228,"objtop", NULL, 3, 0, 0, 0, JOF_UINT16)
/* This opcode stores an index that is unique to the given loop. */
OPDEF(JSOP_TRACE, 228, "trace", NULL, 3, 0, 0, 0, JOF_UINT16)
OPDEF(JSOP_TRACE, 229, "trace", NULL, 3, 0, 0, 0, JOF_UINT16)
/*
* Debugger versions of JSOP_{GET,CALL}UPVAR and the flat closure (_FC) ops.
*/
OPDEF(JSOP_GETUPVAR_DBG, 229,"getupvar_dbg", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME)
OPDEF(JSOP_CALLUPVAR_DBG, 230,"callupvar_dbg", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_DEFFUN_DBGFC, 231,"deffun_dbgfc", NULL, 3, 0, 0, 0, JOF_OBJECT|JOF_DECLARING)
OPDEF(JSOP_DEFLOCALFUN_DBGFC,232,"deflocalfun_dbgfc",NULL, 5, 0, 0, 0, JOF_SLOTOBJECT|JOF_DECLARING|JOF_TMPSLOT)
OPDEF(JSOP_LAMBDA_DBGFC, 233,"lambda_dbgfc", NULL, 3, 0, 1, 19, JOF_OBJECT)
OPDEF(JSOP_GETUPVAR_DBG, 230,"getupvar_dbg", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME)
OPDEF(JSOP_CALLUPVAR_DBG, 231,"callupvar_dbg", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_DEFFUN_DBGFC, 232,"deffun_dbgfc", NULL, 3, 0, 0, 0, JOF_OBJECT|JOF_DECLARING)
OPDEF(JSOP_DEFLOCALFUN_DBGFC,233,"deflocalfun_dbgfc",NULL, 5, 0, 0, 0, JOF_SLOTOBJECT|JOF_DECLARING|JOF_TMPSLOT)
OPDEF(JSOP_LAMBDA_DBGFC, 234,"lambda_dbgfc", NULL, 3, 0, 1, 19, JOF_OBJECT)
/*
* Joined function object as method optimization support.
*/
OPDEF(JSOP_SETMETHOD, 234,"setmethod", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_INITMETHOD, 235,"initmethod", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_UNBRAND, 236,"unbrand", NULL, 1, 1, 1, 0, JOF_BYTE)
OPDEF(JSOP_UNBRANDTHIS, 237,"unbrandthis", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_SETMETHOD, 235,"setmethod", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_INITMETHOD, 236,"initmethod", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_UNBRAND, 237,"unbrand", NULL, 1, 1, 1, 0, JOF_BYTE)
OPDEF(JSOP_UNBRANDTHIS, 238,"unbrandthis", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_SHARPINIT, 238,"sharpinit", NULL, 3, 0, 0, 0, JOF_UINT16|JOF_SHARPSLOT)
OPDEF(JSOP_SHARPINIT, 239,"sharpinit", NULL, 3, 0, 0, 0, JOF_UINT16|JOF_SHARPSLOT)
/* Static binding for globals. */
OPDEF(JSOP_GETGLOBAL, 239,"getglobal", NULL, 3, 0, 1, 19, JOF_GLOBAL|JOF_NAME)
OPDEF(JSOP_SETGLOBAL, 240,"setglobal", NULL, 3, 1, 1, 3, JOF_GLOBAL|JOF_NAME|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_INCGLOBAL, 241,"incglobal", NULL, 3, 0, 1, 15, JOF_GLOBAL|JOF_NAME|JOF_INC|JOF_TMPSLOT2)
OPDEF(JSOP_DECGLOBAL, 242,"decglobal", NULL, 3, 0, 1, 15, JOF_GLOBAL|JOF_NAME|JOF_DEC|JOF_TMPSLOT2)
OPDEF(JSOP_GLOBALINC, 243,"globalinc", NULL, 3, 0, 1, 15, JOF_GLOBAL|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT2)
OPDEF(JSOP_GLOBALDEC, 244,"globaldec", NULL, 3, 0, 1, 15, JOF_GLOBAL|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT2)
OPDEF(JSOP_CALLGLOBAL, 245,"callglobal", NULL, 3, 0, 2, 19, JOF_GLOBAL|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_FORGLOBAL, 246,"forglobal", NULL, 3, 1, 1, 19, JOF_GLOBAL|JOF_NAME|JOF_FOR|JOF_TMPSLOT)
OPDEF(JSOP_GETGLOBAL, 240,"getglobal", NULL, 3, 0, 1, 19, JOF_GLOBAL|JOF_NAME)
OPDEF(JSOP_SETGLOBAL, 241,"setglobal", NULL, 3, 1, 1, 3, JOF_GLOBAL|JOF_NAME|JOF_SET|JOF_DETECTING)
OPDEF(JSOP_INCGLOBAL, 242,"incglobal", NULL, 3, 0, 1, 15, JOF_GLOBAL|JOF_NAME|JOF_INC|JOF_TMPSLOT2)
OPDEF(JSOP_DECGLOBAL, 243,"decglobal", NULL, 3, 0, 1, 15, JOF_GLOBAL|JOF_NAME|JOF_DEC|JOF_TMPSLOT2)
OPDEF(JSOP_GLOBALINC, 244,"globalinc", NULL, 3, 0, 1, 15, JOF_GLOBAL|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT2)
OPDEF(JSOP_GLOBALDEC, 245,"globaldec", NULL, 3, 0, 1, 15, JOF_GLOBAL|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT2)
OPDEF(JSOP_CALLGLOBAL, 246,"callglobal", NULL, 3, 0, 2, 19, JOF_GLOBAL|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_FORGLOBAL, 247,"forglobal", NULL, 3, 1, 1, 19, JOF_GLOBAL|JOF_NAME|JOF_FOR|JOF_TMPSLOT)
/*
* These opcodes contain a reference to the current blockChain object.
@ -621,10 +621,8 @@ OPDEF(JSOP_FORGLOBAL, 246,"forglobal", NULL, 3, 1, 1, 19, JOF_GLOBAL
* does not permit NULL object references, since it stores an index into a table of
* objects.
*/
OPDEF(JSOP_BLOCKCHAIN, 247,"blockchain", NULL, 3, 0, 0, 0, JOF_OBJECT)
OPDEF(JSOP_NULLBLOCKCHAIN,248,"nullblockchain",NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_BLOCKCHAIN, 248,"blockchain", NULL, 3, 0, 0, 0, JOF_OBJECT)
OPDEF(JSOP_NULLBLOCKCHAIN,249,"nullblockchain",NULL, 1, 0, 0, 0, JOF_BYTE)
/* Like JSOP_FUNAPPLY but for f.call instead of f.apply. */
OPDEF(JSOP_FUNCALL, 249,"funcall", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE)
/* When changing bytecodes, don't forget to update JSXDR_BYTECODE_VERSION. */
OPDEF(JSOP_FUNCALL, 250,"funcall", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE)

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

@ -153,15 +153,17 @@ Probes::FunctionName(JSContext *cx, const JSFunction *fun, JSAutoByteString *byt
void
Probes::enterJSFunImpl(JSContext *cx, JSFunction *fun, JSScript *script)
{
JSAutoByteString funNameBytes;
JAVASCRIPT_FUNCTION_ENTRY(ScriptFilename(script), FunctionClassname(fun),
FunctionName(cx, fun));
FunctionName(cx, fun, &funNameBytes));
}
void
Probes::handleFunctionReturn(JSContext *cx, JSFunction *fun, JSScript *script)
{
JSAutoByteString funNameBytes;
JAVASCRIPT_FUNCTION_RETURN(ScriptFilename(script), FunctionClassname(fun),
FunctionName(cx, fun));
FunctionName(cx, fun, &funNameBytes));
}
#endif

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

@ -441,6 +441,25 @@ PropertyTable::change(int log2Delta, JSContext *cx)
return true;
}
bool
PropertyTable::grow(JSContext *cx)
{
JS_ASSERT(needsToGrow());
uint32 size = capacity();
int delta = removedCount < size >> 2;
if (!delta)
METER(compresses);
else
METER(grows);
if (!change(delta, cx) && entryCount + removedCount == size - 1) {
JS_ReportOutOfMemory(cx);
return false;
}
return true;
}
Shape *
Shape::getChild(JSContext *cx, const js::Shape &child, Shape **listp)
{
@ -448,8 +467,47 @@ Shape::getChild(JSContext *cx, const js::Shape &child, Shape **listp)
JS_ASSERT(!child.inDictionary());
if (inDictionary()) {
if (newDictionaryShape(cx, child, listp))
return *listp;
Shape *oldShape = *listp;
PropertyTable *table = oldShape ? oldShape->table : NULL;
/*
* Attempt to grow table if needed before extending *listp, rather than
* risking OOM under table->grow after newDictionaryShape succeeds, and
* then have to fix up *listp.
*/
if (table && table->needsToGrow() && !table->grow(cx))
return NULL;
if (newDictionaryShape(cx, child, listp)) {
Shape *newShape = *listp;
JS_ASSERT(oldShape == newShape->parent);
if (table) {
/* Add newShape to the property table. */
METER(searches);
Shape **spp = table->search(newShape->id, true);
/*
* Beware duplicate formal parameters, allowed by ECMA-262 in
* non-strict mode. Otherwise we know that JSFunction::addLocal
* (our caller) won't pass an id already in the table to us. In
* the case of duplicate formals, the last one wins, so while
* we must not overcount entries, we must store newShape.
*/
if (!SHAPE_FETCH(spp))
++table->entryCount;
SHAPE_STORE_PRESERVING_COLLISION(spp, newShape);
/* Hand the table off from oldShape to newShape. */
oldShape->setTable(NULL);
newShape->setTable(table);
} else {
if (!newShape->table)
newShape->maybeHash(cx);
}
return newShape;
}
return NULL;
}
@ -751,18 +809,10 @@ JSObject::addPropertyInternal(JSContext *cx, jsid id,
table = lastProp->table;
}
} else if ((table = lastProp->table) != NULL) {
/* Check whether we need to grow, if the load factor is >= .75. */
uint32 size = table->capacity();
if (table->entryCount + table->removedCount >= size - (size >> 2)) {
int delta = table->removedCount < size >> 2;
if (!delta)
METER(compresses);
else
METER(grows);
if (!table->change(delta, cx) && table->entryCount + table->removedCount == size - 1) {
JS_ReportOutOfMemory(cx);
if (table->needsToGrow()) {
if (!table->grow(cx))
return NULL;
}
METER(searches);
METER(changeSearches);
spp = table->search(id, true);

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

@ -248,6 +248,19 @@ struct PropertyTable {
/* By definition, hashShift = JS_DHASH_BITS - log2(capacity). */
uint32 capacity() const { return JS_BIT(JS_DHASH_BITS - hashShift); }
/* Whether we need to grow. We want to do this if the load factor is >= 0.75 */
bool needsToGrow() const {
uint32 size = capacity();
return entryCount + removedCount >= size - (size >> 2);
}
/*
* Try to grow the table. On failure, reports out of memory on cx
* and returns false. This will make any extant pointers into the
* table invalid. Don't call this unless needsToGrow() is true.
*/
bool grow(JSContext *cx);
/*
* NB: init and change are fallible but do not report OOM, so callers can
* cope or ignore. They do however use JSRuntime's calloc method in order

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

@ -86,7 +86,7 @@ static const jsbytecode emptyScriptCode[] = {JSOP_STOP, SRC_NULL};
false, /* debugMode */
#endif
const_cast<jsbytecode*>(emptyScriptCode),
{0, NULL}, NULL, NULL, 0, 0, 0,
{0, jsatomid(0)}, NULL, NULL, 0, 0, 0,
0, /* nClosedArgs */
0, /* nClosedVars */
NULL, {NULL},
@ -480,7 +480,7 @@ script_finalize(JSContext *cx, JSObject *obj)
{
JSScript *script = (JSScript *) obj->getPrivate();
if (script)
js_DestroyScript(cx, script);
js_DestroyScriptFromGC(cx, script, NULL);
}
static void
@ -1301,8 +1301,8 @@ js_CallDestroyScriptHook(JSContext *cx, JSScript *script)
hook(cx, script, cx->debugHooks->destroyScriptHookData);
}
void
js_DestroyScript(JSContext *cx, JSScript *script)
static void
DestroyScript(JSContext *cx, JSScript *script, JSThreadData *data)
{
if (script == JSScript::emptyScript()) {
JS_RUNTIME_UNMETER(cx->runtime, liveEmptyScripts);
@ -1359,7 +1359,16 @@ js_DestroyScript(JSContext *cx, JSScript *script)
}
#ifdef JS_TRACER
PurgeScriptFragments(cx, script);
# ifdef JS_THREADSAFE
if (data) {
PurgeScriptFragments(&data->traceMonitor, script);
} else {
for (ThreadDataIter i(cx->runtime); !i.empty(); i.popFront())
PurgeScriptFragments(&i.threadData()->traceMonitor, script);
}
# else
PurgeScriptFragments(&JS_TRACE_MONITOR(cx), script);
# endif
#endif
#if defined(JS_METHODJIT)
@ -1372,6 +1381,20 @@ js_DestroyScript(JSContext *cx, JSScript *script)
JS_RUNTIME_UNMETER(cx->runtime, liveScripts);
}
void
js_DestroyScript(JSContext *cx, JSScript *script)
{
JS_ASSERT(!cx->runtime->gcRunning);
DestroyScript(cx, script, JS_THREAD_DATA(cx));
}
void
js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSThreadData *data)
{
JS_ASSERT(cx->runtime->gcRunning);
DestroyScript(cx, script, data);
}
void
js_TraceScript(JSTracer *trc, JSScript *script)
{

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

@ -510,9 +510,20 @@ js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun);
extern JS_FRIEND_API(void)
js_CallDestroyScriptHook(JSContext *cx, JSScript *script);
/*
* The function must be used only outside the GC for a script that was run
* only on the current thread.
*/
extern void
js_DestroyScript(JSContext *cx, JSScript *script);
/*
* If data is not null, it indicates that the script could been accessed only
* from that thread.
*/
extern void
js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSThreadData *data);
extern void
js_TraceScript(JSTracer *trc, JSScript *script);

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

@ -2229,6 +2229,8 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* frag
trashSelf(false),
whichTreesToTrash(&tempAlloc()),
guardedShapeTable(cx),
initDepth(0),
hadNewInit(false),
rval_ins(NULL),
native_rval_ins(NULL),
newobj_ins(NULL),
@ -2750,6 +2752,7 @@ TraceMonitor::flush()
PodArrayZero(vmfragments);
reFragments = new (alloc) REHashMap(alloc);
tracedScripts.clear();
needFlush = JS_FALSE;
}
@ -5631,7 +5634,11 @@ RecordTree(JSContext* cx, TreeFragment* first, JSScript* outerScript, jsbytecode
AUDIT(recorderStarted);
if (tm->outOfMemory() || OverfullJITCache(tm)) {
if (tm->outOfMemory() ||
OverfullJITCache(tm) ||
!tm->tracedScripts.put(cx->fp()->script())) {
if (!OverfullJITCache(tm))
js_ReportOutOfMemory(cx);
Backoff(cx, (jsbytecode*) f->root->ip);
ResetJIT(cx, FR_OOM);
debug_only_print0(LC_TMTracer,
@ -7017,7 +7024,7 @@ RecordLoopEdge(JSContext* cx, uintN& inlineCallCount)
* it will walk the peer list and find us a free slot or allocate a new
* tree if needed.
*/
bool rv = RecordTree(cx, f->first, NULL, 0, NULL, globalSlots);
bool rv = RecordTree(cx, f->first, NULL, NULL, 0, globalSlots);
#ifdef MOZ_TRACEVIS
if (!rv)
tvso.r = R_FAIL_RECORD_TREE;
@ -7069,7 +7076,7 @@ RecordLoopEdge(JSContext* cx, uintN& inlineCallCount)
bool rv;
switch (lr->exitType) {
case UNSTABLE_LOOP_EXIT:
rv = AttemptToStabilizeTree(cx, globalObj, lr, NULL, 0, NULL);
rv = AttemptToStabilizeTree(cx, globalObj, lr, NULL, NULL, 0);
#ifdef MOZ_TRACEVIS
if (!rv)
tvso.r = R_FAIL_STABILIZE;
@ -7516,7 +7523,7 @@ SetMaxCodeCacheBytes(JSContext* cx, uint32 bytes)
tm->maxCodeCacheBytes = bytes;
}
void
bool
InitJIT(TraceMonitor *tm)
{
#if defined JS_JIT_SPEW
@ -7621,6 +7628,10 @@ InitJIT(TraceMonitor *tm)
jitstats.archIsAMD64 = 1;
#endif
#endif
if (!tm->tracedScripts.init())
return false;
return true;
}
void
@ -7731,17 +7742,20 @@ FinishJIT(TraceMonitor *tm)
}
JS_REQUIRES_STACK void
PurgeScriptFragments(JSContext* cx, JSScript* script)
PurgeScriptFragments(TraceMonitor* tm, JSScript* script)
{
debug_only_printf(LC_TMTracer,
"Purging fragments for JSScript %p.\n", (void*)script);
TraceMonitor* tm = &JS_TRACE_MONITOR(cx);
/* A recorder script is being evaluated and can not be destroyed or GC-ed. */
JS_ASSERT_IF(tm->recorder,
JS_UPTRDIFF(tm->recorder->getTree()->ip, script->code) >= script->length);
TracedScriptSet::Ptr found = tm->tracedScripts.lookup(script);
if (!found)
return;
tm->tracedScripts.remove(found);
for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) {
TreeFragment** fragp = &tm->vmfragments[i];
while (TreeFragment* frag = *fragp) {
@ -14060,18 +14074,20 @@ TraceRecorder::record_JSOP_UINT16()
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_NEWINIT()
{
JSProtoKey key = JSProtoKey(GET_UINT16(cx->regs->pc));
uint32 count = GET_UINT16(cx->regs->pc + UINT16_LEN);
initDepth++;
hadNewInit = true;
JSProtoKey key = JSProtoKey(cx->regs->pc[1]);
LIns* proto_ins;
CHECK_STATUS_A(getClassPrototype(key, proto_ins));
LIns *v_ins;
if (key == JSProto_Array) {
LIns *args[] = { w.immi(count), cx_ins };
v_ins = w.call(&js_InitializerArray_ci, args);
LIns *args[] = { w.immi(0), proto_ins, cx_ins };
v_ins = w.call(&js_NewPreallocatedArray_ci, args);
} else {
LIns *args[] = { w.immi(count), cx_ins };
LIns *args[] = { w.immpNull(), proto_ins, cx_ins };
v_ins = w.call(&js_InitializerObject_ci, args);
}
guard(false, w.eqp0(v_ins), OOM_EXIT);
@ -14079,9 +14095,48 @@ TraceRecorder::record_JSOP_NEWINIT()
return ARECORD_CONTINUE;
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_NEWARRAY()
{
initDepth++;
LIns* proto_ins;
CHECK_STATUS_A(getClassPrototype(JSProto_Array, proto_ins));
unsigned count = GET_UINT24(cx->regs->pc);
LIns *args[] = { w.immi(count), proto_ins, cx_ins };
LIns *v_ins = w.call(&js_NewPreallocatedArray_ci, args);
guard(false, w.eqp0(v_ins), OOM_EXIT);
stack(0, v_ins);
return ARECORD_CONTINUE;
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_NEWOBJECT()
{
initDepth++;
LIns* proto_ins;
CHECK_STATUS_A(getClassPrototype(JSProto_Object, proto_ins));
JSObject* baseobj = cx->fp()->script()->getObject(getFullIndex(0));
LIns *args[] = { w.immpObjGC(baseobj), proto_ins, cx_ins };
LIns *v_ins = w.call(&js_InitializerObject_ci, args);
guard(false, w.eqp0(v_ins), OOM_EXIT);
stack(0, v_ins);
return ARECORD_CONTINUE;
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_ENDINIT()
{
initDepth--;
if (initDepth == 0)
hadNewInit = false;
#ifdef DEBUG
Value& v = stackval(-1);
JS_ASSERT(!v.isPrimitive());
@ -14099,7 +14154,30 @@ TraceRecorder::record_JSOP_INITPROP()
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_INITELEM()
{
return setElem(-3, -2, -1);
Value& v = stackval(-1);
Value& idx = stackval(-2);
Value& lval = stackval(-3);
// The object is either a dense Array or an Object. Only handle the dense case here.
// Also skip array initializers which might be unoptimized NEWINIT initializers.
if (!lval.toObject().isDenseArray() || hadNewInit)
return setElem(-3, -2, -1);
// The index is always the same constant integer.
JS_ASSERT(idx.isInt32());
// Nothing to do for holes, the array's length has already been set.
if (v.isMagic(JS_ARRAY_HOLE))
return ARECORD_CONTINUE;
LIns* obj_ins = get(&lval);
LIns* v_ins = get(&v);
// Set the element.
LIns *slots_ins = w.ldpObjSlots(obj_ins);
box_value_into(v, v_ins, DSlotsAddress(slots_ins, idx.toInt32()));
return ARECORD_CONTINUE;
}
JS_REQUIRES_STACK AbortableRecordingStatus
@ -14379,13 +14457,20 @@ TraceRecorder::unboxNextValue(LIns* &v_ins)
if (JSID_IS_STRING(id)) {
v_ins = unbox_string_id(id_ins);
} else {
} else if (JSID_IS_INT(id)) {
/* id is an integer, convert to a string. */
JS_ASSERT(JSID_IS_INT(id));
LIns *id_to_int_ins = unbox_int_id(id_ins);
LIns* args[] = { id_to_int_ins, cx_ins };
v_ins = w.call(&js_IntToString_ci, args);
guard(false, w.eqp0(v_ins), OOM_EXIT);
} else {
#if JS_HAS_XML_SUPPORT
JS_ASSERT(JSID_IS_OBJECT(id));
JS_ASSERT(JSID_TO_OBJECT(id)->isXMLId());
RETURN_STOP_A("iterated over a property with an XML id");
#else
JS_NEVER_REACHED("unboxNextValue");
#endif
}
/* Increment the cursor by one jsid and store it back. */
@ -15809,7 +15894,13 @@ TraceRecorder::record_JSOP_LENGTH()
JS_ASSERT(obj->isSlowArray());
guardClass(obj_ins, &js_SlowArrayClass, snapshot(BRANCH_EXIT), LOAD_NORMAL);
}
v_ins = w.i2d(w.lduiObjPrivate(obj_ins));
v_ins = w.lduiObjPrivate(obj_ins);
if (obj->getArrayLength() <= JSVAL_INT_MAX) {
guard(true, w.leui(v_ins, w.immi(JSVAL_INT_MAX)), BRANCH_EXIT);
v_ins = w.i2d(v_ins);
} else {
v_ins = w.ui2d(v_ins);
}
} else if (OkToTraceTypedArrays && js_IsTypedArray(obj)) {
// Ensure array is a typed array and is the same type as what was written
guardClass(obj_ins, obj->getClass(), snapshot(BRANCH_EXIT), LOAD_NORMAL);
@ -15823,32 +15914,6 @@ TraceRecorder::record_JSOP_LENGTH()
return ARECORD_CONTINUE;
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_NEWARRAY()
{
LIns *proto_ins;
CHECK_STATUS_A(getClassPrototype(JSProto_Array, proto_ins));
uint32 len = GET_UINT16(cx->regs->pc);
cx->assertValidStackDepth(len);
LIns* args[] = { w.immi(len), proto_ins, cx_ins };
LIns* v_ins = w.call(&js_NewPreallocatedArray_ci, args);
guard(false, w.eqp0(v_ins), OOM_EXIT);
LIns* slots_ins = NULL;
uint32 count = 0;
for (uint32 i = 0; i < len; i++) {
Value& v = stackval(int(i) - int(len));
if (!v.isMagic())
count++;
stobj_set_dslot(v_ins, i, slots_ins, v, get(&v));
}
stack(-int(len), v_ins);
return ARECORD_CONTINUE;
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_JSOP_HOLE()
{
@ -16251,7 +16316,7 @@ RecordTracePoint(JSContext* cx, uintN& inlineCallCount, bool* blacklist, bool ex
switch (lr->exitType) {
case UNSTABLE_LOOP_EXIT:
if (!AttemptToStabilizeTree(cx, globalObj, lr, NULL, 0, NULL))
if (!AttemptToStabilizeTree(cx, globalObj, lr, NULL, NULL, 0))
return TPA_RanStuff;
break;
@ -16295,7 +16360,7 @@ RecordTracePoint(JSContext* cx, uintN& inlineCallCount, bool* blacklist, bool ex
return TPA_Nothing;
if (!ScopeChainCheck(cx, tree))
return TPA_Nothing;
if (!RecordTree(cx, tree->first, NULL, 0, NULL, globalSlots))
if (!RecordTree(cx, tree->first, NULL, NULL, 0, globalSlots))
return TPA_Nothing;
interpret:

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

@ -1082,6 +1082,10 @@ class TraceRecorder
/* The set of objects whose shapes already have been guarded. */
GuardedShapeTable guardedShapeTable;
/* Current initializer depth, and whether any of the initializers are unoptimized NEWINIT. */
int initDepth;
bool hadNewInit;
/***************************************** Temporal state hoisted into the recording session */
/* Carry the return value from a STOP/RETURN to the subsequent record_LeaveFrame. */
@ -1661,14 +1665,14 @@ MonitorTracePoint(JSContext*, uintN& inlineCallCount, bool* blacklist,
extern JS_REQUIRES_STACK TraceRecorder::AbortResult
AbortRecording(JSContext* cx, const char* reason);
extern void
extern bool
InitJIT(TraceMonitor *tm);
extern void
FinishJIT(TraceMonitor *tm);
extern void
PurgeScriptFragments(JSContext* cx, JSScript* script);
PurgeScriptFragments(TraceMonitor* tm, JSScript* script);
extern bool
OverfullJITCache(TraceMonitor* tm);

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

@ -205,7 +205,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id);
* before deserialization of bytecode. If the saved version does not match
* the current version, abort deserialization and invalidate the file.
*/
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 77)
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 78)
/*
* Library-private functions.

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

@ -44,6 +44,9 @@
#include "jstl.h"
#include "assembler/assembler/MacroAssembler.h"
#include "assembler/assembler/LinkBuffer.h"
#include "assembler/assembler/RepatchBuffer.h"
#include "assembler/jit/ExecutableAllocator.h"
#include <limits.h>
namespace js {
namespace mjit {
@ -69,6 +72,7 @@ struct MacroAssemblerTypedefs {
typedef JSC::CodeLocationCall CodeLocationCall;
typedef JSC::ReturnAddressPtr ReturnAddressPtr;
typedef JSC::MacroAssemblerCodePtr MacroAssemblerCodePtr;
typedef JSC::JITCode JITCode;
};
class BaseCompiler : public MacroAssemblerTypedefs
@ -86,14 +90,15 @@ class BaseCompiler : public MacroAssemblerTypedefs
protected:
JSC::ExecutablePool *
getExecPool(size_t size) {
return BaseCompiler::GetExecPool(cx, size);
getExecPool(JSScript *script, size_t size) {
return BaseCompiler::GetExecPool(cx, script, size);
}
public:
static JSC::ExecutablePool *
GetExecPool(JSContext *cx, size_t size) {
JSC::ExecutablePool *pool = cx->jaegerCompartment()->poolForSize(size);
GetExecPool(JSContext *cx, JSScript *script, size_t size) {
JaegerCompartment *jc = script->compartment->jaegerCompartment;
JSC::ExecutablePool *pool = jc->poolForSize(size);
if (!pool)
js_ReportOutOfMemory(cx);
return pool;
@ -106,16 +111,48 @@ class BaseCompiler : public MacroAssemblerTypedefs
class LinkerHelper : public JSC::LinkBuffer
{
protected:
JSContext *cx;
Assembler &masm;
#ifdef DEBUG
bool verifiedRange;
#endif
public:
LinkerHelper(JSContext *cx) : cx(cx)
LinkerHelper(Assembler &masm) : masm(masm)
#ifdef DEBUG
, verifiedRange(false)
#endif
{ }
JSC::ExecutablePool *init(Assembler &masm) {
~LinkerHelper() {
JS_ASSERT(verifiedRange);
}
bool verifyRange(const JSC::JITCode &other) {
#ifdef DEBUG
verifiedRange = true;
#endif
#ifdef JS_CPU_X64
uintptr_t lowest = JS_MIN(uintptr_t(m_code), uintptr_t(other.start()));
uintptr_t myEnd = uintptr_t(m_code) + m_size;
uintptr_t otherEnd = uintptr_t(other.start()) + other.size();
uintptr_t highest = JS_MAX(myEnd, otherEnd);
return (highest - lowest < INT_MAX);
#else
return true;
#endif
}
bool verifyRange(JITScript *jit) {
return verifyRange(JSC::JITCode(jit->code.m_code.executableAddress(), jit->code.m_size));
}
JSC::ExecutablePool *init(JSContext *cx) {
// The pool is incref'd after this call, so it's necessary to release()
// on any failure.
JSC::ExecutablePool *ep = BaseCompiler::GetExecPool(cx, masm.size());
JSScript *script = cx->fp()->script();
JSC::ExecutablePool *ep = BaseCompiler::GetExecPool(cx, script, masm.size());
if (!ep)
return ep;
@ -129,11 +166,30 @@ class LinkerHelper : public JSC::LinkBuffer
return ep;
}
JSC::CodeLocationLabel finalize() {
masm.finalize(*this);
return finalizeCodeAddendum();
}
void maybeLink(MaybeJump jump, JSC::CodeLocationLabel label) {
if (!jump.isSet())
return;
link(jump.get(), label);
}
size_t size() const {
return m_size;
}
};
class Repatcher : public JSC::RepatchBuffer
{
public:
Repatcher(JITScript *jit) : JSC::RepatchBuffer(jit->code)
{ }
Repatcher(const JSC::JITCode &code) : JSC::RepatchBuffer(code)
{ }
};
} /* namespace js */

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

@ -395,7 +395,7 @@ mjit::Compiler::finishThisUp(JITScript **jitp)
stubcc.size() +
doubleList.length() * sizeof(double);
JSC::ExecutablePool *execPool = getExecPool(totalSize);
JSC::ExecutablePool *execPool = getExecPool(script, totalSize);
if (!execPool)
return Compile_Abort;
@ -1506,44 +1506,33 @@ mjit::Compiler::generateMethod()
END_CASE(JSOP_UINT16)
BEGIN_CASE(JSOP_NEWINIT)
{
jsint i = GET_UINT16(PC);
uint32 count = GET_UINT16(PC + UINT16_LEN);
JS_ASSERT(i == JSProto_Array || i == JSProto_Object);
prepareStubCall(Uses(0));
masm.move(Imm32(count), Registers::ArgReg1);
if (i == JSProto_Array)
INLINE_STUBCALL(stubs::NewInitArray);
else
INLINE_STUBCALL(stubs::NewInitObject);
frame.takeReg(Registers::ReturnReg);
frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg);
}
jsop_newinit();
END_CASE(JSOP_NEWINIT)
BEGIN_CASE(JSOP_NEWARRAY)
jsop_newinit();
END_CASE(JSOP_NEWARRAY)
BEGIN_CASE(JSOP_NEWOBJECT)
jsop_newinit();
END_CASE(JSOP_NEWOBJECT)
BEGIN_CASE(JSOP_ENDINIT)
END_CASE(JSOP_ENDINIT)
BEGIN_CASE(JSOP_INITPROP)
{
JSAtom *atom = script->getAtom(fullAtomIndex(PC));
prepareStubCall(Uses(2));
masm.move(ImmPtr(atom), Registers::ArgReg1);
INLINE_STUBCALL(stubs::InitProp);
BEGIN_CASE(JSOP_INITMETHOD)
jsop_initmethod();
frame.pop();
END_CASE(JSOP_INITMETHOD)
BEGIN_CASE(JSOP_INITPROP)
jsop_initprop();
frame.pop();
}
END_CASE(JSOP_INITPROP)
BEGIN_CASE(JSOP_INITELEM)
{
JSOp next = JSOp(PC[JSOP_INITELEM_LENGTH]);
prepareStubCall(Uses(3));
masm.move(Imm32(next == JSOP_ENDINIT ? 1 : 0), Registers::ArgReg1);
INLINE_STUBCALL(stubs::InitElem);
jsop_initelem();
frame.popn(2);
}
END_CASE(JSOP_INITELEM)
BEGIN_CASE(JSOP_INCARG)
@ -1941,18 +1930,6 @@ mjit::Compiler::generateMethod()
frame.push(Value(Int32Value(GET_INT32(PC))));
END_CASE(JSOP_INT32)
BEGIN_CASE(JSOP_NEWARRAY)
{
uint32 len = GET_UINT16(PC);
prepareStubCall(Uses(len));
masm.move(Imm32(len), Registers::ArgReg1);
INLINE_STUBCALL(stubs::NewArray);
frame.popn(len);
frame.takeReg(Registers::ReturnReg);
frame.pushTypedPayload(JSVAL_TYPE_OBJECT, Registers::ReturnReg);
}
END_CASE(JSOP_NEWARRAY)
BEGIN_CASE(JSOP_HOLE)
frame.push(MagicValue(JS_ARRAY_HOLE));
END_CASE(JSOP_HOLE)
@ -1982,16 +1959,6 @@ mjit::Compiler::generateMethod()
INLINE_STUBCALL(stubs::Debugger);
END_CASE(JSOP_DEBUGGER)
BEGIN_CASE(JSOP_INITMETHOD)
{
JSAtom *atom = script->getAtom(fullAtomIndex(PC));
prepareStubCall(Uses(2));
masm.move(ImmPtr(atom), Registers::ArgReg1);
INLINE_STUBCALL(stubs::InitMethod);
frame.pop();
}
END_CASE(JSOP_INITMETHOD)
BEGIN_CASE(JSOP_UNBRAND)
jsop_unbrand();
END_CASE(JSOP_UNBRAND)
@ -4710,6 +4677,41 @@ mjit::Compiler::jsop_arguments()
INLINE_STUBCALL(stubs::Arguments);
}
void
mjit::Compiler::jsop_newinit()
{
bool isArray;
unsigned count = 0;
JSObject *baseobj = NULL;
switch (*PC) {
case JSOP_NEWINIT:
isArray = (PC[1] == JSProto_Array);
break;
case JSOP_NEWARRAY:
isArray = true;
count = GET_UINT24(PC);
break;
case JSOP_NEWOBJECT:
isArray = false;
baseobj = script->getObject(fullAtomIndex(PC));
break;
default:
JS_NOT_REACHED("Bad op");
return;
}
prepareStubCall(Uses(0));
if (isArray) {
masm.move(Imm32(count), Registers::ArgReg1);
INLINE_STUBCALL(stubs::NewInitArray);
} else {
masm.move(ImmPtr(baseobj), Registers::ArgReg1);
INLINE_STUBCALL(stubs::NewInitObject);
}
frame.takeReg(Registers::ReturnReg);
frame.pushInitializerObject(Registers::ReturnReg, *PC == JSOP_NEWARRAY, baseobj);
}
/*
* Note: This function emits tracer hooks into the OOL path. This means if
* it is used in the middle of an in-progress slow path, the stream will be

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

@ -453,6 +453,10 @@ class Compiler : public BaseCompiler
bool jsop_andor(JSOp op, jsbytecode *target);
void jsop_arginc(JSOp op, uint32 slot, bool popped);
void jsop_localinc(JSOp op, uint32 slot, bool popped);
void jsop_newinit();
void jsop_initmethod();
void jsop_initprop();
void jsop_initelem();
bool jsop_setelem();
bool jsop_getelem(bool isCall);
bool isCacheableBaseAndIndex(FrameEntry *obj, FrameEntry *id);

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

@ -73,10 +73,14 @@ mjit::Compiler::tryBinaryConstantFold(JSContext *cx, FrameState &frame, JSOp op,
case JSOP_SUB:
case JSOP_MUL:
case JSOP_DIV:
case JSOP_MOD:
needInt = false;
break;
case JSOP_MOD:
needInt = (L.isInt32() && R.isInt32() &&
L.toInt32() >= 0 && R.toInt32() > 0);
break;
case JSOP_RSH:
needInt = true;
break;
@ -129,10 +133,12 @@ mjit::Compiler::tryBinaryConstantFold(JSContext *cx, FrameState &frame, JSOp op,
}
break;
case JSOP_MOD:
if (dL == 0)
if (needInt)
nL %= nR;
else if (dR == 0)
dL = js_NaN;
else
dL = js_fmod(dR, dL);
dL = js_fmod(dL, dR);
break;
case JSOP_RSH:
@ -828,6 +834,10 @@ mjit::Compiler::jsop_mod()
#if defined(JS_CPU_X86)
FrameEntry *lhs = frame.peek(-2);
FrameEntry *rhs = frame.peek(-1);
if (tryBinaryConstantFold(cx, frame, JSOP_MOD, lhs, rhs))
return;
if ((lhs->isTypeKnown() && lhs->getKnownType() != JSVAL_TYPE_INT32) ||
(rhs->isTypeKnown() && rhs->getKnownType() != JSVAL_TYPE_INT32))
#endif

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

@ -38,6 +38,8 @@
*
* ***** END LICENSE BLOCK ***** */
#include "jsbool.h"
#include "jscntxt.h"
#include "jsemit.h"
#include "jslibmath.h"
#include "jsnum.h"
#include "jsscope.h"
@ -1780,3 +1782,90 @@ mjit::Compiler::jsop_pos()
stubcc.rejoin(Changes(1));
}
void
mjit::Compiler::jsop_initmethod()
{
#ifdef DEBUG
FrameEntry *obj = frame.peek(-2);
#endif
JSAtom *atom = script->getAtom(fullAtomIndex(PC));
/* Initializers with INITMETHOD are not fast yet. */
JS_ASSERT(!obj->initializerObject());
prepareStubCall(Uses(2));
masm.move(ImmPtr(atom), Registers::ArgReg1);
INLINE_STUBCALL(stubs::InitMethod);
}
void
mjit::Compiler::jsop_initprop()
{
FrameEntry *obj = frame.peek(-2);
FrameEntry *fe = frame.peek(-1);
JSAtom *atom = script->getAtom(fullAtomIndex(PC));
JSObject *baseobj = obj->initializerObject();
if (!baseobj) {
prepareStubCall(Uses(2));
masm.move(ImmPtr(atom), Registers::ArgReg1);
INLINE_STUBCALL(stubs::InitProp);
return;
}
JSObject *holder;
JSProperty *prop = NULL;
#ifdef DEBUG
int res =
#endif
js_LookupPropertyWithFlags(cx, baseobj, ATOM_TO_JSID(atom),
JSRESOLVE_QUALIFIED, &holder, &prop);
JS_ASSERT(res >= 0 && prop && holder == baseobj);
RegisterID objReg = frame.copyDataIntoReg(obj);
masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg);
/* Perform the store. */
Shape *shape = (Shape *) prop;
frame.storeTo(fe, Address(objReg, shape->slot * sizeof(Value)));
frame.freeReg(objReg);
}
void
mjit::Compiler::jsop_initelem()
{
FrameEntry *obj = frame.peek(-3);
FrameEntry *id = frame.peek(-2);
FrameEntry *fe = frame.peek(-1);
/*
* The initialized index is always a constant, but we won't remember which
* constant if there are branches inside the code computing the initializer
* expression (e.g. the expression uses the '?' operator). Slow path those
* cases, as well as those where INITELEM is used on an object initializer
* or a non-fast array initializer.
*/
if (!id->isConstant() || !obj->initializerArray()) {
JSOp next = JSOp(PC[JSOP_INITELEM_LENGTH]);
prepareStubCall(Uses(3));
masm.move(Imm32(next == JSOP_ENDINIT ? 1 : 0), Registers::ArgReg1);
INLINE_STUBCALL(stubs::InitElem);
return;
}
JS_ASSERT(id->getValue().isInt32());
if (fe->isConstant() && fe->getValue().isMagic(JS_ARRAY_HOLE)) {
/* The array already has the correct length, nothing to do. */
return;
}
RegisterID objReg = frame.copyDataIntoReg(obj);
masm.loadPtr(Address(objReg, offsetof(JSObject, slots)), objReg);
/* Perform the store. */
frame.storeTo(fe, Address(objReg, id->getValue().toInt32() * sizeof(Value)));
frame.freeReg(objReg);
}

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

@ -41,6 +41,7 @@
#define jsjaeger_valueinfo_h__
#include "jsapi.h"
#include "jstypes.h"
#include "methodjit/MachineRegs.h"
#include "methodjit/RematInfo.h"
#include "assembler/assembler/MacroAssembler.h"
@ -122,6 +123,14 @@ class FrameEntry
return backing() == other->backing();
}
inline bool initializerArray() {
return initArray;
}
inline JSObject *initializerObject() {
return initObject;
}
private:
void setType(JSValueType type_) {
type.setConstant();
@ -244,7 +253,12 @@ class FrameEntry
bool copied;
bool isNumber;
bool tracked;
char padding[1];
bool initArray;
JSObject *initObject;
#if (JS_BITS_PER_WORD == 32)
void *padding;
#endif
};
} /* namespace mjit */

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

@ -342,6 +342,16 @@ FrameState::pushInt32(RegisterID payload)
regstate[payload].associate(fe, RematInfo::DATA);
}
inline void
FrameState::pushInitializerObject(RegisterID payload, bool array, JSObject *baseobj)
{
pushTypedPayload(JSVAL_TYPE_OBJECT, payload);
FrameEntry *fe = peek(-1);
fe->initArray = array;
fe->initObject = baseobj;
}
inline void
FrameState::pushUntypedPayload(JSValueType type, RegisterID payload)
{

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

@ -309,6 +309,12 @@ class FrameState
*/
inline void pushInt32(RegisterID payload);
/*
* Pushes an initializer with specified payload, storing whether it is an array
* or object whose contents can be initialized in fast paths.
*/
inline void pushInitializerObject(RegisterID payload, bool array, JSObject *baseobj);
/*
* Pops a value off the operation stack, freeing any of its resources.
*/

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

@ -49,7 +49,6 @@
#include "jsbool.h"
#include "assembler/assembler/MacroAssemblerCodeRef.h"
#include "assembler/assembler/CodeLocation.h"
#include "assembler/assembler/RepatchBuffer.h"
#include "jsiter.h"
#include "jstypes.h"
#include "methodjit/StubCalls.h"
@ -57,6 +56,7 @@
#include "jspropertycache.h"
#include "methodjit/MonoIC.h"
#include "jsanalyze.h"
#include "methodjit/BaseCompiler.h"
#include "jsinterpinlines.h"
#include "jspropertycacheinlines.h"
@ -890,16 +890,14 @@ FinishExcessFrames(VMFrame &f, JSStackFrame *entryFrame)
#if JS_MONOIC
static void
UpdateTraceHintSingle(JSC::CodeLocationJump jump, JSC::CodeLocationLabel target)
UpdateTraceHintSingle(Repatcher &repatcher, JSC::CodeLocationJump jump, JSC::CodeLocationLabel target)
{
/*
* Hack: The value that will be patched is before the executable address,
* so to get protection right, just unprotect the general region around
* the jump.
*/
uint8 *addr = (uint8 *)(jump.executableAddress());
JSC::RepatchBuffer repatch(addr - 64, 128);
repatch.relink(jump, target);
repatcher.relink(jump, target);
JaegerSpew(JSpew_PICs, "relinking trace hint %p to %p\n",
jump.executableAddress(), target.executableAddress());
@ -908,10 +906,11 @@ UpdateTraceHintSingle(JSC::CodeLocationJump jump, JSC::CodeLocationLabel target)
static void
DisableTraceHint(VMFrame &f, ic::TraceICInfo &tic)
{
UpdateTraceHintSingle(tic.traceHint, tic.jumpTarget);
Repatcher repatcher(f.jit());
UpdateTraceHintSingle(repatcher, tic.traceHint, tic.jumpTarget);
if (tic.hasSlowTraceHint)
UpdateTraceHintSingle(tic.slowTraceHint, tic.jumpTarget);
UpdateTraceHintSingle(repatcher, tic.slowTraceHint, tic.jumpTarget);
}
static void
@ -924,10 +923,12 @@ EnableTraceHintAt(JSScript *script, js::mjit::JITScript *jit, jsbytecode *pc, ui
JaegerSpew(JSpew_PICs, "Enabling trace IC %u in script %p\n", index, script);
UpdateTraceHintSingle(tic.traceHint, tic.stubEntry);
Repatcher repatcher(jit);
UpdateTraceHintSingle(repatcher, tic.traceHint, tic.stubEntry);
if (tic.hasSlowTraceHint)
UpdateTraceHintSingle(tic.slowTraceHint, tic.stubEntry);
UpdateTraceHintSingle(repatcher, tic.slowTraceHint, tic.stubEntry);
}
#endif

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

@ -129,11 +129,11 @@ js::JaegerSpew(JaegerSpewChannel channel, const char *fmt, ...)
if (!(LoggingBits & (1 << uint32(channel))))
return;
fprintf(stdout, "[jaeger] %-7s ", ChannelNames[channel]);
fprintf(stderr, "[jaeger] %-7s ", ChannelNames[channel]);
va_list ap;
va_start(ap, fmt);
vfprintf(stdout, fmt, ap);
vfprintf(stderr, fmt, ap);
va_end(ap);
/* fprintf(stdout, "\n"); */

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

@ -55,6 +55,8 @@
namespace js {
namespace mjit { struct JITScript; }
struct VMFrame
{
union Arguments {
@ -136,6 +138,7 @@ struct VMFrame
JSRuntime *runtime() { return cx->runtime; }
JSStackFrame *&fp() { return regs.fp; }
mjit::JITScript *jit() { return fp()->jit(); }
};
#ifdef JS_CPU_ARM

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

@ -43,7 +43,6 @@
#include "StubCalls.h"
#include "StubCalls-inl.h"
#include "assembler/assembler/LinkBuffer.h"
#include "assembler/assembler/RepatchBuffer.h"
#include "assembler/assembler/MacroAssembler.h"
#include "assembler/assembler/CodeLocation.h"
#include "CodeGenIncludes.h"
@ -72,7 +71,7 @@ typedef JSC::MacroAssembler::Call Call;
static void
PatchGetFallback(VMFrame &f, ic::MICInfo *ic)
{
JSC::RepatchBuffer repatch(ic->stubEntry.executableAddress(), 64);
Repatcher repatch(f.jit());
JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, stubs::GetGlobalName));
repatch.relink(ic->stubCall, fptr);
}
@ -101,21 +100,20 @@ ic::GetGlobalName(VMFrame &f, ic::MICInfo *ic)
ic->u.name.touched = true;
/* Patch shape guard. */
JSC::RepatchBuffer repatch(ic->entry.executableAddress(), 50);
repatch.repatch(ic->shape, obj->shape());
Repatcher repatcher(f.jit());
repatcher.repatch(ic->shape, obj->shape());
/* Patch loads. */
slot *= sizeof(Value);
JSC::RepatchBuffer loads(ic->load.executableAddress(), 32, false);
#if defined JS_CPU_X86
loads.repatch(ic->load.dataLabel32AtOffset(MICInfo::GET_DATA_OFFSET), slot);
loads.repatch(ic->load.dataLabel32AtOffset(MICInfo::GET_TYPE_OFFSET), slot + 4);
repatcher.repatch(ic->load.dataLabel32AtOffset(MICInfo::GET_DATA_OFFSET), slot);
repatcher.repatch(ic->load.dataLabel32AtOffset(MICInfo::GET_TYPE_OFFSET), slot + 4);
#elif defined JS_CPU_ARM
// ic->load actually points to the LDR instruction which fetches the offset, but 'repatch'
// knows how to dereference it to find the integer value.
loads.repatch(ic->load.dataLabel32AtOffset(0), slot);
repatcher.repatch(ic->load.dataLabel32AtOffset(0), slot);
#elif defined JS_PUNBOX64
loads.repatch(ic->load.dataLabel32AtOffset(ic->patchValueOffset), slot);
repatcher.repatch(ic->load.dataLabel32AtOffset(ic->patchValueOffset), slot);
#endif
/* Do load anyway... this time. */
@ -151,7 +149,7 @@ PatchSetFallback(VMFrame &f, ic::MICInfo *ic)
{
JSScript *script = f.fp()->script();
JSC::RepatchBuffer repatch(ic->stubEntry.executableAddress(), 64);
Repatcher repatch(f.jit());
VoidStubMIC stub = ic->u.name.usePropertyCache
? STRICT_VARIANT(DisabledSetGlobal)
: STRICT_VARIANT(DisabledSetGlobalNoCache);
@ -188,28 +186,27 @@ ic::SetGlobalName(VMFrame &f, ic::MICInfo *ic)
ic->u.name.touched = true;
/* Patch shape guard. */
JSC::RepatchBuffer repatch(ic->entry.executableAddress(), 50);
repatch.repatch(ic->shape, obj->shape());
Repatcher repatcher(f.jit());
repatcher.repatch(ic->shape, obj->shape());
/* Patch loads. */
slot *= sizeof(Value);
JSC::RepatchBuffer stores(ic->load.executableAddress(), 32, false);
#if defined JS_CPU_X86
stores.repatch(ic->load.dataLabel32AtOffset(MICInfo::SET_TYPE_OFFSET), slot + 4);
repatcher.repatch(ic->load.dataLabel32AtOffset(MICInfo::SET_TYPE_OFFSET), slot + 4);
uint32 dataOffset;
if (ic->u.name.typeConst)
dataOffset = MICInfo::SET_DATA_CONST_TYPE_OFFSET;
else
dataOffset = MICInfo::SET_DATA_TYPE_OFFSET;
stores.repatch(ic->load.dataLabel32AtOffset(dataOffset), slot);
repatcher.repatch(ic->load.dataLabel32AtOffset(dataOffset), slot);
#elif defined JS_CPU_ARM
// ic->load actually points to the LDR instruction which fetches the offset, but 'repatch'
// knows how to dereference it to find the integer value.
stores.repatch(ic->load.dataLabel32AtOffset(0), slot);
repatcher.repatch(ic->load.dataLabel32AtOffset(0), slot);
#elif defined JS_PUNBOX64
stores.repatch(ic->load.dataLabel32AtOffset(ic->patchValueOffset), slot);
repatcher.repatch(ic->load.dataLabel32AtOffset(ic->patchValueOffset), slot);
#endif
if (ic->u.name.usePropertyCache)
@ -223,12 +220,12 @@ class EqualityICLinker : public LinkerHelper
VMFrame &f;
public:
EqualityICLinker(JSContext *cx, VMFrame &f)
: LinkerHelper(cx), f(f)
EqualityICLinker(Assembler &masm, VMFrame &f)
: LinkerHelper(masm), f(f)
{ }
bool init(Assembler &masm) {
JSC::ExecutablePool *pool = LinkerHelper::init(masm);
bool init(JSContext *cx) {
JSC::ExecutablePool *pool = LinkerHelper::init(cx);
if (!pool)
return false;
JSScript *script = f.fp()->script();
@ -357,10 +354,20 @@ class EqualityCompiler : public BaseCompiler
bool linkForIC(Assembler &masm)
{
EqualityICLinker buffer(cx, f);
if (!buffer.init(masm))
EqualityICLinker buffer(masm, f);
if (!buffer.init(cx))
return false;
Repatcher repatcher(f.jit());
/* Overwrite the call to the IC with a call to the stub. */
JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, ic.stub));
repatcher.relink(ic.stubCall, fptr);
// Silently fail, the IC is disabled now.
if (!buffer.verifyRange(f.jit()))
return true;
/* Set the targets of all type test failures to go to the stub. */
for (size_t i = 0; i < jumpList.length(); i++)
buffer.link(jumpList[i], ic.stubEntry);
@ -370,17 +377,11 @@ class EqualityCompiler : public BaseCompiler
buffer.link(trueJump, ic.target);
buffer.link(falseJump, ic.fallThrough);
CodeLocationLabel cs = buffer.finalizeCodeAddendum();
CodeLocationLabel cs = buffer.finalize();
/* Jump to the newly generated code instead of to the IC. */
JSC::RepatchBuffer jumpRepatcher(ic.jumpToStub.executableAddress(), INLINE_PATH_LENGTH);
jumpRepatcher.relink(ic.jumpToStub, cs);
repatcher.relink(ic.jumpToStub, cs);
/* Overwrite the call to the IC with a call to the stub. */
JSC::RepatchBuffer stubRepatcher(ic.stubCall.executableAddress(), INLINE_PATH_LENGTH);
JSC::FunctionPtr fptr(JS_FUNC_TO_DATA_PTR(void *, ic.stub));
stubRepatcher.relink(ic.stubCall, fptr);
return true;
}
@ -482,9 +483,9 @@ class CallCompiler : public BaseCompiler
{
}
JSC::ExecutablePool *poolForSize(size_t size, CallICInfo::PoolIndex index)
JSC::ExecutablePool *poolForSize(LinkerHelper &linker, CallICInfo::PoolIndex index)
{
JSC::ExecutablePool *ep = getExecPool(size);
JSC::ExecutablePool *ep = linker.init(f.cx);
if (!ep)
return NULL;
JS_ASSERT(!ic.pools[index]);
@ -492,7 +493,17 @@ class CallCompiler : public BaseCompiler
return ep;
}
bool generateFullCallStub(JSScript *script, uint32 flags)
void disable(JITScript *jit)
{
JSC::CodeLocationCall oolCall = ic.slowPathStart.callAtOffset(ic.oolCallOffset);
Repatcher repatch(jit);
JSC::FunctionPtr fptr = callingNew
? JSC::FunctionPtr(JS_FUNC_TO_DATA_PTR(void *, SlowNewFromIC))
: JSC::FunctionPtr(JS_FUNC_TO_DATA_PTR(void *, SlowCallFromIC));
repatch.relink(oolCall, fptr);
}
bool generateFullCallStub(JITScript *from, JSScript *script, uint32 flags)
{
/*
* Create a stub that works with arity mismatches. Like the fast-path,
@ -549,46 +560,55 @@ class CallCompiler : public BaseCompiler
masm.load32(FrameAddress(offsetof(VMFrame, u.call.dynamicArgc)), JSParamReg_Argc);
masm.jump(t0);
JSC::ExecutablePool *ep = poolForSize(masm.size(), CallICInfo::Pool_ScriptStub);
LinkerHelper linker(masm);
JSC::ExecutablePool *ep = poolForSize(linker, CallICInfo::Pool_ScriptStub);
if (!ep)
return false;
JSC::LinkBuffer buffer(&masm, ep);
buffer.link(notCompiled, ic.slowPathStart.labelAtOffset(ic.slowJoinOffset));
masm.finalize(buffer);
JSC::CodeLocationLabel cs = buffer.finalizeCodeAddendum();
if (!linker.verifyRange(from)) {
disable(from);
return true;
}
linker.link(notCompiled, ic.slowPathStart.labelAtOffset(ic.slowJoinOffset));
JSC::CodeLocationLabel cs = linker.finalize();
JaegerSpew(JSpew_PICs, "generated CALL stub %p (%d bytes)\n", cs.executableAddress(),
masm.size());
Repatcher repatch(from);
JSC::CodeLocationJump oolJump = ic.slowPathStart.jumpAtOffset(ic.oolJumpOffset);
uint8 *start = (uint8 *)oolJump.executableAddress();
JSC::RepatchBuffer repatch(start - 32, 64);
repatch.relink(oolJump, cs);
return true;
}
void patchInlinePath(JSScript *script, JSObject *obj)
bool patchInlinePath(JITScript *from, JSScript *script, JSObject *obj)
{
JS_ASSERT(ic.frameSize.isStatic());
JITScript *jit = script->getJIT(callingNew);
/* Very fast path. */
uint8 *start = (uint8 *)ic.funGuard.executableAddress();
JSC::RepatchBuffer repatch(start - 32, 64);
Repatcher repatch(from);
if (!repatch.canRelink(ic.funGuard.jumpAtOffset(ic.hotJumpOffset),
JSC::CodeLocationLabel(jit->fastEntry))) {
return false;
}
ic.fastGuardedObject = obj;
JITScript *jit = script->getJIT(callingNew);
repatch.repatch(ic.funGuard, obj);
repatch.relink(ic.funGuard.jumpAtOffset(ic.hotJumpOffset),
JSC::CodeLocationLabel(jit->fastEntry));
JaegerSpew(JSpew_PICs, "patched CALL path %p (obj: %p)\n", start, ic.fastGuardedObject);
JaegerSpew(JSpew_PICs, "patched CALL path %p (obj: %p)\n",
ic.funGuard.executableAddress(), ic.fastGuardedObject);
return true;
}
bool generateStubForClosures(JSObject *obj)
bool generateStubForClosures(JITScript *from, JSObject *obj)
{
JS_ASSERT(ic.frameSize.isStatic());
@ -609,30 +629,36 @@ class CallCompiler : public BaseCompiler
Jump funGuard = masm.branchPtr(Assembler::NotEqual, t0, ImmPtr(fun));
Jump done = masm.jump();
JSC::ExecutablePool *ep = poolForSize(masm.size(), CallICInfo::Pool_ClosureStub);
LinkerHelper linker(masm);
JSC::ExecutablePool *ep = poolForSize(linker, CallICInfo::Pool_ClosureStub);
if (!ep)
return false;
JSC::LinkBuffer buffer(&masm, ep);
buffer.link(claspGuard, ic.slowPathStart);
buffer.link(funGuard, ic.slowPathStart);
buffer.link(done, ic.funGuard.labelAtOffset(ic.hotPathOffset));
JSC::CodeLocationLabel cs = buffer.finalizeCodeAddendum();
ic.hasJsFunCheck = true;
if (!linker.verifyRange(from)) {
disable(from);
return true;
}
linker.link(claspGuard, ic.slowPathStart);
linker.link(funGuard, ic.slowPathStart);
linker.link(done, ic.funGuard.labelAtOffset(ic.hotPathOffset));
JSC::CodeLocationLabel cs = linker.finalize();
JaegerSpew(JSpew_PICs, "generated CALL closure stub %p (%d bytes)\n",
cs.executableAddress(), masm.size());
uint8 *start = (uint8 *)ic.funJump.executableAddress();
JSC::RepatchBuffer repatch(start - 32, 64);
Repatcher repatch(from);
repatch.relink(ic.funJump, cs);
ic.hasJsFunCheck = true;
return true;
}
bool generateNativeStub()
{
JITScript *jit = f.jit();
/* Snapshot the frameDepth before SplatApplyArgs modifies it. */
uintN initialFrameDepth = f.regs.sp - f.regs.fp->slots();
@ -769,31 +795,35 @@ class CallCompiler : public BaseCompiler
hasException.linkTo(masm.label(), &masm);
masm.throwInJIT();
JSC::ExecutablePool *ep = poolForSize(masm.size(), CallICInfo::Pool_NativeStub);
LinkerHelper linker(masm);
JSC::ExecutablePool *ep = poolForSize(linker, CallICInfo::Pool_NativeStub);
if (!ep)
THROWV(true);
JSC::LinkBuffer buffer(&masm, ep);
buffer.link(done, ic.slowPathStart.labelAtOffset(ic.slowJoinOffset));
buffer.link(funGuard, ic.slowPathStart);
masm.finalize(buffer);
JSC::CodeLocationLabel cs = buffer.finalizeCodeAddendum();
ic.fastGuardedNative = obj;
if (!linker.verifyRange(jit)) {
disable(jit);
return true;
}
linker.link(done, ic.slowPathStart.labelAtOffset(ic.slowJoinOffset));
linker.link(funGuard, ic.slowPathStart);
JSC::CodeLocationLabel cs = linker.finalize();
JaegerSpew(JSpew_PICs, "generated native CALL stub %p (%d bytes)\n",
cs.executableAddress(), masm.size());
uint8 *start = (uint8 *)ic.funJump.executableAddress();
JSC::RepatchBuffer repatch(start - 32, 64);
Repatcher repatch(jit);
repatch.relink(ic.funJump, cs);
ic.fastGuardedNative = obj;
return true;
}
void *update()
{
JITScript *jit = f.jit();
stubs::UncachedCallResult ucr;
if (callingNew)
stubs::UncachedNewHelper(f, ic.frameSize.staticArgc(), &ucr);
@ -803,13 +833,7 @@ class CallCompiler : public BaseCompiler
// If the function cannot be jitted (generally unjittable or empty script),
// patch this site to go to a slow path always.
if (!ucr.codeAddr) {
JSC::CodeLocationCall oolCall = ic.slowPathStart.callAtOffset(ic.oolCallOffset);
uint8 *start = (uint8 *)oolCall.executableAddress();
JSC::RepatchBuffer repatch(start - 32, 64);
JSC::FunctionPtr fptr = callingNew
? JSC::FunctionPtr(JS_FUNC_TO_DATA_PTR(void *, SlowNewFromIC))
: JSC::FunctionPtr(JS_FUNC_TO_DATA_PTR(void *, SlowCallFromIC));
repatch.relink(oolCall, fptr);
disable(jit);
return NULL;
}
@ -828,22 +852,23 @@ class CallCompiler : public BaseCompiler
}
if (!ic.frameSize.isStatic() || ic.frameSize.staticArgc() != fun->nargs) {
if (!generateFullCallStub(script, flags))
if (!generateFullCallStub(jit, script, flags))
THROWV(NULL);
} else {
if (!ic.fastGuardedObject) {
patchInlinePath(script, callee);
} else if (!ic.hasJsFunCheck &&
if (!ic.fastGuardedObject && patchInlinePath(jit, script, callee)) {
// Nothing, done.
} else if (ic.fastGuardedObject &&
!ic.hasJsFunCheck &&
!ic.fastGuardedNative &&
ic.fastGuardedObject->getFunctionPrivate() == fun) {
/*
* Note: Multiple "function guard" stubs are not yet
* supported, thus the fastGuardedNative check.
*/
if (!generateStubForClosures(callee))
if (!generateStubForClosures(jit, callee))
THROWV(NULL);
} else {
if (!generateFullCallStub(script, flags))
if (!generateFullCallStub(jit, script, flags))
THROWV(NULL);
}
}
@ -1033,6 +1058,11 @@ ic::SplatApplyArgs(VMFrame &f)
void
JITScript::purgeMICs()
{
if (!nMICs)
return;
Repatcher repatch(this);
for (uint32 i = 0; i < nMICs; i++) {
ic::MICInfo &mic = mics[i];
switch (mic.kind) {
@ -1040,7 +1070,6 @@ JITScript::purgeMICs()
case ic::MICInfo::GET:
{
/* Patch shape guard. */
JSC::RepatchBuffer repatch(mic.entry.executableAddress(), 50);
repatch.repatch(mic.shape, int(JSObjectMap::INVALID_SHAPE));
/*
@ -1071,6 +1100,11 @@ ic::PurgeMICs(JSContext *cx, JSScript *script)
void
JITScript::sweepCallICs()
{
if (!nCallICs)
return;
Repatcher repatcher(this);
for (uint32 i = 0; i < nCallICs; i++) {
ic::CallICInfo &ic = callICs[i];
@ -1085,11 +1119,8 @@ JITScript::sweepCallICs()
if (!fastFunDead && !nativeDead)
continue;
uint8 *start = (uint8 *)ic.funGuard.executableAddress();
JSC::RepatchBuffer repatch(start - 32, 64);
if (fastFunDead) {
repatch.repatch(ic.funGuard, NULL);
repatcher.repatch(ic.funGuard, NULL);
ic.releasePool(CallICInfo::Pool_ClosureStub);
ic.hasJsFunCheck = false;
ic.fastGuardedObject = NULL;
@ -1100,7 +1131,7 @@ JITScript::sweepCallICs()
ic.fastGuardedNative = NULL;
}
repatch.relink(ic.funJump, ic.slowPathStart);
repatcher.relink(ic.funJump, ic.slowPathStart);
ic.hit = false;
}

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

@ -42,7 +42,6 @@
#include "StubCalls-inl.h"
#include "BaseCompiler.h"
#include "assembler/assembler/LinkBuffer.h"
#include "assembler/assembler/RepatchBuffer.h"
#include "jsscope.h"
#include "jsnum.h"
#include "jsatominlines.h"
@ -59,7 +58,6 @@ using namespace js;
using namespace js::mjit;
using namespace js::mjit::ic;
typedef JSC::RepatchBuffer RepatchBuffer;
typedef JSC::FunctionPtr FunctionPtr;
/* Rough over-estimate of how much memory we need to unprotect. */
@ -73,12 +71,12 @@ class PICLinker : public LinkerHelper
ic::BasePolyIC &ic;
public:
PICLinker(JSContext *cx, ic::BasePolyIC &ic)
: LinkerHelper(cx), ic(ic)
PICLinker(Assembler &masm, ic::BasePolyIC &ic)
: LinkerHelper(masm), ic(ic)
{ }
bool init(Assembler &masm) {
JSC::ExecutablePool *pool = LinkerHelper::init(masm);
bool init(JSContext *cx) {
JSC::ExecutablePool *pool = LinkerHelper::init(cx);
if (!pool)
return false;
if (!ic.execPools.append(pool)) {
@ -137,22 +135,6 @@ class PICStubCompiler : public BaseCompiler
}
};
class PICRepatchBuffer : public JSC::RepatchBuffer
{
ic::BaseIC &ic;
JSC::CodeLocationLabel label;
public:
PICRepatchBuffer(ic::BaseIC &ic, JSC::CodeLocationLabel path)
: JSC::RepatchBuffer(path.executableAddress(), INLINE_PATH_LENGTH),
ic(ic), label(path)
{ }
void relink(int32 offset, JSC::CodeLocationLabel target) {
JSC::RepatchBuffer::relink(label.jumpAtOffset(offset), target);
}
};
class SetPropCompiler : public PICStubCompiler
{
JSObject *obj;
@ -226,9 +208,8 @@ class SetPropCompiler : public PICStubCompiler
obj(obj), atom(atom), lastStubSecondShapeGuard(pic.secondShapeGuard)
{ }
static void reset(ic::PICInfo &pic)
static void reset(Repatcher &repatcher, ic::PICInfo &pic)
{
RepatchBuffer repatcher(pic.fastPathStart.executableAddress(), INLINE_PATH_LENGTH);
repatcher.repatchLEAToLoadPtr(pic.fastPathRejoin.instructionAtOffset(dslotsLoadOffset(pic)));
repatcher.repatch(pic.fastPathStart.dataLabel32AtOffset(
pic.shapeGuard + inlineShapeOffset(pic)),
@ -237,7 +218,6 @@ class SetPropCompiler : public PICStubCompiler
pic.shapeGuard + inlineShapeJump(pic)),
pic.slowPathStart);
RepatchBuffer repatcher2(pic.slowPathStart.executableAddress(), INLINE_PATH_LENGTH);
FunctionPtr target(JS_FUNC_TO_DATA_PTR(void *, ic::SetProp));
repatcher.relink(pic.slowPathCall, target);
}
@ -247,7 +227,7 @@ class SetPropCompiler : public PICStubCompiler
JS_ASSERT(!pic.inlinePathPatched);
JaegerSpew(JSpew_PICs, "patch setprop inline at %p\n", pic.fastPathStart.executableAddress());
PICRepatchBuffer repatcher(pic, pic.fastPathStart);
Repatcher repatcher(f.jit());
int32 offset;
if (inlineSlot) {
@ -284,8 +264,11 @@ class SetPropCompiler : public PICStubCompiler
return Lookup_Cacheable;
}
void patchPreviousToHere(PICRepatchBuffer &repatcher, CodeLocationLabel cs)
void patchPreviousToHere(CodeLocationLabel cs)
{
Repatcher repatcher(pic.lastCodeBlock(f.jit()));
CodeLocationLabel label = pic.lastPathStart();
// 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.
@ -298,9 +281,9 @@ class SetPropCompiler : public PICStubCompiler
#endif
else
shapeGuardJumpOffset = pic.shapeGuard + inlineShapeJump();
repatcher.relink(shapeGuardJumpOffset, cs);
repatcher.relink(label.jumpAtOffset(shapeGuardJumpOffset), cs);
if (lastStubSecondShapeGuard)
repatcher.relink(lastStubSecondShapeGuard, cs);
repatcher.relink(label.jumpAtOffset(lastStubSecondShapeGuard), cs);
}
LookupStatus generateStub(uint32 initialShape, const Shape *shape, bool adding, bool inlineSlot)
@ -469,10 +452,15 @@ class SetPropCompiler : public PICStubCompiler
pic.secondShapeGuard = 0;
}
PICLinker buffer(cx, pic);
if (!buffer.init(masm))
PICLinker buffer(masm, pic);
if (!buffer.init(cx))
return error();
if (!buffer.verifyRange(pic.lastCodeBlock(f.jit())) ||
!buffer.verifyRange(f.jit())) {
return disable("code memory is out of range");
}
buffer.link(shapeGuard, pic.slowPathStart);
if (slowExit.isSet())
buffer.link(slowExit.get(), pic.slowPathStart);
@ -481,22 +469,20 @@ class SetPropCompiler : public PICStubCompiler
buffer.link(done, pic.fastPathRejoin);
if (skipOver.isSet())
buffer.link(skipOver.get(), pic.fastPathRejoin);
CodeLocationLabel cs = buffer.finalizeCodeAddendum();
CodeLocationLabel cs = buffer.finalize();
JaegerSpew(JSpew_PICs, "generate setprop stub %p %d %d at %p\n",
(void*)&pic,
initialShape,
pic.stubsGenerated,
cs.executableAddress());
PICRepatchBuffer repatcher(pic, pic.lastPathStart());
// This function can patch either the inline fast path for a generated
// stub. The stub omits the prefix of the inline fast path that loads
// the shape, so the offsets are different.
patchPreviousToHere(repatcher, cs);
patchPreviousToHere(cs);
pic.stubsGenerated++;
pic.lastStubStart = buffer.locationOf(start);
pic.updateLastPath(buffer, start);
#if defined JS_PUNBOX64
pic.labels.setprop.stubShapeJump = masm.differenceBetween(start, stubShapeJumpLabel);
@ -793,9 +779,8 @@ class GetPropCompiler : public PICStubCompiler
lastStubSecondShapeGuard(pic.secondShapeGuard)
{ }
static void reset(ic::PICInfo &pic)
static void reset(Repatcher &repatcher, ic::PICInfo &pic)
{
RepatchBuffer repatcher(pic.fastPathStart.executableAddress(), INLINE_PATH_LENGTH);
repatcher.repatchLEAToLoadPtr(pic.fastPathRejoin.instructionAtOffset(dslotsLoad(pic)));
repatcher.repatch(pic.fastPathStart.dataLabel32AtOffset(
pic.shapeGuard + inlineShapeOffset(pic)),
@ -808,8 +793,6 @@ class GetPropCompiler : public PICStubCompiler
pic.slowPathStart.labelAtOffset(pic.u.get.typeCheckOffset));
}
RepatchBuffer repatcher2(pic.slowPathStart.executableAddress(), INLINE_PATH_LENGTH);
VoidStubPIC stub;
switch (pic.kind) {
case ic::PICInfo::GET:
@ -843,20 +826,24 @@ class GetPropCompiler : public PICStubCompiler
masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg);
Jump done = masm.jump();
PICLinker buffer(cx, pic);
if (!buffer.init(masm))
PICLinker buffer(masm, pic);
if (!buffer.init(cx))
return error();
if (!buffer.verifyRange(pic.lastCodeBlock(f.jit())) ||
!buffer.verifyRange(f.jit())) {
return disable("code memory is out of range");
}
buffer.link(notArgs, pic.slowPathStart);
buffer.link(overridden, pic.slowPathStart);
buffer.link(done, pic.fastPathRejoin);
CodeLocationLabel start = buffer.finalizeCodeAddendum();
CodeLocationLabel start = buffer.finalize();
JaegerSpew(JSpew_PICs, "generate args length stub at %p\n",
start.executableAddress());
PICRepatchBuffer repatcher(pic, pic.lastPathStart());
patchPreviousToHere(repatcher, start);
patchPreviousToHere(start);
disable("args length done");
@ -877,20 +864,24 @@ class GetPropCompiler : public PICStubCompiler
masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg);
Jump done = masm.jump();
PICLinker buffer(cx, pic);
if (!buffer.init(masm))
PICLinker buffer(masm, pic);
if (!buffer.init(cx))
return error();
if (!buffer.verifyRange(pic.lastCodeBlock(f.jit())) ||
!buffer.verifyRange(f.jit())) {
return disable("code memory is out of range");
}
buffer.link(notArray, pic.slowPathStart);
buffer.link(oob, pic.slowPathStart);
buffer.link(done, pic.fastPathRejoin);
CodeLocationLabel start = buffer.finalizeCodeAddendum();
CodeLocationLabel start = buffer.finalize();
JaegerSpew(JSpew_PICs, "generate array length stub at %p\n",
start.executableAddress());
PICRepatchBuffer repatcher(pic, pic.lastPathStart());
patchPreviousToHere(repatcher, start);
patchPreviousToHere(start);
disable("array length done");
@ -944,21 +935,26 @@ class GetPropCompiler : public PICStubCompiler
Jump done = masm.jump();
PICLinker buffer(cx, pic);
if (!buffer.init(masm))
PICLinker buffer(masm, pic);
if (!buffer.init(cx))
return error();
if (!buffer.verifyRange(pic.lastCodeBlock(f.jit())) ||
!buffer.verifyRange(f.jit())) {
return disable("code memory is out of range");
}
buffer.link(notString, pic.slowPathStart.labelAtOffset(pic.u.get.typeCheckOffset));
buffer.link(shapeMismatch, pic.slowPathStart);
buffer.link(done, pic.fastPathRejoin);
CodeLocationLabel cs = buffer.finalizeCodeAddendum();
CodeLocationLabel cs = buffer.finalize();
JaegerSpew(JSpew_PICs, "generate string call stub at %p\n",
cs.executableAddress());
/* Patch the type check to jump here. */
if (pic.hasTypeCheck()) {
RepatchBuffer repatcher(pic.fastPathStart.executableAddress(), INLINE_PATH_LENGTH);
Repatcher repatcher(f.jit());
repatcher.relink(pic.fastPathStart.jumpAtOffset(GETPROP_INLINE_TYPE_GUARD), cs);
}
@ -981,19 +977,24 @@ class GetPropCompiler : public PICStubCompiler
masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg);
Jump done = masm.jump();
PICLinker buffer(cx, pic);
if (!buffer.init(masm))
PICLinker buffer(masm, pic);
if (!buffer.init(cx))
return error();
if (!buffer.verifyRange(pic.lastCodeBlock(f.jit())) ||
!buffer.verifyRange(f.jit())) {
return disable("code memory is out of range");
}
buffer.link(notString, pic.slowPathStart.labelAtOffset(pic.u.get.typeCheckOffset));
buffer.link(done, pic.fastPathRejoin);
CodeLocationLabel start = buffer.finalizeCodeAddendum();
CodeLocationLabel start = buffer.finalize();
JaegerSpew(JSpew_PICs, "generate string length stub at %p\n",
start.executableAddress());
if (pic.hasTypeCheck()) {
RepatchBuffer repatcher(pic.fastPathStart.executableAddress(), INLINE_PATH_LENGTH);
Repatcher repatcher(f.jit());
repatcher.relink(pic.fastPathStart.jumpAtOffset(GETPROP_INLINE_TYPE_GUARD), start);
}
@ -1005,7 +1006,7 @@ class GetPropCompiler : public PICStubCompiler
LookupStatus patchInline(JSObject *holder, const Shape *shape)
{
spew("patch", "inline");
PICRepatchBuffer repatcher(pic, pic.fastPathStart);
Repatcher repatcher(f.jit());
int32 offset;
if (!holder->hasSlotsArray()) {
@ -1101,24 +1102,28 @@ class GetPropCompiler : public PICStubCompiler
masm.loadObjProp(holder, holderReg, shape, pic.shapeReg, pic.objReg);
Jump done = masm.jump();
PICLinker buffer(cx, pic);
if (!buffer.init(masm))
PICLinker buffer(masm, pic);
if (!buffer.init(cx))
return error();
if (!buffer.verifyRange(pic.lastCodeBlock(f.jit())) ||
!buffer.verifyRange(f.jit())) {
return disable("code memory is out of range");
}
// 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.fastPathRejoin);
CodeLocationLabel cs = buffer.finalizeCodeAddendum();
CodeLocationLabel cs = buffer.finalize();
JaegerSpew(JSpew_PICs, "generated %s stub at %p\n", type, cs.executableAddress());
PICRepatchBuffer repatcher(pic, pic.lastPathStart());
patchPreviousToHere(repatcher, cs);
patchPreviousToHere(cs);
pic.stubsGenerated++;
pic.lastStubStart = buffer.locationOf(start);
pic.updateLastPath(buffer, start);
#if defined JS_PUNBOX64
pic.labels.getprop.stubShapeJump = masm.differenceBetween(start, stubShapeJumpLabel);
@ -1133,8 +1138,11 @@ class GetPropCompiler : public PICStubCompiler
return Lookup_Cacheable;
}
void patchPreviousToHere(PICRepatchBuffer &repatcher, CodeLocationLabel cs)
void patchPreviousToHere(CodeLocationLabel cs)
{
Repatcher repatcher(pic.lastCodeBlock(f.jit()));
CodeLocationLabel label = pic.lastPathStart();
// 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.
@ -1147,9 +1155,9 @@ class GetPropCompiler : public PICStubCompiler
#endif
else
shapeGuardJumpOffset = pic.shapeGuard + inlineShapeJump();
repatcher.relink(shapeGuardJumpOffset, cs);
repatcher.relink(label.jumpAtOffset(shapeGuardJumpOffset), cs);
if (lastStubSecondShapeGuard)
repatcher.relink(lastStubSecondShapeGuard, cs);
repatcher.relink(label.jumpAtOffset(lastStubSecondShapeGuard), cs);
}
LookupStatus update()
@ -1184,13 +1192,11 @@ class ScopeNameCompiler : public PICStubCompiler
getprop(f.cx, NULL, atom, *thisFromCtor())
{ }
static void reset(ic::PICInfo &pic)
static void reset(Repatcher &repatcher, ic::PICInfo &pic)
{
RepatchBuffer repatcher(pic.fastPathStart.executableAddress(), INLINE_PATH_LENGTH);
repatcher.relink(pic.fastPathStart.jumpAtOffset(SCOPENAME_JUMP_OFFSET),
pic.slowPathStart);
RepatchBuffer repatcher2(pic.slowPathStart.executableAddress(), INLINE_PATH_LENGTH);
VoidStubPIC stub = (pic.kind == ic::PICInfo::NAME) ? ic::Name : ic::XName;
FunctionPtr target(JS_FUNC_TO_DATA_PTR(void *, stub));
repatcher.relink(pic.slowPathCall, target);
@ -1276,21 +1282,27 @@ class ScopeNameCompiler : public PICStubCompiler
JS_ASSERT(masm.differenceBetween(failLabel, dbgJumpOffset) == SCOPENAME_JUMP_OFFSET);
PICLinker buffer(cx, pic);
if (!buffer.init(masm))
PICLinker buffer(masm, pic);
if (!buffer.init(cx))
return error();
if (!buffer.verifyRange(pic.lastCodeBlock(f.jit())) ||
!buffer.verifyRange(f.jit())) {
return disable("code memory is out of range");
}
buffer.link(failJump, pic.slowPathStart);
buffer.link(done, pic.fastPathRejoin);
CodeLocationLabel cs = buffer.finalizeCodeAddendum();
CodeLocationLabel cs = buffer.finalize();
JaegerSpew(JSpew_PICs, "generated %s global stub at %p\n", type, cs.executableAddress());
spew("NAME stub", "global");
PICRepatchBuffer repatcher(pic, pic.lastPathStart());
repatcher.relink(SCOPENAME_JUMP_OFFSET, cs);
Repatcher repatcher(pic.lastCodeBlock(f.jit()));
CodeLocationLabel label = pic.lastPathStart();
repatcher.relink(label.jumpAtOffset(SCOPENAME_JUMP_OFFSET), cs);
pic.stubsGenerated++;
pic.lastStubStart = buffer.locationOf(failLabel);
pic.updateLastPath(buffer, failLabel);
if (pic.stubsGenerated == MAX_PIC_STUBS)
disable("max stubs reached");
@ -1379,20 +1391,26 @@ class ScopeNameCompiler : public PICStubCompiler
Label failLabel = masm.label();
Jump failJump = masm.jump();
PICLinker buffer(cx, pic);
if (!buffer.init(masm))
PICLinker buffer(masm, pic);
if (!buffer.init(cx))
return error();
if (!buffer.verifyRange(pic.lastCodeBlock(f.jit())) ||
!buffer.verifyRange(f.jit())) {
return disable("code memory is out of range");
}
buffer.link(failJump, pic.slowPathStart);
buffer.link(done, pic.fastPathRejoin);
CodeLocationLabel cs = buffer.finalizeCodeAddendum();
CodeLocationLabel cs = buffer.finalize();
JaegerSpew(JSpew_PICs, "generated %s call stub at %p\n", type, cs.executableAddress());
PICRepatchBuffer repatcher(pic, pic.lastPathStart());
repatcher.relink(SCOPENAME_JUMP_OFFSET, cs);
Repatcher repatcher(pic.lastCodeBlock(f.jit()));
CodeLocationLabel label = pic.lastPathStart();
repatcher.relink(label.jumpAtOffset(SCOPENAME_JUMP_OFFSET), cs);
pic.stubsGenerated++;
pic.lastStubStart = buffer.locationOf(failLabel);
pic.updateLastPath(buffer, failLabel);
if (pic.stubsGenerated == MAX_PIC_STUBS)
disable("max stubs reached");
@ -1499,14 +1517,14 @@ class BindNameCompiler : public PICStubCompiler
scopeChain(scopeChain), atom(atom)
{ }
static void reset(ic::PICInfo &pic)
static void reset(Repatcher &repatcher, ic::PICInfo &pic)
{
PICRepatchBuffer repatcher(pic, pic.fastPathStart);
repatcher.relink(pic.shapeGuard + inlineJumpOffset(pic), pic.slowPathStart);
int jumpOffset = pic.shapeGuard + inlineJumpOffset(pic);
JSC::CodeLocationJump jump = pic.fastPathStart.jumpAtOffset(jumpOffset);
repatcher.relink(jump, pic.slowPathStart);
RepatchBuffer repatcher2(pic.slowPathStart.executableAddress(), INLINE_PATH_LENGTH);
FunctionPtr target(JS_FUNC_TO_DATA_PTR(void *, ic::BindName));
repatcher2.relink(pic.slowPathCall, target);
repatcher.relink(pic.slowPathCall, target);
}
LookupStatus generateStub(JSObject *obj)
@ -1550,23 +1568,29 @@ class BindNameCompiler : public PICStubCompiler
JS_ASSERT(masm.differenceBetween(failLabel, dbgStubJumpOffset) == BINDNAME_STUB_JUMP_OFFSET);
PICLinker buffer(cx, pic);
if (!buffer.init(masm))
PICLinker buffer(masm, pic);
if (!buffer.init(cx))
return error();
if (!buffer.verifyRange(pic.lastCodeBlock(f.jit())) ||
!buffer.verifyRange(f.jit())) {
return disable("code memory is out of range");
}
buffer.link(failJump, pic.slowPathStart);
buffer.link(done, pic.fastPathRejoin);
CodeLocationLabel cs = buffer.finalizeCodeAddendum();
CodeLocationLabel cs = buffer.finalize();
JaegerSpew(JSpew_PICs, "generated %s stub at %p\n", type, cs.executableAddress());
PICRepatchBuffer repatcher(pic, pic.lastPathStart());
Repatcher repatcher(pic.lastCodeBlock(f.jit()));
CodeLocationLabel label = pic.lastPathStart();
if (!pic.stubsGenerated)
repatcher.relink(pic.shapeGuard + inlineJumpOffset(), cs);
repatcher.relink(label.jumpAtOffset(pic.shapeGuard + inlineJumpOffset()), cs);
else
repatcher.relink(BINDNAME_STUB_JUMP_OFFSET, cs);
repatcher.relink(label.jumpAtOffset(BINDNAME_STUB_JUMP_OFFSET), cs);
pic.stubsGenerated++;
pic.lastStubStart = buffer.locationOf(failLabel);
pic.updateLastPath(buffer, failLabel);
if (pic.stubsGenerated == MAX_PIC_STUBS)
disable("max stubs reached");
@ -1935,7 +1959,7 @@ LookupStatus
BaseIC::disable(JSContext *cx, const char *reason, void *stub)
{
spew(cx, "disabled", reason);
RepatchBuffer repatcher(slowPathStart.executableAddress(), INLINE_PATH_LENGTH);
Repatcher repatcher(cx->fp()->jit());
repatcher.relink(slowPathCall, FunctionPtr(stub));
return Lookup_Uncacheable;
}
@ -1995,20 +2019,15 @@ GetElementIC::error(JSContext *cx)
}
void
GetElementIC::purge()
GetElementIC::purge(Repatcher &repatcher)
{
if (inlineTypeGuardPatched || inlineClaspGuardPatched) {
RepatchBuffer repatcher(fastPathStart.executableAddress(), INLINE_PATH_LENGTH);
// Repatch the inline jumps.
if (inlineTypeGuardPatched)
repatcher.relink(fastPathStart.jumpAtOffset(inlineTypeGuard), slowPathStart);
if (inlineClaspGuardPatched)
repatcher.relink(fastPathStart.jumpAtOffset(inlineClaspGuard), slowPathStart);
}
// Repatch the inline jumps.
if (inlineTypeGuardPatched)
repatcher.relink(fastPathStart.jumpAtOffset(inlineTypeGuard), slowPathStart);
if (inlineClaspGuardPatched)
repatcher.relink(fastPathStart.jumpAtOffset(inlineClaspGuard), slowPathStart);
if (slowCallPatched) {
RepatchBuffer repatcher(slowPathStart.executableAddress(), INLINE_PATH_LENGTH);
if (op == JSOP_GETELEM)
repatcher.relink(slowPathCall, FunctionPtr(JS_FUNC_TO_DATA_PTR(void *, ic::GetElement)));
else if (op == JSOP_CALLELEM)
@ -2092,10 +2111,17 @@ GetElementIC::attachGetProp(JSContext *cx, JSObject *obj, const Value &v, jsid i
Jump done = masm.jump();
PICLinker buffer(cx, *this);
if (!buffer.init(masm))
PICLinker buffer(masm, *this);
if (!buffer.init(cx))
return error(cx);
if (hasLastStringStub && !buffer.verifyRange(lastStringStub))
return disable(cx, "code memory is out of range");
if ((shouldPatchInlineTypeGuard() || shouldPatchUnconditionalClaspGuard()) &&
!buffer.verifyRange(cx->fp()->jit())) {
return disable(cx, "code memory is out of range");
}
// Patch all guards.
buffer.maybeLink(atomIdGuard, slowPathStart);
buffer.maybeLink(atomTypeGuard, slowPathStart);
@ -2103,7 +2129,7 @@ GetElementIC::attachGetProp(JSContext *cx, JSObject *obj, const Value &v, jsid i
buffer.maybeLink(protoGuard, slowPathStart);
buffer.link(done, fastPathRejoin);
CodeLocationLabel cs = buffer.finalizeCodeAddendum();
CodeLocationLabel cs = buffer.finalize();
#if DEBUG
char *chars = js_DeflateString(cx, v.toString()->chars(), v.toString()->length());
JaegerSpew(JSpew_PICs, "generated %s stub at %p for atom 0x%x (\"%s\") shape 0x%x (%s: %d)\n",
@ -2114,7 +2140,7 @@ GetElementIC::attachGetProp(JSContext *cx, JSObject *obj, const Value &v, jsid i
// Update the inline guards, if needed.
if (shouldPatchInlineTypeGuard() || shouldPatchUnconditionalClaspGuard()) {
PICRepatchBuffer repatcher(*this, fastPathStart);
Repatcher repatcher(cx->fp()->jit());
if (shouldPatchInlineTypeGuard()) {
// A type guard is present in the inline path, and this is the
@ -2122,7 +2148,7 @@ GetElementIC::attachGetProp(JSContext *cx, JSObject *obj, const Value &v, jsid i
JS_ASSERT(!inlineTypeGuardPatched);
JS_ASSERT(atomTypeGuard.isSet());
repatcher.relink(inlineTypeGuard, cs);
repatcher.relink(fastPathStart.jumpAtOffset(inlineTypeGuard), cs);
inlineTypeGuardPatched = true;
}
@ -2133,24 +2159,25 @@ GetElementIC::attachGetProp(JSContext *cx, JSObject *obj, const Value &v, jsid i
// because it follows an integer-id guard.
JS_ASSERT(!hasInlineTypeGuard());
repatcher.relink(inlineClaspGuard, cs);
repatcher.relink(fastPathStart.jumpAtOffset(inlineClaspGuard), cs);
inlineClaspGuardPatched = true;
}
}
// If there were previous stub guards, patch them now.
if (hasLastStringStub) {
PICRepatchBuffer repatcher(*this, lastStringStub);
Repatcher repatcher(lastStringStub);
CodeLocationLabel stub(lastStringStub.start());
if (atomGuard)
repatcher.relink(atomGuard, cs);
repatcher.relink(firstShapeGuard, cs);
repatcher.relink(stub.jumpAtOffset(atomGuard), cs);
repatcher.relink(stub.jumpAtOffset(firstShapeGuard), cs);
if (secondShapeGuard)
repatcher.relink(secondShapeGuard, cs);
repatcher.relink(stub.jumpAtOffset(secondShapeGuard), cs);
}
// Update state.
hasLastStringStub = true;
lastStringStub = cs;
lastStringStub = JITCode(cs.executableAddress(), buffer.size());
if (atomIdGuard.isSet()) {
atomGuard = buffer.locationOf(atomIdGuard.get()) - cs;
JS_ASSERT(atomGuard == buffer.locationOf(atomIdGuard.get()) - cs);
@ -2313,20 +2340,15 @@ SetElementIC::error(JSContext *cx)
}
void
SetElementIC::purge()
SetElementIC::purge(Repatcher &repatcher)
{
if (inlineClaspGuardPatched || inlineHoleGuardPatched) {
RepatchBuffer repatcher(fastPathStart.executableAddress(), INLINE_PATH_LENGTH);
// Repatch the inline jumps.
if (inlineClaspGuardPatched)
repatcher.relink(fastPathStart.jumpAtOffset(inlineClaspGuard), slowPathStart);
if (inlineHoleGuardPatched)
repatcher.relink(fastPathStart.jumpAtOffset(inlineHoleGuard), slowPathStart);
}
// Repatch the inline jumps.
if (inlineClaspGuardPatched)
repatcher.relink(fastPathStart.jumpAtOffset(inlineClaspGuard), slowPathStart);
if (inlineHoleGuardPatched)
repatcher.relink(fastPathStart.jumpAtOffset(inlineHoleGuard), slowPathStart);
if (slowCallPatched) {
RepatchBuffer repatcher(slowPathStart.executableAddress(), INLINE_PATH_LENGTH);
void *stub = JS_FUNC_TO_DATA_PTR(void *, APPLY_STRICTNESS(ic::SetElement, strictMode));
repatcher.relink(slowPathCall, FunctionPtr(stub));
}
@ -2409,22 +2431,25 @@ SetElementIC::attachHoleStub(JSContext *cx, JSObject *obj, int32 keyval)
JS_ASSERT(!execPool);
JS_ASSERT(!inlineHoleGuardPatched);
LinkerHelper buffer(cx);
execPool = buffer.init(masm);
LinkerHelper buffer(masm);
execPool = buffer.init(cx);
if (!execPool)
return error(cx);
if (!buffer.verifyRange(cx->fp()->jit()))
return disable(cx, "code memory is out of range");
// Patch all guards.
buffer.link(extendedArray, slowPathStart);
buffer.link(sameProto, slowPathStart);
buffer.link(extendedObject, slowPathStart);
buffer.link(done, fastPathRejoin);
CodeLocationLabel cs = buffer.finalizeCodeAddendum();
CodeLocationLabel cs = buffer.finalize();
JaegerSpew(JSpew_PICs, "generated dense array hole stub at %p\n", cs.executableAddress());
PICRepatchBuffer repatcher(*this, fastPathStart);
repatcher.relink(inlineHoleGuard, cs);
Repatcher repatcher(cx->fp()->jit());
repatcher.relink(fastPathStart.jumpAtOffset(inlineHoleGuard), cs);
inlineHoleGuardPatched = true;
disable(cx, "generated dense array hole stub");
@ -2470,23 +2495,28 @@ template void JS_FASTCALL ic::SetElement<false>(VMFrame &f, SetElementIC *ic);
void
JITScript::purgePICs()
{
if (!nPICs && !nGetElems && !nSetElems)
return;
Repatcher repatcher(this);
for (uint32 i = 0; i < nPICs; i++) {
ic::PICInfo &pic = pics[i];
switch (pic.kind) {
case ic::PICInfo::SET:
case ic::PICInfo::SETMETHOD:
SetPropCompiler::reset(pic);
SetPropCompiler::reset(repatcher, pic);
break;
case ic::PICInfo::NAME:
case ic::PICInfo::XNAME:
ScopeNameCompiler::reset(pic);
ScopeNameCompiler::reset(repatcher, pic);
break;
case ic::PICInfo::BIND:
BindNameCompiler::reset(pic);
BindNameCompiler::reset(repatcher, pic);
break;
case ic::PICInfo::CALL: /* fall-through */
case ic::PICInfo::GET:
GetPropCompiler::reset(pic);
GetPropCompiler::reset(repatcher, pic);
break;
default:
JS_NOT_REACHED("Unhandled PIC kind");
@ -2496,9 +2526,9 @@ JITScript::purgePICs()
}
for (uint32 i = 0; i < nGetElems; i++)
getElems[i].purge();
getElems[i].purge(repatcher);
for (uint32 i = 0; i < nSetElems; i++)
setElems[i].purge();
setElems[i].purge(repatcher);
}
void

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

@ -49,6 +49,7 @@
#include "BaseAssembler.h"
#include "RematInfo.h"
#include "BaseCompiler.h"
#include "assembler/moco/MocoStubs.h"
namespace js {
namespace mjit {
@ -191,15 +192,6 @@ struct BaseIC : public MacroAssemblerTypedefs {
// Slow path stub call.
CodeLocationCall slowPathCall;
// Address of the start of the last generated stub, if any.
CodeLocationLabel lastStubStart;
// Return the start address of the last path in this PIC, which is the
// inline path if no stubs have been generated yet.
CodeLocationLabel lastPathStart() {
return stubsGenerated > 0 ? lastStubStart : fastPathStart;
}
// Whether or not the callsite has been hit at least once.
bool hit : 1;
bool slowCallPatched : 1;
@ -306,7 +298,7 @@ struct GetElementIC : public BasePolyIC {
int secondShapeGuard : 8; // optional, non-zero if present
bool hasLastStringStub : 1;
CodeLocationLabel lastStringStub;
JITCode lastStringStub;
// A limited ValueRemat instance. It may contains either:
// 1) A constant, or
@ -334,7 +326,7 @@ struct GetElementIC : public BasePolyIC {
typeRegHasBaseShape = false;
hasLastStringStub = false;
}
void purge();
void purge(Repatcher &repatcher);
LookupStatus update(JSContext *cx, JSObject *obj, const Value &v, jsid id, Value *vp);
LookupStatus attachGetProp(JSContext *cx, JSObject *obj, const Value &v, jsid id,
Value *vp);
@ -396,7 +388,7 @@ struct SetElementIC : public BaseIC {
inlineClaspGuardPatched = false;
inlineHoleGuardPatched = false;
}
void purge();
void purge(Repatcher &repatcher);
LookupStatus attachHoleStub(JSContext *cx, JSObject *obj, int32 key);
LookupStatus update(JSContext *cx, const Value &objval, const Value &idval);
LookupStatus disable(JSContext *cx, const char *reason);
@ -432,6 +424,32 @@ struct PICInfo : public BasePolyIC {
ValueRemat vr;
} u;
// Address of the start of the last generated stub, if any. Note that this
// does not correctly overlay with the allocated memory; it does however
// overlay the portion that may need to be patched, which is good enough.
JITCode lastStubStart;
// Return the start address of the last path in this PIC, which is the
// inline path if no stubs have been generated yet.
CodeLocationLabel lastPathStart() {
if (!stubsGenerated)
return fastPathStart;
return CodeLocationLabel(lastStubStart.start());
}
// Return a JITCode block corresponding to the code memory to attach a
// new stub to.
JITCode lastCodeBlock(JITScript *jit) {
if (!stubsGenerated)
return JITCode(jit->code.m_code.executableAddress(), jit->code.m_size);
return lastStubStart;
}
void updateLastPath(LinkerHelper &linker, Label label) {
CodeLocationLabel loc = linker.locationOf(label);
lastStubStart = JITCode(loc.executableAddress(), linker.size());
}
Kind kind : 3;
// True if register R holds the base object shape along exits from the

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

@ -487,9 +487,14 @@ stubs::GetElem(VMFrame &f)
goto intern_big_int;
} else {
intern_big_int:
if (!js_InternNonIntElementId(cx, obj, rref, &id))
THROW();
int32_t i;
if (ValueFitsInInt32(rref, &i) && INT_FITS_IN_JSID(i)) {
id = INT_TO_JSID(i);
} else {
intern_big_int:
if (!js_InternNonIntElementId(cx, obj, rref, &id))
THROW();
}
}
if (!obj->getProperty(cx, id, &rval))
@ -1268,15 +1273,6 @@ stubs::Mod(VMFrame &f)
}
}
JSObject *JS_FASTCALL
stubs::NewArray(VMFrame &f, uint32 len)
{
JSObject *obj = js_NewArrayObject(f.cx, len, f.regs.sp - len);
if (!obj)
THROWV(NULL);
return obj;
}
void JS_FASTCALL
stubs::Debugger(VMFrame &f, jsbytecode *pc)
{
@ -1378,19 +1374,28 @@ stubs::NewInitArray(VMFrame &f, uint32 count)
JSObject *obj = NewArrayWithKind(cx, kind);
if (!obj || !obj->ensureSlots(cx, count))
THROWV(NULL);
obj->setArrayLength(count);
return obj;
}
JSObject * JS_FASTCALL
stubs::NewInitObject(VMFrame &f, uint32 count)
stubs::NewInitObject(VMFrame &f, JSObject *baseobj)
{
JSContext *cx = f.cx;
gc::FinalizeKind kind = GuessObjectGCKind(count, false);
JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
if (!obj || !obj->ensureSlots(cx, count))
if (!baseobj) {
gc::FinalizeKind kind = GuessObjectGCKind(0, false);
JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
if (!obj)
THROWV(NULL);
return obj;
}
JSObject *obj = CopyInitializerObject(cx, baseobj);
if (!obj)
THROWV(NULL);
return obj;
}

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

@ -49,8 +49,7 @@ namespace stubs {
void JS_FASTCALL This(VMFrame &f);
JSObject * JS_FASTCALL NewInitArray(VMFrame &f, uint32 count);
JSObject * JS_FASTCALL NewInitObject(VMFrame &f, uint32 count);
JSObject * JS_FASTCALL NewArray(VMFrame &f, uint32 len);
JSObject * JS_FASTCALL NewInitObject(VMFrame &f, JSObject *base);
void JS_FASTCALL Trap(VMFrame &f, jsbytecode *pc);
void JS_FASTCALL Debugger(VMFrame &f, jsbytecode *pc);
void JS_FASTCALL Interrupt(VMFrame &f, jsbytecode *pc);

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

@ -1 +1 @@
04d7771f3f85877cf12395ffecfc4f2f6d4a0b50
1f90e61950c44193ea5a1800c06d7dba8240cfd9

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

@ -2141,27 +2141,63 @@ namespace nanojit
RegisterMask allow = inst->isD() ? FpRegs : GpRegs;
Register dest_reg = prepareResultReg(inst, allow);
Register src_reg = findRegFor(if_false, allow & ~rmask(dest_reg));
// 3. If the test is "true", we branched over the copy.
underrunProtect(2 * sizeof(NIns));
NIns *after_mov = _nIns;
// Try to re-use the result register for one of the arguments.
Register src_true_reg = if_true->isInReg() ? if_true->getReg() : dest_reg;
Register src_false_reg = if_false->isInReg() ? if_false->getReg() : dest_reg;
// 2. If the test is "false", copy the register.
// Note that iftrue and iffalse may actually be the same, though
// it shouldn't happen with the LIR optimizers turned on.
if (src_true_reg == src_false_reg && if_true != if_false) {
// We can't re-use the result register for both arguments,
// so force one into its own register.
src_false_reg = findRegFor(if_false, allow & ~rmask(dest_reg));
NanoAssert(if_false->isInReg());
}
underrunProtect(6 * sizeof(NIns));
// 3. If the test is "true", copy the "true" source register.
NIns *after_mov_true = _nIns;
if (inst->isop(LIR_cmovd)) {
SH4_fmov(src_reg, dest_reg);
SH4_fmov(Register(src_reg + 1), Register(dest_reg + 1));
SH4_fmov(src_true_reg, dest_reg);
SH4_fmov(Register(src_true_reg + 1), Register(dest_reg + 1));
}
else {
SH4_mov(src_reg, dest_reg);
SH4_mov(src_true_reg, dest_reg);
}
// 2. If the test is "false", copy the "false" source register
// then jump over the "mov if true".
NIns *after_mov_false = _nIns;
SH4_nop();
SH4_bra(SH4_LABEL(after_mov_true));
if (inst->isop(LIR_cmovd)) {
SH4_fmov(src_false_reg, dest_reg);
SH4_fmov(Register(src_false_reg + 1), Register(dest_reg + 1));
}
else {
SH4_mov(src_false_reg, dest_reg);
}
findSpecificRegFor(if_true, dest_reg);
freeResourcesOf(inst);
// If we re-used the result register, mark it as active for either if_true
// or if_false (or both in the corner-case where they're the same).
if (src_true_reg == dest_reg) {
NanoAssert(!if_true->isInReg());
findSpecificRegForUnallocated(if_true, dest_reg);
} else if (src_false_reg == dest_reg) {
NanoAssert(!if_false->isInReg());
findSpecificRegForUnallocated(if_false, dest_reg);
} else {
NanoAssert(if_false->isInReg());
NanoAssert(if_true->isInReg());
}
// 1. Branch [or not] according to the condition.
asm_branch(false, condition, after_mov);
freeResourcesOf(inst);
asm_branch(false, condition, after_mov_false);
}
void Assembler::asm_cond(LIns *inst) {
@ -3151,8 +3187,7 @@ namespace nanojit
int reg = 0;
// Find the first register in this set.
while (!(set & rmask((Register)reg)))
reg++;
reg = lsReg(set);
_allocator.free &= ~rmask((Register)reg);

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

@ -1061,11 +1061,11 @@ namespace nanojit
Register r = findRegFor(p, GpRegs);
MOVQSPR(stk_off, r); // movq [rsp+d8], r
if (ty == ARGTYPE_I) {
// extend int32 to int64
// sign extend int32 to int64
NanoAssert(p->isI());
MOVSXDR(r, r);
} else if (ty == ARGTYPE_UI) {
// extend uint32 to uint64
// zero extend uint32 to uint64
NanoAssert(p->isI());
MOVLR(r, r);
} else {
@ -1081,7 +1081,14 @@ namespace nanojit
Register rr, ra;
beginOp1Regs(ins, GpRegs, rr, ra);
NanoAssert(IsGpReg(ra));
MOVLR(rr, ra); // 32bit mov zeros the upper 32bits of the target
// If ra==rr we do nothing. This is valid because we don't assume the
// upper 32-bits of a 64-bit GPR are zero when doing a 32-bit
// operation. More specifically, we widen 32-bit to 64-bit in three
// places, all of which explicitly sign- or zero-extend: asm_ui2uq(),
// asm_regarg() and asm_stkarg(). For the first this is required, for
// the latter two it's unclear if this is required, but it can't hurt.
if (ra != rr)
MOVLR(rr, ra);
endOpRegs(ins, rr, ra);
}

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

@ -2075,13 +2075,14 @@ DisassembleValue(JSContext *cx, jsval v, bool lines, bool recursive)
static JSBool
Disassemble(JSContext *cx, uintN argc, jsval *vp)
{
bool lines = false, recursive = false;
jsval *argv = JS_ARGV(cx, vp);
/* Read options off early arguments */
bool lines = false, recursive = false;
while (argc > 0 && JSVAL_IS_STRING(argv[0])) {
JSString *str = JSVAL_TO_STRING(argv[0]);
lines = JS_MatchStringAndAscii(str, "-l");
recursive = JS_MatchStringAndAscii(str, "-r");
lines |= JS_MatchStringAndAscii(str, "-l");
recursive |= JS_MatchStringAndAscii(str, "-r");
if (!lines && !recursive)
break;
argv++, argc--;
@ -2105,6 +2106,12 @@ DisassFile(JSContext *cx, uintN argc, jsval *vp)
return JS_TRUE;
}
/* Support extra options at the start, just like Dissassemble. */
uintN _argc = argc;
argv += argc-1;
argc = 1;
JSObject *thisobj = JS_THIS_OBJECT(cx, vp);
if (!thisobj)
return JS_FALSE;
@ -2133,7 +2140,7 @@ DisassFile(JSContext *cx, uintN argc, jsval *vp)
return JS_FALSE;
argv[0] = OBJECT_TO_JSVAL(obj); /* I like to root it, root it. */
JSBool ok = Disassemble(cx, 1, vp); /* gross, but works! */
JSBool ok = Disassemble(cx, _argc, vp); /* gross, but works! */
JS_SET_RVAL(cx, vp, JSVAL_VOID);
return ok;
}
@ -4349,9 +4356,11 @@ static const char *const shell_help_messages[] = {
"testUTF8(mode) Perform UTF-8 tests (modes are 1 to 4)",
"throwError() Throw an error from JS_ReportError",
#ifdef DEBUG
"dis([fun]) Disassemble functions into bytecodes\n"
"dis('-r', fun) Disassembles recursively",
"disfile('foo.js') Disassemble script file into bytecodes",
"dis([fun]) Disassemble functions into bytecodes",
"disfile('foo.js') Disassemble script file into bytecodes\n"
" dis and disfile take these options as preceeding string arguments\n"
" \"-r\" (disassemble recursively)\n"
" \"-l\" (show line numbers)",
"dissrc([fun]) Disassemble functions with source lines",
"dumpHeap([fileName[, start[, toFind[, maxDepth[, toIgnore]]]]])\n"
" Interface to JS_DumpHeap with output sent to file",

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

@ -47,9 +47,9 @@ skip script regress-330569.js # Yarr doesn't bail on complex regexps.
script regress-333541.js
skip script regress-335700.js # bug xxx - reftest hang, BigO
skip-if(!xulRuntime.shell) script regress-336409-1.js # no results reported.
skip-if(!xulRuntime.shell&&xulRuntime.XPCOMABI.match(/x86_64/)) script regress-336409-2.js # fails on 64 bit systems for some reason
skip-if(!xulRuntime.shell) script regress-336410-1.js # slow
skip-if(!xulRuntime.shell&&(xulRuntime.XPCOMABI.match(/x86_64/)||xulRuntime.OS=="WINNT")) script regress-336410-2.js # fails in browser on 64 bit systems or Windows.
skip-if(!xulRuntime.shell&&xulRuntime.XPCOMABI.match(/x86_64/)) silentfail script regress-336409-2.js # can fail silently due to out of memory
skip-if(!xulRuntime.shell) silentfail script regress-336410-1.js # can fail silently due to out of memory
skip-if(!xulRuntime.shell&&(xulRuntime.XPCOMABI.match(/x86_64/)||xulRuntime.OS=="WINNT")) silentfail script regress-336410-2.js # can fail silently due to out of memory
script regress-338804-01.js
script regress-338804-02.js
script regress-338804-03.js

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

@ -47,6 +47,7 @@
#include "jsgcchunk.h"
#include "nsIMemoryReporter.h"
#include "mozilla/FunctionTimer.h"
#include "prsystem.h"
/***************************************************************************/
@ -1313,7 +1314,9 @@ XPCJSRuntime::OnJSContextNew(JSContext *cx)
return JS_FALSE;
JS_SetNativeStackQuota(cx, 128 * sizeof(size_t) * 1024);
JS_SetScriptStackQuota(cx, 25 * sizeof(size_t) * 1024 * 1024);
PRInt64 totalMemory = PR_GetPhysicalMemorySize();
JS_SetScriptStackQuota(cx, PR_MAX(25 * sizeof(size_t) * 1024 * 1024,
totalMemory / 4));
// we want to mark the global object ourselves since we use a different color
JS_ToggleOptions(cx, JSOPTION_UNROOTED_GLOBAL);

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

@ -179,8 +179,8 @@ static FARPROC GetProcAddressA(HMODULE hMod, wchar_t *procName);
#ifdef DEBUG
#define XPC_DETECT_LEADING_UPPERCASE_ACCESS_ERRORS
#define XPC_CHECK_WRAPPER_THREADSAFETY
#endif
#define XPC_CHECK_WRAPPER_THREADSAFETY
#if defined(DEBUG_xpc_hacker)
#define XPC_DUMP_AT_SHUTDOWN

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

@ -1202,8 +1202,11 @@ XPCWrappedNative::FinishInit(XPCCallContext &ccx)
mThread = do_GetCurrentThread();
if(HasProto() && GetProto()->ClassIsMainThreadOnly() && !NS_IsMainThread())
{
DEBUG_ReportWrapperThreadSafetyError(ccx,
"MainThread only wrapper created on the wrong thread", this);
return JS_FALSE;
}
#endif
// A hack for bug 517665, increase the probability for GC.
@ -1511,7 +1514,8 @@ XPCWrappedNative::ReparentWrapperIfFound(XPCCallContext& ccx,
if (!XPCPerThreadData::IsMainThread(ccx) ||
(wrapper &&
wrapper->GetProto() &&
!wrapper->GetProto()->ClassIsMainThreadOnly())) {
!wrapper->GetProto()->ClassIsMainThreadOnly()))
{
return NS_ERROR_FAILURE;
}

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

@ -64,6 +64,16 @@ must be one of the following:
random-if(condition) The results of the test are random if a given
condition is met.
silentfail This test may fail silently, and if that happens it should
count as if the test passed. This is useful for cases where
silent failure is the intended behavior (for example, in
an out of memory situation in JavaScript, we stop running
the script silently and immediately, in hopes of reclaiming
enough memory to keep the browser functioning).
silentfail-if(condition) This test may fail silently if the condition
is met.
skip This test should not be run. This is useful when a test fails in a
catastrophic way, such as crashing or hanging the browser. Using
'skip' is preferred to simply commenting out the test because we

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

@ -534,14 +534,15 @@ function ReadManifest(aURL)
}
var expected_status = EXPECTED_PASS;
var allow_silent_fail = false;
var minAsserts = 0;
var maxAsserts = 0;
var slow = false;
while (items[0].match(/^(fails|random|skip|asserts|slow)/)) {
while (items[0].match(/^(fails|random|skip|asserts|slow|silentfail)/)) {
var item = items.shift();
var stat;
var cond;
var m = item.match(/^(fails|random|skip)-if(\(.*\))$/);
var m = item.match(/^(fails|random|skip|silentfail)-if(\(.*\))$/);
if (m) {
stat = m[1];
// Note: m[2] contains the parentheses, and we want them.
@ -569,6 +570,9 @@ function ReadManifest(aURL)
cond = false;
if (Components.utils.evalInSandbox("(" + m[1] + ")", sandbox))
slow = true;
} else if (item == "silentfail") {
cond = false;
allow_silent_fail = true;
} else {
throw "Error 1 in manifest file " + aURL.spec + " line " + lineNo;
}
@ -580,6 +584,8 @@ function ReadManifest(aURL)
expected_status = EXPECTED_RANDOM;
} else if (stat == "skip") {
expected_status = EXPECTED_DEATH;
} else if (stat == "silentfail") {
allow_silent_fail = true;
}
}
}
@ -637,6 +643,7 @@ function ReadManifest(aURL)
CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
gURLs.push( { type: TYPE_LOAD,
expected: expected_status,
allowSilentFail: allow_silent_fail,
prettyPath: prettyPath,
minAsserts: minAsserts,
maxAsserts: maxAsserts,
@ -657,6 +664,7 @@ function ReadManifest(aURL)
CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
gURLs.push( { type: TYPE_SCRIPT,
expected: expected_status,
allowSilentFail: allow_silent_fail,
prettyPath: prettyPath,
minAsserts: minAsserts,
maxAsserts: maxAsserts,
@ -680,6 +688,7 @@ function ReadManifest(aURL)
CI.nsIScriptSecurityManager.DISALLOW_SCRIPT);
gURLs.push( { type: items[0],
expected: expected_status,
allowSilentFail: allow_silent_fail,
prettyPath: prettyPath,
minAsserts: minAsserts,
maxAsserts: maxAsserts,
@ -1223,8 +1232,12 @@ function DocumentLoaded()
}
else if (testcases.length == 0) {
// This failure may be due to a JavaScript Engine bug causing
// early termination of the test.
missing_msg = "No test results reported. (SCRIPT)\n";
// early termination of the test. If we do not allow silent
// failure, report an error.
if (!gURLs[0].allowSilentFail)
missing_msg = "No test results reported. (SCRIPT)\n";
else
dump("REFTEST INFO | An expected silent failure occurred \n");
}
if (missing_msg) {