support class static initialization block (#5488)
This commit is contained in:
23
lib/ast.js
23
lib/ast.js
@@ -534,7 +534,7 @@ var AST_With = DEFNODE("With", "expression", {
|
||||
/* -----[ scope and functions ]----- */
|
||||
|
||||
var AST_Scope = DEFNODE("Scope", "fn_defs may_call_this uses_eval uses_with", {
|
||||
$documentation: "Base class for all statements introducing a lexical scope",
|
||||
$documentation: "Base class for all statements introducing a lambda scope",
|
||||
$propdoc: {
|
||||
uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`",
|
||||
uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
|
||||
@@ -592,6 +592,10 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
||||
}
|
||||
}, AST_Scope);
|
||||
|
||||
var AST_ClassInitBlock = DEFNODE("ClassInitBlock", null, {
|
||||
$documentation: "Value for `class` static initialization blocks",
|
||||
}, AST_Scope);
|
||||
|
||||
var AST_Lambda = DEFNODE("Lambda", "argnames length_read rest safe_ids uses_arguments", {
|
||||
$documentation: "Base class for functions",
|
||||
$propdoc: {
|
||||
@@ -874,7 +878,7 @@ var AST_ClassExpression = DEFNODE("ClassExpression", null, {
|
||||
var AST_ClassProperty = DEFNODE("ClassProperty", "key private static value", {
|
||||
$documentation: "Base class for `class` properties",
|
||||
$propdoc: {
|
||||
key: "[string|AST_Node] property name (AST_Node for computed property)",
|
||||
key: "[string|AST_Node?] property name (AST_Node for computed property, null for initialization block)",
|
||||
private: "[boolean] whether this is a private property",
|
||||
static: "[boolean] whether this is a static property",
|
||||
value: "[AST_Node?] property value (AST_Accessor for getters/setters, AST_LambdaExpression for methods, null if not specified for fields)",
|
||||
@@ -888,7 +892,9 @@ var AST_ClassProperty = DEFNODE("ClassProperty", "key private static value", {
|
||||
},
|
||||
_validate: function() {
|
||||
if (this.TYPE == "ClassProperty") throw new Error("should not instantiate AST_ClassProperty");
|
||||
if (typeof this.key != "string") {
|
||||
if (this instanceof AST_ClassInit) {
|
||||
if (this.key != null) throw new Error("key must be null");
|
||||
} else if (typeof this.key != "string") {
|
||||
if (!(this.key instanceof AST_Node)) throw new Error("key must be string or AST_Node");
|
||||
must_be_expression(this, "key");
|
||||
}
|
||||
@@ -928,6 +934,17 @@ var AST_ClassMethod = DEFNODE("ClassMethod", null, {
|
||||
},
|
||||
}, AST_ClassProperty);
|
||||
|
||||
var AST_ClassInit = DEFNODE("ClassInit", null, {
|
||||
$documentation: "A `class` static initialization block",
|
||||
_validate: function() {
|
||||
if (!this.static) throw new Error("static must be true");
|
||||
if (!(this.value instanceof AST_ClassInitBlock)) throw new Error("value must be AST_ClassInitBlock");
|
||||
},
|
||||
initialize: function() {
|
||||
this.static = true;
|
||||
},
|
||||
}, AST_ClassProperty);
|
||||
|
||||
/* -----[ JUMPS ]----- */
|
||||
|
||||
var AST_Jump = DEFNODE("Jump", null, {
|
||||
|
||||
@@ -1146,7 +1146,7 @@ Compressor.prototype.compress = function(node) {
|
||||
}
|
||||
}
|
||||
props.forEach(function(prop) {
|
||||
if (!prop.static || prop instanceof AST_ClassField && prop.value.contains_this()) {
|
||||
if (!prop.static || is_static_field_or_init(prop) && prop.value.contains_this()) {
|
||||
push(tw);
|
||||
prop.value.walk(tw);
|
||||
pop(tw);
|
||||
@@ -1156,6 +1156,14 @@ Compressor.prototype.compress = function(node) {
|
||||
});
|
||||
return true;
|
||||
});
|
||||
def(AST_ClassInitBlock, function(tw, descend, compressor) {
|
||||
var node = this;
|
||||
push(tw);
|
||||
reset_variables(tw, compressor, node);
|
||||
descend();
|
||||
pop_scope(tw, node);
|
||||
return true;
|
||||
});
|
||||
def(AST_Conditional, function(tw) {
|
||||
this.condition.walk(tw);
|
||||
push(tw);
|
||||
@@ -1843,6 +1851,10 @@ Compressor.prototype.compress = function(node) {
|
||||
|| compressor.option("unsafe") && global_names[this.name];
|
||||
});
|
||||
|
||||
function is_static_field_or_init(prop) {
|
||||
return prop.static && prop.value && (prop instanceof AST_ClassField || prop instanceof AST_ClassInit);
|
||||
}
|
||||
|
||||
function declarations_only(node) {
|
||||
return all(node.definitions, function(var_def) {
|
||||
return !var_def.value;
|
||||
@@ -1852,8 +1864,7 @@ Compressor.prototype.compress = function(node) {
|
||||
function is_declaration(stat, lexical) {
|
||||
if (stat instanceof AST_DefClass) return lexical && !stat.extends && all(stat.properties, function(prop) {
|
||||
if (prop.key instanceof AST_Node) return false;
|
||||
if (prop instanceof AST_ClassField && prop.static && prop.value) return false;
|
||||
return true;
|
||||
return !is_static_field_or_init(prop);
|
||||
});
|
||||
if (stat instanceof AST_Definitions) return (lexical || stat instanceof AST_Var) && declarations_only(stat);
|
||||
if (stat instanceof AST_ExportDeclaration) return is_declaration(stat.body, lexical);
|
||||
@@ -6651,7 +6662,7 @@ Compressor.prototype.compress = function(node) {
|
||||
if (prop.key instanceof AST_Node) prop.key.walk(tw);
|
||||
var value = prop.value;
|
||||
if (!value) return;
|
||||
if (prop instanceof AST_ClassField && prop.static) {
|
||||
if (is_static_field_or_init(prop)) {
|
||||
if (!used && value.contains_this()) used = true;
|
||||
walk_class_prop(value);
|
||||
} else {
|
||||
@@ -8595,16 +8606,20 @@ Compressor.prototype.compress = function(node) {
|
||||
});
|
||||
def(AST_ClassExpression, function(compressor, first_in_statement) {
|
||||
var self = this;
|
||||
var exprs = [], values = [];
|
||||
var exprs = [], values = [], init = 0;
|
||||
var props = self.properties;
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
var prop = props[i];
|
||||
if (prop.key instanceof AST_Node) exprs.push(prop.key);
|
||||
if (prop.static && prop.value
|
||||
&& prop instanceof AST_ClassField
|
||||
&& prop.value.has_side_effects(compressor)) {
|
||||
if (prop.value.contains_this()) return self;
|
||||
values.push(prop.value);
|
||||
if (!is_static_field_or_init(prop)) continue;
|
||||
var value = prop.value;
|
||||
if (!value.has_side_effects(compressor)) continue;
|
||||
if (value.contains_this()) return self;
|
||||
if (prop instanceof AST_ClassInit) {
|
||||
init++;
|
||||
values.push(prop);
|
||||
} else {
|
||||
values.push(value);
|
||||
}
|
||||
}
|
||||
var base = self.extends;
|
||||
@@ -8623,33 +8638,40 @@ Compressor.prototype.compress = function(node) {
|
||||
if (base || self.name || !compressor.has_directive("use strict")) {
|
||||
var node = to_class_expr(self);
|
||||
if (!base) node.extends = null;
|
||||
node.properties = [];
|
||||
if (values) {
|
||||
node.properties.push(make_node(AST_ClassField, self, {
|
||||
static: true,
|
||||
key: exprs.length ? make_sequence(self, exprs) : "c",
|
||||
value: make_sequence(self, values),
|
||||
}));
|
||||
} else if (exprs.length) {
|
||||
node.properties.push(make_node(AST_ClassMethod, self, {
|
||||
key: make_sequence(self, exprs),
|
||||
value: make_node(AST_Function, self, {
|
||||
argnames: [],
|
||||
body: [],
|
||||
}).init_vars(node),
|
||||
}));
|
||||
}
|
||||
node.properties = values ? values.length == init ? values : [ make_node(AST_ClassField, self, {
|
||||
static: true,
|
||||
key: exprs.length ? make_sequence(self, exprs) : "c",
|
||||
value: make_value(),
|
||||
}) ] : exprs.length ? [ make_node(AST_ClassMethod, self, {
|
||||
key: make_sequence(self, exprs),
|
||||
value: make_node(AST_Function, self, {
|
||||
argnames: [],
|
||||
body: [],
|
||||
}).init_vars(node),
|
||||
}) ] : [];
|
||||
return node;
|
||||
}
|
||||
if (values) exprs.push(make_node(AST_Call, self, {
|
||||
expression: make_node(AST_Arrow, self, {
|
||||
argnames: [],
|
||||
body: [],
|
||||
value: make_sequence(self, values),
|
||||
value: make_value(),
|
||||
}).init_vars(self.parent_scope),
|
||||
args: [],
|
||||
}));
|
||||
return make_sequence(self, exprs);
|
||||
|
||||
function make_value() {
|
||||
return make_sequence(self, values.map(function(node) {
|
||||
if (!(node instanceof AST_ClassInit)) return node;
|
||||
var fn = make_node(AST_Arrow, node, node.value);
|
||||
fn.argnames = [];
|
||||
return make_node(AST_Call, node, {
|
||||
expression: fn,
|
||||
args: [],
|
||||
});
|
||||
}));
|
||||
}
|
||||
});
|
||||
def(AST_Conditional, function(compressor) {
|
||||
var consequent = this.consequent.drop_side_effect_free(compressor);
|
||||
|
||||
@@ -1257,6 +1257,11 @@ function OutputStream(options) {
|
||||
}
|
||||
print_method(self, output);
|
||||
});
|
||||
DEFPRINT(AST_ClassInit, function(output) {
|
||||
output.print("static");
|
||||
output.space();
|
||||
print_braced(this.value, output);
|
||||
});
|
||||
|
||||
/* -----[ jumps ]----- */
|
||||
function print_jump(kind, prop) {
|
||||
|
||||
12
lib/parse.js
12
lib/parse.js
@@ -1119,6 +1119,18 @@ function parse($TEXT, options) {
|
||||
}));
|
||||
continue;
|
||||
}
|
||||
if (fixed && is("punc", "{")) {
|
||||
props.push(new AST_ClassInit({
|
||||
start: start,
|
||||
value: new AST_ClassInitBlock({
|
||||
start: start,
|
||||
body: block_(),
|
||||
end: prev(),
|
||||
}),
|
||||
end: prev(),
|
||||
}));
|
||||
continue;
|
||||
}
|
||||
var internal = is("name") && /^#/.test(S.token.value);
|
||||
var key = as_property_key();
|
||||
if (is("punc", "(")) {
|
||||
|
||||
Reference in New Issue
Block a user