зеркало из https://github.com/mozilla/pjs.git
Merge tracemonkey to mozilla-central. a=blockers.
This commit is contained in:
Коммит
6b1320e19a
|
@ -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 ⁣
|
||||
|
||||
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 ⁣
|
||||
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) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче