forked from rollup/rollup
/
BinaryExpression.ts
122 lines (111 loc) · 3.73 KB
/
BinaryExpression.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import type MagicString from 'magic-string';
import { BLANK } from '../../utils/blank';
import type { NodeRenderOptions, RenderOptions } from '../../utils/renderHelpers';
import type { DeoptimizableEntity } from '../DeoptimizableEntity';
import type { HasEffectsContext } from '../ExecutionContext';
import { INTERACTION_ACCESSED, NodeInteraction } from '../NodeInteractions';
import {
EMPTY_PATH,
type ObjectPath,
type PathTracker,
SHARED_RECURSION_TRACKER
} from '../utils/PathTracker';
import ExpressionStatement from './ExpressionStatement';
import type { LiteralValue } from './Literal';
import type * as NodeType from './NodeType';
import { type LiteralValueOrUnknown, UnknownValue } from './shared/Expression';
import { type ExpressionNode, NodeBase } from './shared/Node';
type Operator =
| '!='
| '!=='
| '%'
| '&'
| '*'
| '**'
| '+'
| '-'
| '/'
| '<'
| '<<'
| '<='
| '=='
| '==='
| '>'
| '>='
| '>>'
| '>>>'
| '^'
| '|'
| 'in'
| 'instanceof';
const binaryOperators: {
[operator in Operator]?: (left: LiteralValue, right: LiteralValue) => LiteralValueOrUnknown;
} = {
'!=': (left, right) => left != right,
'!==': (left, right) => left !== right,
'%': (left: any, right: any) => left % right,
'&': (left: any, right: any) => left & right,
'*': (left: any, right: any) => left * right,
// At the moment, "**" will be transpiled to Math.pow
'**': (left: any, right: any) => left ** right,
'+': (left: any, right: any) => left + right,
'-': (left: any, right: any) => left - right,
'/': (left: any, right: any) => left / right,
'<': (left, right) => left! < right!,
'<<': (left: any, right: any) => left << right,
'<=': (left, right) => left! <= right!,
'==': (left, right) => left == right,
'===': (left, right) => left === right,
'>': (left, right) => left! > right!,
'>=': (left, right) => left! >= right!,
'>>': (left: any, right: any) => left >> right,
'>>>': (left: any, right: any) => left >>> right,
'^': (left: any, right: any) => left ^ right,
'|': (left: any, right: any) => left | right
// We use the fallback for cases where we return something unknown
// in: () => UnknownValue,
// instanceof: () => UnknownValue,
};
export default class BinaryExpression extends NodeBase implements DeoptimizableEntity {
declare left: ExpressionNode;
declare operator: keyof typeof binaryOperators;
declare right: ExpressionNode;
declare type: NodeType.tBinaryExpression;
deoptimizeCache(): void {}
getLiteralValueAtPath(
path: ObjectPath,
recursionTracker: PathTracker,
origin: DeoptimizableEntity
): LiteralValueOrUnknown {
if (path.length > 0) return UnknownValue;
const leftValue = this.left.getLiteralValueAtPath(EMPTY_PATH, recursionTracker, origin);
if (typeof leftValue === 'symbol') return UnknownValue;
const rightValue = this.right.getLiteralValueAtPath(EMPTY_PATH, recursionTracker, origin);
if (typeof rightValue === 'symbol') return UnknownValue;
const operatorFn = binaryOperators[this.operator];
if (!operatorFn) return UnknownValue;
return operatorFn(leftValue, rightValue);
}
hasEffects(context: HasEffectsContext): boolean {
// support some implicit type coercion runtime errors
if (
this.operator === '+' &&
this.parent instanceof ExpressionStatement &&
this.left.getLiteralValueAtPath(EMPTY_PATH, SHARED_RECURSION_TRACKER, this) === ''
) {
return true;
}
return super.hasEffects(context);
}
hasEffectsOnInteractionAtPath(path: ObjectPath, { type }: NodeInteraction): boolean {
return type !== INTERACTION_ACCESSED || path.length > 1;
}
render(
code: MagicString,
options: RenderOptions,
{ renderedSurroundingElement }: NodeRenderOptions = BLANK
): void {
this.left.render(code, options, { renderedSurroundingElement });
this.right.render(code, options);
}
}