/*
* Internal definitions.
* Copyright (c) 1998 New Generation Software (NGS) Oy
*
* Author: Markku Rossi <mtr@ngs.fi>
*/
/*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA
*/
/*
* $Source: /usr/local/cvsroot/ngs/js/jsc/compiler.js,v $
* $Id: compiler.js,v 1.42 1999/01/11 09:01:33 mtr Exp $
*/
/*
* Constants.
*/
/* Tokens. */
JSC$tEOF = 128;
JSC$tINTEGER = 129;
JSC$tFLOAT = 130;
JSC$tSTRING = 131;
JSC$tIDENTIFIER = 132;
JSC$tBREAK = 133;
JSC$tCONTINUE = 134;
JSC$tDELETE = 135;
JSC$tELSE = 136;
JSC$tFOR = 137;
JSC$tFUNCTION = 138;
JSC$tIF = 139;
JSC$tIN = 140;
JSC$tNEW = 141;
JSC$tRETURN = 142;
JSC$tTHIS = 143;
JSC$tTYPEOF = 144;
JSC$tVAR = 145;
JSC$tVOID = 146;
JSC$tWHILE = 147;
JSC$tWITH = 148;
JSC$tCASE = 149;
JSC$tCATCH = 150;
JSC$tCLASS = 151;
JSC$tCONST = 152;
JSC$tDEBUGGER = 153;
JSC$tDEFAULT = 154;
JSC$tDO = 155;
JSC$tENUM = 156;
JSC$tEXPORT = 157;
JSC$tEXTENDS = 158;
JSC$tFINALLY = 159;
JSC$tIMPORT = 160;
JSC$tSUPER = 161;
JSC$tSWITCH = 162;
JSC$tTHROW = 163;
JSC$tTRY = 164;
JSC$tNULL = 165;
JSC$tTRUE = 166;
JSC$tFALSE = 167;
JSC$tEQUAL = 168;
JSC$tNEQUAL = 169;
JSC$tLE = 170;
JSC$tGE = 171;
JSC$tAND = 172;
JSC$tOR = 173;
JSC$tPLUSPLUS = 174;
JSC$tMINUSMINUS = 175;
JSC$tMULA = 176;
JSC$tDIVA = 177;
JSC$tMODA = 178;
JSC$tADDA = 179;
JSC$tSUBA = 180;
JSC$tANDA = 181;
JSC$tXORA = 182;
JSC$tORA = 183;
JSC$tLSIA = 184;
JSC$tLSHIFT = 185;
JSC$tRSHIFT = 186;
JSC$tRRSHIFT = 187;
JSC$tRSIA = 188;
JSC$tRRSA = 189;
JSC$tSEQUAL = 190;
JSC$tSNEQUAL = 191;
/* Expressions. */
JSC$EXPR_COMMA = 0;
JSC$EXPR_ASSIGNMENT = 1;
JSC$EXPR_QUEST_COLON = 2;
JSC$EXPR_LOGICAL = 3;
JSC$EXPR_BITWISE = 4;
JSC$EXPR_EQUALITY = 5;
JSC$EXPR_RELATIONAL = 6;
JSC$EXPR_SHIFT = 7;
JSC$EXPR_MULTIPLICATIVE = 8;
JSC$EXPR_ADDITIVE = 9;
JSC$EXPR_THIS = 10;
JSC$EXPR_NULL = 11;
JSC$EXPR_TRUE = 12;
JSC$EXPR_FALSE = 13;
JSC$EXPR_IDENTIFIER = 14;
JSC$EXPR_FLOAT = 15;
JSC$EXPR_INTEGER = 16;
JSC$EXPR_STRING = 17;
JSC$EXPR_CALL = 18;
JSC$EXPR_OBJECT_PROPERTY = 19;
JSC$EXPR_OBJECT_ARRAY = 20;
JSC$EXPR_NEW = 21;
JSC$EXPR_DELETE = 22;
JSC$EXPR_VOID = 23;
JSC$EXPR_TYPEOF = 24;
JSC$EXPR_PREFIX = 25;
JSC$EXPR_POSTFIX = 26;
JSC$EXPR_UNARY = 27;
JSC$EXPR_REGEXP = 28;
JSC$EXPR_ARRAY_INITIALIZER = 29;
JSC$EXPR_OBJECT_INITIALIZER = 30;
/* Statements */
JSC$STMT_BLOCK = 0;
JSC$STMT_FUNCTION_DECLARATION = 1;
JSC$STMT_VARIABLE = 2;
JSC$STMT_EMPTY = 3;
JSC$STMT_EXPR = 4;
JSC$STMT_IF = 5;
JSC$STMT_WHILE = 6;
JSC$STMT_FOR = 7;
JSC$STMT_FOR_IN = 8;
JSC$STMT_CONTINUE = 9;
JSC$STMT_BREAK = 10;
JSC$STMT_RETURN = 11;
JSC$STMT_WITH = 12;
JSC$STMT_TRY = 13;
JSC$STMT_THROW = 14;
JSC$STMT_DO_WHILE = 15;
JSC$STMT_SWITCH = 16;
JSC$STMT_LABELED_STMT = 17;
/* JavaScript types. */
JSC$JS_UNDEFINED = 0;
JSC$JS_NULL = 1;
JSC$JS_BOOLEAN = 2;
JSC$JS_INTEGER = 3;
JSC$JS_STRING = 4;
JSC$JS_FLOAT = 5;
JSC$JS_ARRAY = 6;
JSC$JS_OBJECT = 7;
JSC$JS_BUILTIN = 11;
/*
Local variables:
mode: c
End:
*/
/*
* Lexer.
* Copyright (c) 1998-1999 New Generation Software (NGS) Oy
*
* Author: Markku Rossi <mtr@ngs.fi>
*/
/*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA
*/
/*
* $Source: /usr/local/cvsroot/ngs/js/jsc/compiler.js,v $
* $Id: compiler.js,v 1.42 1999/01/11 09:01:33 mtr Exp $
*/
/*
* Global functions.
*/
function JSC$lexer (stream)
{
var ch, ch2;
JSC$token_value = null;
while ((ch = stream.readByte ()) != -1)
{
if (ch == #'\n')
{
JSC$linenum++;
continue;
}
if (JSC$lexer_is_white_space (ch))
continue;
JSC$token_linenum = JSC$linenum;
if (ch == #'/' && JSC$lexer_peek_char (stream) == #'*')
{
/* Multi line comment. */
stream.readByte ();
while ((ch = stream.readByte ()) != -1
&& (ch != #'*' || JSC$lexer_peek_char (stream) != #'/'))
if (ch == #'\n')
JSC$linenum++;
/* Consume the peeked #'/' character. */
stream.readByte ();
}
else if ((ch == #'/' && JSC$lexer_peek_char (stream) == #'/')
|| (ch == #'#' && JSC$lexer_peek_char (stream) == #'!'))
{
/* Single line comment. */
while ((ch = stream.readByte ()) != -1 && ch != #'\n')
;
if (ch == #'\n')
JSC$linenum++;
}
else if (ch == #'"' || ch == #'\'')
{
/* String constant. */
JSC$token_value = JSC$lexer_read_string (stream, "string", ch);
return JSC$tSTRING;
}
/* Literals. */
else if (ch == #'=' && JSC$lexer_peek_char (stream) == #'=')
{
stream.readByte ();
if (JSC$lexer_peek_char (stream) == #'=')
{
stream.readByte ();
return JSC$tSEQUAL;
}
return JSC$tEQUAL;
}
else if (ch == #'!' && JSC$lexer_peek_char (stream) == #'=')
{
stream.readByte ();
if (JSC$lexer_peek_char (stream) == #'=')
{
stream.readByte ();
return JSC$tSNEQUAL;
}
return JSC$tNEQUAL;
}
else if (ch == #'<' && JSC$lexer_peek_char (stream) == #'=')
{
stream.readByte ();
return JSC$tLE;
}
else if (ch == #'>' && JSC$lexer_peek_char (stream) == #'=')
{
stream.readByte ();
return JSC$tGE;
}
else if (ch == #'&' && JSC$lexer_peek_char (stream) == #'&')
{
stream.readByte ();
return JSC$tAND;
}
else if (ch == #'|' && JSC$lexer_peek_char (stream) == #'|')
{
stream.readByte ();
return JSC$tOR;
}
else if (ch == #'+' && JSC$lexer_peek_char (stream) == #'+')
{
stream.readByte ();
return JSC$tPLUSPLUS;
}
else if (ch == #'-' && JSC$lexer_peek_char (stream) == #'-')
{
stream.readByte ();
return JSC$tMINUSMINUS;
}
else if (ch == #'*' && JSC$lexer_peek_char (stream) == #'=')
{
stream.readByte ();
return JSC$tMULA;
}
else if (ch == #'/' && JSC$lexer_peek_char (stream) == #'=')
{
stream.readByte ();
return JSC$tDIVA;
}
else if (ch == #'%' && JSC$lexer_peek_char (stream) == #'=')
{
stream.readByte ();
return JSC$tMODA;
}
else if (ch == #'+' && JSC$lexer_peek_char (stream) == #'=')
{
stream.readByte ();
return JSC$tADDA;
}
else if (ch == #'-' && JSC$lexer_peek_char (stream) == #'=')
{
stream.readByte ();
return JSC$tSUBA;
}
else if (ch == #'&' && JSC$lexer_peek_char (stream) == #'=')
{
stream.readByte ();
return JSC$tANDA;
}
else if (ch == #'^' && JSC$lexer_peek_char (stream) == #'=')
{
stream.readByte ();
return JSC$tXORA;
}
else if (ch == #'|' && JSC$lexer_peek_char (stream) == #'=')
{
stream.readByte ();
return JSC$tORA;
}
else if (ch == #'<' && JSC$lexer_peek_char (stream) == #'<')
{
stream.readByte ();
if (JSC$lexer_peek_char (stream) == #'=')
{
stream.readByte ();
return JSC$tLSIA;
}
else
return JSC$tLSHIFT;
}
else if (ch == #'>' && JSC$lexer_peek_char (stream) == #'>')
{
stream.readByte ();
ch2 = JSC$lexer_peek_char (stream);
if (ch2 == #'=')
{
stream.readByte ();
return JSC$tRSIA;
}
else if (ch2 == #'>')
{
stream.readByte ();
if (JSC$lexer_peek_char (stream) == #'=')
{
stream.readByte ();
return JSC$tRRSA;
}
else
return JSC$tRRSHIFT;
}
else
return JSC$tRSHIFT;
}
/* Identifiers and keywords. */
else if (JSC$lexer_is_identifier_letter (ch))
{
/* An identifier. */
var id = String.fromCharCode (ch);
while ((ch = stream.readByte ()) != -1
&& (JSC$lexer_is_identifier_letter (ch)
|| JSC$lexer_is_decimal_digit (ch)))
id.append (File.byteToString (ch));
stream.ungetByte (ch);
/* Keywords. */
if (id == "break")
return JSC$tBREAK;
else if (id == "continue")
return JSC$tCONTINUE;
else if (id == "delete")
return JSC$tDELETE;
else if (id == "else")
return JSC$tELSE;
else if (id == "for")
return JSC$tFOR;
else if (id == "function")
return JSC$tFUNCTION;
else if (id == "if")
return JSC$tIF;
else if (id == "in")
return JSC$tIN;
else if (id == "new")
return JSC$tNEW;
else if (id == "return")
return JSC$tRETURN;
else if (id == "this")
return JSC$tTHIS;
else if (id == "typeof")
return JSC$tTYPEOF;
else if (id == "var")
return JSC$tVAR;
else if (id == "void")
return JSC$tVOID;
else if (id == "while")
return JSC$tWHILE;
else if (id == "with")
return JSC$tWITH;
/*
* Future reserved keywords (some of these is already in use
* in this implementation).
*/
else if (id == "case")
return JSC$tCASE;
else if (id == "catch")
return JSC$tCATCH;
else if (id == "class")
return JSC$tCLASS;
else if (id == "const")
return JSC$tCONST;
else if (id == "debugger")
return JSC$tDEBUGGER;
else if (id == "default")
return JSC$tDEFAULT;
else if (id == "do")
return JSC$tDO;
else if (id == "enum")
return JSC$tENUM;
else if (id == "export")
return JSC$tEXPORT;
else if (id == "extends")
return JSC$tEXTENDS;
else if (id == "finally")
return JSC$tFINALLY;
else if (id == "import")
return JSC$tIMPORT;
else if (id == "super")
return JSC$tSUPER;
else if (id == "switch")
return JSC$tSWITCH;
else if (id == "throw")
return JSC$tTHROW;
else if (id == "try")
return JSC$tTRY;
/* Null and boolean literals. */
else if (id == "null")
return JSC$tNULL;
else if (id == "true")
return JSC$tTRUE;
else if (id == "false")
return JSC$tFALSE;
else
{
/* It really is an identifier. */
JSC$token_value = id;
return JSC$tIDENTIFIER;
}
}
/* Character constants. */
else if (ch == #'#' && JSC$lexer_peek_char (stream) == #'\'')
{
/* Skip the starting #'\'' and read more. */
stream.readByte ();
ch = stream.readByte ();
if (ch == #'\\')
{
JSC$token_value
= JSC$lexer_read_backslash_escape (stream, 0, "character");
if (stream.readByte () != #'\'')
error (JSC$filename + ":" + JSC$linenum.toString ()
+ ": malformed character constant");
}
else if (JSC$lexer_peek_char (stream) == #'\'')
{
stream.readByte ();
JSC$token_value = ch;
}
else
error (JSC$filename + ":" + JSC$linenum.toString ()
+ ": malformed character constant");
return JSC$tINTEGER;
}
/* Octal and hex numbers. */
else if (ch == #'0'
&& JSC$lexer_peek_char (stream) != #'.'
&& JSC$lexer_peek_char (stream) != #'e'
&& JSC$lexer_peek_char (stream) != #'E')
{
JSC$token_value = 0;
ch = stream.readByte ();
if (ch == #'x' || ch == #'X')
{
ch = stream.readByte ();
while (JSC$lexer_is_hex_digit (ch))
{
JSC$token_value *= 16;
JSC$token_value += JSC$lexer_hex_to_dec (ch);
ch = stream.readByte ();
}
stream.ungetByte (ch);
}
else
{
while (JSC$lexer_is_octal_digit (ch))
{
JSC$token_value *= 8;
JSC$token_value += ch - #'0';
ch = stream.readByte ();
}
stream.ungetByte (ch);
}
return JSC$tINTEGER;
}
/* Decimal numbers. */
else if (JSC$lexer_is_decimal_digit (ch)
|| (ch == #'.'
&& JSC$lexer_is_decimal_digit (
JSC$lexer_peek_char (stream))))
{
var is_float = false;
var buf = new String (File.byteToString (ch));
var accept_dot = true;
if (ch == #'.')
{
/*
* We started with #'.' and we know that the next character
* is a decimal digit (we peeked it).
*/
is_float = true;
ch = stream.readByte ();
while (JSC$lexer_is_decimal_digit (ch))
{
buf.append (File.byteToString (ch));
ch = stream.readByte ();
}
accept_dot = false;
}
else
{
/* We did start with a decimal digit. */
ch = stream.readByte ();
while (JSC$lexer_is_decimal_digit (ch))
{
buf.append (File.byteToString (ch));
ch = stream.readByte ();
}
}
if ((accept_dot && ch == #'.')
|| ch == #'e' || ch == #'E')
{
is_float = true;
if (ch == #'.')
{
buf.append (File.byteToString (ch));
ch = stream.readByte ();
while (JSC$lexer_is_decimal_digit (ch))
{
buf.append (File.byteToString (ch));
ch = stream.readByte ();
}
}
if (ch == #'e' || ch == #'E')
{
buf.append (File.byteToString (ch));
ch = stream.readByte ();
if (ch == #'+' || ch == #'-')
{
buf.append (File.byteToString (ch));
ch = stream.readByte ();
}
if (!JSC$lexer_is_decimal_digit (ch))
error (JSC$filename + ":" + JSC$linenum.toString ()
+ ": malformed exponent part in a decimal literal");
while (JSC$lexer_is_decimal_digit (ch))
{
buf.append (File.byteToString (ch));
ch = stream.readByte ();
}
}
}
/* Finally, we put the last character pack to the stream. */
stream.ungetByte (ch);
if (is_float)
{
JSC$token_value = parseFloat (buf);
return JSC$tFLOAT;
}
JSC$token_value = parseInt (buf);
return JSC$tINTEGER;
}
/* Just return the character as-is. */
else
return ch;
}
/* EOF reached. */
return JSC$tEOF;
}
/*
* Help functions.
*/
function JSC$lexer_peek_char (stream)
{
var ch2 = stream.readByte ();
stream.ungetByte (ch2);
return ch2;
}
function JSC$lexer_is_identifier_letter (ch)
{
return ((#'a' <= ch && ch <= #'z') || (#'A' <= ch && ch <= #'Z')
|| ch == #'$' || ch == #'_');
}
function JSC$lexer_is_octal_digit (ch)
{
return (#'0' <= ch && ch <= #'7');
}
function JSC$lexer_is_decimal_digit (ch)
{
return #'0' <= ch && ch <= #'9';
}
function JSC$lexer_is_hex_digit (ch)
{
return ((#'0' <= ch && ch <= #'9')
|| (#'a' <= ch && ch <= #'f')
|| (#'A' <= ch && ch <= #'F'));
}
function JSC$lexer_is_white_space (ch)
{
return (ch == #' ' || ch == #'\t' || ch == #'\v' || ch == #'\r'
|| ch == #'\f' || ch == #'\n');
}
function JSC$lexer_hex_to_dec (ch)
{
return ((#'0' <= ch && ch <= #'9')
? ch - #'0'
: ((#'a' <= ch && ch <= #'f')
? 10 + ch - #'a'
: 10 + ch - #'A'));
}
function JSC$lexer_read_backslash_escape (stream, possible_start, name)
{
var ch = stream.readByte ();
if (ch == #'n')
ch = #'\n';
else if (ch == #'t')
ch = #'\t';
else if (ch == #'v')
ch = #'\v';
else if (ch == #'b')
ch = #'\b';
else if (ch == #'r')
ch = #'\r';
else if (ch == #'f')
ch = #'\f';
else if (ch == #'a')
ch = #'\a';
else if (ch == #'\\')
ch = #'\\';
else if (ch == #'?')
ch = #'?';
else if (ch == #'\'')
ch = #'\'';
else if (ch == #'"')
ch = #'"';
else if (ch == #'x')
{
/* HexEscapeSequence. */
var c1, c2;
c1 = stream.readByte ();
c2 = stream.readByte ();
if (c1 == -1 || c2 == -1)
JSC$lexer_eof_in_constant (possible_start, name);
if (!JSC$lexer_is_hex_digit (c1) || !JSC$lexer_is_hex_digit (c2))
error (JSC$filename + ":" + JSC$linenum.toString ()
+ ": \\x used with no following hex digits");
ch = (JSC$lexer_hex_to_dec (c1) << 4) + JSC$lexer_hex_to_dec (c2);
}
else if (ch == #'u')
{
/* UnicodeEscapeSequence. */
var c1, c2, c3, c4;
c1 = stream.readByte ();
c2 = stream.readByte ();
c3 = stream.readByte ();
c4 = stream.readByte ();
if (c1 == -1 || c2 == -1 || c3 == -1 || c4 == -1)
JSC$lexer_eof_in_constant (possible_start, name);
if (!JSC$lexer_is_hex_digit (c1) || !JSC$lexer_is_hex_digit (c2)
|| !JSC$lexer_is_hex_digit (c3) || !JSC$lexer_is_hex_digit (c4))
error (JSC$filename + ":" + JSC$linenum.toString ()
+ ": \\u used with no following hex digits");
ch = ((JSC$lexer_hex_to_dec (c1) << 12)
+ (JSC$lexer_hex_to_dec (c2) << 8)
+ (JSC$lexer_hex_to_dec (c3) << 4)
+ JSC$lexer_hex_to_dec (c4));
}
else if (JSC$lexer_is_octal_digit (ch))
{
var result = ch - #'0';
var i = 1;
if (ch == #'0')
/* Allow three octal digits after '0'. */
i = 0;
ch = stream.readByte ();
while (i < 3 && JSC$lexer_is_octal_digit (ch))
{
result *= 8;
result += ch - #'0';
ch = stream.readByte ();
i++;
}
stream.ungetByte (ch);
ch = result;
}
else
{
if (ch == -1)
error (JSC$filename + ":" + JSC$linenum.toString ()
+ ": unterminated " + name);
JSC$warning (JSC$filename + ":" + JSC$linenum.toString ()
+ ": warning: unknown escape sequence `\\"
+ File.byteToString (ch) + "'");
}
return ch;
}
function JSC$lexer_read_string (stream, name, ender)
{
var str = new String ("");
var done = false, ch;
var possible_start_ln = JSC$linenum;
var warned_line_terminator = false;
while (!done)
{
ch = stream.readByte ();
if (ch == #'\n')
{
if (JSC$warn_strict_ecma && !warned_line_terminator)
{
JSC$warning (JSC$filename + ":" + JSC$linenum.toString ()
+ ": warning: ECMAScript don't allow line terminators in "
+ name + " constants");
warned_line_terminator = true;
}
JSC$linenum++;
}
if (ch == -1)
JSC$lexer_eof_in_constant (possible_start_ln, name);
else if (ch == ender)
done = true;
else
{
if (ch == #'\\')
{
if (JSC$lexer_peek_char (stream) == #'\n')
{
/*
* Backslash followed by a newline character. Ignore
* them both.
*/
stream.readByte ();
JSC$linenum++;
continue;
}
ch = JSC$lexer_read_backslash_escape (stream, possible_start_ln,
name);
}
str.append (ch);
}
}
return str;
}
function JSC$lexer_read_regexp_constant (stream)
{
/* Regexp literal. */
var source = JSC$lexer_read_regexp_source (stream);
/* Check the possible flags. */
var flags = new String ("");
while ((ch = JSC$lexer_peek_char (stream)) == #'g' || ch == #'i')
{
stream.readByte ();
flags.append (File.byteToString (ch));
}
/* Try to compile it. */
var msg = false;
var result;
try
{
result = new RegExp (source, flags);
}
catch (msg)
{
var start = msg.lastIndexOf (":");
msg = (JSC$filename + ":" + JSC$token_linenum.toString ()
+ ": malformed regular expression constant:"
+ msg.substr (start + 1));
}
if (msg)
error (msg);
/* Success. */
return result;
}
function JSC$lexer_read_regexp_source (stream)
{
var str = new String ("");
var done = false, ch;
var possible_start_ln = JSC$linenum;
var warned_line_terminator = false;
var name = "regular expression";
while (!done)
{
ch = stream.readByte ();
if (ch == #'\n')
{
if (JSC$warn_strict_ecma && !warned_line_terminator)
{
JSC$warning (JSC$filename + ":" + JSC$linenum.toString ()
+ ": warning: ECMAScript don't allow line "
+ "terminators in " + name + " constants");
warned_line_terminator = true;
}
JSC$linenum++;
}
if (ch == -1)
JSC$lexer_eof_in_constant (possible_start_ln, name);
else if (ch == #'/')
done = true;
else
{
if (ch == #'\\')
{
ch = stream.readByte ();
if (ch == #'\n')
{
/*
* Backslash followed by a newline character. Ignore
* them both.
*/
JSC$linenum++;
continue;
}
if (ch == -1)
JSC$lexer_eof_in_constant (possible_start_ln, name);
/* Handle the backslash escapes. */
if (ch == #'f')
ch = #'\f';
else if (ch == #'n')
ch = #'\n';
else if (ch == #'r')
ch = #'\r';
else if (ch == #'t')
ch = #'\t';
else if (ch == #'v')
ch == #'\v';
else if (ch == #'c')
{
/* SourceCharacter. */
ch = stream.readByte ();
if (ch == -1)
JSC$lexer_eof_in_constant (possible_start_ln, name);
if (ch == #'\n' && JSC$warn_strict_ecma)
JSC$warning (JSC$filename + ":" + JSC$linenum.toString ()
+ ": warning: ECMAScript don't allow line termiantor after \\c in regular expression constants");
/*
* Append the source-character escape start. The ch
* will be appended later.
*/
str.append ("\\c");
}
else if (ch == #'u' || ch == #'x' || ch == #'0')
{
/* These can be handled with the read_backslash_escape(). */
stream.ungetByte (ch);
ch = JSC$lexer_read_backslash_escape (stream);
}
else
{
/*
* Nothing special. Leave it to the result as-is.
* The regular expression backage will handle it.
*/
stream.ungetByte (ch);
ch = #'\\';
}
}
str.append (File.byteToString (ch));
}
}
return str;
}
function JSC$lexer_eof_in_constant (possible_start, name)
{
var msg = (JSC$filename + ":" + JSC$linenum.toString ()
+ ": unterminated " + name + " constant");
if (possible_start > 0)
msg += (System.lineBreakSequence
+ JSC$filename + ":" + possible_start.toString ()
+ ": possible real start of unterminated " + name + " constant");
error (msg);
}
/*
Local variables:
mode: c
End:
*/
/*
* Parser.
* Copyright (c) 1998 New Generation Software (NGS) Oy
*
* Author: Markku Rossi <mtr@ngs.fi>
*/
/*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA
*/
/*
* $Source: /usr/local/cvsroot/ngs/js/jsc/compiler.js,v $
* $Id: compiler.js,v 1.42 1999/01/11 09:01:33 mtr Exp $
*/
/*
* Global functions.
*/
function JSC$parser_reset ()
{
JSC$function = null;
JSC$global_stmts = null;
JSC$nested_function_declarations = null;
}
function JSC$parser_parse (stream)
{
JSC$linenum = 1;
JSC$filename = stream.name;
JSC$functions = new Array ();
JSC$global_stmts = new Array ();
JSC$nested_function_declarations = new Array ();
JSC$anonymous_function_count = 0;
JSC$parser_peek_token_valid = false;
JSC$num_tokens = 0;
JSC$num_arguments_identifiers = 0;
JSC$num_missing_semicolons = 0;
if (JSC$verbose)
JSC$message ("jsc: parsing");
while (JSC$parser_peek_token (stream) != JSC$tEOF)
if (!JSC$parser_parse_source_element (stream))
JSC$parser_syntax_error ();
if (JSC$verbose)
{
var msg = ("jsc: input stream had " + (JSC$linenum - 1).toString ()
+ " lines, " + JSC$num_tokens.toString () + " tokens");
if (JSC$num_missing_semicolons > 0)
msg += (", " + JSC$num_missing_semicolons.toString ()
+ " missing semicolons");
JSC$message (msg);
}
}
/*
* General help functions.
*/
function JSC$parser_syntax_error ()
{
error (JSC$filename + ":" + JSC$linenum.toString () + ": syntax error");
}
/* All warnings are reported through this function. */
function JSC$warning (line)
{
System.stderr.writeln (line);
}
/* All messages are reported throught this function. */
function JSC$message (line)
{
System.stderr.writeln (line);
}
function JSC$parser_get_token (stream)
{
JSC$num_tokens++;
var token;
if (JSC$parser_peek_token_valid)
{
JSC$parser_peek_token_valid = false;
JSC$parser_token_value = JSC$parser_peek_token_value;
JSC$parser_token_linenum = JSC$parser_peek_token_linenum;
token = JSC$parser_peek_token_token;
}
else
{
token = JSC$lexer (stream);
JSC$parser_token_value = JSC$token_value;
JSC$parser_token_linenum = JSC$token_linenum;
}
if (token == JSC$tIDENTIFIER && JSC$parser_token_value == "arguments")
JSC$num_arguments_identifiers++;
return token;
}
function JSC$parser_peek_token (stream)
{
if (JSC$parser_peek_token_valid)
return JSC$parser_peek_token_token;
else
{
JSC$parser_peek_token_token = JSC$lexer (stream);
JSC$parser_peek_token_value = JSC$token_value;
JSC$parser_peek_token_linenum = JSC$token_linenum;
JSC$parser_peek_token_valid = true;
return JSC$parser_peek_token_token;
}
}
function JSC$parser_get_semicolon_asci (stream)
{
var token = JSC$parser_peek_token (stream);
if (token == #';')
{
/* Everything ok. It was there. */
return JSC$parser_get_token (stream);
}
/* No semicolon. Let's see if we can insert it there. */
if (token == #'}'
|| JSC$parser_token_linenum < JSC$parser_peek_token_linenum
|| token == JSC$tEOF)
{
/* Ok, do the automatic semicolon insertion. */
if (JSC$warn_missing_semicolon)
JSC$warning (JSC$filename + ":" + JSC$parser_token_linenum.toString ()
+ ": warning: missing semicolon");
JSC$num_missing_semicolons++;
return #';';
}
/* Sorry, no can do. */
JSC$parser_syntax_error ();
}
function JSC$parser_expr_is_left_hand_side (expr)
{
return (expr.etype == JSC$EXPR_CALL
|| expr.etype == JSC$EXPR_OBJECT_PROPERTY
|| expr.etype == JSC$EXPR_OBJECT_ARRAY
|| expr.etype == JSC$EXPR_NEW
|| expr.etype == JSC$EXPR_THIS
|| expr.etype == JSC$EXPR_IDENTIFIER
|| expr.etype == JSC$EXPR_FLOAT
|| expr.etype == JSC$EXPR_INTEGER
|| expr.etype == JSC$EXPR_STRING
|| expr.etype == JSC$EXPR_REGEXP
|| expr.etype == JSC$EXPR_ARRAY_INITIALIZER
|| expr.etype == JSC$EXPR_NULL
|| expr.etype == JSC$EXPR_TRUE
|| expr.etype == JSC$EXPR_FALSE);
}
function JSC$parser_parse_source_element (stream)
{
if (JSC$parser_parse_function_declaration (stream))
return true;
var stmt = JSC$parser_parse_stmt (stream);
if (!stmt)
return false;
if (stmt.stype == JSC$STMT_VARIABLE)
/*
* This is a variable declaration at the global level. These
* are actually global variables.
*/
stmt.global_level = true;
JSC$global_stmts.push (stmt);
return true;
}
function JSC$parser_parse_function_declaration (stream)
{
var id, args, block;
if (JSC$parser_peek_token (stream) != JSC$tFUNCTION)
return false;
/* Record how many `arguments' identifiers have been seen so far. */
var num_arguments_identifiers = JSC$num_arguments_identifiers;
JSC$parser_get_token (stream);
if (JSC$parser_get_token (stream) != JSC$tIDENTIFIER)
JSC$parser_syntax_error ();
id = JSC$parser_token_value;
var ln = JSC$parser_token_linenum;
var id_given = id;
if (JSC$nested_function_declarations.length > 0)
{
/* This is a nested function declaration. */
id = ".F:" + (JSC$anonymous_function_count++).toString ();
}
JSC$nested_function_declarations.push (id);
if (JSC$parser_get_token (stream) != #'(')
JSC$parser_syntax_error ();
/* Formal parameter list opt. */
args = new Array ();
while (JSC$parser_peek_token (stream) != #')')
{
if (JSC$parser_get_token (stream) != JSC$tIDENTIFIER)
JSC$parser_syntax_error ();
args.push (JSC$parser_token_value);
var token = JSC$parser_peek_token (stream);
if (token == #',')
{
JSC$parser_get_token (stream);
if (JSC$parser_peek_token (stream) != JSC$tIDENTIFIER)
JSC$parser_syntax_error ();
}
else if (token != #')')
JSC$parser_syntax_error ();
}
if (JSC$parser_get_token (stream) != #')')
JSC$parser_syntax_error ();
JSC$parser_peek_token (stream);
var lbrace_ln = JSC$parser_peek_token_linenum;
block = JSC$parser_parse_block (stream);
if (typeof block == "boolean")
JSC$parser_syntax_error ();
/* Did the function use the `arguments' identifier? */
var use_arguments = false;
if (JSC$num_arguments_identifiers > num_arguments_identifiers)
{
use_arguments = true;
if (JSC$warn_deprecated)
JSC$warning (JSC$filename + ":" + ln.toString ()
+ ": warning: the `arguments' property of Function "
+ "instance is deprecated");
}
JSC$functions.push (new JSC$function_declaration (ln, lbrace_ln, id,
id_given, args,
block, use_arguments));
JSC$nested_function_declarations.pop ();
return true;
}
function JSC$parser_parse_block (stream)
{
var block;
if (JSC$parser_peek_token (stream) != #'{')
return false;
JSC$parser_get_token (stream) != #'{';
var ln = JSC$parser_peek_token_linenum;
/* Do we have a statement list? */
if (JSC$parser_peek_token (stream) != #'}')
/* Yes we have. */
block = JSC$parser_parse_stmt_list (stream);
else
/* Do we don't */
block = new Array ();
if (JSC$parser_get_token (stream) != #'}')
JSC$parser_syntax_error ();
block.linenum = ln;
return block;
}
function JSC$parser_parse_stmt_list (stream)
{
var list, done, item;
list = new Array ();
done = false;
while (!done)
{
item = JSC$parser_parse_stmt (stream);
if (typeof item == "boolean")
{
/* Can't parse more statements. We'r done. */
done = true;
}
else
list.push (item);
}
return list;
}
function JSC$parser_parse_stmt (stream)
{
var item, token;
if (typeof (item = JSC$parser_parse_block (stream)) != "boolean")
return new JSC$stmt_block (item.linenum, item);
else if (JSC$parser_parse_function_declaration (stream))
{
/* XXX The function declaration as statement might be incomplete. */
if (JSC$nested_function_declarations.length == 0)
/* Function declaration at top-level statements. */
return new JSC$stmt_empty (JSC$parser_token_linenum);
/* Function declaration inside another function. */
var container_id = JSC$nested_function_declarations.pop ();
JSC$nested_function_declarations.push (container_id);
var f = JSC$functions[JSC$functions.length - 1];
var function_id = f.name;
var given_id = f.name_given;
return new JSC$stmt_function_declaration (JSC$parser_token_linenum,
container_id, function_id,
given_id);
}
else if (typeof (item = JSC$parser_parse_variable_stmt (stream))
!= "boolean")
return item;
else if (typeof (item = JSC$parser_parse_if_stmt (stream))
!= "boolean")
return item;
else if (typeof (item = JSC$parser_parse_iteration_stmt (stream))
!= "boolean")
return item;
else if (typeof (item = JSC$parser_parse_expr (stream))
!= "boolean")
{
if (item.etype == JSC$EXPR_IDENTIFIER)
{
/* Possible `Labeled Statement'. */
token = JSC$parser_peek_token (stream);
if (token == #':' && item.linenum == JSC$parser_peek_token_linenum)
{
/* Yes it is. */
JSC$parser_get_token (stream);
var stmt = JSC$parser_parse_stmt (stream);
if (!stmt)
JSC$parser_syntax_error;
return new JSC$stmt_labeled_stmt (item.linenum, item.value,
stmt);
}
/* FALLTHROUGH */
}
JSC$parser_get_semicolon_asci (stream);
return new JSC$stmt_expr (item);
}
else
{
token = JSC$parser_peek_token (stream);
if (token == #';')
{
JSC$parser_get_token (stream);
return new JSC$stmt_empty (JSC$parser_token_linenum);
}
else if (token == JSC$tCONTINUE)
{
JSC$parser_get_token (stream);
/* Check the possible label. */
var label = null;
token = JSC$parser_peek_token (stream);
if (token == JSC$tIDENTIFIER
&& JSC$parser_token_linenum == JSC$parser_peek_token_linenum)
{
JSC$parser_get_token (stream);
label = JSC$parser_token_value;
}
item = new JSC$stmt_continue (JSC$parser_token_linenum, label);
JSC$parser_get_semicolon_asci (stream);
return item;
}
else if (token == JSC$tBREAK)
{
JSC$parser_get_token (stream);
/* Check the possible label. */
var label = null;
token = JSC$parser_peek_token (stream);
if (token == JSC$tIDENTIFIER
&& JSC$parser_token_linenum == JSC$parser_peek_token_linenum)
{
JSC$parser_get_token (stream);
label = JSC$parser_token_value;
}
item = new JSC$stmt_break (JSC$parser_token_linenum, label);
JSC$parser_get_semicolon_asci (stream);
return item;
}
else if (token == JSC$tRETURN)
{
JSC$parser_get_token (stream);
var linenum = JSC$parser_token_linenum;
if (JSC$parser_peek_token (stream) == #';')
{
/* Consume the semicolon. */
JSC$parser_get_token (stream);
item = null;
}
else
{
if (JSC$parser_peek_token_linenum > linenum)
{
/*
* A line terminator between tRETURN and the next
* token that is not a semicolon. ASCI here.
*/
if (JSC$warn_missing_semicolon)
JSC$warning (JSC$filename + ":" + linenum.toString ()
+ ": warning: missing semicolon");
JSC$num_missing_semicolons++;
item = null;
}
else
{
item = JSC$parser_parse_expr (stream);
if (typeof item == "boolean")
JSC$parser_syntax_error ();
JSC$parser_get_semicolon_asci (stream);
}
}
return new JSC$stmt_return (linenum, item);
}
else if (token == JSC$tSWITCH)
{
JSC$parser_get_token (stream);
return JSC$parser_parse_switch (stream);
}
else if (token == JSC$tWITH)
{
JSC$parser_get_token (stream);
var linenum = JSC$parser_token_linenum;
if (JSC$parser_get_token (stream) != #'(')
JSC$parser_syntax_error ();
var expr = JSC$parser_parse_expr (stream);
if (typeof expr == "boolean")
JSC$parser_syntax_error ();
if (JSC$parser_get_token (stream) != #')')
JSC$parser_syntax_error ();
var stmt = JSC$parser_parse_stmt (stream);
if (typeof stmt == "boolean")
JSC$parser_syntax_error ();
return new JSC$stmt_with (linenum, expr, stmt);
}
else if (token == JSC$tTRY)
{
JSC$parser_get_token (stream);
return JSC$parser_parse_try (stream);
}
else if (token == JSC$tTHROW)
{
JSC$parser_get_token (stream);
var linenum = JSC$parser_token_linenum;
/*
* Get the next token's linenum. We need it for strict_ecma
* warning.
*/
JSC$parser_peek_token (stream);
var peek_linenum = JSC$parser_peek_token_linenum;
/* The expression to throw. */
var expr = JSC$parser_parse_expr (stream);
if (typeof expr == "boolean")
JSC$parser_syntax_error ();
if (JSC$warn_strict_ecma && peek_linenum > linenum)
JSC$warning (JSC$filename + ":" + JSC$linenum.toString ()
+ ": warning: ECMAScript don't allow line terminators"
+ " between `throw' and expression");
JSC$parser_get_semicolon_asci (stream);
return new JSC$stmt_throw (linenum, expr);
}
else
/* Can't parse more. We'r done. */
return false;
}
}
function JSC$parser_parse_switch (stream)
{
var linenum = JSC$parser_token_linenum;
if (JSC$parser_get_token (stream) != #'(')
JSC$parser_syntax_error ();
var expr = JSC$parser_parse_expr (stream);
if (!expr)
JSC$parser_syntax_error ();
if (JSC$parser_get_token (stream) != #')')
JSC$parser_syntax_error ();
if (JSC$parser_get_token (stream) != #'{')
JSC$parser_syntax_error ();
/* Parse case clauses. */
var clauses = new Array ();
while (true)
{
var token = JSC$parser_get_token (stream);
if (token == #'}')
break;
else if (token == JSC$tCASE || token == JSC$tDEFAULT)
{
var stmts = new Array ();
stmts.expr = null;
if (token == JSC$tCASE)
{
stmts.expr = JSC$parser_parse_expr (stream);
if (!stmts.expr)
JSC$parser_syntax_error ();
}
if (JSC$parser_get_token (stream) != #':')
JSC$parser_syntax_error ();
stmts.linenum = JSC$parser_token_linenum;
/* Read the statement list. */
while (true)
{
token = JSC$parser_peek_token (stream);
if (token == #'}' || token == JSC$tCASE || token == JSC$tDEFAULT)
/* Done with this branch. */
break;
var stmt = JSC$parser_parse_stmt (stream);
if (!stmt)
JSC$parser_syntax_error ();
stmts.push (stmt);
}
stmts.last_linenum = JSC$parser_token_linenum;
/* One clause parsed. */
clauses.push (stmts);
}
else
JSC$parser_syntax_error ();
}
return new JSC$stmt_switch (linenum, JSC$parser_token_linenum, expr,
clauses);
}
function JSC$parser_parse_try (stream)
{
var linenum = JSC$parser_token_linenum;
var block = JSC$parser_parse_stmt (stream);
if (!block)
JSC$parser_syntax_error ();
var try_block_last_linenum = JSC$parser_token_linenum;
/* Now we must see `catch' or `finally'. */
var token = JSC$parser_peek_token (stream);
if (token != JSC$tCATCH && token != JSC$tFINALLY)
JSC$parser_syntax_error ();
var catch_list = false;
if (token == JSC$tCATCH)
{
/* Parse catch list. */
catch_list = new Array ();
catch_list.linenum = JSC$parser_peek_token_linenum;
while (token == JSC$tCATCH)
{
JSC$parser_get_token (stream);
var c = new Object ();
c.linenum = JSC$parser_token_linenum;
if (JSC$parser_get_token (stream) != #'(')
JSC$parser_syntax_error ();
if (JSC$parser_get_token (stream) != JSC$tIDENTIFIER)
JSC$parser_syntax_error ();
c.id = JSC$parser_token_value;
c.guard = false;
if (JSC$parser_peek_token (stream) == JSC$tIF)
{
JSC$parser_get_token (stream);
c.guard = JSC$parser_parse_expr (stream);
if (!c.guard)
JSC$parser_syntax_error ();
}
if (JSC$parser_get_token (stream) != #')')
JSC$parser_syntax_error ();
c.stmt = JSC$parser_parse_stmt (stream);
if (!c.stmt)
JSC$parser_syntax_error ();
catch_list.push (c);
token = JSC$parser_peek_token (stream);
}
catch_list.last_linenum = JSC$parser_token_linenum;
}
var fin = false;
if (token == JSC$tFINALLY)
{
/* Parse the finally. */
JSC$parser_get_token (stream);
fin = JSC$parser_parse_stmt (stream);
if (!fin)
JSC$parser_syntax_error ();
}
return new JSC$stmt_try (linenum, try_block_last_linenum,
JSC$parser_token_linenum, block, catch_list,
fin);
}
function JSC$parser_parse_variable_stmt (stream)
{
var list, id, expr, token;
if (JSC$parser_peek_token (stream) != JSC$tVAR)
return false;
JSC$parser_get_token (stream);
var ln = JSC$parser_token_linenum;
list = new Array ();
while (true)
{
token = JSC$parser_peek_token (stream);
if (token == JSC$tIDENTIFIER)
{
JSC$parser_get_token ();
id = JSC$parser_token_value;
if (JSC$parser_peek_token (stream) == #'=')
{
JSC$parser_get_token (stream);
expr = JSC$parser_parse_assignment_expr (stream);
if (typeof expr == "boolean")
JSC$parser_syntax_error ();
}
else
expr = null;
list.push (new JSC$var_declaration (id, expr));
/* Check if we have more input. */
if (JSC$parser_peek_token (stream) == #',')
{
/* Yes we have. */
JSC$parser_get_token (stream);
/* The next token must be tIDENTIFIER. */
if (JSC$parser_peek_token (stream) != JSC$tIDENTIFIER)
JSC$parser_syntax_error ();
}
else
{
/* No, we don't. */
JSC$parser_get_semicolon_asci (stream);
break;
}
}
else
{
/* We'r done. */
JSC$parser_get_semicolon_asci (stream);
break;
}
}
/* There must be at least one variable declaration. */
if (list.length == 0)
JSC$parser_syntax_error ();
return new JSC$stmt_variable (ln, list);
}
function JSC$parser_parse_if_stmt (stream)
{
var expr, stmt, stmt2;
if (JSC$parser_peek_token (stream) != JSC$tIF)
return false;
JSC$parser_get_token (stream);
var ln = JSC$parser_token_linenum;
if (JSC$parser_get_token (stream) != #'(')
JSC$parser_syntax_error ();
expr = JSC$parser_parse_expr (stream);
if (typeof expr == "boolean")
JSC$parser_syntax_error ();
if (JSC$parser_get_token (stream) != #')')
JSC$parser_syntax_error ();
stmt = JSC$parser_parse_stmt (stream);
if (typeof stmt == "boolean")
JSC$parser_syntax_error ();
if (JSC$parser_peek_token (stream) == JSC$tELSE)
{
JSC$parser_get_token (stream);
stmt2 = JSC$parser_parse_stmt (stream);
if (typeof stmt2 == "boolean")
JSC$parser_syntax_error ();
}
else
stmt2 = null;
return new JSC$stmt_if (ln, expr, stmt, stmt2);
}
function JSC$parser_parse_iteration_stmt (stream)
{
var token, expr1, expr2, expr3, stmt;
token = JSC$parser_peek_token (stream);
if (token == JSC$tDO)
{
/* do Statement while (Expression); */
JSC$parser_get_token (stream);
var ln = JSC$parser_token_linenum;
stmt = JSC$parser_parse_stmt (stream);
if (typeof stmt == "boolean")
JSC$parser_syntax_error ();
if (JSC$parser_get_token (stream) != JSC$tWHILE)
JSC$parser_syntax_error ();
if (JSC$parser_get_token (stream) != #'(')
JSC$parser_syntax_error ();
expr1 = JSC$parser_parse_expr (stream);
if (typeof expr1 == "boolean")
JSC$parser_syntax_error ();
if (JSC$parser_get_token (stream) != #')')
JSC$parser_syntax_error ();
JSC$parser_get_semicolon_asci (stream);
return new JSC$stmt_do_while (ln, expr1, stmt);
}
else if (token == JSC$tWHILE)
{
/* while (Expression) Statement */
JSC$parser_get_token (stream);
var ln = JSC$parser_token_linenum;
if (JSC$parser_get_token (stream) != #'(')
JSC$parser_syntax_error ();
expr1 = JSC$parser_parse_expr (stream);
if (typeof expr1 == "boolean")
JSC$parser_syntax_error ();
if (JSC$parser_get_token (stream) != #')')
JSC$parser_syntax_error ();
stmt = JSC$parser_parse_stmt (stream);
if (typeof stmt == "boolean")
JSC$parser_syntax_error ();
return new JSC$stmt_while (ln, expr1, stmt);
}
else if (token == JSC$tFOR)
{
JSC$parser_get_token (stream);
var ln = JSC$parser_token_linenum;
if (JSC$parser_get_token (stream) != #'(')
JSC$parser_syntax_error ();
/* Init */
var vars = null;
token = JSC$parser_peek_token (stream);
if (token == JSC$tVAR)
{
JSC$parser_get_token (stream);
vars = new Array ();
while (true)
{
/* The identifier. */
token = JSC$parser_peek_token (stream);
if (token != JSC$tIDENTIFIER)
break;
JSC$parser_get_token (stream);
var id = JSC$parser_token_value;
/* Possible initializer. */
var expr = null;
if (JSC$parser_peek_token (stream) == #'=')
{
JSC$parser_get_token (stream);
expr = JSC$parser_parse_assignment_expr (stream);
if (!expr)
JSC$parser_syntax_error ();
}
vars.push (new JSC$var_declaration (id, expr));
/* Check if we have more input. */
if (JSC$parser_peek_token (stream) == #',')
{
/* Yes we have. */
JSC$parser_get_token (stream);
/* The next token must be tIDENTIFIER. */
if (JSC$parser_peek_token (stream) != JSC$tIDENTIFIER)
JSC$parser_syntax_error ();
}
else
/* No more input. */
break;
}
/* Must have at least one variable declaration. */
if (vars.length == 0)
JSC$parser_syntax_error ();
}
else if (token != #';')
{
expr1 = JSC$parser_parse_expr (stream);
if (typeof expr1 == "boolean")
JSC$parser_syntax_error ();
}
else
expr1 = null;
token = JSC$parser_get_token (stream);
var for_in = false;
if (token == #';')
{
/* Normal for-statement. */
/* Check */
if (JSC$parser_peek_token (stream) != #';')
{
expr2 = JSC$parser_parse_expr (stream);
if (typeof expr2 == "boolean")
JSC$parser_syntax_error ();
}
else
expr2 = null;
if (JSC$parser_get_token (stream) != #';')
JSC$parser_syntax_error ();
/* Increment */
if (JSC$parser_peek_token (stream) != #')')
{
expr3 = JSC$parser_parse_expr (stream);
if (typeof expr3 == "boolean")
JSC$parser_syntax_error ();
}
else
expr3 = null;
}
else if (token == JSC$tIN)
{
/* The `for (VAR in EXPR)'-statement. */
for_in = true;
if (expr1)
{
/* The first expression must be an identifier. */
if (expr1.etype != JSC$EXPR_IDENTIFIER)
JSC$parser_syntax_error ();
}
else
{
/* We must have only one variable declaration. */
if (vars.length != 1)
JSC$parser_syntax_error ();
}
/* The second expressions. */
expr2 = JSC$parser_parse_expr (stream);
if (typeof expr2 == "boolean")
JSC$parser_syntax_error ();
}
else
JSC$parser_syntax_error ();
if (JSC$parser_get_token (stream) != #')')
JSC$parser_syntax_error ();
/* Stmt. */
stmt = JSC$parser_parse_stmt (stream);
if (typeof stmt == "boolean")
JSC$parser_syntax_error ();
if (for_in)
return new JSC$stmt_for_in (ln, vars, expr1, expr2, stmt);
return new JSC$stmt_for (ln, vars, expr1, expr2, expr3, stmt);
}
return false;
}
function JSC$parser_parse_expr (stream)
{
var expr, expr2;
if (typeof (expr = JSC$parser_parse_assignment_expr (stream))
== "boolean")
return false;
/* Check for the comma expression. */
while (JSC$parser_peek_token (stream) == #',')
{
JSC$parser_get_token (stream);
var ln = JSC$parser_token_linenum;
if (typeof (expr2 = JSC$parser_parse_assignment_expr (stream))
== "boolean")
JSC$parser_syntax_error ();
expr = new JSC$expr_comma (ln, expr, expr2);
}
return expr;
}
function JSC$parser_parse_assignment_expr (stream)
{
var expr, expr2, token;
if (typeof (expr = JSC$parser_parse_conditional_expr (stream))
== "boolean")
return false;
if (JSC$parser_expr_is_left_hand_side (expr))
{
token = JSC$parser_peek_token (stream);
if (token == #'=' || token == JSC$tMULA
|| token == JSC$tDIVA || token == JSC$tMODA
|| token == JSC$tADDA || token == JSC$tSUBA
|| token == JSC$tLSIA || token == JSC$tRSIA
|| token == JSC$tRRSA || token == JSC$tANDA
|| token == JSC$tXORA || token == JSC$tORA)
{
JSC$parser_get_token (stream);
var ln = JSC$parser_token_linenum;
expr2 = JSC$parser_parse_assignment_expr (stream);
if (typeof expr2 == "boolean")
JSC$parser_syntax_error ();
expr = new JSC$expr_assignment (ln, token, expr, expr2);
}
}
if (JSC$optimize_constant_folding && expr.constant_folding)
return expr.constant_folding ();
return expr;
}
function JSC$parser_parse_conditional_expr (stream)
{
var expr, expr2, expr3, token;
if (typeof (expr = JSC$parser_parse_logical_or_expr (stream))
== "boolean")
return false;
token = JSC$parser_peek_token (stream);
if (token == #'?')
{
JSC$parser_get_token (stream);
var ln = JSC$parser_token_linenum;
expr2 = JSC$parser_parse_assignment_expr (stream);
if (typeof expr2 == "boolean")
JSC$parser_syntax_error ();
if (JSC$parser_get_token (stream) != #':')
JSC$parser_syntax_error ();
expr3 = JSC$parser_parse_assignment_expr (stream);
if (typeof expr3 == "boolean")
JSC$parser_syntax_error ();
expr = new JSC$expr_quest_colon (ln, expr, expr2, expr3);
}
return expr;
}
function JSC$parser_parse_logical_or_expr (stream)
{
var expr, expr2;
if (typeof (expr = JSC$parser_parse_logical_and_expr (stream))
== "boolean")
return false;
while (JSC$parser_peek_token (stream) == JSC$tOR)
{
JSC$parser_get_token (stream);
var ln = JSC$parser_token_linenum;
expr2 = JSC$parser_parse_logical_and_expr (stream);
if (typeof expr2 == "boolean")
JSC$parser_syntax_error ();
expr = new JSC$expr_logical_or (ln, expr, expr2);
}
return expr;
}
function JSC$parser_parse_logical_and_expr (stream)
{
var expr, expr2;
if (typeof (expr = JSC$parser_parse_bitwise_or_expr (stream))
== "boolean")
return false;
while (JSC$parser_peek_token (stream) == JSC$tAND)
{
JSC$parser_get_token (stream);
var ln = JSC$parser_token_linenum;
expr2 = JSC$parser_parse_bitwise_or_expr (stream);
if (typeof expr2 == "boolean")
JSC$parser_syntax_error ();
expr = new JSC$expr_logical_and (ln, expr, expr2);
}
return expr;
}
function JSC$parser_parse_bitwise_or_expr (stream)
{
var expr, expr2;
if (typeof (expr = JSC$parser_parse_bitwise_xor_expr (stream))
== "boolean")
return false;
while (JSC$parser_peek_token (stream) == #'|')
{
JSC$parser_get_token (stream);
var ln = JSC$parser_token_linenum;
expr2 = JSC$parser_parse_bitwise_xor_expr (stream);
if (typeof expr2 == "boolean")
JSC$parser_syntax_error ();
expr = new JSC$expr_bitwise_or (ln, expr, expr2);
}
return expr;
}
function JSC$parser_parse_bitwise_xor_expr (stream)
{
var expr, expr2;
if (typeof (expr = JSC$parser_parse_bitwise_and_expr (stream))
== "boolean")
return false;
while (JSC$parser_peek_token (stream) == #'^')
{
JSC$parser_get_token (stream);
var ln = JSC$parser_token_linenum;
expr2 = JSC$parser_parse_bitwise_and_expr (stream);
if (typeof expr2 == "boolean")
JSC$parser_syntax_error ();
expr = new JSC$expr_bitwise_xor (ln, expr, expr2);
}
return expr;
}
function JSC$parser_parse_bitwise_and_expr (stream)
{
var expr, expr2;
if (typeof (expr = JSC$parser_parse_equality_expr (stream))
== "boolean")
return false;
while (JSC$parser_peek_token (stream) == #'&')
{
JSC$parser_get_token (stream);
var ln = JSC$parser_token_linenum;
expr2 = JSC$parser_parse_equality_expr (stream);
if (typeof expr2 == "boolean")
JSC$parser_syntax_error ();
expr = new JSC$expr_bitwise_and (ln, expr, expr2);
}
return expr;
}
function JSC$parser_parse_equality_expr (stream)
{
var expr, expr2, token;
if (typeof (expr = JSC$parser_parse_relational_expr (stream))
== "boolean")
return false;
token = JSC$parser_peek_token (stream);
while (token == JSC$tEQUAL || token == JSC$tNEQUAL
|| token == JSC$tSEQUAL || token == JSC$tSNEQUAL)
{
JSC$parser_get_token (stream);
var ln = JSC$parser_token_linenum;
expr2 = JSC$parser_parse_relational_expr (stream);
if (typeof expr2 == "boolean")
JSC$parser_syntax_error ();
expr = new JSC$expr_equality (ln, token, expr, expr2);
token = JSC$parser_peek_token (stream);
}
return expr;
}
function JSC$parser_parse_relational_expr (stream)
{
var expr, expr2, token;
if (typeof (expr = JSC$parser_parse_shift_expr (stream))
== "boolean")
return false;
token = JSC$parser_peek_token (stream);
while (token == #'<' || token == #'>' || token == JSC$tLE
|| token == JSC$tGE)
{
JSC$parser_get_token (stream);
var ln = JSC$parser_token_linenum;
expr2 = JSC$parser_parse_shift_expr (stream);
if (typeof expr2 == "boolean")
JSC$parser_syntax_error ();
expr = new JSC$expr_relational (ln, token, expr, expr2);
token = JSC$parser_peek_token (stream);
}
return expr;
}
function JSC$parser_parse_shift_expr (stream)
{
var expr, expr2, token;
if (typeof (expr = JSC$parser_parse_additive_expr (stream))
== "boolean")
return false;
token = JSC$parser_peek_token (stream);
while (token == JSC$tLSHIFT || token == JSC$tRSHIFT || token == JSC$tRRSHIFT)
{
JSC$parser_get_token (stream);
var ln = JSC$parser_token_linenum;
expr2 = JSC$parser_parse_additive_expr (stream);
if (typeof expr2 == "boolean")
JSC$parser_syntax_error ();
expr = new JSC$expr_shift (ln, token, expr, expr2);
token = JSC$parser_peek_token (stream);
}
return expr;
}
function JSC$parser_parse_additive_expr (stream)
{
var expr, expr2, token;
if (typeof (expr = JSC$parser_parse_multiplicative_expr (stream))
== "boolean")
return false;
token = JSC$parser_peek_token (stream);
while (token == #'+' || token == #'-')
{
JSC$parser_get_token (stream);
var ln = JSC$parser_token_linenum;
expr2 = JSC$parser_parse_multiplicative_expr (stream);
if (typeof expr2 == "boolean")
JSC$parser_syntax_error ();
expr = new JSC$expr_additive (ln, token, expr, expr2);
token = JSC$parser_peek_token (stream);
}
return expr;
}
function JSC$parser_parse_multiplicative_expr (stream)
{
var expr, expr2, token;
if (typeof (expr = JSC$parser_parse_unary_expr (stream)) == "boolean")
return false;
token = JSC$parser_peek_token (stream);
while (token == #'*' || token == #'/' || token == #'%')
{
JSC$parser_get_token (stream);
var ln = JSC$parser_token_linenum;
expr2 = JSC$parser_parse_unary_expr (stream);
if (typeof expr2 == "boolean")
JSC$parser_syntax_error ();
expr = new JSC$expr_multiplicative (ln, token, expr, expr2);
token = JSC$parser_peek_token (stream);
}
return expr;
}
function JSC$parser_parse_unary_expr (stream)
{
var expr, token;
token = JSC$parser_peek_token (stream);
if (token == JSC$tDELETE
|| token == JSC$tVOID
|| token == JSC$tTYPEOF
|| token == JSC$tPLUSPLUS
|| token == JSC$tMINUSMINUS
|| token == #'+'
|| token == #'-'
|| token == #'~'
|| token == #'!')
{
JSC$parser_get_token (stream);
var ln = JSC$parser_token_linenum;
expr = JSC$parser_parse_unary_expr (stream);
if (typeof expr == "boolean")
JSC$parser_syntax_error ();
return new JSC$expr_unary (ln, token, expr);
}
return JSC$parser_parse_postfix_expr (stream);
}
function JSC$parser_parse_postfix_expr (stream)
{
var expr, token;
if (typeof (expr = JSC$parser_parse_left_hand_side_expr (stream))
== "boolean")
return false;
token = JSC$parser_peek_token (stream);
if (token == JSC$tPLUSPLUS || token == JSC$tMINUSMINUS)
{
if (JSC$parser_peek_token_linenum > JSC$parser_token_linenum)
{
if (JSC$warn_missing_semicolon)
JSC$warning (JSC$filename + ":"
+ JSC$parser_token_linenum.toString ()
+ ": warning: automatic semicolon insertion cuts the expression before ++ or --");
}
else
{
JSC$parser_get_token (stream);
var ln = JSC$parser_token_linenum;
return new JSC$expr_postfix (ln, token, expr);
}
}
return expr;
}
function JSC$parser_parse_left_hand_side_expr (stream)
{
var expr, args, token, expr2;
if (typeof (expr = JSC$parser_parse_member_expr (stream))
== "boolean")
return false;
/* Parse the possible first pair of arguments. */
if (JSC$parser_peek_token (stream) == #'(')
{
var ln = JSC$parser_peek_token_linenum;
args = JSC$parser_parse_arguments (stream);
if (typeof args == "boolean")
JSC$parser_syntax_error ();
expr = new JSC$expr_call (ln, expr, args);
}
else
return expr;
/* Parse to possibly following arguments and selectors. */
while ((token = JSC$parser_peek_token (stream)) == #'('
|| token == #'[' || token == #'.')
{
var ln = JSC$parser_peek_token_linenum;
if (token == #'(')
{
args = JSC$parser_parse_arguments (stream);
expr = new JSC$expr_call (ln, expr, args);
}
else if (token == #'[')
{
JSC$parser_get_token (stream);
expr2 = JSC$parser_parse_expr (stream);
if (typeof expr2 == "boolean")
JSC$parser_syntax_error ();
if (JSC$parser_get_token (stream) != #']')
JSC$parser_syntax_error ();
expr = new JSC$expr_object_array (ln, expr, expr2);
}
else
{
JSC$parser_get_token (stream);
if (JSC$parser_get_token (stream) != JSC$tIDENTIFIER)
JSC$parser_syntax_error ();
expr = new JSC$expr_object_property (ln, expr,
JSC$parser_token_value);
}
}
return expr;
}
function JSC$parser_parse_member_expr (stream)
{
var expr, args, token, expr2;
if (typeof (expr = JSC$parser_parse_primary_expr (stream))
== "boolean")
{
token = JSC$parser_peek_token (stream);
if (token == JSC$tNEW)
{
JSC$parser_get_token (stream);
var ln = JSC$parser_token_linenum;
expr = JSC$parser_parse_member_expr (stream);
if (typeof expr == "boolean")
JSC$parser_syntax_error ();
if (JSC$parser_peek_token (stream) == #'(')
{
args = JSC$parser_parse_arguments (stream);
if (typeof args == "boolean")
JSC$parser_syntax_error ();
}
else
return new JSC$expr_new (ln, expr, null);
expr = new JSC$expr_new (ln, expr, args);
}
else
return false;
}
/* Ok, now we have valid starter. */
token = JSC$parser_peek_token (stream);
while (token == #'[' || token == #'.')
{
JSC$parser_get_token (stream);
var ln = JSC$parser_token_linenum;
if (token == #'[')
{
expr2 = JSC$parser_parse_expr (stream);
if (typeof expr2 == "boolean")
JSC$parser_syntax_error ();
if (JSC$parser_get_token (stream) != #']')
JSC$parser_syntax_error ();
expr = new JSC$expr_object_array (ln, expr, expr2);
}
else
{
if (JSC$parser_get_token (stream) != JSC$tIDENTIFIER)
JSC$parser_syntax_error ();
expr = new JSC$expr_object_property (ln, expr,
JSC$parser_token_value);
}
token = JSC$parser_peek_token (stream);
}
return expr;
}
function JSC$parser_parse_primary_expr (stream)
{
var token, val;
token = JSC$parser_peek_token (stream);
var ln = JSC$parser_peek_token_linenum;
if (token == JSC$tTHIS)
val = new JSC$expr_this (ln);
else if (token == JSC$tIDENTIFIER)
val = new JSC$expr_identifier (ln, JSC$parser_peek_token_value);
else if (token == JSC$tFLOAT)
val = new JSC$expr_float (ln, JSC$parser_peek_token_value);
else if (token == JSC$tINTEGER)
val = new JSC$expr_integer (ln, JSC$parser_peek_token_value);
else if (token == JSC$tSTRING)
val = new JSC$expr_string (ln, JSC$parser_peek_token_value);
else if (token == #'/')
{
/*
* Kludge alert! The regular expression constants (/.../) and
* div operands are impossible to distinguish, based only on the
* lexical analysis. Therefore, we need some syntactical
* knowledge when the regular expression constants are possible
* at all. This is the place where they can appear. In all
* other places, the character `/' is interpreted as a div
* operator.
*/
JSC$parser_get_token (stream);
return new JSC$expr_regexp (ln, JSC$lexer_read_regexp_constant (stream));
}
else if (token == JSC$tNULL)
val = new JSC$expr_null (ln);
else if (token == JSC$tTRUE)
val = new JSC$expr_true (ln);
else if (token == JSC$tFALSE)
val = new JSC$expr_false (ln);
else if (token == #'[')
{
/* Array initializer. */
/* TODO: SharpVarDefinition_{opt} */
JSC$parser_get_token (stream);
var items = new Array ();
var pos = 0;
while ((token = JSC$parser_peek_token (stream)) != #']')
{
if (token == #',')
{
JSC$parser_get_token (stream);
items[++pos] = false;
continue;
}
var expr = JSC$parser_parse_assignment_expr (stream);
if (!expr)
JSC$parser_syntax_error ();
items[pos] = expr;
/* Got one expression. It must be followed by ',' or ']'. */
token = JSC$parser_peek_token (stream);
if (token != #',' && token != #']')
JSC$parser_syntax_error ();
}
val = new JSC$expr_array_initializer (ln, items);
}
else if (token == #'{')
{
/* Object literal. */
/* TODO: SharpVarDefinition_{opt} */
JSC$parser_get_token (stream);
var items = new Array ();
while ((token = JSC$parser_peek_token (stream)) != #'}')
{
var pair = new Object ();
token = JSC$parser_get_token (stream);
pair.linenum = JSC$linenum;
pair.id_type = token;
pair.id = JSC$parser_token_value;
if (token != JSC$tIDENTIFIER && token != JSC$tSTRING
&& token != JSC$tINTEGER)
JSC$parser_syntax_error ();
if (JSC$parser_get_token (stream) != #':')
JSC$parser_syntax_error ();
pair.expr = JSC$parser_parse_assignment_expr (stream);
if (!pair.expr)
JSC$parser_syntax_error ();
items.push (pair);
/*
* Got one property, initializer pair. It must be followed
* by ',' or '}'.
*/
token = JSC$parser_peek_token (stream);
if (token == #',')
{
/* Ok, we have more items. */
JSC$parser_get_token (stream);
token = JSC$parser_peek_token (stream);
if (token != JSC$tIDENTIFIER && token != JSC$tSTRING
&& token != JSC$tINTEGER)
JSC$parser_syntax_error ();
}
else if (token != #'}' && token)
JSC$parser_syntax_error ();
}
val = new JSC$expr_object_initializer (ln, items);
}
else if (token == #'(')
{
JSC$parser_get_token (stream);
val = JSC$parser_parse_expr (stream);
if (typeof val == "boolean"
|| JSC$parser_peek_token (stream) != #')')
JSC$parser_syntax_error ();
}
else
return false;
JSC$parser_get_token (stream);
return val;
}
function JSC$parser_parse_arguments (stream)
{
var args, item;
if (JSC$parser_peek_token (stream) != #'(')
return false;
args = new Array ();
JSC$parser_get_token (stream);
while (JSC$parser_peek_token (stream) != #')')
{
item = JSC$parser_parse_assignment_expr (stream);
if (typeof item == "boolean")
JSC$parser_syntax_error ();
args.push (item);
var token = JSC$parser_peek_token (stream);
if (token == #',')
JSC$parser_get_token (stream);
else if (token != #')')
JSC$parser_syntax_error ();
}
JSC$parser_get_token (stream);
return args;
}
/*
Local variables:
mode: c
End:
*/
/*
* Grammar components.
* Copyright (c) 1998 New Generation Software (NGS) Oy
*
* Author: Markku Rossi <mtr@ngs.fi>
*/
/*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA
*/
/*
* $Source: /usr/local/cvsroot/ngs/js/jsc/compiler.js,v $
* $Id: compiler.js,v 1.42 1999/01/11 09:01:33 mtr Exp $
*/
/* General helpers. */
function JSC$gram_reset ()
{
JSC$label_count = 1;
JSC$cont_break = new JSC$ContBreak ();
}
function JSC$alloc_label (num_labels)
{
JSC$label_count += num_labels;
return JSC$label_count - num_labels;
}
function JSC$format_label (num)
{
return ".L" + num.toString ();
}
function JSC$count_locals_from_stmt_list (list)
{
var i;
/* First, count how many variables we need at the toplevel. */
var lcount = 0;
for (i = 0; i < list.length; i++)
lcount += list[i].count_locals (false);
/* Second, count the maximum amount needed by the nested blocks. */
var rmax = 0;
for (i = 0; i < list.length; i++)
{
var rc = list[i].count_locals (true);
if (rc > rmax)
rmax = rc;
}
return lcount + rmax;
}
/*
* The handling of the `continue' and `break' labels for looping
* constructs. The variable `JSC$cont_break' holds an instance of
* JSC$ContBreak class. The instance contains a valid chain of
* looping constructs and the currently active with and try testing
* levels. The actual `continue', `break', and `return' statements
* investigate the chain and generate appropriate `with_pop' and
* `try_pop' operands.
*
* If the instance variable `inswitch' is true, the continue statement
* is inside a switch statement. In this case, the continue statement
* must pop one item from the stack. That item is the value of the
* case expression.
*/
function JSC$ContBreakFrame (loop_break, loop_continue, inswitch, label, next)
{
this.loop_break = loop_break;
this.loop_continue = loop_continue;
this.inswitch = inswitch;
this.label = label;
this.next = next;
this.with_nesting = 0;
this.try_nesting = 0;
}
function JSC$ContBreak ()
{
this.top = new JSC$ContBreakFrame (null, null, false, null);
}
new JSC$ContBreak ();
function JSC$ContBreak$push (loop_break, loop_continue, inswitch, label)
{
this.top = new JSC$ContBreakFrame (loop_break, loop_continue, inswitch,
label, this.top);
}
JSC$ContBreak.prototype.push = JSC$ContBreak$push;
function JSC$ContBreak$pop ()
{
if (this.top == null)
error ("jsc: internal error: continue-break stack underflow");
this.top = this.top.next;
}
JSC$ContBreak.prototype.pop = JSC$ContBreak$pop;
/*
* Count the currently active `try' nesting that should be removed on
* `return' statement.
*/
function JSC$ContBreak$try_return_nesting ()
{
var f;
var count = 0;
for (f = this.top; f; f = f.next)
count += f.try_nesting;
return count;
}
JSC$ContBreak.prototype.try_return_nesting = JSC$ContBreak$try_return_nesting;
/*
* Count currently active `with' nesting that should be removed on
* `continue' or `break' statement.
*/
function JSC$ContBreak$count_with_nesting (label)
{
var f;
var count = 0;
for (f = this.top; f; f = f.next)
{
count += f.with_nesting;
if (label)
{
if (f.label == label)
break;
}
else
if (f.loop_continue)
break;
}
return count;
}
JSC$ContBreak.prototype.count_with_nesting = JSC$ContBreak$count_with_nesting;
/*
* Count the currently active `try' nesting that should be removed on
* `continue' or `break' statement.
*/
function JSC$ContBreak$count_try_nesting (label)
{
var f;
var count = 0;
for (f = this.top; f; f = f.next)
{
count += f.try_nesting;
if (label)
{
if (f.label == label)
break;
}
else
if (f.loop_continue)
break;
}
return count;
}
JSC$ContBreak.prototype.count_try_nesting = JSC$ContBreak$count_try_nesting;
function JSC$ContBreak$count_switch_nesting (label)
{
var f;
var count = 0;
for (f = this.top; f; f = f.next)
{
if (f.inswitch)
count++;
if (label)
{
if (f.label == label)
break;
}
else
if (f.loop_continue)
break;
}
return count;
}
JSC$ContBreak.prototype.count_switch_nesting
= JSC$ContBreak$count_switch_nesting;
function JSC$ContBreak$get_continue (label)
{
var f;
for (f = this.top; f; f = f.next)
if (label)
{
if (f.label == label)
return f.loop_continue;
}
else
if (f.loop_continue)
return f.loop_continue;
return null;
}
JSC$ContBreak.prototype.get_continue = JSC$ContBreak$get_continue;
function JSC$ContBreak$get_break (label)
{
var f;
for (f = this.top; f; f = f.next)
if (label)
{
if (f.label == label)
return f.loop_break;
}
else
if (f.loop_break)
return f.loop_break;
return null;
}
JSC$ContBreak.prototype.get_break = JSC$ContBreak$get_break;
function JSC$ContBreak$is_unique_label (label)
{
var f;
for (f = this.top; f; f = f.next)
if (f.label == label)
return false;
return true;
}
JSC$ContBreak.prototype.is_unique_label = JSC$ContBreak$is_unique_label;
JSC$cont_break = null;
/* Function declaration. */
function JSC$function_declaration (ln, lbrace_ln, name, name_given, args,
block, use_arguments_prop)
{
this.linenum = ln;
this.lbrace_linenum = lbrace_ln;
this.name = name;
this.name_given = name_given;
this.args = args;
this.block = block;
this.use_arguments_prop = use_arguments_prop;
this.asm = JSC$function_declaration_asm;
}
function JSC$function_declaration_asm ()
{
var i, a;
/* Define arguments. */
JSC$ns.push_frame ();
for (i = 0, a = 2; i < this.args.length; i++, a++)
JSC$ns.define_symbol (this.args[i], JSC$SCOPE_ARG, a, this.linenum);
/* Define the function name to be a global symbol. */
new JSC$ASM_symbol (this.linenum, this.name).link ();
/* Check that function gets the required amount of arguments. */
new JSC$ASM_load_arg (this.lbrace_linenum, 1).link ();
new JSC$ASM_add_2_i (this.lbrace_linenum).link ();
new JSC$ASM_min_args (this.lbrace_linenum, this.args.length + 2).link ();
/* Count how many local variables we need. */
var num_locals = JSC$count_locals_from_stmt_list (this.block);
/* Is the `arguments' property of function instance used? */
if (this.use_arguments_prop)
num_locals++;
if (num_locals > 0)
{
new JSC$ASM_locals (this.lbrace_linenum, num_locals).link ();
if (this.use_arguments_prop)
{
/*
* Create an array for the arguments array and store it to
* the first local variable.
*/
var ln = this.lbrace_linenum;
var locn = JSC$ns.alloc_local ();
JSC$ns.define_symbol ("arguments", JSC$SCOPE_LOCAL, locn, ln);
new JSC$ASM_const_i0 (ln).link ();
new JSC$ASM_load_global (ln, "Array").link ();
new JSC$ASM_new (ln).link ();
new JSC$ASM_swap (ln).link ();
new JSC$ASM_apop (ln, 2).link ();
new JSC$ASM_store_local (ln, locn).link ();
/* Push individual argumens to the array. */
/* Init the loop counter. */
new JSC$ASM_const_i0 (ln).link ();
var l_loop = new JSC$ASM_label ();
var l_out = new JSC$ASM_label ();
l_loop.link ();
/* Check if we'r done. */
new JSC$ASM_dup (ln).link ();
new JSC$ASM_load_arg (ln, 1).link ();
new JSC$ASM_cmp_ge (ln).link ();
new JSC$ASM_iftrue_b (ln, l_out).link ();
/* Load the nth argument to the top of the stack. */
new JSC$ASM_dup (ln).link ();
new JSC$ASM_add_2_i (ln).link ();
new JSC$ASM_load_nth_arg (ln).link ();
/* Push it to the array. */
new JSC$ASM_const_i1 (ln).link ();
new JSC$ASM_load_local (ln, locn).link ();
new JSC$ASM_call_method (ln, "push").link ();
new JSC$ASM_pop_n (ln, 4).link ();
/* Increment loop counter and continue. */
new JSC$ASM_add_1_i (ln).link ();
new JSC$ASM_jmp (ln, l_loop).link ();
/* We'r done. */
l_out.link ();
/* Pop the loop counter. */
new JSC$ASM_pop (ln).link ();
}
}
/* Assembler for our body. */
for (i = 0; i < this.block.length; i++)
this.block[i].asm ();
/*
* Every function must return something. We could check if all
* control flows in this function ends to a return, but that would
* bee too hard... Just append a return const_undefined. The optimizer
* will remove it if it is not needed.
*/
var ln;
if (this.block.length > 0)
ln = this.block[this.block.length - 1].linenum;
else
ln = this.linenum;
new JSC$ASM_const_undefined (ln).link ();
new JSC$ASM_return (ln).link ();
/* Pop our namespace. */
JSC$ns.pop_frame ();
}
function JSC$zero_function ()
{
return 0;
}
/*
* Statements.
*/
/* Block. */
function JSC$stmt_block (ln, list)
{
this.stype = JSC$STMT_BLOCK;
this.linenum = ln;
this.stmts = list;
this.asm = JSC$stmt_block_asm;
this.count_locals = JSC$stmt_block_count_locals;
}
function JSC$stmt_block_asm ()
{
JSC$ns.push_frame ();
/* Assembler for our stmts. */
var i;
for (i = 0; i < this.stmts.length; i++)
this.stmts[i].asm ();
JSC$ns.pop_frame ();
}
function JSC$stmt_block_count_locals (recursive)
{
if (!recursive)
return 0;
return JSC$count_locals_from_stmt_list (this.stmts);
}
/* Function declaration. */
function JSC$stmt_function_declaration (ln, container_id, function_id,
given_id)
{
this.stype = JSC$STMT_FUNCTION_DECLARATION;
this.linenum = ln;
this.container_id = container_id;
this.function_id = function_id;
this.given_id = given_id;
this.asm = JSC$stmt_function_declaration_asm;
this.count_locals = JSC$zero_function;
}
function JSC$stmt_function_declaration_asm ()
{
new JSC$ASM_load_global (this.linenum, this.function_id).link ();
new JSC$ASM_load_global (this.linenum, this.container_id).link ();
new JSC$ASM_store_property (this.linenum, this.given_id).link ();
}
/* Empty */
function JSC$stmt_empty (ln)
{
this.stype = JSC$STMT_EMPTY;
this.linenum = ln;
this.asm = JSC$stmt_empty_asm;
this.count_locals = JSC$zero_function;
}
function JSC$stmt_empty_asm ()
{
/* Nothing here. */
}
/* Continue. */
function JSC$stmt_continue (ln, label)
{
this.stype = JSC$STMT_CONTINUE;
this.linenum = ln;
this.label = label;
this.asm = JSC$stmt_continue_asm;
this.count_locals = JSC$zero_function;
}
function JSC$stmt_continue_asm ()
{
var l_cont = JSC$cont_break.get_continue (this.label);
if (l_cont == null)
{
if (this.label)
error (JSC$filename + ":" + this.linenum.toString ()
+ ": label `" + this.label
+ "' not found for continue statement");
else
error (JSC$filename + ":" + this.linenum.toString ()
+ ": continue statement not within a loop");
}
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 ();
nesting = JSC$cont_break.count_switch_nesting (this.label);
if (nesting > 0)
{
/* Pop the value of the switch expression. */
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_cont).link ();
}
/* Break. */
function JSC$stmt_break (ln, label)
{
this.stype = JSC$STMT_BREAK;
this.linenum = ln;
this.label = label;
this.asm = JSC$stmt_break_asm;
this.count_locals = JSC$zero_function;
}
function JSC$stmt_break_asm ()
{
var l_break = JSC$cont_break.get_break (this.label);
if (l_break == null)
{
if (this.label)
error (JSC$filename + ":" + this.linenum.toString ()
+ ": label `" + this.label
+ "' not found for break statement");
else
error (JSC$filename + ":" + this.linenum.toString()
+ ": break statement not within a loop or switch");
}
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 ();
}
/* Labeled statement. */
function JSC$stmt_labeled_stmt (ln, id, stmt)
{
this.stype = JSC$STMT_LABELED_STMT;
this.linenum = ln;
this.id = id;
this.stmt = stmt;
this.asm = JSC$stmt_labeled_stmt_asm;
this.count_locals = JSC$stmt_labeled_stmt_count_locals;
}
function JSC$stmt_labeled_stmt_asm ()
{
var l_continue = new JSC$ASM_label ();
var l_break = new JSC$ASM_label ();
/*
* It is an error if we already have a labeled statement with the
* same id.
*/
if (!JSC$cont_break.is_unique_label (this.id))
error (JSC$filename + ":" + this.linenum.toString ()
+ ": labeled statement is enclosed by another labeled statement "
+ "with the same label");
/* Push the break and continue labels. */
JSC$cont_break.push (l_break, l_continue, false, this.id);
/* Dump the assembler. */
l_continue.link ();
this.stmt.asm ();
l_break.link ();
/* And we'r done with out label scope. */
JSC$cont_break.pop ();
}
function JSC$stmt_labeled_stmt_count_locals (recursive)
{
return this.stmt.count_locals (recursive);
}
/* Expression. */
function JSC$stmt_expr (expr)
{
this.stype = JSC$STMT_EXPR;
this.linenum = expr.linenum;
this.expr = expr;
this.asm = JSC$stmt_expr_asm;
this.count_locals = JSC$zero_function;
}
function JSC$stmt_expr_asm ()
{
this.expr.asm ();
new JSC$ASM_pop (this.linenum).link ();
}
/* If. */
function JSC$stmt_if (ln, expr, stmt1, stmt2)
{
this.stype = JSC$STMT_IF;
this.linenum = ln;
this.expr = expr;
this.stmt1 = stmt1;
this.stmt2 = stmt2;
this.asm = JSC$stmt_if_asm;
this.count_locals = JSC$stmt_if_count_locals;
}
function JSC$stmt_if_asm ()
{
this.expr.asm ();
var l1 = new JSC$ASM_label ();
var l2 = new JSC$ASM_label ();
if (JSC$optimize_type && this.expr.lang_type
&& this.expr.lang_type == JSC$JS_BOOLEAN)
new JSC$ASM_iffalse_b (this.linenum, l1).link ();
else
new JSC$ASM_iffalse (this.linenum, l1).link ();
/* Code for the then branch. */
this.stmt1.asm ();
new JSC$ASM_jmp (this.linenum, l2).link ();
/* Code for the else branch. */
l1.link ();
if (this.stmt2 != null)
this.stmt2.asm ();
/* Done label. */
l2.link ();
}
function JSC$stmt_if_count_locals (recursive)
{
var lcount;
if (!recursive)
{
lcount = 0;
if (this.stmt1.stype == JSC$STMT_VARIABLE)
lcount += this.stmt1.list.length;
if (this.stmt2 != null && this.stmt2.stype == JSC$STMT_VARIABLE)
lcount += this.stmt2.list.length;
}
else
{
lcount = this.stmt1.count_locals (true);
if (this.stmt2)
{
var c = this.stmt2.count_locals (true);
if (c > lcount)
lcount = c;
}
}
return lcount;
}
/* Do...While. */
function JSC$stmt_do_while (ln, expr, stmt)
{
this.stype = JSC$STMT_DO_WHILE;
this.linenum = ln;
this.expr = expr;
this.stmt = stmt;
this.asm = JSC$stmt_do_while_asm;
this.count_locals = JSC$stmt_do_while_count_locals;
}
function JSC$stmt_do_while_asm ()
{
var l1 = new JSC$ASM_label ();
var l2 = new JSC$ASM_label ();
var l3 = new JSC$ASM_label ();
/* Loop label. */
l1.link ();
/* Body. */
JSC$cont_break.push (l3, l2, false, null);
this.stmt.asm ();
JSC$cont_break.pop ();
/* Condition & continue. */
l2.link ();
this.expr.asm ();
if (JSC$optimize_type && this.expr.lang_type
&& this.expr.lang_type == JSC$JS_BOOLEAN)
new JSC$ASM_iftrue_b (this.linenum, l1).link ();
else
new JSC$ASM_iftrue (this.linenum, l1).link ();
/* Break label. */
l3.link ();
}
function JSC$stmt_do_while_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);
}
/* While. */
function JSC$stmt_while (ln, expr, stmt)
{
this.stype = JSC$STMT_WHILE;
this.linenum = ln;
this.expr = expr;
this.stmt = stmt;
this.asm = JSC$stmt_while_asm;
this.count_locals = JSC$stmt_while_count_locals;
}
function JSC$stmt_while_asm ()
{
var l1 = new JSC$ASM_label ();
var l2 = new JSC$ASM_label ();
/* Loop label. */
l1.link ();
/* Condition. */
this.expr.asm ();
if (JSC$optimize_type && this.expr.lang_type
&& this.expr.lang_type == JSC$JS_BOOLEAN)
new JSC$ASM_iffalse_b (this.linenum, l2).link ();
else
new JSC$ASM_iffalse (this.linenum, l2).link ();
/* Body. */
JSC$cont_break.push (l2, l1, false, null);
this.stmt.asm ();
JSC$cont_break.pop ();
/* Goto loop. */
new JSC$ASM_jmp (this.linenum, l1).link ();
/* Break label. */
l2.link ();
}
function JSC$stmt_while_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);
}
/* For. */
function JSC$stmt_for (ln, vars, e1, e2, e3, stmt)
{
this.stype = JSC$STMT_FOR;
this.linenum = ln;
this.vars = vars;
this.expr1 = e1;
this.expr2 = e2;
this.expr3 = e3;
this.stmt = stmt;
this.asm = JSC$stmt_for_asm;
this.count_locals = JSC$stmt_for_count_locals;
}
function JSC$stmt_for_asm ()
{
/* Code for the init. */
if (this.vars)
{
/* We have our own variable scope. */
JSC$ns.push_frame ();
var i;
for (i = 0; i < this.vars.length; i++)
{
var decl = this.vars[i];
JSC$ns.define_symbol (decl.id, JSC$SCOPE_LOCAL,
JSC$ns.alloc_local (), this.linenum);
/* Possible init. */
if (decl.expr)
{
decl.expr.asm ();
var r = JSC$ns.lookup_symbol (decl.id);
if (r == null || r.scope != JSC$SCOPE_LOCAL)
error (JSC$filename + ":" + this.liennum.toString ()
+ ": internal compiler error in local variable "
+ "declaration in for statement");
new JSC$ASM_store_local (this.linenum, r.value).link ();
}
}
}
else if (this.expr1 != null)
{
this.expr1.asm ();
new JSC$ASM_pop (this.linenum).link ();
}
var l1 = new JSC$ASM_label ();
var l2 = new JSC$ASM_label ();
var l3 = new JSC$ASM_label ();
/* Loop label. */
l1.link ();
/* Condition. */
var type_op = false;
if (this.expr2 != null)
{
this.expr2.asm ();
if (JSC$optimize_type && this.expr2.lang_type
&& this.expr2.lang_type == JSC$JS_BOOLEAN)
type_op = true;
}
else
{
new JSC$ASM_const_true (this.linenum).link ();
type_op = JSC$optimize_type;
}
if (type_op)
new JSC$ASM_iffalse_b (this.linenum, l3).link ();
else
new JSC$ASM_iffalse (this.linenum, l3).link ();
JSC$cont_break.push (l3, l2, false, null);
/* Body. */
this.stmt.asm ();
JSC$cont_break.pop ();
/* Continue label. */
l2.link ();
/* Increment. */
if (this.expr3 != null)
{
this.expr3.asm ();
new JSC$ASM_pop (this.linenum).link ();
}
/* Goto loop. */
new JSC$ASM_jmp (this.linenum, l1).link ();
/* Break label. */
l3.link ();
if (this.vars)
/* Pop the local variable scope. */
JSC$ns.pop_frame ();
}
function JSC$stmt_for_count_locals (recursive)
{
var count = 0;
if (recursive)
{
if (this.vars)
count += this.vars.length;
count += this.stmt.count_locals (true);
}
else
{
if (this.stmt.stype == JSC$STMT_VARIABLE)
count += this.stmt.list.length;
}
return count;
}
/* For...in. */
function JSC$stmt_for_in (ln, vars, e1, e2, stmt)
{
this.stype = JSC$STMT_FOR_IN;
this.linenum = ln;
this.vars = vars;
this.expr1 = e1;
this.expr2 = e2;
this.stmt = stmt;
this.asm = JSC$stmt_for_in_asm;
this.count_locals = JSC$stmt_for_in_count_locals;
}
function JSC$stmt_for_in_asm ()
{
var local_id;
if (this.vars)
{
/* We need our own variable scope here. */
JSC$ns.push_frame ();
var decl = this.vars[0];
local_id = JSC$ns.alloc_local ();
JSC$ns.define_symbol (decl.id, JSC$SCOPE_LOCAL, local_id, this.linenum);
/* Possible init. */
if (decl.expr)
{
decl.expr.asm ();
new JSC$ASM_store_local (this.linenum, local_id).link ();
}
}
/* Init the world. */
this.expr2.asm ();
new JSC$ASM_dup (this.linenum).link ();
new JSC$ASM_const_i0 (this.linenum).link ();
new JSC$ASM_swap (this.linenum).link ();
new JSC$ASM_const_i0 (this.linenum).link ();
var l_loop = new JSC$ASM_label ();
var l_cont = new JSC$ASM_label ();
var l_iffalse_b = new JSC$ASM_label ();
var l_break = new JSC$ASM_label ();
/* Loop label. */
l_loop.link ();
/* Fetch nth. */
new JSC$ASM_nth (this.linenum).link ();
new JSC$ASM_iffalse_b (this.linenum, l_iffalse_b).link ();
/* Store value to variable. */
if (this.vars)
new JSC$ASM_store_local (this.linenum, local_id).link ();
else
JSC$asm_expr_lvalue_store_asm (this.expr1);
/* Body. */
JSC$cont_break.push (l_break, l_cont, false, null);
this.stmt.asm ();
JSC$cont_break.pop ();
/* Continue label. */
l_cont.link ();
/* Increment. */
new JSC$ASM_const_i1 (this.linenum).link ();
new JSC$ASM_add (this.linenum).link ();
new JSC$ASM_dup (this.linenum).link ();
new JSC$ASM_roll (this.linenum, -3).link ();
new JSC$ASM_dup (this.linenum).link ();
new JSC$ASM_roll (this.linenum, 4).link ();
new JSC$ASM_swap (this.linenum).link ();
/* Goto loop. */
new JSC$ASM_jmp (this.linenum, l_loop).link ();
/* Out label. */
l_iffalse_b.link ();
new JSC$ASM_pop (this.linenum).link ();
/* Break label. */
l_break.link ();
new JSC$ASM_pop_n (this.linenum, 2).link ();
if (this.vars)
/* Pop the variable scope. */
JSC$ns.pop_frame ();
}
function JSC$stmt_for_in_count_locals (recursive)
{
var count = 0;
if (recursive)
{
if (this.vars)
count++;
count += this.stmt.count_locals (true);
}
else
{
if (this.stmt.stype == JSC$STMT_VARIABLE)
count += this.stmt.list.length;
}
return count;
}
/* Variable. */
function JSC$stmt_variable (ln, list)
{
this.stype = JSC$STMT_VARIABLE;
this.linenum = ln;
this.global_level = false;
this.list = list;
this.asm = JSC$stmt_variable_asm;
this.count_locals = JSC$stmt_variable_count_locals;
}
function JSC$stmt_variable_asm ()
{
var j, r;
/* Define all local variables to our namespace. */
for (j = 0; j < this.list.length; j++)
{
var i = this.list[j];
if (!this.global_level)
JSC$ns.define_symbol (i.id, JSC$SCOPE_LOCAL,
JSC$ns.alloc_local (), this.linenum);
if (i.expr)
{
i.expr.asm ();
if (this.global_level)
new JSC$ASM_store_global (this.linenum, i.id).link ();
else
{
r = JSC$ns.lookup_symbol (i.id);
if (r == null || r.scope != JSC$SCOPE_LOCAL)
error (JSC$filename + ":" + this.linenum.toString()
+ ": internal compiler error in local variable declaration");
new JSC$ASM_store_local (this.linenum, r.value).link ();
}
}
}
}
function JSC$stmt_variable_count_locals (recursive)
{
if (!recursive)
{
if (this.global_level)
/* We define these as global variables. */
return 0;
return this.list.length;
}
return 0;
}
function JSC$var_declaration (id, expr)
{
this.id = id;
this.expr = expr;
}
/*
* Expressions.
*/
/* This. */
function JSC$expr_this (ln)
{
this.etype = JSC$EXPR_THIS;
this.linenum = ln;
this.asm = JSC$expr_this_asm;
}
function JSC$expr_this_asm ()
{
new JSC$ASM_load_arg (this.linenum, 0).link ();
}
/* Identifier. */
function JSC$expr_identifier (ln, value)
{
this.etype = JSC$EXPR_IDENTIFIER;
this.linenum = ln;
this.value = value;
this.asm = JSC$expr_identifier_asm;
}
function JSC$expr_identifier_asm ()
{
JSC$asm_expr_lvalue_load_asm (this);
}
/* Float. */
function JSC$expr_float (ln, value)
{
this.etype = JSC$EXPR_FLOAT;
this.lang_type = JSC$JS_FLOAT;
this.linenum = ln;
this.value = value;
this.asm = JSC$expr_float_asm;
}
function JSC$expr_float_asm ()
{
new JSC$ASM_const (this.linenum, this.value).link ();
}
/* Integer. */
function JSC$expr_integer (ln, value)
{
this.etype = JSC$EXPR_INTEGER;
this.lang_type = JSC$JS_INTEGER;
this.linenum = ln;
this.value = value;
this.asm = JSC$expr_integer_asm;
}
function JSC$expr_integer_asm ()
{
if (this.value == 0)
new JSC$ASM_const_i0 (this.linenum).link ();
else if (this.value == 1)
new JSC$ASM_const_i1 (this.linenum).link ();
else if (this.value == 2)
new JSC$ASM_const_i2 (this.linenum).link ();
else if (this.value == 3)
new JSC$ASM_const_i3 (this.linenum).link ();
else
new JSC$ASM_const_i (this.linenum, this.value).link ();
}
/* String. */
function JSC$expr_string (ln, value)
{
this.etype = JSC$EXPR_STRING;
this.lang_type = JSC$JS_STRING;
this.linenum = ln;
this.value = value;
this.asm = JSC$expr_string_asm;
}
function JSC$expr_string_asm ()
{
new JSC$ASM_const (this.linenum, this.value).link ();
}
/* Regexp. */
function JSC$expr_regexp (ln, value)
{
this.etype = JSC$EXPR_REGEXP;
this.lang_type = JSC$JS_BUILTIN;
this.linenum = ln;
this.value = value;
this.asm = JSC$expr_regexp_asm;
}
function JSC$expr_regexp_asm ()
{
new JSC$ASM_const (this.linenum, this.value).link ();
}
/* Array initializer. */
function JSC$expr_array_initializer (ln, items)
{
this.etype = JSC$EXPR_ARRAY_INITIALIZER;
this.lang_type = JSC$JS_ARRAY;
this.linenum = ln;
this.items = items;
this.asm = JSC$expr_array_initializer_asm;
}
function JSC$expr_array_initializer_asm ()
{
/* Generate assembler for the individual items. */
var i;
for (i = this.items.length - 1; i >= 0; i--)
{
if (this.items[i])
this.items[i].asm ();
else
new JSC$ASM_const_undefined (this.linenum).link ();
}
/*
* The number of items as a negative integer. The Array object's
* constructor knows that if the number of arguments is negative, it
* is called from the array initializer. Why? Because the code:
*
* new Array (5);
*
* creates an array of length of 5, but code:
*
* [5]
*
* creates an array with one item: integer number five. These cases
* must be separatable from the code and that's why the argument
* counts for the array initializers are negative.
*/
new JSC$ASM_const (this.linenum, -this.items.length).link ();
/* Call the constructor. */
new JSC$ASM_load_global (this.linenum, "Array").link ();
new JSC$ASM_new (this.linenum).link ();
new JSC$ASM_swap (this.linenum).link ();
new JSC$ASM_apop (this.linenum, this.items.length + 2).link ();
}
/* Object initializer. */
function JSC$expr_object_initializer (ln, items)
{
this.etype = JSC$EXPR_OBJECT_INITIALIZER;
this.lang_type = JSC$JS_OBJECT;
this.linenum = ln;
this.items = items;
this.asm = JSC$expr_object_initializer_asm;
}
function JSC$expr_object_initializer_asm ()
{
/* Create a new object. */
new JSC$ASM_const_i0 (this.linenum).link ();
new JSC$ASM_load_global (this.linenum, "Object").link ();
new JSC$ASM_new (this.linenum).link ();
new JSC$ASM_swap (this.linenum).link ();
new JSC$ASM_apop (this.linenum, 2).link ();
/* Insert the items. */
for (var i = 0; i < this.items.length; i++)
{
var item = this.items[i];
new JSC$ASM_dup (item.linenum).link ();
item.expr.asm ();
new JSC$ASM_swap (item.linenum).link ();
switch (item.id_type)
{
case JSC$tIDENTIFIER:
new JSC$ASM_store_property (item.linenum, item.id).link ();
break;
case JSC$tSTRING:
new JSC$ASM_const (item.linenum, item.id).link ();
new JSC$ASM_store_array (item.linenum).link ();
break;
case JSC$tINTEGER:
switch (item.id)
{
case 0:
new JSC$ASM_const_i0 (item.linenum).link ();
break;
case 1:
new JSC$ASM_const_i1 (item.linenum).link ();
break;
case 2:
new JSC$ASM_const_i2 (item.linenum).link ();
break;
case 3:
new JSC$ASM_const_i3 (item.linenum).link ();
break;
default:
new JSC$ASM_const_i (item.linenum, item.id).link ();
break;
}
new JSC$ASM_store_array (item.linenum).link ();
break;
}
}
}
/* Null. */
function JSC$expr_null (ln)
{
this.etype = JSC$EXPR_NULL;
this.lang_type = JSC$JS_NULL;
this.linenum = ln;
this.asm = JSC$expr_null_asm;
}
function JSC$expr_null_asm ()
{
new JSC$ASM_const_null (this.linenum).link ();
}
/* True. */
function JSC$expr_true (ln)
{
this.etype = JSC$EXPR_TRUE;
this.lang_type = JSC$JS_BOOLEAN;
this.linenum = ln;
this.asm = JSC$expr_true_asm;
}
function JSC$expr_true_asm ()
{
new JSC$ASM_const_true (this.linenum).link ();
}
/* False. */
function JSC$expr_false (ln)
{
this.etype = JSC$EXPR_FALSE;
this.lang_type = JSC$JS_BOOLEAN;
this.linenum = ln;
this.asm = JSC$expr_false_asm;
}
function JSC$expr_false_asm ()
{
new JSC$ASM_const_false (this.linenum).link ();
}
/* Multiplicative expr. */
function JSC$expr_multiplicative (ln, type, e1, e2)
{
this.etype = JSC$EXPR_MULTIPLICATIVE;
this.linenum = ln;
this.type = type;
this.e1 = e1;
this.e2 = e2;
this.asm = JSC$expr_multiplicative_asm;
}
function JSC$expr_multiplicative_asm ()
{
this.e1.asm ();
this.e2.asm ();
if (this.type == #'*')
new JSC$ASM_mul (this.linenum).link ();
else if (this.type == #'/')
new JSC$ASM_div (this.linenum).link ();
else
new JSC$ASM_mod (this.linenum).link ();
}
/* Additive expr. */
function JSC$expr_additive (ln, type, e1, e2)
{
this.etype = JSC$EXPR_ADDITIVE;
this.linenum = ln;
this.type = type;
this.e1 = e1;
this.e2 = e2;
this.asm = JSC$expr_additive_asm;
this.constant_folding = JSC$expr_additive_constant_folding;
}
function JSC$expr_additive_asm ()
{
this.e1.asm ();
this.e2.asm ();
if (this.type == #'+')
new JSC$ASM_add (this.linenum).link ();
else
new JSC$ASM_sub (this.linenum).link ();
}
function JSC$expr_additive_constant_folding ()
{
if (this.e1.constant_folding)
this.e1 = this.e1.constant_folding ();
if (this.e2.constant_folding)
this.e2 = this.e2.constant_folding ();
/* This could be smarter. */
if (this.e1.lang_type && this.e2.lang_type
&& this.e1.lang_type == this.e2.lang_type)
{
switch (this.e1.lang_type)
{
case JSC$JS_INTEGER:
return new JSC$expr_integer (this.linenum,
this.type == #'+'
? this.e1.value + this.e2.value
: this.e1.value - this.e2.value);
break;
case JSC$JS_FLOAT:
return new JSC$expr_float (this.linenum,
this.type == #'+'
? this.e1.value + this.e2.value
: this.e1.value - this.e2.value);
break;
case JSC$JS_STRING:
if (this.type == #'+')
/* Only the addition is available for the strings. */
return new JSC$expr_string (this.linenum,
this.e1.value + this.e2.value);
break;
default:
/* FALLTHROUGH */
break;
}
}
return this;
}
/* Shift expr. */
function JSC$expr_shift (ln, type, e1, e2)
{
this.etype = JSC$EXPR_SHIFT;
this.linenum = ln;
this.type = type;
this.e1 = e1;
this.e2 = e2;
this.asm = JSC$expr_shift_asm;
}
function JSC$expr_shift_asm ()
{
this.e1.asm ();
this.e2.asm ();
if (this.type == JSC$tLSHIFT)
new JSC$ASM_shift_left (this.linenum).link ();
else if (this.type == JSC$tRSHIFT)
new JSC$ASM_shift_right (this.linenum).link ();
else
new JSC$ASM_shift_rright (this.linenum).link ();
}
/* Relational expr. */
function JSC$expr_relational (ln, type, e1, e2)
{
this.etype = JSC$EXPR_RELATIONAL;
this.lang_type = JSC$JS_BOOLEAN;
this.linenum = ln;
this.type = type;
this.e1 = e1;
this.e2 = e2;
this.asm = JSC$expr_relational_asm;
}
function JSC$expr_relational_asm ()
{
this.e1.asm ();
this.e2.asm ();
if (this.type == #'<')
new JSC$ASM_cmp_lt (this.linenum).link ();
else if (this.type == #'>')
new JSC$ASM_cmp_gt (this.linenum).link ();
else if (this.type == JSC$tLE)
new JSC$ASM_cmp_le (this.linenum).link ();
else
new JSC$ASM_cmp_ge (this.linenum).link ();
}
/* Equality expr. */
function JSC$expr_equality (ln, type, e1, e2)
{
this.etype = JSC$EXPR_EQUALITY;
this.lang_type = JSC$JS_BOOLEAN;
this.linenum = ln;
this.type = type;
this.e1 = e1;
this.e2 = e2;
this.asm = JSC$expr_equality_asm;
}
function JSC$expr_equality_asm ()
{
this.e1.asm ();
this.e2.asm ();
switch (this.type)
{
case JSC$tEQUAL:
new JSC$ASM_cmp_eq (this.linenum).link ();
break;
case JSC$tNEQUAL:
new JSC$ASM_cmp_ne (this.linenum).link ();
break;
case JSC$tSEQUAL:
new JSC$ASM_cmp_seq (this.linenum).link ();
break;
case JSC$tSNEQUAL:
new JSC$ASM_cmp_sne (this.linenum).link ();
break;
default:
error ("jsc: expr_equality: internal compiler error");
break;
}
}
/* Bitwise and expr. */
function JSC$expr_bitwise_and (ln, e1, e2)
{
this.etype = JSC$EXPR_BITWISE;
this.linenum = ln;
this.e1 = e1;
this.e2 = e2;
this.asm = JSC$expr_bitwise_and_asm;
}
function JSC$expr_bitwise_and_asm ()
{
this.e1.asm ();
this.e2.asm ();
new JSC$ASM_and (this.linenum).link ();
}
/* Bitwise or expr. */
function JSC$expr_bitwise_or (ln, e1, e2)
{
this.etype = JSC$EXPR_BITWISE;
this.linenum = ln;
this.e1 = e1;
this.e2 = e2;
this.asm = JSC$expr_bitwise_or_asm;
}
function JSC$expr_bitwise_or_asm ()
{
this.e1.asm ();
this.e2.asm ();
new JSC$ASM_or (this.linenum).link ();
}
/* Bitwise xor expr. */
function JSC$expr_bitwise_xor (ln, e1, e2)
{
this.etype = JSC$EXPR_BITWISE;
this.linenum = ln;
this.e1 = e1;
this.e2 = e2;
this.asm = JSC$expr_bitwise_xor_asm;
}
function JSC$expr_bitwise_xor_asm ()
{
this.e1.asm ();
this.e2.asm ();
new JSC$ASM_xor (this.linenum).link ();
}
/* Logical and expr. */
function JSC$expr_logical_and (ln, e1, e2)
{
this.etype = JSC$EXPR_LOGICAL;
if (e1.lang_type && e2.lang_type
&& e1.lang_type == JSC$JS_BOOLEAN && e2.lang_type == JSC$JS_BOOLEAN)
this.lang_type = JSC$JS_BOOLEAN;
this.linenum = ln;
this.e1 = e1;
this.e2 = e2;
this.asm = JSC$expr_logical_and_asm;
}
function JSC$expr_logical_and_asm ()
{
this.e1.asm ();
var l = new JSC$ASM_label ();
new JSC$ASM_dup (this.linenum).link ();
if (JSC$optimize_type && this.e1.lang_type
&& this.e1.lang_type == JSC$JS_BOOLEAN)
new JSC$ASM_iffalse_b (this.linenum, l).link ();
else
new JSC$ASM_iffalse (this.linenum, l).link ();
new JSC$ASM_pop (this.linenum).link ();
this.e2.asm ();
/* Done label. */
l.link ();
}
/* Logical or expr. */
function JSC$expr_logical_or (ln, e1, e2)
{
this.etype = JSC$EXPR_LOGICAL;
if (e1.lang_type && e2.lang_type
&& e1.lang_type == JSC$JS_BOOLEAN && e2.lang_type == JSC$JS_BOOLEAN)
this.lang_type = JSC$JS_BOOLEAN;
this.linenum = ln;
this.e1 = e1;
this.e2 = e2;
this.asm = JSC$expr_logical_or_asm;
}
function JSC$expr_logical_or_asm ()
{
this.e1.asm ();
var l = new JSC$ASM_label ();
new JSC$ASM_dup (this.linenum).link ();
if (JSC$optimize_type && this.e1.lang_type
&& this.e1.lang_type == JSC$JS_BOOLEAN)
new JSC$ASM_iftrue_b (this.linenum, l).link ();
else
new JSC$ASM_iftrue (this.linenum, l).link ();
new JSC$ASM_pop (this.linenum).link ();
this.e2.asm ();
/* Done label. */
l.link ();
}
/* New expr. */
function JSC$expr_new (ln, expr, args)
{
this.etype = JSC$EXPR_NEW;
this.linenum = ln;
this.expr = expr;
this.args = args;
this.asm = JSC$expr_new_asm;
}
function JSC$expr_new_asm ()
{
var i;
if (this.args)
{
/* Code for the arguments. */
for (i = this.args.length - 1; i >= 0; i--)
this.args[i].asm ();
if (this.args.length == 0)
new JSC$ASM_const_i0 (this.linenum).link ();
else if (this.args.length == 1)
new JSC$ASM_const_i1 (this.linenum).link ();
else if (this.args.length == 2)
new JSC$ASM_const_i2 (this.linenum).link ();
else if (this.args.length == 3)
new JSC$ASM_const_i3 (this.linenum).link ();
else
new JSC$ASM_const (this.linenum, this.args.length).link ();
}
else
{
/* A `new Foo' call. This is identical to `new Foo ()'. */
new JSC$ASM_const_i0 (this.linenum).link ();
}
/* Object. */
this.expr.asm ();
/* Call new. */
new JSC$ASM_new (this.linenum).link ();
/* Replace the constructor's return value with the object. */
new JSC$ASM_swap (this.linenum).link ();
/* Remove the arguments and return the new object. */
new JSC$ASM_apop (this.linenum,
(this.args ? this.args.length : 0) + 2).link ();
}
/* Object property expr. */
function JSC$expr_object_property (ln, expr, id)
{
this.etype = JSC$EXPR_OBJECT_PROPERTY;
this.linenum = ln;
this.expr = expr;
this.id = id;
this.asm = JSC$expr_object_property_asm;
}
function JSC$expr_object_property_asm ()
{
JSC$asm_expr_lvalue_load_asm (this);
}
/* Object array expr. */
function JSC$expr_object_array (ln, expr1, expr2)
{
this.etype = JSC$EXPR_OBJECT_ARRAY;
this.linenum = ln;
this.expr1 = expr1;
this.expr2 = expr2;
this.asm = JSC$expr_object_array_asm;
}
function JSC$expr_object_array_asm ()
{
JSC$asm_expr_lvalue_load_asm (this);
}
/* Call. */
function JSC$expr_call (ln, expr, args)
{
this.etype = JSC$EXPR_CALL;
this.linenum = ln;
this.expr = expr;
this.args = args;
this.asm = JSC$expr_call_asm;
}
function JSC$expr_call_asm ()
{
var i;
/* Code for the arguments. */
for (i = this.args.length - 1; i >= 0; i--)
this.args[i].asm ();
if (this.args.length == 0)
new JSC$ASM_const_i0 (this.linenum).link ();
else if (this.args.length == 1)
new JSC$ASM_const_i1 (this.linenum).link ();
else if (this.args.length == 2)
new JSC$ASM_const_i2 (this.linenum).link ();
else if (this.args.length == 3)
new JSC$ASM_const_i3 (this.linenum).link ();
else
new JSC$ASM_const (this.linenum, this.args.length).link ();
/* Check the function type. */
if (this.expr.etype == JSC$EXPR_IDENTIFIER)
{
/* The simple subroutine or global object method invocation. */
var saved_with_nesting = JSC$cont_break.top.with_nesting;
JSC$cont_break.top.with_nesting = 0;
JSC$asm_expr_lvalue_load_asm (this.expr);
JSC$cont_break.top.with_nesting = saved_with_nesting;
if (JSC$cont_break.top.with_nesting > 0)
new JSC$ASM_jsr_w (this.linenum, this.expr.value).link ();
else
new JSC$ASM_jsr (this.linenum).link ();
new JSC$ASM_apop (this.linenum, this.args.length + 2).link ();
}
else if (this.expr.etype == JSC$EXPR_OBJECT_PROPERTY)
{
/* Method invocation. */
this.expr.expr.asm ();
new JSC$ASM_call_method (this.linenum, this.expr.id).link ();
new JSC$ASM_apop (this.linenum, this.args.length + 2).link ();
}
else
{
/* Something like a function pointer invocation. */
JSC$asm_expr_lvalue_load_asm (this.expr);
new JSC$ASM_jsr (this.linenum).link ();
new JSC$ASM_apop (this.linenum, this.args.length + 2).link ();
}
}
/* Assignment. */
function JSC$expr_assignment (ln, type, expr1, expr2)
{
this.etype = JSC$EXPR_ASSIGNMENT;
this.linenum = ln;
this.type = type;
this.expr1 = expr1;
this.expr2 = expr2;
this.asm = JSC$expr_assignment_asm;
}
function JSC$expr_assignment_asm ()
{
if (this.type != #'=')
JSC$asm_expr_lvalue_load_asm (this.expr1);
/* Count the rvalue. */
this.expr2.asm ();
if (this.type == #'=')
/* Nothing here. */
;
else if (this.type == JSC$tMULA)
new JSC$ASM_mul (this.linenum).link ();
else if (this.type == JSC$tDIVA)
new JSC$ASM_div (this.linenum).link ();
else if (this.type == JSC$tMODA)
new JSC$ASM_mod (this.linenum).link ();
else if (this.type == JSC$tADDA)
new JSC$ASM_add (this.linenum).link ();
else if (this.type == JSC$tSUBA)
new JSC$ASM_sub (this.linenum).link ();
else if (this.type == JSC$tLSIA)
new JSC$ASM_shift_left (this.linenum).link ();
else if (this.type == JSC$tRSIA)
new JSC$ASM_shift_right (this.linenum).link ();
else if (this.type == JSC$tRRSA)
new JSC$ASM_shift_rright (this.linenum).link ();
else if (this.type == JSC$tANDA)
new JSC$ASM_and (this.linenum).link ();
else if (this.type == JSC$tXORA)
new JSC$ASM_xor (this.linenum).link ();
else if (this.type == JSC$tORA)
new JSC$ASM_or (this.linenum).link ();
else
error (JSC$filename + ":" + this.linenum.toString ()
+ ": internal compiler error in assignment expression");
/* Duplicate the value. */
new JSC$ASM_dup (this.linenum).link ();
/* Store it to the lvalue. */
JSC$asm_expr_lvalue_store_asm (this.expr1);
}
function JSC$asm_expr_lvalue_load_asm (expr)
{
var i;
if (expr.etype == JSC$EXPR_IDENTIFIER)
{
/* Must check global / local / argument. */
i = JSC$ns.lookup_symbol (expr.value);
if (i == null)
{
if (JSC$cont_break.top.with_nesting > 0)
new JSC$ASM_load_global_w (expr.linenum, expr.value).link ();
else
new JSC$ASM_load_global (expr.linenum, expr.value).link ();
}
else if (i.scope == JSC$SCOPE_ARG)
{
if (JSC$cont_break.top.with_nesting > 0 && JSC$warn_with_clobber)
JSC$warning (JSC$filename + ":" + expr.linenum.toString ()
+ ": warning: the with-lookup of symbol `" + i.symbol
+ "' is clobbered by the argument definition");
new JSC$ASM_load_arg (expr.linenum, i.value).link ();
}
else
{
if (JSC$cont_break.top.with_nesting > 0 && JSC$warn_with_clobber)
JSC$warning (JSC$filename + ":" + expr.linenum.toString ()
+ ": warning: the with-lookup of symbol `" + i.symbol
+ "' is clobbered by the local variable definition");
new JSC$ASM_load_local (expr.linenum, i.value).link ();
}
}
else if (expr.etype == JSC$EXPR_OBJECT_PROPERTY)
{
expr.expr.asm ();
new JSC$ASM_load_property (expr.linenum, expr.id).link ();
}
else if (expr.etype == JSC$EXPR_OBJECT_ARRAY)
{
expr.expr1.asm ();
expr.expr2.asm ();
new JSC$ASM_load_array (expr.linenum).link ();
}
else
error (JSC$filename + ":" + expr.linenum.toString () + ": syntax error");
}
function JSC$asm_expr_lvalue_store_asm (expr)
{
var i;
if (expr.etype == JSC$EXPR_IDENTIFIER)
{
i = JSC$ns.lookup_symbol (expr.value);
if (i == null)
new JSC$ASM_store_global (expr.linenum, expr.value).link ();
else if (i.scope == JSC$SCOPE_ARG)
new JSC$ASM_store_arg (expr.linenum, i.value).link ();
else
new JSC$ASM_store_local (expr.linenum, i.value).link ();
}
else if (expr.etype == JSC$EXPR_OBJECT_PROPERTY)
{
expr.expr.asm ();
new JSC$ASM_store_property (expr.linenum, expr.id).link ();
}
else if (expr.etype == JSC$EXPR_OBJECT_ARRAY)
{
expr.expr1.asm ();
expr.expr2.asm ();
new JSC$ASM_store_array (expr.linenum).link ();
}
else
error (JSC$filename + ":" + expr.linenum.toString () + ": syntax error");
}
/* Quest colon. */
function JSC$expr_quest_colon (ln, e1, e2, e3)
{
this.etype = JSC$EXPR_QUEST_COLON;
this.linenum = ln;
this.e1 = e1;
this.e2 = e2;
this.e3 = e3;
this.asm = JSC$expr_quest_colon_asm;
}
function JSC$expr_quest_colon_asm()
{
/* Code for the condition. */
this.e1.asm ();
var l1 = new JSC$ASM_label ();
var l2 = new JSC$ASM_label ();
if (JSC$optimize_type && this.e1.lang_type
&& this.e1.lang_type == JSC$JS_BOOLEAN)
new JSC$ASM_iffalse_b (this.linenum, l1).link ();
else
new JSC$ASM_iffalse (this.linenum, l1).link ();
/* Code for the true branch. */
this.e2.asm ();
new JSC$ASM_jmp (this.linenum, l2).link ();
/* Code for the false branch. */
l1.link ();
this.e3.asm ();
/* Done label. */
l2.link ();
}
/* Unary. */
function JSC$expr_unary (ln, type, expr)
{
this.etype = JSC$EXPR_UNARY;
this.linenum = ln;
this.type = type;
this.expr = expr;
this.asm = JSC$expr_unary_asm;
}
function JSC$expr_unary_asm ()
{
if (this.type == #'!')
{
this.expr.asm ();
new JSC$ASM_not (this.linenum).link ();
}
else if (this.type == #'+')
{
this.expr.asm ();
/* Nothing here. */
}
else if (this.type == #'~')
{
this.expr.asm ();
new JSC$ASM_const (this.linenum, -1).link ();
new JSC$ASM_xor (this.linenum).link ();
}
else if (this.type == #'-')
{
this.expr.asm ();
new JSC$ASM_neg (this.linenum).link ();
}
else if (this.type == JSC$tDELETE)
{
if (this.expr.etype == JSC$EXPR_OBJECT_PROPERTY)
{
this.expr.expr.asm ();
new JSC$ASM_delete_property (this.linenum, this.expr.id).link ();
}
else if (this.expr.etype == JSC$EXPR_OBJECT_ARRAY)
{
this.expr.expr1.asm ();
this.expr.expr2.asm ();
new JSC$ASM_delete_array (this.linenum).link ();
}
else if (this.expr.etype == JSC$EXPR_IDENTIFIER)
{
if (JSC$cont_break.top.with_nesting == 0)
error (JSC$filename + ":" + this.linenum.toString ()
+ ": `delete property' called outside of a with-block");
new JSC$ASM_const_null (this.linenum).link ();
new JSC$ASM_delete_property (this.linenum, this.expr.value).link ();
}
else
error (JSC$filename + ":" + this.linenum.toString ()
+ ": illegal target for the delete operand");
}
else if (this.type == JSC$tVOID)
{
this.expr.asm ();
new JSC$ASM_pop (this.linenum).link ();
new JSC$ASM_const_undefined (this.linenum).link ();
}
else if (this.type == JSC$tTYPEOF)
{
this.expr.asm ();
new JSC$ASM_typeof (this.linenum).link ();
}
else if (this.type == JSC$tPLUSPLUS
|| this.type == JSC$tMINUSMINUS)
{
/* Fetch the old value. */
JSC$asm_expr_lvalue_load_asm (this.expr);
/* Do the operation. */
new JSC$ASM_const_i1 (this.linenum).link ();
if (this.type == JSC$tPLUSPLUS)
new JSC$ASM_add (this.linenum).link ();
else
new JSC$ASM_sub (this.linenum).link ();
/* Duplicate the value and store one copy pack to lvalue. */
new JSC$ASM_dup (this.linenum).link ();
JSC$asm_expr_lvalue_store_asm (this.expr);
}
else
{
error ("jsc: internal error: unary expr's type is "
+ this.type.toString ());
}
}
/* Postfix. */
function JSC$expr_postfix (ln, type, expr)
{
this.etype = JSC$EXPR_POSTFIX;
this.linenum = ln;
this.type = type;
this.expr = expr;
this.asm = JSC$expr_postfix_asm;
}
function JSC$expr_postfix_asm ()
{
/* Fetch the old value. */
JSC$asm_expr_lvalue_load_asm (this.expr);
/* Duplicate the value since it is the expression's value. */
new JSC$ASM_dup (this.linenum).link ();
/* Do the operation. */
new JSC$ASM_const_i1 (this.linenum).link ();
if (this.type == JSC$tPLUSPLUS)
new JSC$ASM_add (this.linenum).link ();
else
new JSC$ASM_sub (this.linenum).link ();
/* And finally, store it back. */
JSC$asm_expr_lvalue_store_asm (this.expr);
}
/* Postfix. */
function JSC$expr_comma (ln, expr1, expr2)
{
this.etype = JSC$EXPR_COMMA;
this.linenum = ln;
this.expr1 = expr1;
this.expr2 = expr2;
this.asm = JSC$expr_comma_asm;
}
function JSC$expr_comma_asm ()
{
this.expr1.asm ();
new JSC$ASM_pop (this.linenum).link ();
this.expr2.asm ();
}
/*
Local variables:
mode: c
End:
*/
/*
* Namespace handling.
* Copyright (c) 1998 New Generation Software (NGS) Oy
*
* Author: Markku Rossi <mtr@ngs.fi>
*/
/*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA
*/
/*
* $Source: /usr/local/cvsroot/ngs/js/jsc/compiler.js,v $
* $Id: compiler.js,v 1.42 1999/01/11 09:01:33 mtr Exp $
*/
/*
* Global functions.
*/
JSC$SCOPE_ARG = 1;
JSC$SCOPE_LOCAL = 2;
function JSC$NameSpace ()
{
this.frame = new JSC$NameSpaceFrame ();
this.push_frame = JSC$NameSpace_push_frame;
this.pop_frame = JSC$NameSpace_pop_frame;
this.alloc_local = JSC$NameSpace_alloc_local;
this.define_symbol = JSC$NameSpace_define_symbol;
this.lookup_symbol = JSC$NameSpace_lookup_symbol;
}
function JSC$NameSpace_push_frame ()
{
var f = new JSC$NameSpaceFrame ();
f.num_locals = this.frame.num_locals;
f.next = this.frame;
this.frame = f;
}
function JSC$NameSpace_pop_frame ()
{
var i;
for (i = this.frame.defs; i != null; i = i.next)
if (i.usecount == 0)
{
if (i.scope == JSC$SCOPE_ARG)
{
if (JSC$warn_unused_argument)
JSC$warning (JSC$filename + ":" + i.linenum.toString ()
+ ": warning: unused argument `" + i.symbol + "'");
}
else
{
if (JSC$warn_unused_variable)
JSC$warning (JSC$filename + ":" + i.linenum.toString ()
+ ": warning: unused variable `" + i.symbol + "'");
}
}
this.frame = this.frame.next;
}
function JSC$NameSpace_alloc_local ()
{
return this.frame.num_locals++;
}
function JSC$NameSpace_define_symbol (symbol, scope, value, linenum)
{
var i;
for (i = this.frame.defs; i != null; i = i.next)
if (i.symbol == symbol)
{
if (i.scope == scope)
error (JSC$filename + ":" + i.linenum.toString()
+ ": redeclaration of `" + i.symbol + "'");
if (i.scope == JSC$SCOPE_ARG && JSC$warn_shadow)
JSC$warning (JSC$filename + ":" + linenum.toString ()
+ ": warning: declaration of `" + symbol
+ "' shadows a parameter");
i.scope = scope;
i.value = value;
i.linenum = linenum;
return;
}
/* Create a new definition. */
i = new JSC$SymbolDefinition (symbol, scope, value, linenum);
i.next = this.frame.defs;
this.frame.defs = i;
}
function JSC$NameSpace_lookup_symbol (symbol)
{
var f, i;
for (f = this.frame; f != null; f = f.next)
for (i = f.defs; i != null; i = i.next)
if (i.symbol == symbol)
{
i.usecount++;
return i;
}
return null;
}
/*
* Static helpers.
*/
function JSC$NameSpaceFrame ()
{
this.next = null;
this.defs = null;
this.num_locals = 0;
}
function JSC$SymbolDefinition (symbol, scope, value, linenum)
{
this.next = null;
this.symbol = symbol;
this.scope = scope;
this.value = value;
this.linenum = linenum;
this.usecount = 0;
}
/*
Local variables:
mode: c
End:
*/
/*
* Input stream definitions.
* Copyright (c) 1998 New Generation Software (NGS) Oy
*
* Author: Markku Rossi <mtr@ngs.fi>
*/
/*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA
*/
/*
* $Source: /usr/local/cvsroot/ngs/js/jsc/compiler.js,v $
* $Id: compiler.js,v 1.42 1999/01/11 09:01:33 mtr Exp $
*/
/*
* File stream.
*/
function JSC$StreamFile (name)
{
this.name = name;
this.stream = new File (name);
this.error = "";
this.open = JSC$StreamFile_open;
this.close = JSC$StreamFile_close;
this.rewind = JSC$StreamFile_rewind;
this.readByte = JSC$StreamFile_read_byte;
this.ungetByte = JSC$StreamFile_unget_byte;
this.readln = JSC$StreamFile_readln;
}
function JSC$StreamFile_open ()
{
if (!this.stream.open ("r"))
{
this.error = System.strerror (System.errno);
return false;
}
return true;
}
function JSC$StreamFile_close ()
{
return this.stream.close ();
}
function JSC$StreamFile_rewind ()
{
return this.stream.setPosition (0);
}
function JSC$StreamFile_read_byte ()
{
return this.stream.readByte ();
}
function JSC$StreamFile_unget_byte (byte)
{
this.stream.ungetByte (byte);
}
function JSC$StreamFile_readln ()
{
return this.stream.readln ();
}
/*
* String stream.
*/
function JSC$StreamString (str)
{
this.name = "StringStream";
this.string = str;
this.pos = 0;
this.unget_byte = -1;
this.error = "";
this.open = JSC$StreamString_open;
this.close = JSC$StreamString_close;
this.rewind = JSC$StreamString_rewind;
this.readByte = JSC$StreamString_read_byte;
this.ungetByte = JSC$StreamString_unget_byte;
this.readln = JSC$StreamString_readln;
}
function JSC$StreamString_open ()
{
return true;
}
function JSC$StreamString_close ()
{
return true;
}
function JSC$StreamString_rewind ()
{
this.pos = 0;
this.unget_byte = -1;
this.error = "";
return true;
}
function JSC$StreamString_read_byte ()
{
var ch;
if (this.unget_byte >= 0)
{
ch = this.unget_byte;
this.unget_byte = -1;
return ch;
}
if (this.pos >= this.string.length)
return -1;
return this.string.charCodeAt (this.pos++);
}
function JSC$StreamString_unget_byte (byte)
{
this.unget_byte = byte;
}
function JSC$StreamString_readln ()
{
var line = new String ("");
var ch;
while ((ch = this.readByte ()) != -1 && ch != #'\n')
line.append (String.pack ("C", ch));
return line;
}
/*
Local variables:
mode: c
End:
*/
/*
* JavaScript Assembler.
* Copyright (c) 1998 New Generation Software (NGS) Oy
*
* Author: Markku Rossi <mtr@ngs.fi>
*/
/*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA
*/
/*
* $Source: /usr/local/cvsroot/ngs/js/jsc/compiler.js,v $
* $Id: compiler.js,v 1.42 1999/01/11 09:01:33 mtr Exp $
*/
/* Byte-code file definitions. */
JSC$BC_MAGIC = 0xc0014a53;
JSC$BC_SECT_CODE = 0;
JSC$BC_SECT_CONSTANTS = 1;
JSC$BC_SECT_SYMTAB = 2;
JSC$BC_SECT_DEBUG = 3;
JSC$CONST_INT = 3;
JSC$CONST_STRING = 4;
JSC$CONST_FLOAT = 5;
JSC$CONST_SYMBOL = 10;
JSC$CONST_REGEXP = 11;
JSC$CONST_NAN = 13;
JSC$CONST_REGEXP_FLAG_G = 0x01;
JSC$CONST_REGEXP_FLAG_I = 0x02;
JSC$DEBUG_FILENAME = 1;
JSC$DEBUG_LINENUMBER = 2;
/* Opcode definitions. */
JSC$OP_HALT = 0;
JSC$OP_DONE = 1;
JSC$OP_NOP = 2;
JSC$OP_DUP = 3;
JSC$OP_POP = 4;
JSC$OP_POP_N = 5;
JSC$OP_APOP = 6;
JSC$OP_SWAP = 7;
JSC$OP_ROLL = 8;
JSC$OP_CONST = 9;
JSC$OP_CONST_NULL = 10;
JSC$OP_CONST_TRUE = 11;
JSC$OP_CONST_FALSE = 12;
JSC$OP_CONST_UNDEFINED = 13;
JSC$OP_CONST_I0 = 14;
JSC$OP_CONST_I1 = 15;
JSC$OP_CONST_I2 = 16;
JSC$OP_CONST_I3 = 17;
JSC$OP_CONST_I = 18;
JSC$OP_LOAD_GLOBAL = 19;
JSC$OP_STORE_GLOBAL = 20;
JSC$OP_LOAD_ARG = 21;
JSC$OP_STORE_ARG = 22;
JSC$OP_LOAD_LOCAL = 23;
JSC$OP_STORE_LOCAL = 24;
JSC$OP_LOAD_PROPERTY = 25;
JSC$OP_STORE_PROPERTY = 26;
JSC$OP_LOAD_ARRAY = 27;
JSC$OP_STORE_ARRAY = 28;
JSC$OP_NTH = 29;
JSC$OP_CMP_EQ = 30;
JSC$OP_CMP_NE = 31;
JSC$OP_CMP_LT = 32;
JSC$OP_CMP_GT = 33;
JSC$OP_CMP_LE = 34;
JSC$OP_CMP_GE = 35;
JSC$OP_CMP_SEQ = 36;
JSC$OP_CMP_SNE = 37;
JSC$OP_SUB = 38;
JSC$OP_ADD = 39;
JSC$OP_MUL = 40;
JSC$OP_DIV = 41;
JSC$OP_MOD = 42;
JSC$OP_NEG = 43;
JSC$OP_AND = 44;
JSC$OP_NOT = 45;
JSC$OP_OR = 46;
JSC$OP_XOR = 47;
JSC$OP_SHIFT_LEFT = 48;
JSC$OP_SHIFT_RIGHT = 49;
JSC$OP_SHIFT_RRIGHT = 50;
JSC$OP_IFFALSE = 51;
JSC$OP_IFTRUE = 52;
JSC$OP_CALL_METHOD = 53;
JSC$OP_JMP = 54;
JSC$OP_JSR = 55;
JSC$OP_RETURN = 56;
JSC$OP_TYPEOF = 57;
JSC$OP_NEW = 58;
JSC$OP_DELETE_PROPERTY = 59;
JSC$OP_DELETE_ARRAY = 60;
JSC$OP_LOCALS = 61;
JSC$OP_MIN_ARGS = 62;
JSC$OP_LOAD_NTH_ARG = 63;
JSC$OP_WITH_PUSH = 64;
JSC$OP_WITH_POP = 65;
JSC$OP_TRY_PUSH = 66;
JSC$OP_TRY_POP = 67;
JSC$OP_THROW = 68;
/* Type aware operands. */
JSC$OP_IFFALSE_B = 69;
JSC$OP_IFTRUE_B = 70;
JSC$OP_ADD_1_I = 71;
JSC$OP_ADD_2_I = 72;
JSC$OP_LOAD_GLOBAL_W = 73;
JSC$OP_JSR_W = 74;
/* Internal values. */
JSC$ASM_SYMBOL = 1000;
JSC$ASM_LABEL = 1001;
/*
* General helpers.
*/
/* Generate byte-code for operands with Int8 value. */
function JSC$ASM_bytecode_int8 ()
{
return String.pack ("C", this.value);
}
/* Generate byte-code for operands with Int16 value. */
function JSC$ASM_bytecode_int16 ()
{
return String.pack ("n", this.value);
}
/* Generate byte-code for operands with Int32 value. */
function JSC$ASM_bytecode_int32 ()
{
return String.pack ("N", this.value);
}
/* Generate byte-code for operands with Symbol value. */
function JSC$ASM_bytecode_symbol ()
{
var cid = JSC$asm_genconstant (String.pack ("C", JSC$CONST_SYMBOL)
+ this.value + String.pack ("C", 0));
return String.pack ("N", cid);
}
/* Generate byte-code for local jump operands. */
function JSC$ASM_bytecode_local_jump ()
{
var delta = this.value.offset - (this.offset + this.size);
return String.pack ("N", delta);
}
/*
* Assembler operands.
*/
/* Symbol. */
function JSC$ASM_symbol (ln, value)
{
this.type = JSC$ASM_SYMBOL;
this.linenum = ln;
this.value = value;
this.size = 0;
this.print = JSC$ASM_symbol_print;
this.link = JSC$asm_link;
}
function JSC$ASM_symbol_print (stream)
{
stream.write ("\n" + this.value + ":\n");
}
/* Label */
function JSC$ASM_label ()
{
this.type = JSC$ASM_LABEL;
this.linenum = 0;
this.size = 0;
this.value = JSC$asm_label_count++;
this.referenced = false;
this.next = null;
this.print = JSC$ASM_label_print;
this.link = JSC$asm_link;
this.format = JSC$ASM_label_format;
}
function JSC$ASM_label_print (stream)
{
stream.write (this.format () + ":\n");
}
function JSC$ASM_label_format ()
{
return ".L" + this.value.toString ();
}
/* halt */
function JSC$ASM_halt (ln)
{
this.type = JSC$OP_HALT;
this.linenum = ln;
this.size = 1;
this.print = JSC$ASM_halt_print;
this.link = JSC$asm_link;
}
function JSC$ASM_halt_print (stream)
{
stream.write ("\thalt\n");
}
/* done */
function JSC$ASM_done (ln)
{
this.type = JSC$OP_DONE;
this.linenum = ln;
this.size = 1;
this.print = JSC$ASM_done_print;
this.link = JSC$asm_link;
}
function JSC$ASM_done_print (stream)
{
stream.write ("\tdone\n");
}
/* nop */
function JSC$ASM_nop (ln)
{
this.type = JSC$OP_NOP;
this.linenum = ln;
this.size = 1;
this.print = JSC$ASM_nop_print;
this.link = JSC$asm_link;
}
function JSC$ASM_nop_print (stream)
{
stream.write ("\tnop\n");
}
/* dup */
function JSC$ASM_dup (ln)
{
this.type = JSC$OP_DUP;
this.linenum = ln;
this.stack_delta = 1;
this.size = 1;
this.print = JSC$ASM_dup_print;
this.link = JSC$asm_link;
}
function JSC$ASM_dup_print (stream)
{
stream.write ("\tdup\n");
}
/* pop */
function JSC$ASM_pop (ln)
{
this.type = JSC$OP_POP;
this.linenum = ln;
this.stack_delta = -1;
this.size = 1;
this.print = JSC$ASM_pop_print;
this.link = JSC$asm_link;
}
function JSC$ASM_pop_print (stream)
{
stream.write ("\tpop\n");
}
/* pop_n */
function JSC$ASM_pop_n (ln, value)
{
this.type = JSC$OP_POP_N;
this.linenum = ln;
this.value = value;
this.stack_delta = -value;
this.size = 2;
this.print = JSC$ASM_pop_n_print;
this.link = JSC$asm_link;
this.bytecode = JSC$ASM_bytecode_int8;
}
function JSC$ASM_pop_n_print (stream)
{
stream.write ("\tpop_n\t\t" + this.value.toString () + "\n");
}
/* apop */
function JSC$ASM_apop (ln, value)
{
this.type = JSC$OP_APOP;
this.linenum = ln;
this.value = value;
this.stack_delta = -value;
this.size = 2;
this.print = JSC$ASM_apop_print;
this.link = JSC$asm_link;
this.bytecode = JSC$ASM_bytecode_int8;
}
function JSC$ASM_apop_print (stream)
{
stream.write ("\tapop\t\t" + this.value.toString () + "\n");
}
/* swap */
function JSC$ASM_swap (ln)
{
this.type = JSC$OP_SWAP;
this.linenum = ln;
this.size = 1;
this.print = JSC$ASM_swap_print;
this.link = JSC$asm_link;
}
function JSC$ASM_swap_print (stream)
{
stream.write ("\tswap\n");
}
/* roll */
function JSC$ASM_roll (ln, value)
{
this.type = JSC$OP_ROLL;
this.linenum = ln;
this.value = value;
this.size = 2;
this.print = JSC$ASM_roll_print;
this.link = JSC$asm_link;
this.bytecode = JSC$ASM_bytecode_int8;
}
function JSC$ASM_roll_print (stream)
{
stream.write ("\troll\t\t" + this.value.toString () + "\n");
}
/* const */
function JSC$ASM_const (ln, value)
{
this.type = JSC$OP_CONST;
this.linenum = ln;
this.value = value;
this.stack_delta = 1;
this.size = 5;
this.print = JSC$ASM_const_print;
this.link = JSC$asm_link;
this.bytecode = JSC$ASM_const_bytecode;
}
function JSC$ASM_const_print (stream)
{
if (typeof this.value == "number")
stream.write ("\tconst\t\t" + this.value.toString () + "\n");
else if (typeof this.value == "string"
|| typeof this.value == "#builtin")
{
var i, c;
var ender, src;
var stringp = (typeof this.value == "string");
if (stringp)
{
ender = "\"";
src = this.value;
}
else
{
ender = "/";
src = this.value.source;
}
stream.write ("\tconst\t\t" + ender);
for (i = 0; i < src.length; i++)
{
c = src.charCodeAt (i);
if (c == ender[0] || c == #'\\')
stream.write ("\\" + src.charAt (i));
else if (c == #'\n')
stream.write ("\\n");
else if (c == #'\r')
stream.write ("\\r");
else if (c == #'\t')
stream.write ("\\t");
else if (c == #'\f')
stream.write ("\\f");
else
stream.write (src.charAt (i));
}
stream.write (ender);
if (!stringp)
{
if (this.value.global)
stream.write ("g");
if (this.value.ignoreCase)
stream.write ("i");
}
stream.write ("\n");
}
}
function JSC$ASM_const_bytecode ()
{
var cid;
if (typeof this.value == "number")
{
if (isInt (this.value))
cid = JSC$asm_genconstant (String.pack ("CN", JSC$CONST_INT,
this.value));
else if (isFloat (this.value))
cid = JSC$asm_genconstant (String.pack ("Cd", JSC$CONST_FLOAT,
this.value));
else
cid = JSC$asm_genconstant (String.pack ("C", JSC$CONST_NAN));
}
else if (typeof this.value == "string")
cid = JSC$asm_genconstant (String.pack ("CN", JSC$CONST_STRING,
this.value.length)
+ this.value);
else if (typeof this.value == "#builtin")
{
/* Regular expression. */
var flags = 0;
if (this.value.global)
flags |= JSC$CONST_REGEXP_FLAG_G;
if (this.value.ignoreCase)
flags |= JSC$CONST_REGEXP_FLAG_I;
cid = JSC$asm_genconstant (String.pack ("CCN", JSC$CONST_REGEXP, flags,
this.value.source.length)
+ this.value.source);
}
else
error ("ASM_const_bytecode(): unknown type: " + typeof this.value);
return String.pack ("N", cid);
}
/* const_null */
function JSC$ASM_const_null (ln)
{
this.type = JSC$OP_CONST_NULL;
this.linenum = ln;
this.stack_delta = 1;
this.size = 1;
this.print = JSC$ASM_const_null_print;
this.link = JSC$asm_link;
}
function JSC$ASM_const_null_print (stream)
{
stream.write ("\tconst_null\n");
}
/* const_true */
function JSC$ASM_const_true (ln)
{
this.type = JSC$OP_CONST_TRUE;
this.linenum = ln;
this.stack_delta = 1;
this.size = 1;
this.print = JSC$ASM_const_true_print;
this.link = JSC$asm_link;
}
function JSC$ASM_const_true_print (stream)
{
stream.write ("\tconst_true\n");
}
/* const_false */
function JSC$ASM_const_false (ln)
{
this.type = JSC$OP_CONST_FALSE;
this.linenum = ln;
this.stack_delta = 1;
this.size = 1;
this.print = JSC$ASM_const_false_print;
this.link = JSC$asm_link;
}
function JSC$ASM_const_false_print (stream)
{
stream.write ("\tconst_false\n");
}
/* const_undefined */
function JSC$ASM_const_undefined (ln)
{
this.type = JSC$OP_CONST_UNDEFINED;
this.linenum = ln;
this.stack_delta = 1;
this.size = 1;
this.print = JSC$ASM_const_undefined_print;
this.link = JSC$asm_link;
}
function JSC$ASM_const_undefined_print (stream)
{
stream.write ("\tconst_undefined\n");
}
/* const_i0 */
function JSC$ASM_const_i0 (ln)
{
this.type = JSC$OP_CONST_I0;
this.linenum = ln;
this.stack_delta = 1;
this.size = 1;
this.print = JSC$ASM_const_i0_print;
this.link = JSC$asm_link;
}
function JSC$ASM_const_i0_print (stream)
{
stream.write ("\tconst_i0\n");
}
/* const_i1 */
function JSC$ASM_const_i1 (ln)
{
this.type = JSC$OP_CONST_I1;
this.linenum = ln;
this.stack_delta = 1;
this.size = 1;
this.print = JSC$ASM_const_i1_print;
this.link = JSC$asm_link;
}
function JSC$ASM_const_i1_print (stream)
{
stream.write ("\tconst_i1\n");
}
/* const_i2 */
function JSC$ASM_const_i2 (ln)
{
this.type = JSC$OP_CONST_I2;
this.linenum = ln;
this.stack_delta = 1;
this.size = 1;
this.print = JSC$ASM_const_i2_print;
this.link = JSC$asm_link;
}
function JSC$ASM_const_i2_print (stream)
{
stream.write ("\tconst_i2\n");
}
/* const_i3 */
function JSC$ASM_const_i3 (ln)
{
this.type = JSC$OP_CONST_I3;
this.linenum = ln;
this.stack_delta = 1;
this.size = 1;
this.print = JSC$ASM_const_i3_print;
this.link = JSC$asm_link;
}
function JSC$ASM_const_i3_print (stream)
{
stream.write ("\tconst_i3\n");
}
/* const_i */
function JSC$ASM_const_i (ln, value)
{
this.type = JSC$OP_CONST_I;
this.linenum = ln;
this.value = value;
this.stack_delta = 1;
this.size = 5;
this.print = JSC$ASM_const_i_print;
this.bytecode = JSC$ASM_bytecode_int32;
this.link = JSC$asm_link;
}
function JSC$ASM_const_i_print (stream)
{
stream.write ("\tconst_i\t\t" + this.value.toString () + "\n");
}
/* load_global */
function JSC$ASM_load_global (ln, value)
{
this.type = JSC$OP_LOAD_GLOBAL;
this.linenum = ln;
this.value = value;
this.stack_delta = 1;
this.size = 5;
this.print = JSC$ASM_load_global_print;
this.bytecode = JSC$ASM_bytecode_symbol;
this.link = JSC$asm_link;
}
function JSC$ASM_load_global_print (stream)
{
stream.write ("\tload_global\t" + this.value + "\n");
}
/* store_global */
function JSC$ASM_store_global (ln, value)
{
this.type = JSC$OP_STORE_GLOBAL;
this.linenum = ln;
this.value = value;
this.stack_delta = -1;
this.size = 5;
this.print = JSC$ASM_store_global_print;
this.bytecode = JSC$ASM_bytecode_symbol;
this.link = JSC$asm_link;
}
function JSC$ASM_store_global_print (stream)
{
stream.write ("\tstore_global\t" + this.value + "\n");
}
/* load_arg */
function JSC$ASM_load_arg (ln, value)
{
this.type = JSC$OP_LOAD_ARG;
this.linenum = ln;
this.value = value;
this.stack_delta = 1;
this.size = 2;
this.print = JSC$ASM_load_arg_print;
this.bytecode = JSC$ASM_bytecode_int8;
this.link = JSC$asm_link;
}
function JSC$ASM_load_arg_print (stream)
{
stream.write ("\tload_arg\t" + this.value.toString () + "\n");
}
/* store_arg */
function JSC$ASM_store_arg (ln, value)
{
this.type = JSC$OP_STORE_ARG;
this.linenum = ln;
this.value = value;
this.stack_delta = -1;
this.size = 2;
this.print = JSC$ASM_store_arg_print;
this.bytecode = JSC$ASM_bytecode_int8;
this.link = JSC$asm_link;
}
function JSC$ASM_store_arg_print (stream)
{
stream.write ("\tstore_arg\t" + this.value.toString () + "\n");
}
/* load_local */
function JSC$ASM_load_local (ln, value)
{
this.type = JSC$OP_LOAD_LOCAL;
this.linenum = ln;
this.value = value;
this.stack_delta = 1;
this.size = 3;
this.print = JSC$ASM_load_local_print;
this.bytecode = JSC$ASM_bytecode_int16;
this.link = JSC$asm_link;
}
function JSC$ASM_load_local_print (stream)
{
stream.write ("\tload_local\t" + this.value.toString () + "\n");
}
/* store_local */
function JSC$ASM_store_local (ln, value)
{
this.type = JSC$OP_STORE_LOCAL;
this.linenum = ln;
this.value = value;
this.stack_delta = -1;
this.size = 3;
this.print = JSC$ASM_store_local_print;
this.bytecode = JSC$ASM_bytecode_int16;
this.link = JSC$asm_link;
}
function JSC$ASM_store_local_print (stream)
{
stream.write ("\tstore_local\t" + this.value.toString () + "\n");
}
/* load_property */
function JSC$ASM_load_property (ln, value)
{
this.type = JSC$OP_LOAD_PROPERTY;
this.linenum = ln;
this.value = value;
this.size = 5;
this.print = JSC$ASM_load_property_print;
this.bytecode = JSC$ASM_bytecode_symbol;
this.link = JSC$asm_link;
}
function JSC$ASM_load_property_print (stream)
{
stream.write ("\tload_property\t" + this.value + "\n");
}
/* store_property */
function JSC$ASM_store_property (ln, value)
{
this.type = JSC$OP_STORE_PROPERTY;
this.linenum = ln;
this.value = value;
this.stack_delta = -2;
this.size = 5;
this.print = JSC$ASM_store_property_print;
this.bytecode = JSC$ASM_bytecode_symbol;
this.link = JSC$asm_link;
}
function JSC$ASM_store_property_print (stream)
{
stream.write ("\tstore_property\t" + this.value + "\n");
}
/* load_array */
function JSC$ASM_load_array (ln)
{
this.type = JSC$OP_LOAD_ARRAY;
this.linenum = ln;
this.stack_delta = -1;
this.size = 1;
this.print = JSC$ASM_load_array_print;
this.link = JSC$asm_link;
}
function JSC$ASM_load_array_print (stream)
{
stream.write ("\tload_array\n");
}
/* store_array */
function JSC$ASM_store_array (ln)
{
this.type = JSC$OP_STORE_ARRAY;
this.linenum = ln;
this.stack_delta = -3;
this.size = 1;
this.print = JSC$ASM_store_array_print;
this.link = JSC$asm_link;
}
function JSC$ASM_store_array_print (stream)
{
stream.write ("\tstore_array\n");
}
/* nth */
function JSC$ASM_nth (ln)
{
this.type = JSC$OP_NTH;
this.linenum = ln;
this.size = 1;
this.print = JSC$ASM_nth_print;
this.link = JSC$asm_link;
}
function JSC$ASM_nth_print (stream)
{
stream.write ("\tnth\n");
}
/* cmp_eq */
function JSC$ASM_cmp_eq (ln)
{
this.type = JSC$OP_CMP_EQ;
this.linenum = ln;
this.stack_delta = -1;
this.size = 1;
this.print = JSC$ASM_cmp_eq_print;
this.link = JSC$asm_link;
}
function JSC$ASM_cmp_eq_print (stream)
{
stream.write ("\tcmp_eq\n");
}
/* cmp_ne */
function JSC$ASM_cmp_ne (ln)
{
this.type = JSC$OP_CMP_NE;
this.linenum = ln;
this.stack_delta = -1;
this.size = 1;
this.print = JSC$ASM_cmp_ne_print;
this.link = JSC$asm_link;
}
function JSC$ASM_cmp_ne_print (stream)
{
stream.write ("\tcmp_ne\n");
}
/* cmp_lt */
function JSC$ASM_cmp_lt (ln)
{
this.type = JSC$OP_CMP_LT;
this.linenum = ln;
this.stack_delta = -1;
this.size = 1;
this.print = JSC$ASM_cmp_lt_print;
this.link = JSC$asm_link;
}
function JSC$ASM_cmp_lt_print (stream)
{
stream.write ("\tcmp_lt\n");
}
/* cmp_gt */
function JSC$ASM_cmp_gt (ln)
{
this.type = JSC$OP_CMP_GT;
this.linenum = ln;
this.stack_delta = -1;
this.size = 1;
this.print = JSC$ASM_cmp_gt_print;
this.link = JSC$asm_link;
}
function JSC$ASM_cmp_gt_print (stream)
{
stream.write ("\tcmp_gt\n");
}
/* cmp_le */
function JSC$ASM_cmp_le (ln)
{
this.type = JSC$OP_CMP_LE;
this.linenum = ln;
this.stack_delta = -1;
this.size = 1;
this.print = JSC$ASM_cmp_le_print;
this.link = JSC$asm_link;
}
function JSC$ASM_cmp_le_print (stream)
{
stream.write ("\tcmp_le\n");
}
/* cmp_ge */
function JSC$ASM_cmp_ge (ln)
{
this.type = JSC$OP_CMP_GE;
this.linenum = ln;
this.stack_delta = -1;
this.size = 1;
this.print = JSC$ASM_cmp_ge_print;
this.link = JSC$asm_link;
}
function JSC$ASM_cmp_ge_print (stream)
{
stream.write ("\tcmp_ge\n");
}
/* cmp_seq */
function JSC$ASM_cmp_seq (ln)
{
this.type = JSC$OP_CMP_SEQ;
this.linenum = ln;
this.stack_delta = -1;
this.size = 1;
this.print = JSC$ASM_cmp_seq_print;
this.link = JSC$asm_link;
}
function JSC$ASM_cmp_seq_print (stream)
{
stream.write ("\tcmp_seq\n");
}
/* cmp_sne */
function JSC$ASM_cmp_sne (ln)
{
this.type = JSC$OP_CMP_SNE;
this.linenum = ln;
this.stack_delta = -1;
this.size = 1;
this.print = JSC$ASM_cmp_sne_print;
this.link = JSC$asm_link;
}
function JSC$ASM_cmp_sne_print (stream)
{
stream.write ("\tcmp_sne\n");
}
/* sub */
function JSC$ASM_sub (ln)
{
this.type = JSC$OP_SUB;
this.linenum = ln;
this.stack_delta = -1;
this.size = 1;
this.print = JSC$ASM_sub_print;
this.link = JSC$asm_link;
}
function JSC$ASM_sub_print (stream)
{
stream.write ("\tsub\n");
}
/* add */
function JSC$ASM_add (ln)
{
this.type = JSC$OP_ADD;
this.linenum = ln;
this.stack_delta = -1;
this.size = 1;
this.print = JSC$ASM_add_print;
this.link = JSC$asm_link;
}
function JSC$ASM_add_print (stream)
{
stream.write ("\tadd\n");
}
/* mul */
function JSC$ASM_mul (ln)
{
this.type = JSC$OP_MUL;
this.linenum = ln;
this.stack_delta = -1;
this.size = 1;
this.print = JSC$ASM_mul_print;
this.link = JSC$asm_link;
}
function JSC$ASM_mul_print (stream)
{
stream.write ("\tmul\n");
}
/* div */
function JSC$ASM_div (ln)
{
this.type = JSC$OP_DIV;
this.linenum = ln;
this.stack_delta = -1;
this.size = 1;
this.print = JSC$ASM_div_print;
this.link = JSC$asm_link;
}
function JSC$ASM_div_print (stream)
{
stream.write ("\tdiv\n");
}
/* mod */
function JSC$ASM_mod (ln)
{
this.type = JSC$OP_MOD;
this.linenum = ln;
this.stack_delta = -1;
this.size = 1;
this.print = JSC$ASM_mod_print;
this.link = JSC$asm_link;
}
function JSC$ASM_mod_print (stream)
{
stream.write ("\tmod\n");
}
/* neg */
function JSC$ASM_neg (ln)
{
this.type = JSC$OP_NEG;
this.linenum = ln;
this.size = 1;
this.print = JSC$ASM_neg_print;
this.link = JSC$asm_link;
}
function JSC$ASM_neg_print (stream)
{
stream.write ("\tneg\n");
}
/* and */
function JSC$ASM_and (ln)
{
this.type = JSC$OP_AND;
this.linenum = ln;
this.stack_delta = -1;
this.size = 1;
this.print = JSC$ASM_and_print;
this.link = JSC$asm_link;
}
function JSC$ASM_and_print (stream)
{
stream.write ("\tand\n");
}
/* not */
function JSC$ASM_not (ln)
{
this.type = JSC$OP_NOT;
this.linenum = ln;
this.size = 1;
this.print = JSC$ASM_not_print;
this.link = JSC$asm_link;
}
function JSC$ASM_not_print (stream)
{
stream.write ("\tnot\n");
}
/* or */
function JSC$ASM_or (ln)
{
this.type = JSC$OP_OR;
this.linenum = ln;
this.stack_delta = -1;
this.size = 1;
this.print = JSC$ASM_or_print;
this.link = JSC$asm_link;
}
function JSC$ASM_or_print (stream)
{
stream.write ("\tor\n");
}
/* xor */
function JSC$ASM_xor (ln)
{
this.type = JSC$OP_XOR;
this.linenum = ln;
this.stack_delta = -1;
this.size = 1;
this.print = JSC$ASM_xor_print;
this.link = JSC$asm_link;
}
function JSC$ASM_xor_print (stream)
{
stream.write ("\txor\n");
}
/* shift_left */
function JSC$ASM_shift_left (ln)
{
this.type = JSC$OP_SHIFT_LEFT;
this.linenum = ln;
this.stack_delta = -1;
this.size = 1;
this.print = JSC$ASM_shift_left_print;
this.link = JSC$asm_link;
}
function JSC$ASM_shift_left_print (stream)
{
stream.write ("\tshift_left\n");
}
/* shift_right */
function JSC$ASM_shift_right (ln)
{
this.type = JSC$OP_SHIFT_RIGHT;
this.linenum = ln;
this.stack_delta = -1;
this.size = 1;
this.print = JSC$ASM_shift_right_print;
this.link = JSC$asm_link;
}
function JSC$ASM_shift_right_print (stream)
{
stream.write ("\tshift_right\n");
}
/* shift_rright */
function JSC$ASM_shift_rright (ln)
{
this.type = JSC$OP_SHIFT_RRIGHT;
this.linenum = ln;
this.stack_delta = -1;
this.size = 1;
this.print = JSC$ASM_shift_rright_print;
this.link = JSC$asm_link;
}
function JSC$ASM_shift_rright_print (stream)
{
stream.write ("\tshift_rright\n");
}
/* iffalse */
function JSC$ASM_iffalse (ln, value)
{
this.type = JSC$OP_IFFALSE;
this.linenum = ln;
this.value = value;
this.stack_delta = -1;
this.size = 5;
this.print = JSC$ASM_iffalse_print;
this.bytecode = JSC$ASM_bytecode_local_jump;
this.link = JSC$asm_link;
}
function JSC$ASM_iffalse_print (stream)
{
stream.write ("\tiffalse\t\t" + this.value.format () + "\n");
}
/* iftrue */
function JSC$ASM_iftrue (ln, value)
{
this.type = JSC$OP_IFTRUE;
this.linenum = ln;
this.value = value;
this.stack_delta = -1;
this.size = 5;
this.print = JSC$ASM_iftrue_print;
this.bytecode = JSC$ASM_bytecode_local_jump;
this.link = JSC$asm_link;
}
function JSC$ASM_iftrue_print (stream)
{
stream.write ("\tiftrue\t\t" + this.value.format () + "\n");
}
/* call_method */
function JSC$ASM_call_method (ln, value)
{
this.type = JSC$OP_CALL_METHOD;
this.linenum = ln;
this.value = value;
this.stack_delta = 1;
this.size = 5;
this.print = JSC$ASM_call_method_print;
this.bytecode = JSC$ASM_bytecode_symbol;
this.link = JSC$asm_link;
}
function JSC$ASM_call_method_print (stream)
{
stream.write ("\tcall_method\t" + this.value + "\n");
}
/* jmp */
function JSC$ASM_jmp (ln, value)
{
this.type = JSC$OP_JMP;
this.linenum = ln;
this.value = value;
this.size = 5;
this.print = JSC$ASM_jmp_print;
this.bytecode = JSC$ASM_bytecode_local_jump;
this.link = JSC$asm_link;
}
function JSC$ASM_jmp_print (stream)
{
stream.write ("\tjmp\t\t" + this.value.format () + "\n");
}
/* jsr */
function JSC$ASM_jsr (ln)
{
this.type = JSC$OP_JSR;
this.linenum = ln;
this.stack_delta = 1;
this.size = 1;
this.print = JSC$ASM_jsr_print;
this.link = JSC$asm_link;
}
function JSC$ASM_jsr_print (stream)
{
stream.write ("\tjsr\n");
}
/* return */
function JSC$ASM_return (ln)
{
this.type = JSC$OP_RETURN;
this.linenum = ln;
this.size = 1;
this.print = JSC$ASM_return_print;
this.link = JSC$asm_link;
}
function JSC$ASM_return_print (stream)
{
stream.write ("\treturn\n");
}
/* typeof */
function JSC$ASM_typeof (ln)
{
this.type = JSC$OP_TYPEOF;
this.linenum = ln;
this.size = 1;
this.print = JSC$ASM_typeof_print;
this.link = JSC$asm_link;
}
function JSC$ASM_typeof_print (stream)
{
stream.write ("\ttypeof\n");
}
/* new */
function JSC$ASM_new (ln)
{
this.type = JSC$OP_NEW;
this.linenum = ln;
this.stack_delta = 1;
this.size = 1;
this.print = JSC$ASM_new_print;
this.link = JSC$asm_link;
}
function JSC$ASM_new_print (stream)
{
stream.write ("\tnew\n");
}
/* delete_property */
function JSC$ASM_delete_property (ln, value)
{
this.type = JSC$OP_DELETE_PROPERTY;
this.linenum = ln;
this.value = value;
this.size = 5;
this.print = JSC$ASM_delete_property_print;
this.bytecode = JSC$ASM_bytecode_symbol;
this.link = JSC$asm_link;
}
function JSC$ASM_delete_property_print (stream)
{
stream.write ("\tdelete_property\t" + this.value + "\n");
}
/* delete_array */
function JSC$ASM_delete_array (ln)
{
this.type = JSC$OP_DELETE_ARRAY;
this.linenum = ln;
this.stack_delta = -1;
this.size = 1;
this.print = JSC$ASM_delete_array_print;
this.link = JSC$asm_link;
}
function JSC$ASM_delete_array_print (stream)
{
stream.write ("\tdelete_array\n");
}
/* locals */
function JSC$ASM_locals (ln, value)
{
this.type = JSC$OP_LOCALS;
this.linenum = ln;
this.value = value;
this.stack_delta = value;
this.size = 3;
this.print = JSC$ASM_locals_print;
this.bytecode = JSC$ASM_bytecode_int16;
this.link = JSC$asm_link;
}
function JSC$ASM_locals_print (stream)
{
stream.write ("\tlocals\t\t" + this.value.toString () + "\n");
}
/* min_args */
function JSC$ASM_min_args (ln, value)
{
this.type = JSC$OP_MIN_ARGS;
this.linenum = ln;
this.value = value;
this.stack_delta = -1;
this.size = 2;
this.print = JSC$ASM_min_args_print;
this.bytecode = JSC$ASM_bytecode_int8;
this.link = JSC$asm_link;
}
function JSC$ASM_min_args_print (stream)
{
stream.write ("\tmin_args\t" + this.value.toString () + "\n");
}
/* load_nth_arg */
function JSC$ASM_load_nth_arg (ln)
{
this.type = JSC$OP_LOAD_NTH_ARG;
this.linenum = ln;
this.size = 1;
this.print = JSC$ASM_load_nth_arg_print;
this.link = JSC$asm_link;
}
function JSC$ASM_load_nth_arg_print (stream)
{
stream.write ("\tload_nth_arg\n");
}
/* with_push */
function JSC$ASM_with_push (ln)
{
this.type = JSC$OP_WITH_PUSH;
this.linenum = ln;
this.stack_delta = -1;
this.size = 1;
this.print = JSC$ASM_with_push_print;
this.link = JSC$asm_link;
}
function JSC$ASM_with_push_print (stream)
{
stream.write ("\twith_push\n");
}
/* with_pop */
function JSC$ASM_with_pop (ln, value)
{
this.type = JSC$OP_WITH_POP;
this.linenum = ln;
this.value = value;
this.size = 2;
this.print = JSC$ASM_with_pop_print;
this.bytecode = JSC$ASM_bytecode_int8;
this.link = JSC$asm_link;
}
function JSC$ASM_with_pop_print (stream)
{
stream.write ("\twith_pop\t" + this.value.toString () + "\n");
}
/* try_push */
function JSC$ASM_try_push (ln, value)
{
this.type = JSC$OP_TRY_PUSH;
this.linenum = ln;
this.value = value;
this.size = 5;
this.print = JSC$ASM_try_push_print;
this.bytecode = JSC$ASM_bytecode_local_jump;
this.link = JSC$asm_link;
}
function JSC$ASM_try_push_print (stream)
{
stream.write ("\ttry_push\t" + this.value.format () + "\n");
}
/* try_pop */
function JSC$ASM_try_pop (ln, value)
{
this.type = JSC$OP_TRY_POP;
this.linenum = ln;
this.value = value;
this.size = 2;
this.print = JSC$ASM_try_pop_print;
this.bytecode = JSC$ASM_bytecode_int8;
this.link = JSC$asm_link;
}
function JSC$ASM_try_pop_print (stream)
{
stream.write ("\ttry_pop\t\t" + this.value.toString () + "\n");
}
/* throw */
function JSC$ASM_throw (ln)
{
this.type = JSC$OP_THROW;
this.linenum = ln;
this.stack_delta = -1;
this.size = 1;
this.print = JSC$ASM_throw_print;
this.link = JSC$asm_link;
}
function JSC$ASM_throw_print (stream)
{
stream.write ("\tthrow\n");
}
/* iffalse_b */
function JSC$ASM_iffalse_b (ln, value)
{
this.type = JSC$OP_IFFALSE_B;
this.linenum = ln;
this.value = value;
this.stack_delta = -1;
this.size = 5;
this.print = JSC$ASM_iffalse_b_print;
this.bytecode = JSC$ASM_bytecode_local_jump;
this.link = JSC$asm_link;
}
function JSC$ASM_iffalse_b_print (stream)
{
stream.write ("\tiffalse_b\t" + this.value.format () + "\n");
}
/* iftrue */
function JSC$ASM_iftrue_b (ln, value)
{
this.type = JSC$OP_IFTRUE_B;
this.linenum = ln;
this.value = value;
this.stack_delta = -1;
this.size = 5;
this.print = JSC$ASM_iftrue_b_print;
this.bytecode = JSC$ASM_bytecode_local_jump;
this.link = JSC$asm_link;
}
function JSC$ASM_iftrue_b_print (stream)
{
stream.write ("\tiftrue_b\t" + this.value.format () + "\n");
}
/* add_1_i */
function JSC$ASM_add_1_i (ln)
{
this.type = JSC$OP_ADD_1_I;
this.linenum = ln;
this.size = 1;
this.print = JSC$ASM_add_1_i_print;
this.link = JSC$asm_link;
}
function JSC$ASM_add_1_i_print (stream)
{
stream.write ("\tadd_1_i\n");
}
/* add_2_i */
function JSC$ASM_add_2_i (ln)
{
this.type = JSC$OP_ADD_2_I;
this.linenum = ln;
this.size = 1;
this.print = JSC$ASM_add_2_i_print;
this.link = JSC$asm_link;
}
function JSC$ASM_add_2_i_print (stream)
{
stream.write ("\tadd_2_i\n");
}
/* load_global_w */
function JSC$ASM_load_global_w (ln, value)
{
this.type = JSC$OP_LOAD_GLOBAL_W;
this.linenum = ln;
this.value = value;
this.stack_delta = 1;
this.size = 5;
this.print = JSC$ASM_load_global_w_print;
this.bytecode = JSC$ASM_bytecode_symbol;
this.link = JSC$asm_link;
}
function JSC$ASM_load_global_w_print (stream)
{
stream.write ("\tload_global_w\t" + this.value + "\n");
}
/* jsr_w */
function JSC$ASM_jsr_w (ln, value)
{
this.type = JSC$OP_JSR_W;
this.linenum = ln;
this.value = value;
this.stack_delta = 1;
this.size = 5;
this.print = JSC$ASM_jsr_w_print;
this.bytecode = JSC$ASM_bytecode_symbol;
this.link = JSC$asm_link;
}
function JSC$ASM_jsr_w_print (stream)
{
stream.write ("\tjsr_w\t\t" + this.value + "\n");
}
/*
* General helpers.
*/
function JSC$asm_link ()
{
this.next = null;
if (JSC$asm_tail != null)
{
JSC$asm_tail_prev = JSC$asm_tail;
JSC$asm_tail.next = this;
}
else
JSC$asm_head = this;
JSC$asm_tail = this;
}
/*
* The phases of the assembler.
*/
/* This is called from the compiler initialization code. */
function JSC$asm_reset ()
{
JSC$asm_label_count = 1;
JSC$asm_head = JSC$asm_tail = JSC$asm_tail_prev = null;
JSC$asm_constcount = 0;
JSC$asm_constants = null;
JSC$asm_known_constants = null;
}
function JSC$asm_generate ()
{
var i;
if (JSC$verbose)
JSC$message ("jsc: generating assembler");
JSC$ns = new JSC$NameSpace ();
/* Functions. */
for (i = 0; i < JSC$functions.length; i++)
JSC$functions[i].asm ();
/* Global statements. */
if (JSC$global_stmts.length > 0)
{
/* Define the `.global' symbol. */
new JSC$ASM_symbol (JSC$global_stmts[0].linenum, ".global").link ();
/* Handle local variables. */
var num_locals = JSC$count_locals_from_stmt_list (JSC$global_stmts);
if (num_locals > 0)
new JSC$ASM_locals (JSC$global_stmts[0].linenum, num_locals).link ();
/* Generate assembler. */
for (i = 0; i < JSC$global_stmts.length; i++)
JSC$global_stmts[i].asm ();
/*
* Fix things so that also the global statement returns something
* (this is required when we use eval() in JavaScript).
*/
if (JSC$asm_tail_prev == null)
{
/* This is probably illegal, but we don't panic. */
new JSC$ASM_const_undefined (0).link ();
}
else
{
/*
* If the latest op is `pop', remove it. Otherwise, append
* a `const_undefined'.
*/
if (JSC$asm_tail.type == JSC$OP_POP)
{
JSC$asm_tail = JSC$asm_tail_prev;
JSC$asm_tail.next = null;
JSC$asm_tail_prev = null;
}
else
new JSC$ASM_const_undefined (JSC$asm_tail.linenum).link ();
}
}
JSC$ns = null;
}
function JSC$asm_print (src_stream, stream)
{
var i;
var last_ln;
var annotate = src_stream ? true : false;
if (annotate)
{
stream.write ("; -*- asm -*-\n");
/* Set the prev properties. */
var prev = null;
for (i = JSC$asm_head; i != null; prev = i, i = i.next)
i.prev = prev;
/*
* Fix the label line numbers to be the same that the next
* assembler operand has.
*/
last_ln = 0;
for (i = JSC$asm_tail; i != null; i = i.prev)
{
if (i.type == JSC$ASM_LABEL)
i.linenum = last_ln;
else if (typeof i.linenum != "undefined")
last_ln = i.linenum;
}
}
last_ln = 0;
for (i = JSC$asm_head; i != null; i = i.next)
{
if (typeof i.linenum == "undefined")
{
if (annotate)
stream.write ("; undefined linenum\n");
}
else
while (annotate && i.linenum > last_ln)
{
var line = src_stream.readln ();
stream.write ("; " + line + "\n");
last_ln++;
}
i.print (stream);
}
}
function JSC$asm_is_load_op (op)
{
return (op.type == JSC$OP_LOAD_GLOBAL
|| op.type == JSC$OP_LOAD_ARG
|| op.type == JSC$OP_LOAD_LOCAL);
}
function JSC$asm_is_store_op (op)
{
return (op.type == JSC$OP_STORE_GLOBAL
|| op.type == JSC$OP_STORE_ARG
|| op.type == JSC$OP_STORE_LOCAL);
}
function JSC$asm_is_local_jump (op)
{
return (op.type == JSC$OP_JMP
|| op.type == JSC$OP_IFFALSE
|| op.type == JSC$OP_IFTRUE
|| op.type == JSC$OP_IFFALSE_B
|| op.type == JSC$OP_IFTRUE_B
|| op.type == JSC$OP_TRY_PUSH);
}
function JSC$asm_is_const_op (op)
{
return (JSC$OP_CONST <= op.type && op.type <= JSC$OP_CONST_I3);
}
function JSC$asm_lookup_next_op (item)
{
while (item != null &&
(item.type == JSC$ASM_LABEL || item.type == JSC$ASM_SYMBOL))
item = item.next;
return item;
}
function JSC$asm_optimize (flags)
{
var item;
/* Simple peephole optimization. */
if ((flags & JSC$FLAG_OPTIMIZE_PEEPHOLE) != 0)
{
if (JSC$verbose)
JSC$message ("jsc: optimize: peephole");
for (item = JSC$asm_head; item != null; item = item.next)
{
/*
* Optimization for dup ... pop cases where pop removes the
* item duplicated by dup.
*/
if (item.next != null && item.next.type == JSC$OP_DUP)
{
var balance = 2;
var found = false;
var i1;
for (i1 = item.next.next;
i1 != null && i1.next != null;
i1 = i1.next)
{
var i2 = i1.next;
/*
* The lookup ends on branches, and on dup, throw,
* and try_pop operands. We optimize on a basic
* block and we match the closest dup-pop pairs.
*/
if (JSC$asm_is_local_jump (i1)
|| i1.type == JSC$OP_JSR
|| i1.type == JSC$OP_NEW
|| i1.type == JSC$OP_CALL_METHOD
|| i1.type == JSC$OP_RETURN
|| i1.type == JSC$ASM_SYMBOL
|| i1.type == JSC$ASM_LABEL
|| i1.type == JSC$OP_DUP
|| i1.type == JSC$OP_TRY_POP
|| i1.type == JSC$OP_THROW)
break;
if (i1.stack_delta)
{
balance += i1.stack_delta;
if (balance <= 0)
/* Going to negative. Stop here. */
break;
}
if (i2.type == JSC$OP_POP && balance == 1)
{
/* Found a matching pop. */
found = true;
i1.next = i2.next;
break;
}
}
if (found)
{
/* The dup can be removed. */
item.next = item.next.next;
}
}
/* Two instruction optimization (starting from item.next). */
if (item.next != null && item.next.next != null)
{
var i1 = item.next;
var i2 = i1.next;
if (i1.type == JSC$OP_APOP
&& i2.type == JSC$OP_POP)
{
/*
* i1: apop n
* i2: pop -> pop_n n + 1
*/
var i = new JSC$ASM_pop_n (i1.linenum, i1.value + 1);
item.next = i;
i.next = i2.next;
}
}
if (item.next != null && item.next.next != null)
{
var i1 = item.next;
var i2 = i1.next;
if (i1.type == JSC$OP_CONST_TRUE
&& (i2.type == JSC$OP_IFFALSE
|| i2.type == JSC$OP_IFFALSE_B))
{
/*
* i1: const_true
* i2: iffalse{,_b} .LX => ---
*/
item.next = i2.next;
}
}
if (item.next != null && item.next.next != null)
{
var i1 = item.next;
var i2 = i1.next;
if (i1.type == JSC$OP_CONST_FALSE
&& (i2.type == JSC$OP_IFTRUE
|| i2.type == JSC$OP_IFTRUE_B))
{
/*
* i1: const_false
* i2: iftrue{,_b} .LX => ---
*/
item.next = i2.next;
}
}
if (item.next != null && item.next.next != null)
{
var i1 = item.next;
var i2 = i1.next;
if ((i1.type == JSC$OP_CONST_FALSE
&& (i2.type == JSC$OP_IFFALSE
|| i2.type == JSC$OP_IFFALSE_B))
|| (i1.type == JSC$OP_CONST_TRUE
&& (i2.type == JSC$OP_IFTRUE
|| i2.type == JSC$OP_IFTRUE_B)))
{
/*
* i1: const_false
* i2: iffalse{,_b} .LX => jmp .LX
*
* i1: const_true
* i2: iftrue{,_b} .LX => jmp .LX
*/
var i = new JSC$ASM_jmp (i1.linenum, i2.value);
item.next = i;
i.next = i2.next;
}
}
}
}
/* Jumps to jumps. */
if ((flags & JSC$FLAG_OPTIMIZE_JUMPS) != 0)
{
if (JSC$verbose)
JSC$message ("jsc: optimize: jumps to jumps");
for (item = JSC$asm_head; item != null; item = item.next)
if (JSC$asm_is_local_jump (item))
{
var i2;
/* Operand's value is a label */
i2 = JSC$asm_lookup_next_op (item.value);
if (i2 != null && i2.type == JSC$OP_JMP)
/* Ok, we can jump there directly. */
item.value = i2.value;
}
}
if ((flags & JSC$FLAG_OPTIMIZE_HEAVY) != 0)
JSC$optimize_heavy ();
/*
* Optimizations for the size of the generated byte-code. It isn't
* probably worth of doing these optimization for interactive
* scripts since these won't affect the speed of the execution.
* However, these optimizations make the byte-code files smaller so
* these are nice for batch-compiled files.
*/
if ((flags & JSC$FLAG_OPTIMIZE_BC_SIZE) != 0)
{
var delta = true;
while (delta)
{
delta = false;
/* Remove un-referenced labels. */
if (JSC$verbose)
JSC$message ("jsc: optimize: removing un-referenced labels");
/* First, make all labels unreferenced. */
for (item = JSC$asm_head; item != null; item = item.next)
if (item.type == JSC$ASM_LABEL)
item.referenced = false;
/* Second, mark all referenced labels. */
for (item = JSC$asm_head; item != null; item = item.next)
if (JSC$asm_is_local_jump (item))
item.value.referenced = true;
/* Third, remove all un-referenced labels. */
for (item = JSC$asm_head; item != null; item = item.next)
while (item.next != null && item.next.type == JSC$ASM_LABEL
&& !item.next.referenced
&& item.next.next != null)
{
delta = true;
item.next = item.next.next;
}
/* Dead code elimination. */
if (JSC$verbose)
JSC$message ("jsc: optimize: dead code elimination");
for (item = JSC$asm_head; item != null; item = item.next)
if (item.type == JSC$OP_RETURN || item.type == JSC$OP_JMP)
while (item.next != null && item.next.type != JSC$ASM_SYMBOL
&& item.next.type != JSC$ASM_LABEL)
{
delta = true;
item.next = item.next.next;
}
/* Simple peephole optimization. */
if (JSC$verbose)
JSC$message ("jsc: optimize: peephole");
for (item = JSC$asm_head; item != null; item = item.next)
{
/* Two instruction optimization (starting from item.next). */
if (item.next != null && item.next.next != null)
{
var i1 = item.next;
var i2 = i1.next;
if (i1.type == JSC$OP_JMP
&& i2.type == JSC$ASM_LABEL
&& i1.value == i2)
{
/*
* i1: jmp .LX
* i2: .LX => .LX
*/
item.next = i2;
delta = true;
}
}
}
}
}
}
function JSC$optimize_heavy ()
{
if (JSC$verbose)
JSC$message ("jsc: optimize: liveness analyzing");
/* First, set the prev pointers and zero usage flags. */
var item, prev = null;
for (item = JSC$asm_head; item != null; prev = item, item = item.next)
{
item.prev = prev;
item.live_args = 0;
item.live_locals = 0;
item.live_used = false;
}
/* For each function. */
var ftail, fhead;
for (ftail = JSC$asm_tail; ftail != null; ftail = fhead.prev)
{
var change = true;
/* While there is change in the liveness. */
while (change)
{
change = false;
for (fhead = ftail;
fhead.type != JSC$ASM_SYMBOL;
fhead = fhead.prev)
{
var floc, farg;
if (fhead.next != null)
{
floc = fhead.next.live_locals;
farg = fhead.next.live_args;
}
else
floc = farg = 0;
if (fhead.type == JSC$OP_LOAD_LOCAL && fhead.value < 32)
floc |= (1 << fhead.value);
if (fhead.type == JSC$OP_STORE_LOCAL && fhead.value < 32)
floc &= ~(1 << fhead.value);
if (fhead.type == JSC$OP_LOAD_ARG && fhead.value < 32)
farg |= (1 << fhead.value);
if (fhead.type == JSC$OP_STORE_ARG && fhead.value < 32)
farg &= ~(1 << fhead.value);
if (JSC$asm_is_local_jump (fhead))
{
floc |= fhead.value.live_locals;
fhead.value.live_used = true;
}
if (fhead.live_used && (fhead.live_locals != floc
|| fhead.live_args != farg))
change = true;
fhead.live_used = false;
fhead.live_locals = floc;
fhead.live_args = farg;
}
}
}
/*
* When we have the liveness analyzing performed, we can do some
* fancy optimizations.
*/
if (JSC$verbose)
JSC$message ("jsc: optimize: peephole");
for (item = JSC$asm_head; item != null; item = item.next)
{
/* Three instruction optimization. */
if (item.next != null && item.next.next != null
&& item.next.next.next != null)
{
var i1 = item.next;
var i2 = i1.next;
var i3 = i2.next;
if (i1.type == JSC$OP_STORE_LOCAL
&& i2.type == JSC$OP_LOAD_LOCAL
&& i1.value == i2.value
&& (i3.live_locals & (1 << i1.value)) == 0)
{
/*
* i1: store_local n
* i2: load_local n
* i3: nnn (n not live) => nnn
*/
item.next = i3;
}
}
}
}
function JSC$asm_finalize ()
{
var item;
var offset = 0;
for (item = JSC$asm_head; item != null; item = item.next)
{
item.offset = offset;
offset += item.size;
}
}
function JSC$ConstantReg ()
{
}
function JSC$asm_genconstant (val)
{
if (JSC$asm_known_constants == null)
JSC$asm_known_constants = new JSC$ConstantReg ();
/* Lookup <val> from a list of known constants. */
var id = JSC$asm_known_constants[val];
if (typeof id == "number")
return id;
/* This is a new constant. */
JSC$asm_constants.append (val);
JSC$asm_known_constants[val] = JSC$asm_constcount;
return JSC$asm_constcount++;
}
function JSC$asm_bytecode ()
{
var item;
var symtab = new String ("");
var nsymtab_entries = 0;
var code = new String ("");
var debug = new String ("");
var debug_last_linenum = 0;
if (JSC$verbose)
JSC$message ("jsc: generating byte-code");
if (JSC$generate_debug_info)
/* Source file name. */
debug.append (String.pack ("CN", JSC$DEBUG_FILENAME, JSC$filename.length)
+ JSC$filename);
JSC$asm_constants = new String ("");
for (item = JSC$asm_head; item != null; item = item.next)
{
if (item.type == JSC$ASM_SYMBOL)
{
symtab.append (item.value + String.pack ("CN", 0, item.offset));
nsymtab_entries++;
}
else if (item.type == JSC$ASM_LABEL)
;
else
{
/* Real assembler operands. */
if (JSC$generate_debug_info)
if (item.linenum != debug_last_linenum)
{
debug.append (String.pack ("CNN", JSC$DEBUG_LINENUMBER,
item.offset + item.size,
item.linenum));
debug_last_linenum = item.linenum;
}
if (item.size == 1)
/* We handle these. */
code.append (String.pack ("C", item.type));
else
{
/*
* All operands which take an argument, have a method to create
* the byte code entry for their argument.
*/
code.append (String.pack ("C", item.type) + item.bytecode ());
}
}
}
symtab = String.pack ("N", nsymtab_entries) + symtab;
if (JSC$verbose)
{
var msg = ("jsc: code=" + code.length.toString ()
+ ", constants=" + JSC$asm_constants.length.toString ()
+ ", symtab=" + symtab.length.toString ());
if (JSC$generate_debug_info)
msg += ", debug=" + debug.length.toString ();
msg += (", headers="
+ (32 + (JSC$generate_debug_info ? 8 : 0)).toString ()
+ ", total="
+ (code.length + JSC$asm_constants.length + symtab.length
+ debug.length + 32
+ (JSC$generate_debug_info ? 8 : 0)).toString ());
JSC$message (msg);
}
return (String.pack ("NN", JSC$BC_MAGIC,
3 + (JSC$generate_debug_info ? 1 : 0))
+ String.pack ("NN", JSC$BC_SECT_CODE, code.length) + code
+ String.pack ("NN", JSC$BC_SECT_CONSTANTS,
JSC$asm_constants.length)
+ JSC$asm_constants
+ String.pack ("NN", JSC$BC_SECT_SYMTAB, symtab.length) + symtab
+ (JSC$generate_debug_info
? String.pack ("NN", JSC$BC_SECT_DEBUG, debug.length) + debug
: ""));
}
/*
Local variables:
mode: c
End:
*/
/*
* Public entry points to the JavaScript compiler.
* Copyright (c) 1998 New Generation Software (NGS) Oy
*
* Author: Markku Rossi <mtr@ngs.fi>
*/
/*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA
*/
/*
* $Source: /usr/local/cvsroot/ngs/js/jsc/compiler.js,v $
* $Id: compiler.js,v 1.42 1999/01/11 09:01:33 mtr Exp $
*/
/*
* Definitions.
*/
JSC$FLAG_VERBOSE = 0x00000001;
JSC$FLAG_ANNOTATE_ASSEMBLER = 0x00000002;
JSC$FLAG_GENERATE_DEBUG_INFO = 0x00000004;
JSC$FLAG_GENERATE_EXECUTABLE_BC_FILES = 0x00000008;
JSC$FLAG_OPTIMIZE_PEEPHOLE = 0x00000020;
JSC$FLAG_OPTIMIZE_JUMPS = 0x00000040;
JSC$FLAG_OPTIMIZE_BC_SIZE = 0x00000080;
JSC$FLAG_OPTIMIZE_HEAVY = 0x00000100;
JSC$FLAG_OPTIMIZE_MASK = 0x0000fff0;
JSC$FLAG_WARN_UNUSED_ARGUMENT = 0x00010000;
JSC$FLAG_WARN_UNUSED_VARIABLE = 0x00020000;
JSC$FLAG_WARN_SHADOW = 0x00040000;
JSC$FLAG_WARN_WITH_CLOBBER = 0x00080000;
JSC$FLAG_WARN_MISSING_SEMICOLON = 0x00100000;
JSC$FLAG_WARN_STRICT_ECMA = 0x00200000;
JSC$FLAG_WARN_DEPRECATED = 0x00400000;
JSC$FLAG_WARN_MASK = 0xffff0000;
/*
* Global interfaces to the compiler.
*/
function JSC$compile_file (fname, flags, asm_file, bc_file)
{
var stream = new JSC$StreamFile (fname);
return JSC$compile_stream (stream, flags, asm_file, bc_file);
}
function JSC$compile_string (str, flags, asm_file, bc_file)
{
var stream = new JSC$StreamString (str);
return JSC$compile_stream (stream, flags, asm_file, bc_file);
}
function JSC$compiler_reset ()
{
/* Reset compiler to a known initial state. */
JSC$parser_reset ();
JSC$gram_reset ();
JSC$asm_reset ();
}
function JSC$compile_stream (stream, flags, asm_file, bc_file)
{
var result = false;
JSC$compiler_reset ();
if (stream.open ())
{
try
{
JSC$verbose = ((flags & JSC$FLAG_VERBOSE) != 0);
JSC$generate_debug_info
= ((flags & JSC$FLAG_GENERATE_DEBUG_INFO) != 0);
JSC$warn_unused_argument
= ((flags & JSC$FLAG_WARN_UNUSED_ARGUMENT) != 0);
JSC$warn_unused_variable
= ((flags & JSC$FLAG_WARN_UNUSED_VARIABLE) != 0);
JSC$warn_shadow
= ((flags & JSC$FLAG_WARN_SHADOW) != 0);
JSC$warn_with_clobber
= ((flags & JSC$FLAG_WARN_WITH_CLOBBER) != 0);
JSC$warn_missing_semicolon
= ((flags & JSC$FLAG_WARN_MISSING_SEMICOLON) != 0);
JSC$warn_strict_ecma
= ((flags & JSC$FLAG_WARN_STRICT_ECMA) != 0);
JSC$warn_deprecated
= ((flags & JSC$FLAG_WARN_DEPRECATED) != 0);
/* Compilation and assembler generation time optimizations. */
JSC$optimize_constant_folding = true;
JSC$optimize_type = true;
JSC$parser_parse (stream);
/* Assembler. */
JSC$asm_generate ();
/*
* We don't need the syntax tree anymore. Free it and save
* some memory.
*/
JSC$parser_reset ();
/* Optimize if requested. */
if ((flags & JSC$FLAG_OPTIMIZE_MASK) != 0)
JSC$asm_optimize (flags);
if (typeof asm_file == "string")
{
var asm_stream = new File (asm_file);
var src_stream = false;
if (asm_stream.open ("w"))
{
if ((flags & JSC$FLAG_ANNOTATE_ASSEMBLER) != 0)
if (stream.rewind ())
src_stream = stream;
JSC$asm_print (src_stream, asm_stream);
asm_stream.close ();
}
else
JSC$message ("jsc: couldn't create asm output file \""
+ asm_file + "\": "
+ System.strerror (System.errno));
}
JSC$asm_finalize ();
result = JSC$asm_bytecode ();
/* Remove all intermediate results from the memory. */
JSC$compiler_reset ();
if (typeof bc_file == "string")
{
var ostream = new File (bc_file);
if (ostream.open ("w"))
{
ostream.write (result);
ostream.close ();
if ((flags & JSC$FLAG_GENERATE_EXECUTABLE_BC_FILES) != 0)
{
/* Add execute permissions to the output file. */
var st = File.stat (bc_file);
if (st)
{
if (!File.chmod (bc_file, st[2] | 0111))
JSC$message ("jsc: couldn't add execute "
+ "permissions to bc file \""
+ bc_file + "\": "
+ System.strerror (System.errno));
}
else
JSC$message ("jsc: couldn't stat bc file \"" + bc_file
+ "\": "
+ System.strerror (System.errno));
}
}
else
JSC$message ("jsc: couldn't create bc file \"" + bc_file
+ "\": " + System.strerror (System.errno));
}
}
finally
{
stream.close ();
}
}
else
error ("jsc: couldn't open input stream \"" + stream.name + "\": "
+ stream.error);
return result;
}
/*
Local variables:
mode: c
End:
*/
syntax highlighted by Code2HTML, v. 0.9.1