Merge branch 'master' into harmony-2.8.0

This commit is contained in:
alexlamsl
2017-02-27 04:37:48 +08:00
63 changed files with 4471 additions and 480 deletions

View File

@@ -81,7 +81,9 @@ function DEFNODE(type, props, methods, base) {
ctor.DEFMETHOD = function(name, method) {
this.prototype[name] = method;
};
exports["AST_" + type] = ctor;
if (typeof exports !== "undefined") {
exports["AST_" + type] = ctor;
}
return ctor;
};
@@ -143,12 +145,13 @@ var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", {
}, AST_Statement);
function walk_body(node, visitor) {
if (node.body instanceof AST_Node) {
node.body._walk(visitor);
var body = node.body;
if (body instanceof AST_Node) {
body._walk(visitor);
}
else for (var i = 0, len = body.length; i < len; i++) {
body[i]._walk(visitor);
}
else node.body.forEach(function(stat){
stat._walk(visitor);
});
};
var AST_Block = DEFNODE("Block", "body", {
@@ -474,9 +477,10 @@ var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments is_generator",
_walk: function(visitor) {
return visitor._visit(this, function(){
if (this.name) this.name._walk(visitor);
this.argnames.forEach(function(arg){
arg._walk(visitor);
});
var argnames = this.argnames;
for (var i = 0, len = argnames.length; i < len; i++) {
argnames[i]._walk(visitor);
}
walk_body(this, visitor);
});
}
@@ -704,9 +708,10 @@ var AST_Definitions = DEFNODE("Definitions", "definitions", {
},
_walk: function(visitor) {
return visitor._visit(this, function(){
this.definitions.forEach(function(def){
def._walk(visitor);
});
var definitions = this.definitions;
for (var i = 0, len = definitions.length; i < len; i++) {
definitions[i]._walk(visitor);
}
});
}
}, AST_Statement);
@@ -806,9 +811,10 @@ var AST_Call = DEFNODE("Call", "expression args", {
_walk: function(visitor) {
return visitor._visit(this, function(){
this.expression._walk(visitor);
this.args.forEach(function(arg){
arg._walk(visitor);
});
var args = this.args;
for (var i = 0, len = args.length; i < len; i++) {
args[i]._walk(visitor);
}
});
}
});
@@ -979,9 +985,10 @@ var AST_Array = DEFNODE("Array", "elements", {
},
_walk: function(visitor) {
return visitor._visit(this, function(){
this.elements.forEach(function(el){
el._walk(visitor);
});
var elements = this.elements;
for (var i = 0, len = elements.length; i < len; i++) {
elements[i]._walk(visitor);
}
});
}
});
@@ -993,9 +1000,10 @@ var AST_Object = DEFNODE("Object", "properties", {
},
_walk: function(visitor) {
return visitor._visit(this, function(){
this.properties.forEach(function(prop){
prop._walk(visitor);
});
var properties = this.properties;
for (var i = 0, len = properties.length; i < len; i++) {
properties[i]._walk(visitor);
}
});
}
});

File diff suppressed because it is too large Load Diff

View File

