зеркало из https://github.com/mozilla/gecko-dev.git
Optimize lightweight function calls, and bound their recursion (27767, r=rogerl@netscape.com).
This commit is contained in:
Родитель
0376d3f7af
Коммит
17eca8e2c8
|
@ -107,7 +107,7 @@ js_FlushPropertyCacheByProp(JSContext *cx, JSProperty *prop)
|
|||
/*
|
||||
* Class for for/in loop property iterator objects.
|
||||
*/
|
||||
#define JSSLOT_ITER_STATE (JSSLOT_START)
|
||||
#define JSSLOT_ITER_STATE (JSSLOT_START)
|
||||
|
||||
static void
|
||||
prop_iterator_finalize(JSContext *cx, JSObject *obj)
|
||||
|
@ -411,6 +411,65 @@ js_SetLocalVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
|||
return JS_TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the 'this' parameter and store it in frame as frame.thisp.
|
||||
* Activation objects ("Call" objects not created with "new Call()", i.e.,
|
||||
* "Call" objects that have private data) may not be referred to by 'this',
|
||||
* as dictated by ECMA.
|
||||
*
|
||||
* N.B.: fp->argv must be set, and fp->argv[-2] must be the callee object
|
||||
* reference, usually a function object. Also, fp->constructing must be
|
||||
* set if we are preparing for a constructor call.
|
||||
*/
|
||||
static JSBool
|
||||
ComputeThis(JSContext *cx, JSObject *thisp, JSStackFrame *fp)
|
||||
{
|
||||
JSObject *parent;
|
||||
|
||||
if (thisp &&
|
||||
!(OBJ_GET_CLASS(cx, thisp) == &js_CallClass &&
|
||||
JS_GetPrivate(cx, thisp) != NULL))
|
||||
{
|
||||
/* Some objects (e.g., With) delegate 'this' to another object. */
|
||||
thisp = OBJ_THIS_OBJECT(cx, thisp);
|
||||
if (!thisp)
|
||||
return JS_FALSE;
|
||||
|
||||
/* Default return value for a constructor is the new object. */
|
||||
if (fp->constructing)
|
||||
fp->rval = OBJECT_TO_JSVAL(thisp);
|
||||
} else {
|
||||
/*
|
||||
* ECMA requires "the global object", but in the presence of multiple
|
||||
* top-level objects (windows, frames, or certain layers in the client
|
||||
* object model), we prefer fun's parent. An example that causes this
|
||||
* code to run:
|
||||
*
|
||||
* // in window w1
|
||||
* function f() { return this }
|
||||
* function g() { return f }
|
||||
*
|
||||
* // in window w2
|
||||
* var h = w1.g()
|
||||
* alert(h() == w1)
|
||||
*
|
||||
* The alert should display "true".
|
||||
*/
|
||||
JS_ASSERT(!fp->constructing);
|
||||
parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(fp->argv[-2]));
|
||||
if (!parent) {
|
||||
thisp = cx->globalObject;
|
||||
} else {
|
||||
/* walk up to find the top-level object */
|
||||
thisp = parent;
|
||||
while ((parent = OBJ_GET_PARENT(cx, thisp)) != NULL)
|
||||
thisp = parent;
|
||||
}
|
||||
}
|
||||
fp->thisp = thisp;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find a function reference and its 'this' object implicit first parameter
|
||||
* under argc arguments on cx's stack, and call the function. Push missing
|
||||
|
@ -477,16 +536,16 @@ js_Invoke(JSContext *cx, uintN argc, uintN flags)
|
|||
ok = clasp->convert(cx, funobj, JSTYPE_FUNCTION, &v);
|
||||
if (!ok)
|
||||
goto out2;
|
||||
}
|
||||
|
||||
if (JSVAL_IS_FUNCTION(cx, v)) {
|
||||
funobj = JSVAL_TO_OBJECT(v);
|
||||
parent = OBJ_GET_PARENT(cx, funobj);
|
||||
fun = (JSFunction *) JS_GetPrivate(cx, funobj);
|
||||
if (JSVAL_IS_FUNCTION(cx, v)) {
|
||||
funobj = JSVAL_TO_OBJECT(v);
|
||||
parent = OBJ_GET_PARENT(cx, funobj);
|
||||
fun = (JSFunction *) JS_GetPrivate(cx, funobj);
|
||||
|
||||
/* Make vp refer to funobj to keep it available as argv[-2]. */
|
||||
*vp = v;
|
||||
goto have_fun;
|
||||
/* Make vp refer to funobj to keep it available as argv[-2]. */
|
||||
*vp = v;
|
||||
goto have_fun;
|
||||
}
|
||||
}
|
||||
fun = NULL;
|
||||
script = NULL;
|
||||
|
@ -530,55 +589,10 @@ have_fun:
|
|||
frame.special = 0;
|
||||
frame.dormantNext = NULL;
|
||||
|
||||
/*
|
||||
* Compute the 'this' parameter and store it in frame as frame.thisp.
|
||||
* Activation objects ("Call" objects not created with "new Call", i.e.,
|
||||
* "Call" objects with private data) may not be referred to by 'this'
|
||||
* as dictated by ECMA.
|
||||
*/
|
||||
if (thisp &&
|
||||
!(OBJ_GET_CLASS(cx, thisp) == &js_CallClass &&
|
||||
JS_GetPrivate(cx, thisp) != NULL))
|
||||
{
|
||||
/* Some objects (e.g., With) delegate 'this' to another object. */
|
||||
thisp = OBJ_THIS_OBJECT(cx, thisp);
|
||||
if (!thisp) {
|
||||
ok = JS_FALSE;
|
||||
goto out2;
|
||||
}
|
||||
|
||||
/* Default return value for a constructor is the new object. */
|
||||
if (frame.constructing)
|
||||
frame.rval = OBJECT_TO_JSVAL(thisp);
|
||||
} else {
|
||||
/*
|
||||
* ECMA requires "the global object", but in the presence of multiple
|
||||
* top-level objects (windows, frames, or certain layers in the client
|
||||
* object model), we prefer fun's parent. An example that causes this
|
||||
* code to run:
|
||||
*
|
||||
* // in window w1
|
||||
* function f() { return this }
|
||||
* function g() { return f }
|
||||
*
|
||||
* // in window w2
|
||||
* var h = w1.g()
|
||||
* alert(h() == w1)
|
||||
*
|
||||
* The alert should display "true".
|
||||
*/
|
||||
JS_ASSERT(!frame.constructing);
|
||||
if (!parent) {
|
||||
thisp = cx->globalObject;
|
||||
} else {
|
||||
/* walk up to find the top-level object */
|
||||
JSObject *tmp;
|
||||
thisp = parent;
|
||||
while ((tmp = OBJ_GET_PARENT(cx, thisp)) != NULL)
|
||||
thisp = tmp;
|
||||
}
|
||||
}
|
||||
frame.thisp = thisp;
|
||||
/* Compute the 'this' parameter and store it in frame as frame.thisp. */
|
||||
ok = ComputeThis(cx, thisp, &frame);
|
||||
if (!ok)
|
||||
goto out2;
|
||||
|
||||
/* From here on, control must flow through label out: to return. */
|
||||
cx->fp = &frame;
|
||||
|
@ -813,16 +827,16 @@ js_Execute(JSContext *cx, JSObject *chain, JSScript *script, JSFunction *fun,
|
|||
/*
|
||||
* Here we wrap the call to js_Interpret with code to (conditionally)
|
||||
* save and restore the old stack frame chain into a chain of 'dormant'
|
||||
* frame chains. Since we are replacing cx->fp we were running into the
|
||||
* problem that if gc was called, then some of the objects associated
|
||||
* with the old frame chain (stored here in the C stack as 'oldfp') were
|
||||
* not rooted and were being collected. This was bad. So, now we
|
||||
* preserve the links to these 'dormant' frame chains in cx before
|
||||
* calling js_Interpret and cleanup afterwards. gc walks these dormant
|
||||
* chains and marks objects in the same way that it marks object in the
|
||||
* primary cx->fp chain.
|
||||
* frame chains. Since we are replacing cx->fp, we were running into
|
||||
* the problem that if GC was called under this frame, some of the GC
|
||||
* things associated with the old frame chain (available here only in
|
||||
* the C variable 'oldfp') were not rooted and were being collected.
|
||||
*
|
||||
* So, now we preserve the links to these 'dormant' frame chains in cx
|
||||
* before calling js_Interpret and cleanup afterwards. The GC walks
|
||||
* these dormant chains and marks objects in the same way that it marks
|
||||
* objects in the primary cx->fp chain.
|
||||
*/
|
||||
|
||||
if (oldfp && oldfp != down) {
|
||||
JS_ASSERT(!oldfp->dormantNext);
|
||||
oldfp->dormantNext = cx->dormantFrameChain;
|
||||
|
@ -999,17 +1013,20 @@ CheckRedeclaration(JSContext *cx, JSObject *obj, JSObject *obj2, jsid id,
|
|||
#define MAX_INTERP_LEVEL 30
|
||||
#endif
|
||||
|
||||
#define MAX_INLINE_CALL_COUNT 1000
|
||||
|
||||
JSBool
|
||||
js_Interpret(JSContext *cx, jsval *result)
|
||||
{
|
||||
JSRuntime *rt;
|
||||
JSStackFrame *fp;
|
||||
JSScript *script;
|
||||
JSVersion newvers, oldvers;
|
||||
uintN inlineCallCount;
|
||||
JSObject *obj, *obj2, *proto, *parent;
|
||||
JSVersion currentVersion, originalVersion;
|
||||
JSBranchCallback onbranch;
|
||||
JSBool ok, cond;
|
||||
ptrdiff_t depth, len;
|
||||
jsint depth, len;
|
||||
jsval *sp, *newsp;
|
||||
void *mark;
|
||||
jsbytecode *pc, *pc2, *endpc;
|
||||
|
@ -1036,8 +1053,7 @@ js_Interpret(JSContext *cx, jsval *result)
|
|||
FILE *tracefp;
|
||||
#endif
|
||||
#if JS_HAS_SWITCH_STATEMENT
|
||||
jsint low, high;
|
||||
uintN off, npairs;
|
||||
jsint low, high, off, npairs;
|
||||
JSBool match;
|
||||
#endif
|
||||
#if JS_HAS_GETTER_SETTER
|
||||
|
@ -1056,20 +1072,18 @@ js_Interpret(JSContext *cx, jsval *result)
|
|||
*result = JSVAL_VOID;
|
||||
rt = cx->runtime;
|
||||
|
||||
/*
|
||||
* Set registerized frame pointer and derived pointers.
|
||||
*/
|
||||
/* Set registerized frame pointer and derived script pointer. */
|
||||
fp = cx->fp;
|
||||
script = fp->script;
|
||||
obj = fp->scopeChain;
|
||||
|
||||
/*
|
||||
* Optimized Get and SetVersion for proper script language versioning.
|
||||
*/
|
||||
newvers = script->version;
|
||||
oldvers = cx->version;
|
||||
if (newvers != oldvers)
|
||||
JS_SetVersion(cx, newvers);
|
||||
/* Count of JS function calls that nest in this C js_Interpret frame. */
|
||||
inlineCallCount = 0;
|
||||
|
||||
/* Optimized Get and SetVersion for proper script language versioning. */
|
||||
currentVersion = script->version;
|
||||
originalVersion = cx->version;
|
||||
if (currentVersion != originalVersion)
|
||||
JS_SetVersion(cx, currentVersion);
|
||||
|
||||
/*
|
||||
* Prepare to call a user-supplied branch handler, and abort the script
|
||||
|
@ -1092,19 +1106,19 @@ js_Interpret(JSContext *cx, jsval *result)
|
|||
/*
|
||||
* Allocate operand and pc stack slots for the script's worst-case depth.
|
||||
*/
|
||||
depth = (ptrdiff_t)script->depth;
|
||||
depth = (jsint) script->depth;
|
||||
newsp = js_AllocStack(cx, (uintN)(2 * depth), &mark);
|
||||
if (!newsp) {
|
||||
ok = JS_FALSE;
|
||||
sp = NULL;
|
||||
goto out;
|
||||
}
|
||||
newsp += depth;
|
||||
fp->sp = sp = newsp;
|
||||
sp = newsp;
|
||||
SAVE_SP(fp);
|
||||
|
||||
while (pc < endpc) {
|
||||
fp->pc = pc;
|
||||
op = (JSOp)*pc;
|
||||
op = (JSOp) *pc;
|
||||
do_op:
|
||||
cs = &js_CodeSpec[op];
|
||||
len = cs->length;
|
||||
|
@ -1205,12 +1219,65 @@ js_Interpret(JSContext *cx, jsval *result)
|
|||
case JSOP_RETURN:
|
||||
CHECK_BRANCH(-1);
|
||||
fp->rval = POP();
|
||||
if (inlineCallCount)
|
||||
inline_return:
|
||||
{
|
||||
JSInlineFrame *ifp = (JSInlineFrame *) fp;
|
||||
void *hookData = ifp->hookData;
|
||||
|
||||
if (hookData)
|
||||
cx->runtime->callHook(cx, fp, JS_FALSE, &ok, hookData);
|
||||
#if JS_HAS_ARGS_OBJECT
|
||||
if (fp->argsobj)
|
||||
ok &= js_PutArgsObject(cx, fp);
|
||||
#endif
|
||||
|
||||
/* Store the return value in the caller's operand frame. */
|
||||
vp = fp->argv - 2;
|
||||
*vp = fp->rval;
|
||||
|
||||
/* Restore newsp for sanity-checking assertions about sp. */
|
||||
newsp = ifp->oldsp;
|
||||
|
||||
/* Restore cx->fp and release the inline frame's space. */
|
||||
cx->fp = fp = fp->down;
|
||||
JS_ARENA_RELEASE(&cx->stackPool, ifp->mark);
|
||||
|
||||
/* Restore sp to point just above the return value. */
|
||||
fp->sp = vp + 1;
|
||||
RESTORE_SP(fp);
|
||||
|
||||
/* Restore the calling script's interpreter registers. */
|
||||
script = fp->script;
|
||||
depth = (jsint) script->depth;
|
||||
pc = fp->pc;
|
||||
endpc = script->code + script->length;
|
||||
|
||||
/* Store the generating pc for the return value. */
|
||||
vp[-depth] = (jsval)pc;
|
||||
|
||||
/* Restore version and version control variable. */
|
||||
if (script->version != currentVersion) {
|
||||
currentVersion = script->version;
|
||||
JS_SetVersion(cx, currentVersion);
|
||||
}
|
||||
|
||||
/* Set remaining variables for 'goto advance_pc'. */
|
||||
op = (JSOp) *pc;
|
||||
cs = &js_CodeSpec[op];
|
||||
len = cs->length;
|
||||
|
||||
/* Resume execution in the calling frame. */
|
||||
inlineCallCount--;
|
||||
if (ok)
|
||||
goto advance_pc;
|
||||
}
|
||||
goto out;
|
||||
|
||||
#if JS_HAS_SWITCH_STATEMENT
|
||||
case JSOP_DEFAULT:
|
||||
(void) POP();
|
||||
/* fall through */
|
||||
/* FALL THROUGH */
|
||||
#endif
|
||||
case JSOP_GOTO:
|
||||
len = GET_JUMP_OFFSET(pc);
|
||||
|
@ -2288,7 +2355,105 @@ js_Interpret(JSContext *cx, jsval *result)
|
|||
case JSOP_CALL:
|
||||
case JSOP_EVAL:
|
||||
argc = GET_ARGC(pc);
|
||||
vp = sp - (argc + 2);
|
||||
lval = *vp;
|
||||
SAVE_SP(fp);
|
||||
|
||||
if (JSVAL_IS_FUNCTION(cx, lval) &&
|
||||
(obj = JSVAL_TO_OBJECT(lval),
|
||||
fun = (JSFunction *) JS_GetPrivate(cx, obj),
|
||||
fun->script &&
|
||||
fun->flags == 0 &&
|
||||
argc >= (uintN)(fun->nargs + fun->extra)))
|
||||
/* inline_call: */
|
||||
{
|
||||
uintN nframeslots, nvars;
|
||||
jsval *oldsp;
|
||||
void *mark;
|
||||
JSInlineFrame *newifp;
|
||||
JSInterpreterHook hook;
|
||||
|
||||
/* Restrict recursion of lightweight functions. */
|
||||
if (inlineCallCount == MAX_INLINE_CALL_COUNT) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_OVER_RECURSED);
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
/* Compute the number of stack slots needed for fun. */
|
||||
nframeslots = (sizeof(JSInlineFrame) + sizeof(jsval) - 1)
|
||||
/ sizeof(jsval);
|
||||
nvars = fun->nvars;
|
||||
script = fun->script;
|
||||
depth = (jsint) script->depth;
|
||||
|
||||
/* Allocate the frame and space for vars and operands. */
|
||||
oldsp = newsp;
|
||||
newsp = js_AllocStack(cx, nframeslots + nvars + 2 * depth,
|
||||
&mark);
|
||||
if (!newsp) {
|
||||
ok = JS_FALSE;
|
||||
goto bad_inline_call;
|
||||
}
|
||||
newifp = (JSInlineFrame *) newsp;
|
||||
newsp += nframeslots;
|
||||
|
||||
/* Initialize the stack frame. */
|
||||
memset(newifp, 0, sizeof(JSInlineFrame));
|
||||
newifp->frame.script = script;
|
||||
newifp->frame.fun = fun;
|
||||
newifp->frame.argc = argc;
|
||||
newifp->frame.argv = vp + 2;
|
||||
newifp->frame.rval = JSVAL_VOID;
|
||||
newifp->frame.nvars = nvars;
|
||||
newifp->frame.vars = newsp;
|
||||
newifp->frame.down = fp;
|
||||
newifp->frame.scopeChain = OBJ_GET_PARENT(cx, obj);
|
||||
newifp->oldsp = oldsp;
|
||||
newifp->mark = mark;
|
||||
|
||||
/* Compute the 'this' parameter now that argv is set. */
|
||||
ok = ComputeThis(cx, JSVAL_TO_OBJECT(vp[1]), &newifp->frame);
|
||||
if (!ok) {
|
||||
js_FreeStack(cx, mark);
|
||||
goto bad_inline_call;
|
||||
}
|
||||
|
||||
/* Push void to initialize local variables. */
|
||||
sp = newsp;
|
||||
while (nvars--)
|
||||
PUSH(JSVAL_VOID);
|
||||
sp += depth;
|
||||
newsp = sp;
|
||||
SAVE_SP(&newifp->frame);
|
||||
|
||||
/* Call the debugger hook if present. */
|
||||
hook = cx->runtime->callHook;
|
||||
if (hook) {
|
||||
newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0,
|
||||
cx->runtime->callHookData);
|
||||
}
|
||||
|
||||
/* Switch to new version if necessary. */
|
||||
if (script->version != currentVersion) {
|
||||
currentVersion = script->version;
|
||||
JS_SetVersion(cx, currentVersion);
|
||||
}
|
||||
|
||||
/* Push the frame and set interpreter registers. */
|
||||
cx->fp = fp = &newifp->frame;
|
||||
pc = script->code;
|
||||
endpc = pc + script->length;
|
||||
inlineCallCount++;
|
||||
continue;
|
||||
|
||||
bad_inline_call:
|
||||
script = fp->script;
|
||||
depth = (jsint) script->depth;
|
||||
newsp = oldsp;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ok = js_Invoke(cx, argc, 0);
|
||||
RESTORE_SP(fp);
|
||||
if (!ok)
|
||||
|
@ -2413,15 +2578,15 @@ js_Interpret(JSContext *cx, jsval *result)
|
|||
goto out;
|
||||
}
|
||||
|
||||
pc2 += 2;
|
||||
pc2 += JUMP_OFFSET_LEN;
|
||||
low = GET_JUMP_OFFSET(pc2);
|
||||
pc2 += 2;
|
||||
pc2 += JUMP_OFFSET_LEN;
|
||||
high = GET_JUMP_OFFSET(pc2);
|
||||
|
||||
i -= low;
|
||||
if ((jsuint)i <= (jsuint)(high - low)) {
|
||||
pc2 += 2 + 2 * i;
|
||||
off = GET_JUMP_OFFSET(pc2);
|
||||
pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i;
|
||||
off = (jsint) GET_JUMP_OFFSET(pc2);
|
||||
if (off)
|
||||
len = off;
|
||||
}
|
||||
|
@ -2438,9 +2603,9 @@ js_Interpret(JSContext *cx, jsval *result)
|
|||
goto advance_pc;
|
||||
}
|
||||
|
||||
pc2 += 2;
|
||||
npairs = GET_ATOM_INDEX(pc2);
|
||||
pc2 += 2;
|
||||
pc2 += JUMP_OFFSET_LEN;
|
||||
npairs = (jsint) GET_ATOM_INDEX(pc2);
|
||||
pc2 += ATOM_INDEX_LEN;
|
||||
|
||||
#define SEARCH_PAIRS(MATCH_CODE) \
|
||||
while (npairs) { \
|
||||
|
@ -2448,11 +2613,11 @@ js_Interpret(JSContext *cx, jsval *result)
|
|||
rval = ATOM_KEY(atom); \
|
||||
MATCH_CODE \
|
||||
if (match) { \
|
||||
pc2 += 2; \
|
||||
pc2 += ATOM_INDEX_LEN; \
|
||||
len = GET_JUMP_OFFSET(pc2); \
|
||||
goto advance_pc; \
|
||||
} \
|
||||
pc2 += 4; \
|
||||
pc2 += ATOM_INDEX_LEN + JUMP_OFFSET_LEN; \
|
||||
npairs--; \
|
||||
}
|
||||
if (JSVAL_IS_STRING(lval)) {
|
||||
|
@ -3041,7 +3206,7 @@ js_Interpret(JSContext *cx, jsval *result)
|
|||
#endif /* JS_HAS_INITIALIZERS */
|
||||
|
||||
#if JS_HAS_EXCEPTIONS
|
||||
/* reset the stack to the given depth */
|
||||
/* Reset the stack to the given depth. */
|
||||
case JSOP_SETSP:
|
||||
i = (jsint) GET_ATOM_INDEX(pc);
|
||||
JS_ASSERT(i >= 0);
|
||||
|
@ -3049,9 +3214,8 @@ js_Interpret(JSContext *cx, jsval *result)
|
|||
break;
|
||||
|
||||
case JSOP_GOSUB:
|
||||
i = PTRDIFF(pc, script->main, jsbytecode) + len;
|
||||
len = GET_JUMP_OFFSET(pc);
|
||||
JS_ASSERT(js_CodeSpec[JSOP_GOSUB].length == 3);
|
||||
i = PTRDIFF(pc, script->main, jsbytecode) + 3;
|
||||
PUSH(INT_TO_JSVAL(i));
|
||||
break;
|
||||
|
||||
|
@ -3216,12 +3380,20 @@ out:
|
|||
no_catch:
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Check whether control fell off the end of a lightweight function, or an
|
||||
* exception thrown under such a function was not caught by it. If so, go
|
||||
* to the inline code under JSOP_RETURN.
|
||||
*/
|
||||
if (inlineCallCount)
|
||||
goto inline_return;
|
||||
|
||||
/*
|
||||
* Restore the previous frame's execution state.
|
||||
*/
|
||||
js_FreeStack(cx, mark);
|
||||
if (newvers != oldvers)
|
||||
JS_SetVersion(cx, oldvers);
|
||||
if (currentVersion != originalVersion)
|
||||
JS_SetVersion(cx, originalVersion);
|
||||
cx->interpLevel--;
|
||||
return ok;
|
||||
}
|
||||
|
|
|
@ -71,6 +71,13 @@ struct JSStackFrame {
|
|||
JSStackFrame *dormantNext; /* next dormant frame chain */
|
||||
};
|
||||
|
||||
typedef struct JSInlineFrame {
|
||||
JSStackFrame frame; /* base struct */
|
||||
jsval *oldsp; /* old frame's operand stack base */
|
||||
void *mark; /* mark before inline frame */
|
||||
void *hookData; /* debugger call hook data */
|
||||
} JSInlineFrame;
|
||||
|
||||
/* JS stack frame special flags. */
|
||||
#define JSFRAME_DEBUGGER 0x1 /* frame for JS_EvaluateInStackFrame */
|
||||
#define JSFRAME_EVAL 0x2 /* frame for obj_eval */
|
||||
|
|
Загрузка…
Ссылка в новой задаче