jsemit.c
来自「一个基于alice开发的机器人」· C语言 代码 · 共 1,845 行 · 第 1/5 页
C
1,845 行
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
* JS bytecode generation.
*/
#include "jsstddef.h"
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif
#include <string.h>
#include "jstypes.h"
#include "jsarena.h" /* Added by JSIFY */
#include "jsutil.h" /* Added by JSIFY */
#include "jsbit.h"
#include "jsprf.h"
#include "jsapi.h"
#include "jsatom.h"
#include "jscntxt.h"
#include "jsconfig.h"
#include "jsemit.h"
#include "jsfun.h"
#include "jsnum.h"
#include "jsopcode.h"
#include "jsparse.h"
#include "jsscan.h"
#include "jsscope.h"
#include "jsscript.h"
/* Allocation chunk counts, must be powers of two in general. */
#define BYTECODE_CHUNK 256 /* code allocation increment */
#define SRCNOTE_CHUNK 64 /* initial srcnote allocation increment */
#define TRYNOTE_CHUNK 64 /* trynote allocation increment */
/* Macros to compute byte sizes from typed element counts. */
#define BYTECODE_SIZE(n) ((n) * sizeof(jsbytecode))
#define SRCNOTE_SIZE(n) ((n) * sizeof(jssrcnote))
#define TRYNOTE_SIZE(n) ((n) * sizeof(JSTryNote))
JS_FRIEND_API(JSBool)
js_InitCodeGenerator(JSContext *cx, JSCodeGenerator *cg,
JSArenaPool *codePool, JSArenaPool *notePool,
const char *filename, uintN lineno,
JSPrincipals *principals)
{
memset(cg, 0, sizeof *cg);
TREE_CONTEXT_INIT(&cg->treeContext);
cg->treeContext.flags |= TCF_COMPILING;
cg->codePool = codePool;
cg->notePool = notePool;
cg->codeMark = JS_ARENA_MARK(codePool);
cg->noteMark = JS_ARENA_MARK(notePool);
cg->tempMark = JS_ARENA_MARK(&cx->tempPool);
cg->current = &cg->main;
cg->filename = filename;
cg->firstLine = cg->prolog.currentLine = cg->main.currentLine = lineno;
cg->principals = principals;
ATOM_LIST_INIT(&cg->atomList);
cg->prolog.noteMask = cg->main.noteMask = SRCNOTE_CHUNK - 1;
return JS_TRUE;
}
JS_FRIEND_API(void)
js_FinishCodeGenerator(JSContext *cx, JSCodeGenerator *cg)
{
TREE_CONTEXT_FINISH(&cg->treeContext);
JS_ARENA_RELEASE(cg->codePool, cg->codeMark);
JS_ARENA_RELEASE(cg->notePool, cg->noteMark);
JS_ARENA_RELEASE(&cx->tempPool, cg->tempMark);
}
static ptrdiff_t
EmitCheck(JSContext *cx, JSCodeGenerator *cg, JSOp op, ptrdiff_t delta)
{
jsbytecode *base, *limit, *next;
ptrdiff_t offset, length;
size_t incr, size;
base = CG_BASE(cg);
next = CG_NEXT(cg);
limit = CG_LIMIT(cg);
offset = PTRDIFF(next, base, jsbytecode);
if (next + delta > limit) {
length = offset + delta;
length = (length <= BYTECODE_CHUNK)
? BYTECODE_CHUNK
: JS_BIT(JS_CeilingLog2(length));
incr = BYTECODE_SIZE(length);
if (!base) {
JS_ARENA_ALLOCATE_CAST(base, jsbytecode *, cg->codePool, incr);
} else {
size = BYTECODE_SIZE(PTRDIFF(limit, base, jsbytecode));
incr -= size;
JS_ARENA_GROW_CAST(base, jsbytecode *, cg->codePool, size, incr);
}
if (!base) {
JS_ReportOutOfMemory(cx);
return -1;
}
CG_BASE(cg) = base;
CG_LIMIT(cg) = base + length;
CG_NEXT(cg) = base + offset;
}
return offset;
}
static void
UpdateDepth(JSContext *cx, JSCodeGenerator *cg, ptrdiff_t target)
{
jsbytecode *pc;
const JSCodeSpec *cs;
intN nuses;
pc = CG_CODE(cg, target);
cs = &js_CodeSpec[pc[0]];
nuses = cs->nuses;
if (nuses < 0)
nuses = 2 + GET_ARGC(pc); /* stack: fun, this, [argc arguments] */
cg->stackDepth -= nuses;
if (cg->stackDepth < 0) {
char numBuf[12];
JS_snprintf(numBuf, sizeof numBuf, "%d", target);
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_STACK_UNDERFLOW,
cg->filename ? cg->filename : "stdin", numBuf);
}
cg->stackDepth += cs->ndefs;
if ((uintN)cg->stackDepth > cg->maxStackDepth)
cg->maxStackDepth = cg->stackDepth;
}
ptrdiff_t
js_Emit1(JSContext *cx, JSCodeGenerator *cg, JSOp op)
{
ptrdiff_t offset = EmitCheck(cx, cg, op, 1);
if (offset >= 0) {
*CG_NEXT(cg)++ = (jsbytecode)op;
UpdateDepth(cx, cg, offset);
}
return offset;
}
ptrdiff_t
js_Emit2(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1)
{
ptrdiff_t offset = EmitCheck(cx, cg, op, 2);
if (offset >= 0) {
jsbytecode *next = CG_NEXT(cg);
next[0] = (jsbytecode)op;
next[1] = op1;
CG_NEXT(cg) = next + 2;
UpdateDepth(cx, cg, offset);
}
return offset;
}
ptrdiff_t
js_Emit3(JSContext *cx, JSCodeGenerator *cg, JSOp op, jsbytecode op1,
jsbytecode op2)
{
ptrdiff_t offset = EmitCheck(cx, cg, op, 3);
if (offset >= 0) {
jsbytecode *next = CG_NEXT(cg);
next[0] = (jsbytecode)op;
next[1] = op1;
next[2] = op2;
CG_NEXT(cg) = next + 3;
UpdateDepth(cx, cg, offset);
}
return offset;
}
ptrdiff_t
js_EmitN(JSContext *cx, JSCodeGenerator *cg, JSOp op, size_t extra)
{
ptrdiff_t length = 1 + (ptrdiff_t)extra;
ptrdiff_t offset = EmitCheck(cx, cg, op, length);
if (offset >= 0) {
jsbytecode *next = CG_NEXT(cg);
*next = (jsbytecode)op;
memset(next + 1, 0, BYTECODE_SIZE(extra));
CG_NEXT(cg) = next + length;
UpdateDepth(cx, cg, offset);
}
return offset;
}
/* XXX too many "... statement" L10N gaffes below -- fix via js.msg! */
const char js_with_statement_str[] = "with statement";
static const char *statementName[] = {
"block", /* BLOCK */
"label statement", /* LABEL */
"if statement", /* IF */
"else statement", /* ELSE */
"switch statement", /* SWITCH */
js_with_statement_str, /* WITH */
"try statement", /* TRY */
"catch block", /* CATCH */
"finally statement", /* FINALLY */
"do loop", /* DO_LOOP */
"for loop", /* FOR_LOOP */
"for/in loop", /* FOR_IN_LOOP */
"while loop", /* WHILE_LOOP */
};
static const char *
StatementName(JSCodeGenerator *cg)
{
if (!cg->treeContext.topStmt)
return "script";
return statementName[cg->treeContext.topStmt->type];
}
static void
ReportStatementTooLarge(JSContext *cx, JSCodeGenerator *cg)
{
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEED_DIET,
StatementName(cg));
}
/**
Span-dependent instructions in JS bytecode consist of the jump (JOF_JUMP)
and switch (JOF_LOOKUPSWITCH, JOF_TABLESWITCH) format opcodes, subdivided
into unconditional (gotos and gosubs), and conditional jumps or branches
(which pop a value, test it, and jump depending on its value). Most jumps
have just one immediate operand, a signed offset from the jump opcode's pc
to the target bytecode. The lookup and table switch opcodes may contain
many jump offsets.
Mozilla bug #80981 (http://bugzilla.mozilla.org/show_bug.cgi?id=80981) was
fixed by adding extended "X" counterparts to the opcodes/formats (NB: X is
suffixed to prefer JSOP_ORX thereby avoiding a JSOP_XOR name collision for
the extended form of the JSOP_OR branch opcode). The unextended or short
formats have 16-bit signed immediate offset operands, the extended or long
formats have 32-bit signed immediates. The span-dependency problem consists
of selecting as few long instructions as possible, or about as few -- since
jumps can span other jumps, extending one jump may cause another to need to
be extended.
Most JS scripts are short, so need no extended jumps. We optimize for this
case by generating short jumps until we know a long jump is needed. After
that point, we keep generating short jumps, but each jump's 16-bit immediate
offset operand is actually an unsigned index into cg->spanDeps, an array of
JSSpanDep structs. Each struct tells the top offset in the script of the
opcode, the "before" offset of the jump (which will be the same as top for
simplex jumps, but which will index further into the bytecode array for a
non-initial jump offset in a lookup or table switch), the after "offset"
adjusted during span-dependent instruction selection (initially the same
value as the "before" offset), and the jump target (more below).
Since we generate cg->spanDeps lazily, from within js_SetJumpOffset, we must
ensure that all bytecode generated so far can be inspected to discover where
the jump offset immediate operands lie within CG_CODE(cg). But the bonus is
that we generate span-dependency records sorted by their offsets, so we can
binary-search when trying to find a JSSpanDep for a given bytecode offset,
or the nearest JSSpanDep at or above a given pc.
To avoid limiting scripts to 64K jumps, if the cg->spanDeps index overflows
65534, we store SPANDEP_INDEX_HUGE in the jump's immediate operand. This
tells us that we need to binary-search for the cg->spanDeps entry by the
jump opcode's bytecode offset (sd->before).
Jump targets need to be maintained in a data structure that lets us look
up an already-known target by its address (jumps may have a common target),
and that also lets us update the addresses (script-relative, a.k.a. absolute
offsets) of targets that come after a jump target (for when a jump below
that target needs to be extended). We use an AVL tree, implemented using
recursion, but with some tricky optimizations to its height-balancing code
(see http://www.enteract.com/~bradapp/ftp/src/libs/C++/AvlTrees.html).
A final wrinkle: backpatch chains are linked by jump-to-jump offsets with
positive sign, even though they link "backward" (i.e., toward lower bytecode
address). We don't want to waste space and search time in the AVL tree for
such temporary backpatch deltas, so we use a single-bit wildcard scheme to
tag true JSJumpTarget pointers and encode untagged, signed (positive) deltas
in JSSpanDep.target pointers, depending on whether the JSSpanDep has a known
target, or is still awaiting backpatching.
Note that backpatch chains would present a problem for BuildSpanDepTable,
which inspects bytecode to build cg->spanDeps on demand, when the first
short jump offset overflows. To solve this temporary problem, we emit a
proxy bytecode (JSOP_BACKPATCH; JSOP_BACKPATCH_PUSH for jumps that push a
result on the interpreter's stack, namely JSOP_GOSUB; or JSOP_BACKPATCH_POP
for branch ops) whose nuses/ndefs counts help keep the stack balanced, but
whose opcode format distinguishes its backpatch delta immediate operand from
a normal jump offset.
*/
static int
BalanceJumpTargets(JSJumpTarget **jtp)
{
JSJumpTarget *jt, *jt2, *root;
int dir, otherDir, heightChanged;
JSBool doubleRotate;
jt = *jtp;
JS_ASSERT(jt->balance != 0);
if (jt->balance < -1) {
dir = JT_RIGHT;
doubleRotate = (jt->kids[JT_LEFT]->balance > 0);
} else if (jt->balance > 1) {
dir = JT_LEFT;
doubleRotate = (jt->kids[JT_RIGHT]->balance < 0);
} else {
return 0;
}
otherDir = JT_OTHER_DIR(dir);
if (doubleRotate) {
jt2 = jt->kids[otherDir];
*jtp = root = jt2->kids[dir];
jt->kids[otherDir] = root->kids[dir];
root->kids[dir] = jt;
jt2->kids[dir] = root->kids[otherDir];
root->kids[otherDir] = jt2;
heightChanged = 1;
root->kids[JT_LEFT]->balance = -JS_MAX(root->balance, 0);
root->kids[JT_RIGHT]->balance = -JS_MIN(root->balance, 0);
root->balance = 0;
} else {
*jtp = root = jt->kids[otherDir];
jt->kids[otherDir] = root->kids[dir];
root->kids[dir] = jt;
heightChanged = (root->balance != 0);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?