Skip to content

Commit 1fb6724

Browse files
petebacondarwinthePunderWoman
authored andcommitted
fix(compiler-cli): allow linker to process minified booleans (#41747)
Some partial libraries have been minified, which results in boolean literals being converted to `!0` and `!1`. This commit ensures that the linker can process these values. Fixes #41655 PR Close #41747
1 parent 6221227 commit 1fb6724

File tree

5 files changed

+77
-8
lines changed

5 files changed

+77
-8
lines changed

packages/compiler-cli/linker/babel/src/ast/babel_ast_host.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,18 @@ export class BabelAstHost implements AstHost<t.Expression> {
3838
return num.value;
3939
}
4040

41-
isBooleanLiteral = t.isBooleanLiteral;
41+
isBooleanLiteral(bool: t.Expression): boolean {
42+
return t.isBooleanLiteral(bool) || isMinifiedBooleanLiteral(bool);
43+
}
4244

4345
parseBooleanLiteral(bool: t.Expression): boolean {
44-
assert(bool, t.isBooleanLiteral, 'a boolean literal');
45-
return bool.value;
46+
if (t.isBooleanLiteral(bool)) {
47+
return bool.value;
48+
} else if (isMinifiedBooleanLiteral(bool)) {
49+
return !bool.argument.value;
50+
} else {
51+
throw new FatalLinkerError(bool, 'Unsupported syntax, expected a boolean literal.');
52+
}
4653
}
4754

4855
isArrayLiteral = t.isArrayExpression;
@@ -165,3 +172,13 @@ type ArgumentType = t.CallExpression['arguments'][number];
165172
function isNotSpreadArgument(arg: ArgumentType): arg is Exclude<ArgumentType, t.SpreadElement> {
166173
return !t.isSpreadElement(arg);
167174
}
175+
176+
type MinifiedBooleanLiteral = t.Expression&t.UnaryExpression&{argument: t.NumericLiteral};
177+
178+
/**
179+
* Return true if the node is either `!0` or `!1`.
180+
*/
181+
function isMinifiedBooleanLiteral(node: t.Expression): node is MinifiedBooleanLiteral {
182+
return t.isUnaryExpression(node) && node.prefix && node.operator === '!' &&
183+
t.isNumericLiteral(node.argument) && (node.argument.value === 0 || node.argument.value === 1);
184+
}

packages/compiler-cli/linker/babel/test/ast/babel_ast_host_spec.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ describe('BabelAstHost', () => {
9696
expect(host.isBooleanLiteral(expr('false'))).toBe(true);
9797
});
9898

99+
it('should return true if the expression is a minified boolean literal', () => {
100+
expect(host.isBooleanLiteral(expr('!0'))).toBe(true);
101+
expect(host.isBooleanLiteral(expr('!1'))).toBe(true);
102+
});
103+
99104
it('should return false if the expression is not a boolean literal', () => {
100105
expect(host.isBooleanLiteral(expr('"moo"'))).toBe(false);
101106
expect(host.isBooleanLiteral(expr('\'moo\''))).toBe(false);
@@ -106,6 +111,8 @@ describe('BabelAstHost', () => {
106111
expect(host.isBooleanLiteral(expr('null'))).toBe(false);
107112
expect(host.isBooleanLiteral(expr('\'a\' + \'b\''))).toBe(false);
108113
expect(host.isBooleanLiteral(expr('\`moo\`'))).toBe(false);
114+
expect(host.isBooleanLiteral(expr('!2'))).toBe(false);
115+
expect(host.isBooleanLiteral(expr('~1'))).toBe(false);
109116
});
110117
});
111118

@@ -115,6 +122,11 @@ describe('BabelAstHost', () => {
115122
expect(host.parseBooleanLiteral(expr('false'))).toEqual(false);
116123
});
117124

125+
it('should extract a minified boolean value', () => {
126+
expect(host.parseBooleanLiteral(expr('!0'))).toEqual(true);
127+
expect(host.parseBooleanLiteral(expr('!1'))).toEqual(false);
128+
});
129+
118130
it('should error if the value is not a boolean literal', () => {
119131
expect(() => host.parseBooleanLiteral(expr('"moo"')))
120132
.toThrowError('Unsupported syntax, expected a boolean literal.');

packages/compiler-cli/linker/src/ast/ast_host.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,17 @@ export interface AstHost<TExpression> {
3636
parseNumericLiteral(num: TExpression): number;
3737

3838
/**
39-
* Return `true` if the given expression is a boolean literal, or false otherwise.
39+
* Return `true` if the given expression can be considered a boolean literal, or false otherwise.
40+
*
41+
* Note that this should also cover the special case of some minified code where `true` and
42+
* `false` are replaced by `!0` and `!1` respectively.
4043
*/
4144
isBooleanLiteral(node: TExpression): boolean;
4245
/**
4346
* Parse the boolean value from the given expression, or throw if it is not a boolean literal.
47+
*
48+
* Note that this should also cover the special case of some minified code where `true` and
49+
* `false` are replaced by `!0` and `!1` respectively.
4450
*/
4551
parseBooleanLiteral(bool: TExpression): boolean;
4652

packages/compiler-cli/linker/src/ast/typescript/typescript_ast_host.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,18 @@ export class TypeScriptAstHost implements AstHost<ts.Expression> {
4747
return parseInt(num.text);
4848
}
4949

50-
isBooleanLiteral(node: ts.Expression): node is ts.FalseLiteral|ts.TrueLiteral {
51-
return node.kind === ts.SyntaxKind.TrueKeyword || node.kind === ts.SyntaxKind.FalseKeyword;
50+
isBooleanLiteral(node: ts.Expression): boolean {
51+
return isBooleanLiteral(node) || isMinifiedBooleanLiteral(node);
5252
}
5353

5454
parseBooleanLiteral(bool: ts.Expression): boolean {
55-
assert(bool, this.isBooleanLiteral, 'a boolean literal');
56-
return bool.kind === ts.SyntaxKind.TrueKeyword;
55+
if (isBooleanLiteral(bool)) {
56+
return bool.kind === ts.SyntaxKind.TrueKeyword;
57+
} else if (isMinifiedBooleanLiteral(bool)) {
58+
return !(+bool.operand.text);
59+
} else {
60+
throw new FatalLinkerError(bool, 'Unsupported syntax, expected a boolean literal.');
61+
}
5762
}
5863

5964
isArrayLiteral = ts.isArrayLiteralExpression;
@@ -160,3 +165,20 @@ function isNotSpreadElement(e: ts.Expression|ts.SpreadElement): e is ts.Expressi
160165
function isPropertyName(e: ts.PropertyName): e is ts.Identifier|ts.StringLiteral|ts.NumericLiteral {
161166
return ts.isIdentifier(e) || ts.isStringLiteral(e) || ts.isNumericLiteral(e);
162167
}
168+
169+
/**
170+
* Return true if the node is either `true` or `false` literals.
171+
*/
172+
function isBooleanLiteral(node: ts.Expression): node is ts.TrueLiteral|ts.FalseLiteral {
173+
return node.kind === ts.SyntaxKind.TrueKeyword || node.kind === ts.SyntaxKind.FalseKeyword;
174+
}
175+
176+
type MinifiedBooleanLiteral = ts.PrefixUnaryExpression&{operand: ts.NumericLiteral};
177+
178+
/**
179+
* Return true if the node is either `!0` or `!1`.
180+
*/
181+
function isMinifiedBooleanLiteral(node: ts.Expression): node is MinifiedBooleanLiteral {
182+
return ts.isPrefixUnaryExpression(node) && node.operator === ts.SyntaxKind.ExclamationToken &&
183+
ts.isNumericLiteral(node.operand) && (node.operand.text === '0' || node.operand.text === '1');
184+
}

packages/compiler-cli/linker/test/ast/typescript/typescript_ast_host_spec.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ describe('TypeScriptAstHost', () => {
9494
expect(host.isBooleanLiteral(expr('false'))).toBe(true);
9595
});
9696

97+
it('should return true if the expression is a minified boolean literal', () => {
98+
expect(host.isBooleanLiteral(expr('!0'))).toBe(true);
99+
expect(host.isBooleanLiteral(expr('!1'))).toBe(true);
100+
});
101+
97102
it('should return false if the expression is not a boolean literal', () => {
98103
expect(host.isBooleanLiteral(expr('"moo"'))).toBe(false);
99104
expect(host.isBooleanLiteral(expr('\'moo\''))).toBe(false);
@@ -104,6 +109,8 @@ describe('TypeScriptAstHost', () => {
104109
expect(host.isBooleanLiteral(expr('null'))).toBe(false);
105110
expect(host.isBooleanLiteral(expr('\'a\' + \'b\''))).toBe(false);
106111
expect(host.isBooleanLiteral(expr('\`moo\`'))).toBe(false);
112+
expect(host.isBooleanLiteral(expr('!2'))).toBe(false);
113+
expect(host.isBooleanLiteral(expr('~1'))).toBe(false);
107114
});
108115
});
109116

@@ -113,6 +120,11 @@ describe('TypeScriptAstHost', () => {
113120
expect(host.parseBooleanLiteral(expr('false'))).toEqual(false);
114121
});
115122

123+
it('should extract a minified boolean value', () => {
124+
expect(host.parseBooleanLiteral(expr('!0'))).toEqual(true);
125+
expect(host.parseBooleanLiteral(expr('!1'))).toEqual(false);
126+
});
127+
116128
it('should error if the value is not a boolean literal', () => {
117129
expect(() => host.parseBooleanLiteral(expr('"moo"')))
118130
.toThrowError('Unsupported syntax, expected a boolean literal.');

0 commit comments

Comments
 (0)