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
This commit is contained in:
parent
6ca6302cf1
commit
7744e1e673
|
@ -38,11 +38,18 @@ export class BabelAstHost implements AstHost<t.Expression> {
|
|||
return num.value;
|
||||
}
|
||||
|
||||
isBooleanLiteral = t.isBooleanLiteral;
|
||||
isBooleanLiteral(bool: t.Expression): boolean {
|
||||
return t.isBooleanLiteral(bool) || isMinifiedBooleanLiteral(bool);
|
||||
}
|
||||
|
||||
parseBooleanLiteral(bool: t.Expression): boolean {
|
||||
assert(bool, t.isBooleanLiteral, 'a boolean literal');
|
||||
return bool.value;
|
||||
if (t.isBooleanLiteral(bool)) {
|
||||
return bool.value;
|
||||
} else if (isMinifiedBooleanLiteral(bool)) {
|
||||
return !bool.argument.value;
|
||||
} else {
|
||||
throw new FatalLinkerError(bool, 'Unsupported syntax, expected a boolean literal.');
|
||||
}
|
||||
}
|
||||
|
||||
isArrayLiteral = t.isArrayExpression;
|
||||
|
@ -165,3 +172,13 @@ type ArgumentType = t.CallExpression['arguments'][number];
|
|||
function isNotSpreadArgument(arg: ArgumentType): arg is Exclude<ArgumentType, t.SpreadElement> {
|
||||
return !t.isSpreadElement(arg);
|
||||
}
|
||||
|
||||
type MinifiedBooleanLiteral = t.Expression&t.UnaryExpression&{argument: t.NumericLiteral};
|
||||
|
||||
/**
|
||||
* Return true if the node is either `!0` or `!1`.
|
||||
*/
|
||||
function isMinifiedBooleanLiteral(node: t.Expression): node is MinifiedBooleanLiteral {
|
||||
return t.isUnaryExpression(node) && node.prefix && node.operator === '!' &&
|
||||
t.isNumericLiteral(node.argument) && (node.argument.value === 0 || node.argument.value === 1);
|
||||
}
|
||||
|
|
|
@ -96,6 +96,11 @@ describe('BabelAstHost', () => {
|
|||
expect(host.isBooleanLiteral(expr('false'))).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true if the expression is a minified boolean literal', () => {
|
||||
expect(host.isBooleanLiteral(expr('!0'))).toBe(true);
|
||||
expect(host.isBooleanLiteral(expr('!1'))).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if the expression is not a boolean literal', () => {
|
||||
expect(host.isBooleanLiteral(expr('"moo"'))).toBe(false);
|
||||
expect(host.isBooleanLiteral(expr('\'moo\''))).toBe(false);
|
||||
|
@ -106,6 +111,8 @@ describe('BabelAstHost', () => {
|
|||
expect(host.isBooleanLiteral(expr('null'))).toBe(false);
|
||||
expect(host.isBooleanLiteral(expr('\'a\' + \'b\''))).toBe(false);
|
||||
expect(host.isBooleanLiteral(expr('\`moo\`'))).toBe(false);
|
||||
expect(host.isBooleanLiteral(expr('!2'))).toBe(false);
|
||||
expect(host.isBooleanLiteral(expr('~1'))).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -115,6 +122,11 @@ describe('BabelAstHost', () => {
|
|||
expect(host.parseBooleanLiteral(expr('false'))).toEqual(false);
|
||||
});
|
||||
|
||||
it('should extract a minified boolean value', () => {
|
||||
expect(host.parseBooleanLiteral(expr('!0'))).toEqual(true);
|
||||
expect(host.parseBooleanLiteral(expr('!1'))).toEqual(false);
|
||||
});
|
||||
|
||||
it('should error if the value is not a boolean literal', () => {
|
||||
expect(() => host.parseBooleanLiteral(expr('"moo"')))
|
||||
.toThrowError('Unsupported syntax, expected a boolean literal.');
|
||||
|
|
|
@ -36,11 +36,17 @@ export interface AstHost<TExpression> {
|
|||
parseNumericLiteral(num: TExpression): number;
|
||||
|
||||
/**
|
||||
* Return `true` if the given expression is a boolean literal, or false otherwise.
|
||||
* Return `true` if the given expression can be considered a boolean literal, or false otherwise.
|
||||
*
|
||||
* Note that this should also cover the special case of some minified code where `true` and
|
||||
* `false` are replaced by `!0` and `!1` respectively.
|
||||
*/
|
||||
isBooleanLiteral(node: TExpression): boolean;
|
||||
/**
|
||||
* Parse the boolean value from the given expression, or throw if it is not a boolean literal.
|
||||
*
|
||||
* Note that this should also cover the special case of some minified code where `true` and
|
||||
* `false` are replaced by `!0` and `!1` respectively.
|
||||
*/
|
||||
parseBooleanLiteral(bool: TExpression): boolean;
|
||||
|
||||
|
|
|
@ -47,13 +47,18 @@ export class TypeScriptAstHost implements AstHost<ts.Expression> {
|
|||
return parseInt(num.text);
|
||||
}
|
||||
|
||||
isBooleanLiteral(node: ts.Expression): node is ts.FalseLiteral|ts.TrueLiteral {
|
||||
return node.kind === ts.SyntaxKind.TrueKeyword || node.kind === ts.SyntaxKind.FalseKeyword;
|
||||
isBooleanLiteral(node: ts.Expression): boolean {
|
||||
return isBooleanLiteral(node) || isMinifiedBooleanLiteral(node);
|
||||
}
|
||||
|
||||
parseBooleanLiteral(bool: ts.Expression): boolean {
|
||||
assert(bool, this.isBooleanLiteral, 'a boolean literal');
|
||||
return bool.kind === ts.SyntaxKind.TrueKeyword;
|
||||
if (isBooleanLiteral(bool)) {
|
||||
return bool.kind === ts.SyntaxKind.TrueKeyword;
|
||||
} else if (isMinifiedBooleanLiteral(bool)) {
|
||||
return !(+bool.operand.text);
|
||||
} else {
|
||||
throw new FatalLinkerError(bool, 'Unsupported syntax, expected a boolean literal.');
|
||||
}
|
||||
}
|
||||
|
||||
isArrayLiteral = ts.isArrayLiteralExpression;
|
||||
|
@ -160,3 +165,20 @@ function isNotSpreadElement(e: ts.Expression|ts.SpreadElement): e is ts.Expressi
|
|||
function isPropertyName(e: ts.PropertyName): e is ts.Identifier|ts.StringLiteral|ts.NumericLiteral {
|
||||
return ts.isIdentifier(e) || ts.isStringLiteral(e) || ts.isNumericLiteral(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the node is either `true` or `false` literals.
|
||||
*/
|
||||
function isBooleanLiteral(node: ts.Expression): node is ts.TrueLiteral|ts.FalseLiteral {
|
||||
return node.kind === ts.SyntaxKind.TrueKeyword || node.kind === ts.SyntaxKind.FalseKeyword;
|
||||
}
|
||||
|
||||
type MinifiedBooleanLiteral = ts.PrefixUnaryExpression&{operand: ts.NumericLiteral};
|
||||
|
||||
/**
|
||||
* Return true if the node is either `!0` or `!1`.
|
||||
*/
|
||||
function isMinifiedBooleanLiteral(node: ts.Expression): node is MinifiedBooleanLiteral {
|
||||
return ts.isPrefixUnaryExpression(node) && node.operator === ts.SyntaxKind.ExclamationToken &&
|
||||
ts.isNumericLiteral(node.operand) && (node.operand.text === '0' || node.operand.text === '1');
|
||||
}
|
||||
|
|
|
@ -94,6 +94,11 @@ describe('TypeScriptAstHost', () => {
|
|||
expect(host.isBooleanLiteral(expr('false'))).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true if the expression is a minified boolean literal', () => {
|
||||
expect(host.isBooleanLiteral(expr('!0'))).toBe(true);
|
||||
expect(host.isBooleanLiteral(expr('!1'))).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if the expression is not a boolean literal', () => {
|
||||
expect(host.isBooleanLiteral(expr('"moo"'))).toBe(false);
|
||||
expect(host.isBooleanLiteral(expr('\'moo\''))).toBe(false);
|
||||
|
@ -104,6 +109,8 @@ describe('TypeScriptAstHost', () => {
|
|||
expect(host.isBooleanLiteral(expr('null'))).toBe(false);
|
||||
expect(host.isBooleanLiteral(expr('\'a\' + \'b\''))).toBe(false);
|
||||
expect(host.isBooleanLiteral(expr('\`moo\`'))).toBe(false);
|
||||
expect(host.isBooleanLiteral(expr('!2'))).toBe(false);
|
||||
expect(host.isBooleanLiteral(expr('~1'))).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -113,6 +120,11 @@ describe('TypeScriptAstHost', () => {
|
|||
expect(host.parseBooleanLiteral(expr('false'))).toEqual(false);
|
||||
});
|
||||
|
||||
it('should extract a minified boolean value', () => {
|
||||
expect(host.parseBooleanLiteral(expr('!0'))).toEqual(true);
|
||||
expect(host.parseBooleanLiteral(expr('!1'))).toEqual(false);
|
||||
});
|
||||
|
||||
it('should error if the value is not a boolean literal', () => {
|
||||
expect(() => host.parseBooleanLiteral(expr('"moo"')))
|
||||
.toThrowError('Unsupported syntax, expected a boolean literal.');
|
||||
|
|
Loading…
Reference in New Issue