Skip to content

Commit

Permalink
Add @__KEY__ annotation to mangle string literals (#1365)
Browse files Browse the repository at this point in the history
* Add @__KEY__ annotation

Adding a @__KEY__ annotation before a string literal will now cause
it to be mangled like a property name during the mangle_props phase.

* Never double mangle _KEY string literals

* Add tests for _KEY annotation

* Add _KEY annotation to readme
  • Loading branch information
jpdutoit committed Apr 13, 2023
1 parent 8f0b4b1 commit 3ab7bbf
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 2 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1188,6 +1188,7 @@ Annotations in Terser are a way to tell it to treat a certain function call diff
* `/*@__INLINE__*/` - forces a function to be inlined somewhere.
* `/*@__NOINLINE__*/` - Makes sure the called function is not inlined into the call site.
* `/*@__PURE__*/` - Marks a function call as pure. That means, it can safely be dropped.
* `/*@__KEY__*/` - Marks a string literal as a property to also mangle it when mangling properties.

You can use either a `@` sign at the start, or a `#`.

Expand All @@ -1201,6 +1202,9 @@ function_always_inlined_here()
function_cant_be_inlined_into_here()

const x = /*#__PURE__*/i_am_dropped_if_x_is_not_used()

function lookup(object, key) { return object[key]; }
lookup({ i_will_be_mangled_too: "bar" }, /*@__KEY__*/ "i_will_be_mangled_too");
```

### ESTree / SpiderMonkey AST
Expand Down
2 changes: 2 additions & 0 deletions lib/ast.js
Original file line number Diff line number Diff line change
Expand Up @@ -3134,6 +3134,7 @@ class TreeTransformer extends TreeWalker {
const _PURE = 0b00000001;
const _INLINE = 0b00000010;
const _NOINLINE = 0b00000100;
const _KEY = 0b00001000;

export {
AST_Accessor,
Expand Down Expand Up @@ -3278,4 +3279,5 @@ export {
_INLINE,
_NOINLINE,
_PURE,
_KEY,
};
8 changes: 7 additions & 1 deletion lib/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,8 @@ import {
AST_Yield,
_INLINE,
_NOINLINE,
_PURE
_PURE,
_KEY
} from "./ast.js";

var LATEST_RAW = ""; // Only used for numbers and template strings
Expand Down Expand Up @@ -2225,6 +2226,7 @@ function parse($TEXT, options) {
value : tok.value,
quote : tok.quote
});
annotate(ret);
break;
case "regexp":
const [_, source, flags] = tok.value.match(/^\/(.*)\/(\w*)$/);
Expand Down Expand Up @@ -3111,6 +3113,10 @@ function parse($TEXT, options) {
set_annotation(node, _NOINLINE);
break;
}
if (/[@#]__KEY__/.test(comment.value)) {
set_annotation(node, _KEY);
break;
}
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions lib/propmangle.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
import {
defaults,
push_uniq,
has_annotation,
clear_annotation,
} from "./utils/index.js";
import { base54 } from "./scope.js";
import {
Expand All @@ -67,6 +69,7 @@ import {
AST_Sub,
TreeTransformer,
TreeWalker,
_KEY,
} from "./ast.js";
import { domprops } from "../tools/domprops.js";

Expand Down Expand Up @@ -263,6 +266,8 @@ function mangle_properties(ast, options) {
addStrings(node.args[1], add);
} else if (node instanceof AST_Binary && node.operator === "in") {
addStrings(node.left, add);
} else if (node instanceof AST_String && has_annotation(node, _KEY)) {
add(node.value);
}
}));

Expand Down Expand Up @@ -296,6 +301,10 @@ function mangle_properties(ast, options) {
node.args[1] = mangleStrings(node.args[1]);
} else if (node instanceof AST_Binary && node.operator === "in") {
node.left = mangleStrings(node.left);
} else if (node instanceof AST_String && has_annotation(node, _KEY)) {
// Clear _KEY annotation to prevent double mangling
clear_annotation(node, _KEY);
node.value = mangle(node.value);
}
}));

Expand Down Expand Up @@ -361,6 +370,8 @@ function mangle_properties(ast, options) {
var last = node.expressions.length - 1;
node.expressions[last] = mangleStrings(node.expressions[last]);
} else if (node instanceof AST_String) {
// Clear _KEY annotation to prevent double mangling
clear_annotation(node, _KEY);
node.value = mangle(node.value);
} else if (node instanceof AST_Conditional) {
node.consequent = mangleStrings(node.consequent);
Expand Down
7 changes: 6 additions & 1 deletion lib/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,10 @@ function set_annotation(node, annotation) {
node._annotations |= annotation;
}

function clear_annotation(node, annotation) {
node._annotations &= ~annotation;
}

export {
characters,
defaults,
Expand All @@ -286,5 +290,6 @@ export {
sort_regexp_flags,
string_template,
has_annotation,
set_annotation
set_annotation,
clear_annotation,
};
83 changes: 83 additions & 0 deletions test/compress/mangleprops-key-annotation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
key_annotation_negative_case: {
options = {
defaults: false,
inline: false,
};
mangle = {
properties: true,
};
input: {
function lookup(object, key) {
return object[key];
}
lookup({ someprop: "bar" }, "someprop");
}
expect: {
function lookup(o, p) {
return o[p];
}
lookup({ o: "bar" }, "someprop");
}
}

key_annotation_basic_use: {
options = {
defaults: false,
inline: false,
};
mangle = {
properties: true,
};
input: {
function lookup(object, key) {
return object[key];
}
lookup({ someprop: "bar" }, /*@__KEY__*/ "someprop");
}
expect: {
function lookup(o,p){
return o[p]
}
lookup({o:"bar"},"o");
}
}

key_annotation_in_operator: {
options = {
defaults: false,
inline: false,
};
mangle = {
properties: true,
};
input: {
const object = { someprop: "bar", o: true}
"someprop" in object;
/*@__KEY__*/ "someprop" in object;
}
expect: {
const object={o:"bar", p : true};
"o"in object;
"o"in object;
}
}
key_annotation_in_operator_tricky_case: {
options = {
defaults: false,
inline: false,
};
mangle = {
properties: true,
};
input: {
// By default someprop mangles to "o", add key "o" to try and trip up the mangler
const object = { someprop: "bar", o: true}
"someprop" in object;
/*@__KEY__*/ "someprop" in object;
}
expect: {
const object={o:"bar", p: true};
"o"in object;
"o"in object;
}
}

0 comments on commit 3ab7bbf

Please sign in to comment.