📄 gram.js
字号:
var nesting = JSC$cont_break.count_with_nesting (this.label);
if (nesting > 0)
new JSC$ASM_with_pop (this.linenum, nesting).link ();
nesting = JSC$cont_break.count_try_nesting (this.label);
if (nesting > 0)
new JSC$ASM_try_pop (this.linenum, nesting).link ();
/*
* For non-labeled breaks, the switch nesting is handled in the
* stmt_switch(). The code after the label, returned by the
* get_break(), will handle the switch nesting in these cases.
* For the labeled breaks, we must pop the switch nesting here.
*/
if (this.label)
{
nesting = JSC$cont_break.count_switch_nesting (this.label);
if (nesting > 0)
{
if (nesting == 1)
new JSC$ASM_pop (this.linenum).link ();
else
new JSC$ASM_pop_n (this.linenum, nesting).link ();
}
}
new JSC$ASM_jmp (this.linenum, l_break).link ();
}
/* Return. */
function JSC$stmt_return (ln, expr)
{
this.stype = JSC$STMT_RETURN;
this.linenum = ln;
this.expr = expr;
this.asm = JSC$stmt_return_asm;
this.count_locals = JSC$zero_function;
}
function JSC$stmt_return_asm ()
{
var nesting = JSC$cont_break.try_return_nesting ();
if (nesting > 0)
new JSC$ASM_try_pop (this.linenum, nesting).link ();
if (this.expr != null)
this.expr.asm ();
else
new JSC$ASM_const_undefined (this.linenum).link ();
new JSC$ASM_return (this.linenum).link ();
}
/* Switch. */
function JSC$stmt_switch (ln, last_ln, expr, clauses)
{
this.stype = JSC$STMT_SWITCH;
this.linenum = ln;
this.last_linenum = last_ln;
this.expr = expr;
this.clauses = clauses;
this.asm = JSC$stmt_switch_asm;
this.count_locals = JSC$stmt_switch_count_locals;
}
function JSC$stmt_switch_asm ()
{
/* Evaluate the switch expression to the top of the stack. */
this.expr.asm ();
/* The switch statement define a break label. */
var l_break = new JSC$ASM_label ();
JSC$cont_break.push (l_break, null, true, null);
/* For each clause (except the first), insert check and body labels. */
var i;
for (i = 1; i < this.clauses.length; i++)
{
this.clauses[i].l_check = new JSC$ASM_label ();
this.clauses[i].l_body = new JSC$ASM_label ();
}
/* Generate code for each clause. */
for (i = 0; i < this.clauses.length; i++)
{
/* Is this the last clause? */
var last = i + 1 >= this.clauses.length;
var c = this.clauses[i];
var next_check, next_body;
if (last)
next_check = next_body = l_break;
else
{
next_check = this.clauses[i + 1].l_check;
next_body = this.clauses[i + 1].l_body;
}
if (c.expr)
{
/*
* Must check if this clause matches the expression. If c.expr
* is null, this is the default clause that matches always.
*/
if (i > 0)
c.l_check.link ();
new JSC$ASM_dup (c.linenum).link ();
c.expr.asm ();
new JSC$ASM_cmp_eq (c.linenum).link ();
new JSC$ASM_iffalse_b (c.linenum, next_check).link ();
}
else
{
if (i > 0)
/* The check label for the default case. */
c.l_check.link ();
}
/* Generate assembler for the body. */
if (i > 0)
c.l_body.link ();
var j;
for (j = 0; j < c.length; j++)
c[j].asm ();
/* And finally, jump to the next body. (this is the fallthrough case). */
new JSC$ASM_jmp (c.last_linenum, next_body).link ();
}
JSC$cont_break.pop ();
/* The break label. */
l_break.link ();
/* Pop the value of the switch expression. */
new JSC$ASM_pop (this.last_linenum).link ();
}
function JSC$stmt_switch_count_locals (recursive)
{
var locals = 0;
var i, j;
if (recursive)
{
/* For the recursive cases, we need the maximum of our clause stmts. */
for (i = 0; i < this.clauses.length; i++)
{
var c = this.clauses[i];
for (j = 0; j < c.length; j++)
{
var l = c[j].count_locals (true);
if (l > locals)
locals = l;
}
}
}
else
{
/*
* The case clauses are not blocks. Therefore, we need the amount,
* needed by the clauses at the top-level.
*/
for (i = 0; i < this.clauses.length; i++)
{
var c = this.clauses[i];
for (j = 0; j < c.length; j++)
locals += c[j].count_locals (false);
}
}
return locals;
}
/* With. */
function JSC$stmt_with (ln, expr, stmt)
{
this.stype = JSC$STMT_WITH;
this.linenum = ln;
this.expr = expr;
this.stmt = stmt;
this.asm = JSC$stmt_with_asm;
this.count_locals = JSC$stmt_with_count_locals;
}
function JSC$stmt_with_asm ()
{
this.expr.asm ();
new JSC$ASM_with_push (this.linenum).link ();
JSC$cont_break.top.with_nesting++;
this.stmt.asm ();
JSC$cont_break.top.with_nesting--;
new JSC$ASM_with_pop (this.linenum, 1).link ();
}
function JSC$stmt_with_count_locals (recursive)
{
if (!recursive)
{
if (this.stmt.stype == JSC$STMT_VARIABLE)
return this.stmt.list.length;
return 0;
}
else
return this.stmt.count_locals (true);
}
/* Try. */
function JSC$stmt_try (ln, try_block_last_ln, try_last_ln, block, catch_list,
fin)
{
this.stype = JSC$STMT_TRY;
this.linenum = ln;
this.try_block_last_linenum = try_block_last_ln;
this.try_last_linenum = try_last_ln;
this.block = block;
this.catch_list = catch_list;
this.fin = fin;
this.asm = JSC$stmt_try_asm;
this.count_locals = JSC$stmt_try_count_locals;
}
function JSC$stmt_try_asm ()
{
var l_finally = new JSC$ASM_label ();
/* Protect and execute the try-block. */
var l_try_error = new JSC$ASM_label ();
new JSC$ASM_try_push (this.linenum, l_try_error).link ();
JSC$cont_break.top.try_nesting++;
this.block.asm ();
JSC$cont_break.top.try_nesting--;
new JSC$ASM_try_pop (this.try_block_last_linenum, 1).link ();
/*
* All ok so far. Push a `false' to indicate no error and jump to
* the finally block (or out if we have no finally block).
*/
new JSC$ASM_const_false (this.try_block_last_linenum).link ();
new JSC$ASM_jmp (this.try_block_last_linenum, l_finally).link ();
/*
* Handle try block failures. The thrown value is on the top of the
* stack.
*/
l_try_error.link ();
if (this.catch_list)
{
/*
* We keep one boolean variable below the thrown value. Its default
* value is false. When one of our catch blocks are entered, it is
* set to true to indicate that we shouldn't throw the error
* anymore.
*/
new JSC$ASM_const_false (this.catch_list.linenum).link ();
new JSC$ASM_swap (this.catch_list.linenum).link ();
/* Protect and execute the catch list. */
var l_catch_list_error = new JSC$ASM_label ();
new JSC$ASM_try_push (this.catch_list.linenum,
l_catch_list_error).link ();
JSC$cont_break.top.try_nesting++;
/* Insert start and body labels for each catch list item. */
var i;
for (i = 0; i < this.catch_list.length; i++)
{
this.catch_list[i].l_start = new JSC$ASM_label ();
this.catch_list[i].l_body = new JSC$ASM_label ();
}
/* A label for the catch list end. */
var l_catch_list_end = new JSC$ASM_label ();
/* Process the individual catches. */
for (i = 0; i < this.catch_list.length; i++)
{
var c = this.catch_list[i];
/* This is the starting point of this catch frame. */
c.l_start.link ();
/*
* Create a new namespace frame and bind the catch's
* identifier to the thrown exception.
*/
JSC$ns.push_frame ();
JSC$ns.define_symbol (c.id, JSC$SCOPE_LOCAL, JSC$ns.alloc_local (),
c.linenum);
new JSC$ASM_dup (c.linenum).link ();
new JSC$ASM_store_local (c.linenum,
JSC$ns.lookup_symbol (c.id).value).link ();
/* Check the possible guard. We must protect its calculation. */
if (c.guard)
{
var l_guard_error = new JSC$ASM_label ();
new JSC$ASM_try_push (c.linenum, l_guard_error).link ();
JSC$cont_break.top.try_nesting++;
/* Calculate the guard. */
c.guard.asm ();
JSC$cont_break.top.try_nesting--;
new JSC$ASM_try_pop (c.linenum, 1).link ();
/*
* Wow! We managed to do it. Now, let's check if we
* accept this catch case.
*/
var next;
if (i + 1 >= this.catch_list.length)
next = l_catch_list_end;
else
next = this.catch_list[i + 1].l_start;
if (c.guard.lang_type == JSC$JS_BOOLEAN)
new JSC$ASM_iffalse_b (c.linenum, next).link ();
else
new JSC$ASM_iffalse (c.linenum, next).link ();
/* Yes, we do accept it. Just jump to do the stuffs. */
new JSC$ASM_jmp (c.linenum, c.l_body).link ();
/*
* The evaluation of the guard failed. Do the cleanup
* and jump to the next case.
*/
l_guard_error.link ();
/* Pop the exception. */
new JSC$ASM_pop (c.linenum).link ();
/* Check the next case. */
new JSC$ASM_jmp (c.linenum, next).link ();
}
/*
* We did enter the catch body. Let's update our boolean
* status variable to reflect this fact.
*/
c.l_body.link ();
new JSC$ASM_swap (c.linenum).link ();
new JSC$ASM_pop (c.linenum).link ();
new JSC$ASM_const_true (c.linenum).link ();
new JSC$ASM_swap (c.linenum).link ();
/* Code for the catch body. */
c.stmt.asm ();
/* We'r done with the namespace frame. */
JSC$ns.pop_frame ();
/*
* The next catch tag, or the l_catch_list_end follows us,
* so we don't need a jumps here.
*/
}
/*
* The catch list was evaluated without errors.
*/
l_catch_list_end.link ();
JSC$cont_break.top.try_nesting--;
new JSC$ASM_try_pop (this.catch_list.last_linenum, 1).link ();
/* Did we enter any of our catch lists? */
var l_we_did_enter = new JSC$ASM_label ();
new JSC$ASM_swap (this.catch_list.last_linenum).link ();
new JSC$ASM_iftrue_b (this.catch_list.last_linenum,
l_we_did_enter).link ();
/* No we didn't. */
/*
* Push `true' to indicate an exception and jump to the finally
* block. The exception is now on the top of the stack.
*/
new JSC$ASM_const_true (this.catch_list.last_linenum).link ();
new JSC$ASM_jmp (this.catch_list.last_linenum, l_finally).link ();
/* Yes, we did enter one (or many) of our catch lists. */
l_we_did_enter.link ();
/* Pop the try-block's exception */
new JSC$ASM_pop (this.catch_list.last_linenum).link ();
/*
* Push a `false' to indicate "no errors" and jump to the
* finally block.
*/
new JSC$ASM_const_false (this.catch_list.last_linenum).link ();
new JSC$ASM_jmp (this.catch_list.last_linenum, l_finally).link ();
/*
* Handle catch list failures. The thrown value is on the top of the
* stack.
*/
l_catch_list_error.link ();
/*
* Pop the try-block's exception and our boolean `did we enter a
* catch block' variable. They are below our new exception.
*/
new JSC$ASM_apop (this.catch_list.last_linenum, 2).link ();
/*
* Push `true' to indicate an exception. We will fallthrough to
* the finally part, so no jump is needed here.
*/
new JSC$ASM_const_true (this.catch_list.last_linenum).link ();
}
else
{
/* No catch list. */
new JSC$ASM_const_true (this.try_block_last_linenum).link ();
}
/* The possible finally block. */
l_finally.link ();
if (this.fin)
/* Execute it without protection. */
this.fin.asm ();
/* We'r almost there. Let's see if we have to raise a new exception. */
var l_out = new JSC$ASM_label ();
new JSC$ASM_iffalse_b (this.try_last_linenum, l_out).link ();
/* Do raise it. */
new JSC$ASM_throw (this.try_last_linenum).link ();
/* The possible exception is handled. Please, continue. */
l_out.link ();
}
function JSC$stmt_try_count_locals (recursive)
{
var count = 0;
var c;
if (recursive)
{
c = this.block.count_locals (true);
if (c > count)
count = c;
if (this.catch_list)
{
var i;
for (i = 0; i < this.catch_list.length; i++)
{
c = this.catch_list[i].stmt.count_locals (true);
if (c > count)
count = c;
}
}
if (this.fin)
{
c = this.fin.count_locals (true);
if (c > count)
count = c;
}
}
else
{
if (this.block.stype == JSC$STMT_VARIABLE)
count += this.block.list.length;
if (this.catch_list)
{
/* One for the call variable. */
count++;
var i;
for (i = 0; i < this.catch_list.length; i++)
if (this.catch_list[i].stmt.stype == JSC$STMT_VARIABLE)
count += this.catch_list[i].stmt.list.length;
}
if (this.fin)
if (this.fin.stype == JSC$STMT_VARIABLE)
count += this.fin.list.length;
}
return count;
}
/* Throw. */
function JSC$stmt_throw (ln, expr)
{
this.stype = JSC$STMT_THROW;
this.linenum = ln;
this.expr = expr;
this.asm = JSC$stmt_throw_asm;
this.count_locals = JSC$zero_function;
}
function JSC$stmt_throw_asm ()
{
this.expr.asm ();
new JSC$ASM_throw (this.linenum).link ();
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -