diff --git a/src/ast/nodes/BinaryExpression.ts b/src/ast/nodes/BinaryExpression.ts index 66e657949ff..c0ff4321ae9 100644 --- a/src/ast/nodes/BinaryExpression.ts +++ b/src/ast/nodes/BinaryExpression.ts @@ -1,7 +1,11 @@ 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'; import * as NodeType from './NodeType'; import { ExpressionNode, NodeBase } from './shared/Node'; @@ -36,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, @@ -60,6 +66,18 @@ 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 && + this.left.getLiteralValueAtPath(EMPTY_PATH, EMPTY_IMMUTABLE_TRACKER, this) === '' + ) { + 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'));