From 0c6fce47d60a8f4b266a9726ba1d3c4bf531f484 Mon Sep 17 00:00:00 2001 From: Lukas Taegert-Atkinson Date: Mon, 10 Jun 2019 09:42:33 +0200 Subject: [PATCH 1/2] Support implicit type coercion errors in es5-shim --- src/ast/nodes/BinaryExpression.ts | 9 +++++++++ .../samples/deopt-string-concatenation/_config.js | 4 ++++ .../samples/deopt-string-concatenation/_expected.js | 11 +++++++++++ test/form/samples/deopt-string-concatenation/main.js | 11 +++++++++++ 4 files changed, 35 insertions(+) create mode 100644 test/form/samples/deopt-string-concatenation/_config.js create mode 100644 test/form/samples/deopt-string-concatenation/_expected.js create mode 100644 test/form/samples/deopt-string-concatenation/main.js diff --git a/src/ast/nodes/BinaryExpression.ts b/src/ast/nodes/BinaryExpression.ts index 66e657949ff..4a53a8b6bcd 100644 --- a/src/ast/nodes/BinaryExpression.ts +++ b/src/ast/nodes/BinaryExpression.ts @@ -2,6 +2,7 @@ import { DeoptimizableEntity } from '../DeoptimizableEntity'; import { ExecutionPathOptions } from '../ExecutionPathOptions'; import { ImmutableEntityPathTracker } from '../utils/ImmutableEntityPathTracker'; import { EMPTY_PATH, LiteralValueOrUnknown, ObjectPath, UNKNOWN_VALUE } from '../values'; +import ExpressionStatement from './ExpressionStatement'; import { LiteralValue } from './Literal'; import * as NodeType from './NodeType'; import { ExpressionNode, NodeBase } from './shared/Node'; @@ -60,6 +61,14 @@ export default class BinaryExpression extends NodeBase { return operatorFn(leftValue as LiteralValue, rightValue as LiteralValue); } + hasEffects(options: ExecutionPathOptions): boolean { + // support some implicit type coercion runtime errors + if (this.operator === '+' && this.parent instanceof ExpressionStatement) { + return true; + } + return super.hasEffects(options); + } + hasEffectsWhenAccessedAtPath(path: ObjectPath, _options: ExecutionPathOptions) { return path.length > 1; } diff --git a/test/form/samples/deopt-string-concatenation/_config.js b/test/form/samples/deopt-string-concatenation/_config.js new file mode 100644 index 00000000000..36697ba2394 --- /dev/null +++ b/test/form/samples/deopt-string-concatenation/_config.js @@ -0,0 +1,4 @@ +module.exports = { + description: + 'deoptimize concatenation when used as an expression statement to better support es5-shim' +}; diff --git a/test/form/samples/deopt-string-concatenation/_expected.js b/test/form/samples/deopt-string-concatenation/_expected.js new file mode 100644 index 00000000000..68a30dc4f66 --- /dev/null +++ b/test/form/samples/deopt-string-concatenation/_expected.js @@ -0,0 +1,11 @@ +function parseInt(str, radix) { + if (typeof str === 'symbol') { + '' + str; + } + + var string = trim(String(str)); + var defaultedRadix = $Number(radix) || (hexRegex.test(string) ? 16 : 10); + return origParseInt(string, defaultedRadix); +} + +console.log(parseInt('1')); diff --git a/test/form/samples/deopt-string-concatenation/main.js b/test/form/samples/deopt-string-concatenation/main.js new file mode 100644 index 00000000000..68a30dc4f66 --- /dev/null +++ b/test/form/samples/deopt-string-concatenation/main.js @@ -0,0 +1,11 @@ +function parseInt(str, radix) { + if (typeof str === 'symbol') { + '' + str; + } + + var string = trim(String(str)); + var defaultedRadix = $Number(radix) || (hexRegex.test(string) ? 16 : 10); + return origParseInt(string, defaultedRadix); +} + +console.log(parseInt('1')); From 0d6bf37c5ce420cf88d21da86c74eae0e3ff75a6 Mon Sep 17 00:00:00 2001 From: Lukas Taegert-Atkinson Date: Mon, 10 Jun 2019 09:52:28 +0200 Subject: [PATCH 2/2] Make the check even more specific --- src/ast/nodes/BinaryExpression.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/ast/nodes/BinaryExpression.ts b/src/ast/nodes/BinaryExpression.ts index 4a53a8b6bcd..c0ff4321ae9 100644 --- a/src/ast/nodes/BinaryExpression.ts +++ b/src/ast/nodes/BinaryExpression.ts @@ -1,6 +1,9 @@ import { DeoptimizableEntity } from '../DeoptimizableEntity'; import { ExecutionPathOptions } from '../ExecutionPathOptions'; -import { ImmutableEntityPathTracker } from '../utils/ImmutableEntityPathTracker'; +import { + EMPTY_IMMUTABLE_TRACKER, + ImmutableEntityPathTracker +} from '../utils/ImmutableEntityPathTracker'; import { EMPTY_PATH, LiteralValueOrUnknown, ObjectPath, UNKNOWN_VALUE } from '../values'; import ExpressionStatement from './ExpressionStatement'; import { LiteralValue } from './Literal'; @@ -37,12 +40,14 @@ const binaryOperators: { '|': (left: any, right: any) => left | right }; -export default class BinaryExpression extends NodeBase { +export default class BinaryExpression extends NodeBase implements DeoptimizableEntity { left!: ExpressionNode; operator!: keyof typeof binaryOperators; right!: ExpressionNode; type!: NodeType.tBinaryExpression; + deoptimizeCache(): void {} + getLiteralValueAtPath( path: ObjectPath, recursionTracker: ImmutableEntityPathTracker, @@ -63,7 +68,11 @@ export default class BinaryExpression extends NodeBase { hasEffects(options: ExecutionPathOptions): boolean { // support some implicit type coercion runtime errors - if (this.operator === '+' && this.parent instanceof ExpressionStatement) { + if ( + this.operator === '+' && + this.parent instanceof ExpressionStatement && + this.left.getLiteralValueAtPath(EMPTY_PATH, EMPTY_IMMUTABLE_TRACKER, this) === '' + ) { return true; } return super.hasEffects(options);