Skip to content

Commit

Permalink
basic support for class static blocks. Closes #1093
Browse files Browse the repository at this point in the history
  • Loading branch information
fabiosantoscode committed Aug 20, 2022
1 parent 5ca7d86 commit c677e28
Show file tree
Hide file tree
Showing 12 changed files with 248 additions and 13 deletions.
23 changes: 23 additions & 0 deletions lib/ast.js
Expand Up @@ -2272,6 +2272,28 @@ var AST_DefClass = DEFNODE("DefClass", null, function AST_DefClass(props) {
$documentation: "A class definition",
}, AST_Class);

var AST_ClassStaticBlock = DEFNODE("ClassStaticBlock", "body block_scope", function AST_ClassStaticBlock (props) {
this.body = props.body;
this.block_scope = props.block_scope;
this.start = props.start;
this.end = props.end;
}, {
$documentation: "A block containing statements to be executed in the context of the class",
$propdoc: {
body: "[AST_Statement*] an array of statements",
},
_walk: function(visitor) {
return visitor._visit(this, function() {
walk_body(this, visitor);
});
},
_children_backwards(push) {
let i = this.body.length;
while (i--) push(this.body[i]);
},
clone: clone_block_scope,
}, AST_Scope);

var AST_ClassExpression = DEFNODE("ClassExpression", null, function AST_ClassExpression(props) {
if (props) {
this.name = props.name;
Expand Down Expand Up @@ -3072,6 +3094,7 @@ export {
AST_ClassExpression,
AST_ClassPrivateProperty,
AST_ClassProperty,
AST_ClassStaticBlock,
AST_ConciseMethod,
AST_Conditional,
AST_Const,
Expand Down
4 changes: 3 additions & 1 deletion lib/cli.js
Expand Up @@ -279,7 +279,9 @@ export async function run_cli({ program, packageJson, fs, path }) {
result.enclosed = value.block_scope.enclosed;
}
value.CTOR.PROPS.forEach(function(prop) {
result[prop] = value[prop];
if (prop !== "block_scope") {
result[prop] = value[prop];
}
});
return result;
}
Expand Down
9 changes: 9 additions & 0 deletions lib/compress/drop-side-effect-free.js
Expand Up @@ -50,6 +50,7 @@ import {
AST_Call,
AST_Chain,
AST_Class,
AST_ClassStaticBlock,
AST_ClassProperty,
AST_ConciseMethod,
AST_Conditional,
Expand Down Expand Up @@ -156,6 +157,14 @@ def_drop_side_effect_free(AST_Class, function (compressor) {
if (trimmed_extends)
with_effects.push(trimmed_extends);
for (const prop of this.properties) {
if (prop instanceof AST_ClassStaticBlock) {
if (prop.body.some(stat => stat.has_side_effects(compressor))) {
return this;
} else {
continue;
}
}

const trimmed_prop = prop.drop_side_effect_free(compressor);
if (trimmed_prop)
with_effects.push(trimmed_prop);
Expand Down
23 changes: 12 additions & 11 deletions lib/compress/index.js
Expand Up @@ -58,6 +58,7 @@ import {
AST_Class,
AST_ClassExpression,
AST_ClassProperty,
AST_ClassStaticBlock,
AST_ConciseMethod,
AST_Conditional,
AST_Const,
Expand Down Expand Up @@ -833,18 +834,13 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
}
if ((node instanceof AST_Defun || node instanceof AST_DefClass) && node !== self) {
const def = node.name.definition();
let keep = def.global && !drop_funcs || in_use_ids.has(def.id);
if (!keep) {
const keep = def.global && !drop_funcs || in_use_ids.has(def.id);
// Class "extends" and static blocks may have side effects
const has_side_effects = !keep
&& node instanceof AST_Class
&& node.has_side_effects(compressor);
if (!keep && !has_side_effects) {
def.eliminated++;
if (node instanceof AST_DefClass) {
// Classes might have extends with side effects
const side_effects = node.drop_side_effect_free(compressor);
if (side_effects) {
return make_node(AST_SimpleStatement, node, {
body: side_effects
});
}
}
return in_list ? MAP.skip : make_node(AST_EmptyStatement, node);
}
}
Expand Down Expand Up @@ -3923,6 +3919,11 @@ def_optimize(AST_Class, function(self) {
return self;
});

def_optimize(AST_ClassStaticBlock, function(self, compressor) {
tighten_body(self.body, compressor);
return self;
});

def_optimize(AST_Yield, function(self, compressor) {
if (self.expression && !self.is_star && is_undefined(self.expression, compressor)) {
self.expression = null;
Expand Down
22 changes: 21 additions & 1 deletion lib/compress/inference.js
Expand Up @@ -52,6 +52,8 @@ import {
AST_Case,
AST_Chain,
AST_Class,
AST_DefClass,
AST_ClassStaticBlock,
AST_ClassProperty,
AST_ConciseMethod,
AST_Conditional,
Expand Down Expand Up @@ -320,6 +322,9 @@ export function is_nullish(node, compressor) {
}
return any(this.properties, compressor);
});
def_has_side_effects(AST_ClassStaticBlock, function(compressor) {
return any(this.body, compressor);
});
def_has_side_effects(AST_Binary, function(compressor) {
return this.left.has_side_effects(compressor)
|| this.right.has_side_effects(compressor);
Expand Down Expand Up @@ -419,6 +424,9 @@ export function is_nullish(node, compressor) {
if (this.extends && this.extends.may_throw(compressor)) return true;
return any(this.properties, compressor);
});
def_may_throw(AST_ClassStaticBlock, function (compressor) {
return any(this.body, compressor);
});

def_may_throw(AST_Array, function(compressor) {
return any(this.elements, compressor);
Expand Down Expand Up @@ -587,6 +595,9 @@ export function is_nullish(node, compressor) {
if (prop.static && prop.value && !prop.value.is_constant_expression(scope)) {
return false;
}
if (prop instanceof AST_ClassStaticBlock) {
return false;
}
}

return all_refs_local.call(this, scope);
Expand Down Expand Up @@ -910,9 +921,18 @@ export const aborts = (thing) => thing && thing.aborts();
}
return null;
}
def_aborts(AST_Import, function() { return null; });
def_aborts(AST_Import, return_null);
def_aborts(AST_BlockStatement, block_aborts);
def_aborts(AST_SwitchBranch, block_aborts);
def_aborts(AST_DefClass, function () {
for (const prop of this.properties) {
if (prop instanceof AST_ClassStaticBlock) {
if (prop.aborts()) return prop;
}
}
return null;
});
def_aborts(AST_ClassStaticBlock, block_aborts);
def_aborts(AST_If, function() {
return this.alternative && aborts(this.body) && aborts(this.alternative) && this;
});
Expand Down
5 changes: 5 additions & 0 deletions lib/compress/reduce-vars.js
Expand Up @@ -52,6 +52,7 @@ import {
AST_Case,
AST_Chain,
AST_Class,
AST_ClassStaticBlock,
AST_ClassExpression,
AST_Conditional,
AST_Default,
Expand Down Expand Up @@ -377,6 +378,10 @@ def_reduce_vars(AST_Class, function(tw, descend) {
return true;
});

def_reduce_vars(AST_ClassStaticBlock, function(tw, descend, compressor) {
reset_block_variables(compressor, this);
});

def_reduce_vars(AST_Conditional, function(tw) {
this.condition.walk(tw);
push(tw);
Expand Down
16 changes: 16 additions & 0 deletions lib/mozilla-ast.js
Expand Up @@ -60,6 +60,7 @@ import {
AST_Catch,
AST_Chain,
AST_Class,
AST_ClassStaticBlock,
AST_ClassExpression,
AST_ClassProperty,
AST_ClassPrivateProperty,
Expand Down Expand Up @@ -444,6 +445,14 @@ import { is_basic_identifier_string } from "./parse.js";
});
},

StaticBlock: function(M) {
return new AST_ClassStaticBlock({
start : my_start_token(M),
end : my_end_token(M),
body : M.body.map(from_moz),
});
},

ArrayExpression: function(M) {
return new AST_Array({
start : my_start_token(M),
Expand Down Expand Up @@ -1583,6 +1592,13 @@ import { is_basic_identifier_string } from "./parse.js";
};
});

def_to_moz(AST_ClassStaticBlock, function To_Moz_StaticBlock(M) {
return {
type: "StaticBlock",
body: M.body.map(to_moz),
};
});

def_to_moz(AST_NewTarget, function To_Moz_MetaProperty() {
return {
type: "MetaProperty",
Expand Down
6 changes: 6 additions & 0 deletions lib/output.js
Expand Up @@ -70,6 +70,7 @@ import {
AST_ClassExpression,
AST_ClassPrivateProperty,
AST_ClassProperty,
AST_ClassStaticBlock,
AST_ConciseMethod,
AST_PrivateGetter,
AST_PrivateMethod,
Expand Down Expand Up @@ -2194,6 +2195,11 @@ function OutputStream(options) {
}
self._print_getter_setter(type, false, output);
});
DEFPRINT(AST_ClassStaticBlock, function (self, output) {
output.print("static");
output.space();
print_braced(self, output);
});
AST_Symbol.DEFMETHOD("_do_print", function(output) {
var def = this.definition();
output.print_name(def ? def.mangled_name || def.name : this.name);
Expand Down
24 changes: 24 additions & 0 deletions lib/parse.js
Expand Up @@ -67,6 +67,7 @@ import {
AST_ClassExpression,
AST_ClassPrivateProperty,
AST_ClassProperty,
AST_ClassStaticBlock,
AST_ConciseMethod,
AST_PrivateGetter,
AST_PrivateMethod,
Expand Down Expand Up @@ -2562,6 +2563,10 @@ function parse($TEXT, options) {
var accessor_type = null;

if (is_class && name === "static" && is_not_method_start()) {
const static_block = class_static_block();
if (static_block != null) {
return static_block;
}
is_static = true;
name = as_property_name();
}
Expand Down Expand Up @@ -2668,6 +2673,25 @@ function parse($TEXT, options) {
}
}

function class_static_block() {
if (!is("punc", "{")) {
return null;
}

const start = S.token;
const body = [];

next();

while (!is("punc", "}")) {
body.push(statement());
}

next();

return new AST_ClassStaticBlock({ start, body, end: prev() });
}

function maybe_import_assertion() {
if (is("name", "assert") && !has_newline_before(S.token)) {
next();
Expand Down
6 changes: 6 additions & 0 deletions lib/size.js
Expand Up @@ -10,6 +10,7 @@ import {
AST_Call,
AST_Case,
AST_Class,
AST_ClassStaticBlock,
AST_ClassPrivateProperty,
AST_ClassProperty,
AST_ConciseMethod,
Expand Down Expand Up @@ -401,6 +402,11 @@ AST_Class.prototype._size = function () {
);
};

AST_ClassStaticBlock.prototype._size = function () {
// "class{}" + semicolons
return 7 + list_overhead(this.body);
};

AST_ClassProperty.prototype._size = function () {
return (
static_size(this.static)
Expand Down
5 changes: 5 additions & 0 deletions lib/transform.js
Expand Up @@ -53,6 +53,7 @@ import {
AST_Catch,
AST_Chain,
AST_Class,
AST_ClassStaticBlock,
AST_Conditional,
AST_Definitions,
AST_Destructuring,
Expand Down Expand Up @@ -285,6 +286,10 @@ def_transform(AST_Class, function(self, tw) {
self.properties = do_list(self.properties, tw);
});

def_transform(AST_ClassStaticBlock, function(self, tw) {
self.body = do_list(self.body, tw);
});

def_transform(AST_Expansion, function(self, tw) {
self.expression = self.expression.transform(tw);
});
Expand Down

0 comments on commit c677e28

Please sign in to comment.