Skip to content

Commit

Permalink
implement compression of #private in ...
Browse files Browse the repository at this point in the history
  • Loading branch information
fabiosantoscode committed Nov 1, 2022
1 parent 4c60b48 commit 660ebae
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 26 deletions.
39 changes: 39 additions & 0 deletions lib/ast.js
Expand Up @@ -2250,6 +2250,29 @@ var AST_ClassPrivateProperty = DEFNODE("ClassPrivateProperty", "", function AST_
$documentation: "A class property for a private property",
}, AST_ClassProperty);

var AST_PrivateIn = DEFNODE("PrivateIn", "key value", function AST_PrivateIn(props) {
if (props) {
this.key = props.key;
this.value = props.value;
this.start = props.start;
this.end = props.end;
}

this.flags = 0;
}, {
$documentation: "An `in` binop when the key is private, eg #x in this",
_walk: function(visitor) {
return visitor._visit(this, function() {
this.key._walk(visitor);
this.value._walk(visitor);
});
},
_children_backwards(push) {
push(this.value);
push(this.key);
},
});

var AST_DefClass = DEFNODE("DefClass", null, function AST_DefClass(props) {
if (props) {
this.name = props.name;
Expand Down Expand Up @@ -2653,6 +2676,20 @@ var AST_LabelRef = DEFNODE("LabelRef", null, function AST_LabelRef(props) {
$documentation: "Reference to a label symbol",
}, AST_Symbol);

var AST_SymbolPrivateProperty = DEFNODE("SymbolPrivateProperty", null, function AST_SymbolPrivateProperty(props) {
if (props) {
this.scope = props.scope;
this.name = props.name;
this.thedef = props.thedef;
this.start = props.start;
this.end = props.end;
}

this.flags = 0;
}, {
$documentation: "A symbol that refers to a private property",
}, AST_Symbol);

var AST_This = DEFNODE("This", null, function AST_This(props) {
if (props) {
this.scope = props.scope;
Expand Down Expand Up @@ -3093,6 +3130,7 @@ export {
AST_Class,
AST_ClassExpression,
AST_ClassPrivateProperty,
AST_PrivateIn,
AST_ClassProperty,
AST_ClassStaticBlock,
AST_ConciseMethod,
Expand Down Expand Up @@ -3185,6 +3223,7 @@ export {
AST_SymbolVar,
AST_TemplateSegment,
AST_TemplateString,
AST_SymbolPrivateProperty,
AST_This,
AST_Throw,
AST_Token,
Expand Down
27 changes: 26 additions & 1 deletion lib/mozilla-ast.js
Expand Up @@ -113,6 +113,7 @@ import {
AST_PrivateGetter,
AST_PrivateMethod,
AST_PrivateSetter,
AST_PrivateIn,
AST_PropAccess,
AST_RegExp,
AST_Return,
Expand All @@ -128,6 +129,7 @@ import {
AST_SymbolCatch,
AST_SymbolClass,
AST_SymbolClassProperty,
AST_SymbolPrivateProperty,
AST_SymbolConst,
AST_SymbolDefClass,
AST_SymbolDefun,
Expand Down Expand Up @@ -432,7 +434,9 @@ import { is_basic_identifier_string } from "./parse.js";
if (M.computed) {
key = from_moz(M.key);
} else {
if (M.key.type !== "Identifier") throw new Error("Non-Identifier key in PropertyDefinition");
if (M.key.type !== "Identifier" && M.key.type !== "PrivateIdentifier") {
throw new Error("Non-Identifier key in PropertyDefinition");
}
key = from_moz(M.key);
}

Expand Down Expand Up @@ -867,6 +871,18 @@ import { is_basic_identifier_string } from "./parse.js";
},

BinaryExpression: function(M) {
if (M.left.type === "PrivateIdentifier") {
return new AST_PrivateIn({
start: my_start_token(M),
end: my_end_token(M),
key: new AST_SymbolPrivateProperty({
start: my_start_token(M.left),
end: my_end_token(M.left),
name: M.left.name
}),
value: from_moz(M.right),
});
}
return new AST_Binary({
start: my_start_token(M),
end: my_end_token(M),
Expand Down Expand Up @@ -1451,6 +1467,15 @@ import { is_basic_identifier_string } from "./parse.js";
};
});

def_to_moz(AST_PrivateIn, function To_Moz_BinaryExpression_PrivateIn(M) {
return {
type: "BinaryExpression",
left: { type: "PrivateIdentifier", name: M.key.name },
operator: "in",
right: to_moz(M.value),
};
});

def_to_moz(AST_Array, function To_Moz_ArrayExpression(M) {
return {
type: "ArrayExpression",
Expand Down
17 changes: 16 additions & 1 deletion lib/output.js
Expand Up @@ -74,7 +74,9 @@ import {
AST_ConciseMethod,
AST_PrivateGetter,
AST_PrivateMethod,
AST_SymbolPrivateProperty,
AST_PrivateSetter,
AST_PrivateIn,
AST_Conditional,
AST_Const,
AST_Constant,
Expand Down Expand Up @@ -1795,7 +1797,10 @@ function OutputStream(options) {
if (node instanceof AST_Scope && !(node instanceof AST_Arrow)) {
return true;
}
if (node instanceof AST_Binary && node.operator == "in") {
if (
node instanceof AST_Binary && node.operator == "in"
|| node instanceof AST_PrivateIn
) {
return walk_abort; // makes walk() return true
}
});
Expand Down Expand Up @@ -2184,6 +2189,16 @@ function OutputStream(options) {
}
self._print_getter_setter(type, true, output);
});
DEFPRINT(AST_PrivateIn, function(self, output) {
self.key.print(output);
output.space();
output.print("in");
output.space();
self.value.print(output);
});
DEFPRINT(AST_SymbolPrivateProperty, function(self, output) {
output.print("#" + self.name);
});
DEFPRINT(AST_ConciseMethod, function(self, output) {
var type;
if (self.is_generator && self.async) {
Expand Down
36 changes: 27 additions & 9 deletions lib/parse.js
Expand Up @@ -69,6 +69,7 @@ import {
AST_ClassProperty,
AST_ClassStaticBlock,
AST_ConciseMethod,
AST_PrivateIn,
AST_PrivateGetter,
AST_PrivateMethod,
AST_PrivateSetter,
Expand Down Expand Up @@ -145,6 +146,7 @@ import {
AST_TemplateSegment,
AST_TemplateString,
AST_This,
AST_SymbolPrivateProperty,
AST_Throw,
AST_Token,
AST_Toplevel,
Expand Down Expand Up @@ -1041,7 +1043,7 @@ var PRECEDENCE = (function(a, ret) {
{}
);

var ATOMIC_START_TOKEN = makePredicate([ "atom", "num", "big_int", "string", "regexp", "name", "privatename"]);
var ATOMIC_START_TOKEN = makePredicate([ "atom", "num", "big_int", "string", "regexp", "name"]);

/* -----[ Parser ]----- */

Expand Down Expand Up @@ -1216,6 +1218,7 @@ function parse($TEXT, options) {
case "privatename":
if(is("privatename") && !S.in_class)
croak("Private field must be used in an enclosing class");

if (S.token.value == "async" && is_token(peek(), "keyword", "function")) {
next();
next();
Expand Down Expand Up @@ -2179,11 +2182,6 @@ function parse($TEXT, options) {
case "name":
ret = _make_symbol(AST_SymbolRef);
break;
case "privatename":
if(!S.in_class)
croak("Private field must be used in an enclosing class");
ret = _make_symbol(AST_SymbolRef);
break;
case "num":
ret = new AST_Number({
start: tok,
Expand Down Expand Up @@ -2365,6 +2363,29 @@ function parse($TEXT, options) {
if (is("template_head")) {
return subscripts(template_string(), allow_calls);
}
if (is("privatename")) {
if(!S.in_class) {
croak("Private field must be used in an enclosing class");
}

const start = S.token;
const key = new AST_SymbolPrivateProperty({
start,
name: start.value,
end: start
});
next();
expect_token("operator", "in");

const private_in = new AST_PrivateIn({
start,
key,
value: subscripts(as_atom_node(), allow_calls),
end: prev()
});

return subscripts(private_in, allow_calls);
}
if (ATOMIC_START_TOKEN.has(S.token.type)) {
return subscripts(as_atom_node(), allow_calls);
}
Expand Down Expand Up @@ -3220,9 +3241,6 @@ function parse($TEXT, options) {
&& left.operator !== "--" && left.operator !== "++")
unexpected(left.start);
var prec = op != null ? PRECEDENCE[op] : null;
// #<privatename> without subscription is only legal in `#<privatename> in <expression>` when inside a class.
if(left instanceof AST_SymbolRef && left.start.type === "privatename" && S.in_class && op !== "in")
croak("Unexpected privatename token");
if (prec != null && (prec > min_prec || (op === "**" && min_prec === prec))) {
next();
var right = expr_op(maybe_unary(true), prec, no_in);
Expand Down
2 changes: 2 additions & 0 deletions lib/propmangle.js
Expand Up @@ -61,6 +61,7 @@ import {
AST_PrivateMethod,
AST_PrivateGetter,
AST_PrivateSetter,
AST_PrivateIn,
AST_Sequence,
AST_String,
AST_Sub,
Expand Down Expand Up @@ -153,6 +154,7 @@ function mangle_private_properties(ast, options) {
|| node instanceof AST_PrivateMethod
|| node instanceof AST_PrivateGetter
|| node instanceof AST_PrivateSetter
|| node instanceof AST_PrivateIn
) {
node.key.name = mangle_private(node.key.name);
} else if (node instanceof AST_DotHash) {
Expand Down
5 changes: 5 additions & 0 deletions lib/size.js
Expand Up @@ -53,6 +53,7 @@ import {
AST_PrivateGetter,
AST_PrivateMethod,
AST_PrivateSetter,
AST_PrivateIn,
AST_RegExp,
AST_Return,
AST_Sequence,
Expand Down Expand Up @@ -395,6 +396,10 @@ AST_PrivateGetter.prototype._size = AST_PrivateSetter.prototype._size = function
return AST_ConciseMethod.prototype._size.call(this) + 4;
};

AST_PrivateIn.prototype._size = function () {
return 5; // "#", and " in "
};

AST_Class.prototype._size = function () {
return (
(this.name ? 8 : 7)
Expand Down
6 changes: 6 additions & 0 deletions lib/transform.js
Expand Up @@ -47,6 +47,7 @@ import {
AST_Array,
AST_Await,
AST_Binary,
AST_PrivateIn,
AST_Block,
AST_Call,
AST_Case,
Expand Down Expand Up @@ -259,6 +260,11 @@ def_transform(AST_Binary, function(self, tw) {
self.right = self.right.transform(tw);
});

def_transform(AST_PrivateIn, function(self, tw) {
self.key = self.key.transform(tw);
self.value = self.value.transform(tw);
});

def_transform(AST_Conditional, function(self, tw) {
self.condition = self.condition.transform(tw);
self.consequent = self.consequent.transform(tw);
Expand Down
23 changes: 14 additions & 9 deletions test/compress/class-properties.js
Expand Up @@ -449,7 +449,7 @@ nested_private_properties_can_be_mangled: {

allow_private_field_with_in_operator : {
no_mozilla_ast = true;
node_version = ">=12"
node_version = ">=16"
mangle = {
properties: true
}
Expand All @@ -463,23 +463,28 @@ allow_private_field_with_in_operator : {
}
}
}
expect:{class A{#i;i(i){p in i;p in this;return p in this}}}
expect:{class A{#i;i(i){#i in i;#i in this;return #i in this}}}
}

allow_subscript_private_field: {
no_mozilla_ast = true;
node_version = ">=12"
mangle = {
properties: true
}
node_version = ">=16"
options = { defaults: true }
input: {
class A {
#p;
constructor(p) {
this.#p = p;
}
isA (input) {
console.log(#p in this && "PASS");
console.log(input.#p);
return this.#p;
}
}
new A("FAIL").isA(new A("PASS"))
}
expect: {class A{#s;i(s){console.log(s.#s);return this.#s}}}
}
expect_stdout: [
"PASS",
"PASS"
]
}
12 changes: 6 additions & 6 deletions test/compress/syntax-errors.js
Expand Up @@ -356,7 +356,7 @@ private_field_out_of_class_field: {
`
expect_error: ({
name: "SyntaxError",
message: "Private field must be declared in an enclosing class",
message: "Private field must be used in an enclosing class",
line: 3,
col: 24
})
Expand All @@ -371,7 +371,7 @@ private_field_out_of_class_field_in_operator: {
`
expect_error:({
name: "SyntaxError",
message: "Private field must be declared in an enclosing class",
message: "Private field must be used in an enclosing class",
line: 3,
col: 12
})
Expand All @@ -381,16 +381,16 @@ invaild__in_operator_expression_in_class_field: {
input: `
class A {
#p;
isA () {
isA () {
#p + 10;
return this.#p;
}
}
}
`
expect_error: ({
name: "SyntaxError",
message: "Unexpected privatename token",
message: "Unexpected token operator 芦+禄, expected operator 芦in禄",
line: 5,
col: 19
})
}
}

0 comments on commit 660ebae

Please sign in to comment.