Improve yield support and restrict usage of strict

- Partially reverting 91cdb93e57 and eaf3911c31 and reimplement
- Add generators support for objects and classes
- Only classes can have static methods so restrict use of it

Special thanks to @rvanvelzen and @kzc for reviewing this patch and
providing constructive feedback over and over again.
This commit is contained in:
Anthony Van de Gejuchte
2016-05-26 17:00:37 +02:00
parent 8ad8d7b717
commit dcfc514c38
10 changed files with 318 additions and 32 deletions

View File

@@ -44,11 +44,11 @@
"use strict";
var KEYWORDS = 'break case catch class const continue debugger default delete do else export extends finally for function if in instanceof new return switch throw try typeof var let void while with import yield';
var KEYWORDS = 'break case catch class const continue debugger default delete do else export extends finally for function if in instanceof new return switch throw try typeof var let void while with import';
var KEYWORDS_ATOM = 'false null true';
var RESERVED_WORDS = 'abstract boolean byte char class double enum export extends final float goto implements import int interface let long native package private protected public short static super synchronized this throws transient volatile'
+ " " + KEYWORDS_ATOM + " " + KEYWORDS;
var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case';
var KEYWORDS_BEFORE_EXPRESSION = 'return new delete throw else case yield';
KEYWORDS = makePredicate(KEYWORDS);
RESERVED_WORDS = makePredicate(RESERVED_WORDS);
@@ -68,7 +68,6 @@ var OPERATORS = makePredicate([
"instanceof",
"typeof",
"new",
"yield",
"void",
"delete",
"++",
@@ -113,6 +112,8 @@ var OPERATORS = makePredicate([
var WHITESPACE_CHARS = makePredicate(characters(" \u00a0\n\r\t\f\u000b\u200b\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\uFEFF"));
var PUNC_AFTER_EXPRESSION = makePredicate(characters(";]),:"));
var PUNC_BEFORE_EXPRESSION = makePredicate(characters("[{(,.;:"));
var PUNC_CHARS = makePredicate(characters("[]{}(),;:`"));
@@ -647,7 +648,6 @@ var UNARY_PREFIX = makePredicate([
"typeof",
"void",
"delete",
"yield",
"--",
"++",
"!",
@@ -711,6 +711,7 @@ function parse($TEXT, options) {
prev : null,
peeked : null,
in_function : 0,
in_generator : -1,
in_directives : true,
in_loop : 0,
labels : []
@@ -776,6 +777,10 @@ function parse($TEXT, options) {
);
};
function is_in_generator() {
return S.in_generator === S.in_function;
}
function semicolon(optional) {
if (is("punc", ";")) next();
else if (!optional && !can_insert_semicolon()) unexpected();
@@ -944,6 +949,10 @@ function parse($TEXT, options) {
function labeled_statement() {
var label = as_symbol(AST_Label);
if (label.name === "yield" && is_in_generator()) {
// Ecma-262, 12.1.1 Static Semantics: Early Errors
croak("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
@@ -1093,7 +1102,7 @@ function parse($TEXT, options) {
unexpected();
var args = params_or_seq_().as_params(croak);
var body = _function_body(true);
var body = _function_body(true, is_generator);
return new ctor({
start : args.start,
end : body.end,
@@ -1131,10 +1140,13 @@ function parse($TEXT, options) {
});
}
function _function_body(block) {
function _function_body(block, generator) {
var loop = S.in_loop;
var labels = S.labels;
var current_generator = S.in_generator;
++S.in_function;
if (generator)
S.in_generator = S.in_function;
if (block)
S.in_directives = true;
S.in_loop = 0;
@@ -1146,9 +1158,39 @@ function parse($TEXT, options) {
--S.in_function;
S.in_loop = loop;
S.labels = labels;
S.in_generator = current_generator;
return a;
}
function _yield_expression() {
// Previous token must be keyword yield and not be interpret as an identifier
if (!is_in_generator()) {
throw new JS_Parse_Error("Unexpected yield expression outside generator function",
S.prev.file, S.prev.line, S.prev.col, S.prev.pos);
}
var star = false;
var has_expression = true;
var tmp;
// Get expression behind yield, default to value `undefined` stored as `null` in ast
// Expression must start on same line, yield* has a mandatory expression
if (is("operator", "*")) {
star = true;
next();
if (S.token.nlb) {
unexpected(S.prev);
}
} else if (can_insert_semicolon() ||
(is("punc") && PUNC_AFTER_EXPRESSION(S.token.value))) {
has_expression = false;
}
return new AST_Yield({
is_star : star,
expression : has_expression ? expression() : null
});
}
function if_() {
var cond = parenthesised(), body = statement(), belse = null;
if (is("keyword", "else")) {
@@ -1585,7 +1627,7 @@ function parse($TEXT, options) {
}
if (KindOfClass === AST_DefClass && !class_name) {
croak();
unexpected();
}
if (S.token.value == "extends") {
@@ -1599,8 +1641,8 @@ function parse($TEXT, options) {
while (!is("punc", "}")) {
start = S.token;
name = as_property_name();
method = concise_method_or_getset(name, start);
if (!method) { croak(); }
method = concise_method_or_getset(name, start, true);
if (!method) { unexpected(); }
a.push(method);
if (is("punc", ";")) { next(); }
}
@@ -1616,21 +1658,28 @@ function parse($TEXT, options) {
});
}
function concise_method_or_getset(name, start) {
function concise_method_or_getset(name, start, is_class) {
var is_static = false;
if (name === "static" && !is("punc", "(")) {
var is_generator = false;
if (is_class && name === "static" && !is("punc", "(")) {
is_static = true;
name = S.token.value;
next();
}
if (name === "*") {
is_generator = true;
name = S.token.value;
next();
}
if (is("punc", "(")) {
return new AST_ConciseMethod({
start : start,
static : is_static,
name : new AST_SymbolMethod({ name: name }),
argnames : params_or_seq_().as_params(croak),
body : _function_body(true),
end : prev()
is_generator: is_generator,
start : start,
static : is_static,
name : new AST_SymbolMethod({ name: name }),
argnames : params_or_seq_().as_params(croak),
body : _function_body(true, is_generator),
end : prev()
});
}
if (name == "get") {
@@ -1870,10 +1919,6 @@ function parse($TEXT, options) {
var start = S.token;
if (is("operator") && UNARY_PREFIX(start.value)) {
next();
if (start.type === "operator" && start.value === "yield" && is("operator", "*")) {
start.value = "yield*";
next();
}
handle_regexp();
var ex = make_unary(AST_UnaryPrefix, start.value, maybe_unary(allow_calls));
ex.start = start;
@@ -1947,6 +1992,11 @@ function parse($TEXT, options) {
var maybe_assign = function(no_in) {
var start = S.token;
if (start.type == "name" && start.value == "yield" && is_in_generator()) {
next();
return _yield_expression();
}
if (start.type == "punc" && start.value == "(" && peek().value == ")") {
next();
next();