@@ -71,7 +71,7 @@ function OutputStream(options) {
unescape_regexps : false,
inline_script : false,
width : 80,
max_line_len : 32000,
max_line_len : false,
beautify : false,
source_map : null,
bracketize : false,
@@ -232,6 +232,7 @@ function OutputStream(options) {
var might_need_space = false;
var might_need_semicolon = false;
var might_add_newline = 0;
var last = null;
function last_char() {
@@ -244,10 +245,22 @@ function OutputStream(options) {
return char;
};
function maybe_newline() {
if (options.max_line_len && current_col > options.max_line_len)
print("\n");
};
var ensure_line_len = options.max_line_len ? function() {
if (current_col > options.max_line_len) {
if (might_add_newline) {
var left = OUTPUT.slice(0, might_add_newline);
var right = OUTPUT.slice(might_add_newline);
OUTPUT = left + "\n" + right;
current_line++;
current_pos++;
current_col = right.length;
}
if (current_col > options.max_line_len) {
AST_Node.warn("Output exceeds {max_line_len} characters", options);
}
}
might_add_newline = 0;
} : noop;
var requireSemicolonChars = makePredicate("( [ + * / - , .");
@@ -263,6 +276,7 @@ function OutputStream(options) {
current_col++;
current_pos++;
} else {
ensure_line_len();
OUTPUT += "\n";
current_pos++;
current_line++;
@@ -283,6 +297,7 @@ function OutputStream(options) {
if (!options.beautify && options.preserve_line && stack[stack.length - 1]) {
var target_line = stack[stack.length - 1].start.line;
while (current_line < target_line) {
ensure_line_len();
OUTPUT += "\n";
current_pos++;
current_line++;
@@ -294,8 +309,9 @@ function OutputStream(options) {
if (might_need_space) {
var prev = last_char();
if ((is_identifier_char(prev)
&& (is_identifier_char(ch) || ch == "\\"))
|| (/^[\+\-\/]$/.test(ch) && ch == prev))
&& (is_identifier_char(ch) || ch == "\\"))
|| (ch == "/" && ch == prev)
|| ((ch == "+" || ch == "-") && ch == last))
{
OUTPUT += " ";
current_col++;
@@ -303,16 +319,16 @@ function OutputStream(options) {
}
might_need_space = false;
}
OUTPUT += str;
current_pos += str.length;
var a = str.split(/\r?\n/), n = a.length - 1;
current_line += n;
if (n == 0) {
current_col += a[n].length;
} else {
current_col += a[0].length;
if (n > 0) {
ensure_line_len();
current_col = a[n].length;
}
current_pos += str.length;
last = str;
OUTPUT += str;
};
var star = function(){
@@ -342,7 +358,10 @@ function OutputStream(options) {
var newline = options.beautify ? function() {
print("\n");
} : maybe_newline;
} : options.max_line_len ? function() {
ensure_line_len();
might_add_newline = OUTPUT.length;
} : noop;
var semicolon = options.beautify ? function() {
print(";");
@@ -419,6 +438,9 @@ function OutputStream(options) {
} : noop;
function get() {
if (might_add_newline) {
ensure_line_len();
}
return OUTPUT;
};
@@ -474,7 +496,6 @@ function OutputStream(options) {
pos : function() { return current_pos },
push_node : function(node) { stack.push(node) },
pop_node : function() { return stack.pop() },
stack : function() { return stack },
parent : function(n) {
return stack[stack.length - 2 - (n || 0)];
}
@@ -1113,7 +1134,10 @@ function OutputStream(options) {
output.space();
output.print("else");
output.space();
force_statement(self.alternative, output);
if (self.alternative instanceof AST_If)
self.alternative.print(output);
else
force_statement(self.alternative, output);
} else {
self._do_print_body(output);
}
@@ -1669,30 +1693,6 @@ function OutputStream(options) {
}
};
// return true if the node at the top of the stack (that means the
// innermost node in the current output) is lexically the first in
// a statement.
function first_in_statement(output) {
var a = output.stack(), i = a.length, node = a[--i], p = a[--i];
while (i > 0) {
if (p instanceof AST_Statement && p.body === node)
return true;
if ((p instanceof AST_Seq && p.car === node ) ||
(p instanceof AST_Call && p.expression === node && !(p instanceof AST_New) ) ||
(p instanceof AST_Dot && p.expression === node ) ||
(p instanceof AST_Sub && p.expression === node ) ||
(p instanceof AST_Conditional && p.condition === node ) ||
(p instanceof AST_Binary && p.left === node ) ||
(p instanceof AST_UnaryPostfix && p.expression === node ))
{
node = p;
p = a[--i];
} else {
return false;
}
}
};
// self should be AST_New. decide if we want to show parens or not.
function need_constructor_parens(self, output) {
// Always print parentheses with arguments

View File

@@ -239,12 +239,11 @@ function JS_Parse_Error(message, filename, line, col, pos) {
this.line = line;
this.col = col;
this.pos = pos;
this.stack = new Error().stack;
};
JS_Parse_Error.prototype.toString = function() {
return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack;
};
JS_Parse_Error.prototype = Object.create(Error.prototype);
JS_Parse_Error.prototype.constructor = JS_Parse_Error;
JS_Parse_Error.prototype.name = "SyntaxError";
configure_error_stack(JS_Parse_Error);
function js_error(message, filename, line, col, pos) {
throw new JS_Parse_Error(message, filename, line, col, pos);
@@ -404,13 +403,13 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
});
if (prefix) num = prefix + num;
if (RE_OCT_NUMBER.test(num) && next_token.has_directive("use strict")) {
parse_error("SyntaxError: Legacy octal literals are not allowed in strict mode");
parse_error("Legacy octal literals are not allowed in strict mode");
}
var valid = parse_js_number(num);
if (!isNaN(valid)) {
return token("num", valid);
} else {
parse_error("SyntaxError: Invalid syntax: " + num);
parse_error("Invalid syntax: " + num);
}
};
@@ -428,13 +427,13 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
if (peek() == "{") {
next(true);
if (peek() === "}")
parse_error("SyntaxError: Expecting hex-character between {}");
parse_error("Expecting hex-character between {}");
while (peek() == "0") next(true); // No significance
var result, length = find("}", true) - S.pos;
// Avoid 32 bit integer overflow (1 << 32 === 1)
// We know first character isn't 0 and thus out of range anyway
if (length > 6 || (result = hex_bytes(length)) > 0x10FFFF) {
parse_error("SyntaxError: Unicode reference out of bounce");
parse_error("Unicode reference out of bounce");
}
next(true);
return from_char_code(result);
@@ -464,7 +463,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
// Parse
if (ch === "0") return "\0";
if (ch.length > 0 && next_token.has_directive("use strict"))
parse_error("SyntaxError: Legacy octal escape sequences are not allowed in strict mode");
parse_error("Legacy octal escape sequences are not allowed in strict mode");
return String.fromCharCode(parseInt(ch, 8));
}
@@ -473,18 +472,18 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
for (; n > 0; --n) {
var digit = parseInt(next(true), 16);
if (isNaN(digit))
parse_error("SyntaxError: Invalid hex-character pattern in string");
parse_error("Invalid hex-character pattern in string");
num = (num << 4) | digit;
}
return num;
};
var read_string = with_eof_error("SyntaxError: Unterminated string constant", function(quote_char){
var read_string = with_eof_error("Unterminated string constant", function(quote_char){
var quote = next(), ret = "";
for (;;) {
var ch = next(true, true);
if (ch == "\\") ch = read_escaped_char(true);
else if (NEWLINE_CHARS(ch)) parse_error("SyntaxError: Unterminated string constant");
else if (NEWLINE_CHARS(ch)) parse_error("Unterminated string constant");
else if (ch == quote) break;
ret += ch;
}
@@ -493,7 +492,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
return tok;
});
var read_template_characters = with_eof_error("SyntaxError: Unterminated template", function(begin){
var read_template_characters = with_eof_error("Unterminated template", function(begin){
if (begin) {
S.template_braces.push(S.brace_counter);
}
@@ -543,7 +542,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
return next_token;
};
var skip_multiline_comment = with_eof_error("SyntaxError: Unterminated multiline comment", function(){
var skip_multiline_comment = with_eof_error("Unterminated multiline comment", function(){
var regex_allowed = S.regex_allowed;
var i = find("*/", true);
var text = S.text.substring(S.pos, i).replace(/\r\n|\r|\u2028|\u2029/g, '\n');
@@ -555,13 +554,13 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
return next_token;
});
var read_name = with_eof_error("SyntaxError: Unterminated identifier name", function() {
var read_name = with_eof_error("Unterminated identifier name", function() {
var name = "", ch, escaped = false, hex;
var read_escaped_identifier_char = function() {
escaped = true;
next();
if (peek() !== "u") {
parse_error("SyntaxError: Expecting UnicodeEscapeSequence -- uXXXX or u{XXXX}");
parse_error("Expecting UnicodeEscapeSequence -- uXXXX or u{XXXX}");
}
return read_escaped_char();
}
@@ -570,7 +569,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
if ((name = peek()) === "\\") {
name = read_escaped_identifier_char();
if (!is_identifier_start(name)) {
parse_error("SyntaxError: First identifier char is an invalid identifier char");
parse_error("First identifier char is an invalid identifier char");
}
} else if (is_identifier_start(name)){
next();
@@ -583,7 +582,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
if ((ch = peek()) === "\\") {
ch = read_escaped_identifier_char();
if (!is_identifier_char(ch)) {
parse_error("SyntaxError: Invalid escaped identifier char");
parse_error("Invalid escaped identifier char");
}
} else {
if (!is_identifier_char(ch)) {
@@ -594,15 +593,15 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
name += ch;
}
if (RESERVED_WORDS(name) && escaped) {
parse_error("SyntaxError: Escaped characters are not allowed in keywords");
parse_error("Escaped characters are not allowed in keywords");
}
return name;
});
var read_regexp = with_eof_error("SyntaxError: Unterminated regular expression", function(regexp){
var read_regexp = with_eof_error("Unterminated regular expression", function(regexp){
var prev_backslash = false, ch, in_class = false;
while ((ch = next(true))) if (NEWLINE_CHARS(ch)) {
parse_error("SyntaxError: Unexpected line terminator");
parse_error("Unexpected line terminator");
} else if (prev_backslash) {
regexp += "\\" + ch;
prev_backslash = false;
@@ -623,7 +622,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
try {
return token("regexp", new RegExp(regexp, mods));
} catch(e) {
parse_error("SyntaxError: " + e.message);
parse_error(e.message);
}
});
@@ -752,7 +751,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
}
break;
}
parse_error("SyntaxError: Unexpected character '" + ch + "'");
parse_error("Unexpected character '" + ch + "'");
};
next_token.next = next;
@@ -914,14 +913,14 @@ function parse($TEXT, options) {
function unexpected(token) {
if (token == null)
token = S.token;
token_error(token, "SyntaxError: Unexpected token: " + token.type + " (" + token.value + ")");
token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")");
};
function expect_token(type, val) {
if (is(type, val)) {
return next();
}
token_error(S.token, "SyntaxError: Unexpected token " + S.token.type + " «" + S.token.value + "»" + ", expected " + type + " «" + val + "»");
token_error(S.token, "Unexpected token " + S.token.type + " «" + S.token.value + "»" + ", expected " + type + " «" + val + "»");
};
function expect(punc) { return expect_token("punc", punc); };
@@ -1058,7 +1057,7 @@ function parse($TEXT, options) {
case "return":
if (S.in_function == 0 && !options.bare_returns)
croak("SyntaxError: 'return' outside of function");
croak("'return' outside of function");
return new AST_Return({
value: ( is("punc", ";")
? (next(), null)
@@ -1075,7 +1074,7 @@ function parse($TEXT, options) {
case "throw":
if (S.token.nlb)
croak("SyntaxError: Illegal newline after 'throw'");
croak("Illegal newline after 'throw'");
return new AST_Throw({
value: (tmp = expression(true), semicolon(), tmp)
});
@@ -1094,7 +1093,7 @@ function parse($TEXT, options) {
case "with":
if (S.input.has_directive("use strict")) {
croak("SyntaxError: Strict mode may not include a with statement");
croak("Strict mode may not include a with statement");
}
return new AST_With({
expression : parenthesised(),
@@ -1115,14 +1114,14 @@ function parse($TEXT, options) {
var label = as_symbol(AST_Label);
if (label.name === "yield" && is_in_generator()) {
// Ecma-262, 12.1.1 Static Semantics: Early Errors
token_error(S.prev, "SyntaxError: Yield cannot be used as label inside generators");
token_error(S.prev, "Yield cannot be used as label inside generators");
}
if (find_if(function(l){ return l.name == label.name }, S.labels)) {
// ECMA-262, 12.12: An ECMAScript program is considered
// syntactically incorrect if it contains a
// LabelledStatement that is enclosed by a
// LabelledStatement with the same Identifier as label.
croak("SyntaxError: Label " + label.name + " defined twice");
croak("Label " + label.name + " defined twice");
}
expect(":");
S.labels.push(label);
@@ -1135,7 +1134,7 @@ function parse($TEXT, options) {
label.references.forEach(function(ref){
if (ref instanceof AST_Continue) {
ref = ref.label.start;
croak("SyntaxError: Continue label `" + label.name + "` refers to non-IterationStatement.",
croak("Continue label `" + label.name + "` refers to non-IterationStatement.",
ref.line, ref.col, ref.pos);
}
});
@@ -1155,11 +1154,11 @@ function parse($TEXT, options) {
if (label != null) {
ldef = find_if(function(l){ return l.name == label.name }, S.labels);
if (!ldef)
croak("SyntaxError: Undefined label " + label.name);
croak("Undefined label " + label.name);
label.thedef = ldef;
}
else if (S.in_loop == 0)
croak("SyntaxError: " + type.TYPE + " not inside a loop or switch");
croak(type.TYPE + " not inside a loop or switch");
semicolon();
var stat = new type({ label: label });
if (ldef) ldef.references.push(stat);
@@ -1180,7 +1179,7 @@ function parse($TEXT, options) {
if (is_in || is_of) {
if ((init instanceof AST_Definitions) &&
init.definitions.length > 1)
croak("SyntaxError: Only one variable declaration allowed in for..in loop");
croak("Only one variable declaration allowed in for..in loop");
next();
if (is_in) {
return for_in(init);
@@ -1232,7 +1231,7 @@ function parse($TEXT, options) {
var arrow_function = function(args) {
if (S.token.nlb) {
croak("SyntaxError: Unexpected newline before arrow (=>)");
croak("Unexpected newline before arrow (=>)");
}
expect_token("arrow", "=>");
@@ -1302,7 +1301,7 @@ function parse($TEXT, options) {
case "eval":
case "yield":
if (strict_mode) {
token_error(token, "SyntaxError: Unexpected " + token.value + " identifier as parameter inside strict mode");
token_error(token, "Unexpected " + token.value + " identifier as parameter inside strict mode");
}
break;
default:
@@ -1331,7 +1330,7 @@ function parse($TEXT, options) {
},
check_strict: function() {
if (tracker.is_strict() && duplicate !== false) {
token_error(duplicate, "SyntaxError: Parameter " + duplicate.value + " was used already");
token_error(duplicate, "Parameter " + duplicate.value + " was used already");
}
}
};
@@ -1457,7 +1456,7 @@ function parse($TEXT, options) {
}));
next();
} else {
croak("SyntaxError: Invalid function parameter");
croak("Invalid function parameter");
}
if (is("operator", "=") && is_expand === false) {
used_parameters.mark_default_assignment(S.token);
@@ -1568,7 +1567,7 @@ function parse($TEXT, options) {
end: prev()
});
} else {
croak("SyntaxError: Invalid function parameter");
croak("Invalid function parameter");
}
}
@@ -1631,7 +1630,7 @@ function parse($TEXT, options) {
function _yield_expression() {
// Previous token must be keyword yield and not be interpret as an identifier
if (!is_in_generator()) {
croak("SyntaxError: Unexpected yield expression outside generator function",
croak("Unexpected yield expression outside generator function",
S.prev.line, S.prev.col, S.prev.pos);
}
var star = false;
@@ -1747,7 +1746,7 @@ function parse($TEXT, options) {
});
}
if (!bcatch && !bfinally)
croak("SyntaxError: Missing catch/finally blocks");
croak("Missing catch/finally blocks");
return new AST_Try({
body : body,
bcatch : bcatch,
@@ -1872,7 +1871,7 @@ function parse($TEXT, options) {
break;
case "operator":
if (!is_identifier_string(tok.value)) {
croak("SyntaxError: Invalid getter/setter name: " + tok.value,
croak("Invalid getter/setter name: " + tok.value,
tok.line, tok.col, tok.pos);
}
ret = _make_symbol(AST_SymbolRef);
@@ -1985,6 +1984,10 @@ function parse($TEXT, options) {
});
});
var create_accessor = embed_tokens(function() {
return function_(AST_Accessor);
});
var object_or_object_destructuring_ = embed_tokens(function() {
var start = S.token, first = true, a = [];
expect("{");
@@ -2142,7 +2145,7 @@ function parse($TEXT, options) {
key : name,
quote : name instanceof AST_SymbolMethod ?
property_token.quote : undefined,
value : function_(AST_Accessor),
value : create_accessor(),
end : prev()
});
}
@@ -2156,7 +2159,7 @@ function parse($TEXT, options) {
key : name,
quote : name instanceof AST_SymbolMethod ?
property_token.quote : undefined,
value : function_(AST_Accessor),
value : create_accessor(),
end : prev()
});
}
@@ -2286,7 +2289,7 @@ function parse($TEXT, options) {
}
case "name":
if (tmp.value === "yield" && S.input.has_directive("use strict") && !is_in_generator()) {
token_error(tmp, "SyntaxError: Unexpected yield identifier inside strict mode");
token_error(tmp, "Unexpected yield identifier inside strict mode");
}
case "string":
case "num":
@@ -2325,11 +2328,11 @@ function parse($TEXT, options) {
function as_symbol(type, noerror) {
if (!is("name")) {
if (!noerror) croak("SyntaxError: Name expected");
if (!noerror) croak("Name expected");
return null;
}
if (is("name", "yield") && S.input.has_directive("use strict")) {
token_error(S.prev, "SyntaxError: Unexpected yield identifier inside strict mode");
token_error(S.prev, "Unexpected yield identifier inside strict mode");
}
var sym = _make_symbol(type);
next();
@@ -2418,7 +2421,7 @@ function parse($TEXT, options) {
function make_unary(ctor, op, expr) {
if ((op == "++" || op == "--") && !is_assignable(expr))
croak("SyntaxError: Invalid use of " + op + " operator");
croak("Invalid use of " + op + " operator");
return new ctor({ operator: op, expression: expr });
};
@@ -2482,7 +2485,7 @@ function parse($TEXT, options) {
next();
return _yield_expression();
} else if (S.input.has_directive("use strict")) {
token_error(S.token, "SyntaxError: Unexpected yield identifier inside strict mode")
token_error(S.token, "Unexpected yield identifier inside strict mode")
}
}
@@ -2525,7 +2528,7 @@ function parse($TEXT, options) {
// Only allow expansion as last element
if (node.elements[i] instanceof AST_Expansion) {
if (i + 1 !== node.elements.length) {
token_error(node.elements[i].start, "SyntaxError: Spread must the be last element in destructuring array");
token_error(node.elements[i].start, "Spread must the be last element in destructuring array");
}
node.elements[i].expression = walk(node.elements[i].expression);
}
@@ -2565,7 +2568,7 @@ function parse($TEXT, options) {
end : prev()
});
}
croak("SyntaxError: Invalid assignment");
croak("Invalid assignment");
}
return left;
};

View File

@@ -174,6 +174,7 @@ function mangle_properties(ast, options) {
// only function declarations after this line
function can_mangle(name) {
if (!is_identifier(name)) return false;
if (unmangleable.indexOf(name) >= 0) return false;
if (reserved.indexOf(name) >= 0) return false;
if (options.only_cache) {

View File

@@ -53,7 +53,6 @@ function SymbolDef(scope, index, orig) {
this.mangled_name = null;
this.object_destructuring_arg = false;
this.undeclared = false;
this.constant = false;
this.index = index;
this.id = SymbolDef.next_id++;
};
@@ -105,15 +104,13 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
var scope = self.parent_scope = null;
var labels = new Dictionary();
var defun = null;
var last_var_had_const_pragma = false;
var nesting = 0;
var in_destructuring = null;
var in_export;
var tw = new TreeWalker(function(node, descend){
if (node.is_block_scope()) {
var save_scope = scope;
scope = new AST_Scope(node);
scope.init_scope_vars(nesting);
scope.init_scope_vars();
scope.parent_scope = save_scope;
if (!(node instanceof AST_Scope)) {
scope.uses_with = save_scope.uses_with;
@@ -131,13 +128,13 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
return true;
}
if (node instanceof AST_Scope) {
node.init_scope_vars(nesting);
node.init_scope_vars();
var save_scope = node.parent_scope = scope;
var save_defun = defun;
var save_labels = labels;
defun = scope = node;
labels = new Dictionary();
++nesting; descend(); --nesting;
descend();
scope = save_scope;
defun = save_defun;
labels = save_labels;
@@ -200,14 +197,10 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
// inside the class.
(node.scope = defun.parent_scope).def_function(node, in_export);
}
else if (node instanceof AST_Var) {
last_var_had_const_pragma = node.has_const_pragma();
}
else if (node instanceof AST_SymbolVar
|| node instanceof AST_SymbolConst
|| node instanceof AST_SymbolLet) {
var def = ((node instanceof AST_SymbolBlockDeclaration) ? scope : defun).def_variable(node, in_export);
def.constant = node instanceof AST_SymbolConst || last_var_had_const_pragma;
def.destructuring = in_destructuring;
def.init = tw.parent().value;
}
@@ -232,17 +225,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
var cls = null;
var globals = self.globals = new Dictionary();
var tw = new TreeWalker(function(node, descend){
function isModified(node, level) {
var parent = tw.parent(level);
if (parent instanceof AST_Unary && (parent.operator === "++" || parent.operator === "--")
|| parent instanceof AST_Assign && parent.left === node
|| parent instanceof AST_Call && parent.expression === node) {
return true;
} else if (parent instanceof AST_PropAccess && parent.expression === node) {
return isModified(parent, level + 1);
}
}
if (node instanceof AST_Lambda) {
var prev_func = func;
func = node;
@@ -273,21 +255,9 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
node.scope.uses_arguments = true;
}
if (!sym) {
var g;
if (globals.has(name)) {
g = globals.get(name);
} else {
g = new SymbolDef(self, globals.size(), node);
g.undeclared = true;
g.global = true;
globals.set(name, g);
}
sym = g;
sym = self.def_global(node);
}
node.thedef = sym;
if (isModified(node, 0)) {
sym.modified = true;
}
node.reference(options);
return true;
}
@@ -299,7 +269,20 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options){
}
});
AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
AST_Toplevel.DEFMETHOD("def_global", function(node){
var globals = this.globals, name = node.name;
if (globals.has(name)) {
return globals.get(name);
} else {
var g = new SymbolDef(this, globals.size(), node);
g.undeclared = true;
g.global = true;
globals.set(name, g);
return g;
}
});
AST_Scope.DEFMETHOD("init_scope_vars", function(){
this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
@@ -307,7 +290,6 @@ AST_Scope.DEFMETHOD("init_scope_vars", function(nesting){
this.parent_scope = null; // the parent scope
this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes
this.cname = -1; // the current index for mangling functions/variables
this.nesting = nesting; // the nesting level of this scope (0 means toplevel)
});
AST_Node.DEFMETHOD("is_block_scope", function(){
@@ -338,15 +320,14 @@ AST_SymbolRef.DEFMETHOD("reference", function(options) {
var s = this.scope;
while (s) {
push_uniq(s.enclosed, def);
if (s === def.scope) break;
if (options.keep_fnames) {
s.variables.each(function(d) {
s.functions.each(function(d) {
push_uniq(def.scope.enclosed, d);
});
}
if (s === def.scope) break;
s = s.parent_scope;
}
this.frame = this.scope.nesting - def.scope.nesting;
});
AST_Scope.DEFMETHOD("find_variable", function(name){
@@ -455,12 +436,6 @@ AST_Symbol.DEFMETHOD("global", function(){
return this.definition().global;
});
AST_Var.DEFMETHOD("has_const_pragma", function() {
var comments_before = this.start && this.start.comments_before;
var lastComment = comments_before && comments_before[comments_before.length - 1];
return lastComment && /@const\b/.test(lastComment.value);
});
AST_Toplevel.DEFMETHOD("_default_mangler_options", function(options){
return defaults(options, {
except : [],

View File

@@ -78,13 +78,28 @@ function repeat_string(str, i) {
return d;
};
function configure_error_stack(fn) {
Object.defineProperty(fn.prototype, "stack", {
get: function() {
var err = new Error(this.message);
err.name = this.name;
try {
throw err;
} catch(e) {
return e.stack;
}
}
});
}
function DefaultsError(msg, defs) {
Error.call(this, msg);
this.msg = msg;
this.message = msg;
this.defs = defs;
};
DefaultsError.prototype = Object.create(Error.prototype);
DefaultsError.prototype.constructor = DefaultsError;
DefaultsError.prototype.name = "DefaultsError";
configure_error_stack(DefaultsError);
DefaultsError.croak = function(msg, defs) {
throw new DefaultsError(msg, defs);
@@ -320,3 +335,26 @@ Dictionary.fromObject = function(obj) {
function HOP(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
// return true if the node at the top of the stack (that means the
// innermost node in the current output) is lexically the first in
// a statement.
function first_in_statement(stack) {
var node = stack.parent(-1);
for (var i = 0, p; p = stack.parent(i); i++) {
if (p instanceof AST_Statement && p.body === node)
return true;
if ((p instanceof AST_Seq && p.car === node ) ||
(p instanceof AST_Call && p.expression === node && !(p instanceof AST_New) ) ||
(p instanceof AST_Dot && p.expression === node ) ||
(p instanceof AST_Sub && p.expression === node ) ||
(p instanceof AST_Conditional && p.condition === node ) ||
(p instanceof AST_Binary && p.left === node ) ||
(p instanceof AST_UnaryPostfix && p.expression === node ))
{
node = p;
} else {
return false;
}
}
}