Skip to content

Commit 4d6c8d1

Browse files
committed
fix(CallExpression): Properly parse computed properties in call expressions
Example: var Y = 'forEach'; d[Y[0]](...);
1 parent fde4c34 commit 4d6c8d1

File tree

1 file changed

+27
-50
lines changed

1 file changed

+27
-50
lines changed

src/nodes/CallExpression.ts

Lines changed: 27 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,29 @@ import type ESTree from 'estree';
33
import BaseJSNode from './BaseJSNode.js';
44
import { JinterError } from '../utils/index.js';
55

6+
const builtins: { [key: string]: any } = {
7+
// Override the forEach method so that the "this" arg is set correctly
8+
forEach: (args: any[], target: any, visitor: Visitor) => {
9+
const arr = target;
10+
11+
// Set forEach's “this” arg
12+
if (args.length > 1) {
13+
visitor.scope.set('_this', args.slice(-1)[0]);
14+
}
15+
16+
// Execute callback function
17+
let index = 0;
18+
19+
for (const element of arr) {
20+
args[0]([ element, index++, arr ]);
21+
}
22+
},
23+
// Also override the toString method so that it stringifies the correct object
24+
toString: (_args: any[], target: any) => {
25+
return target.toString();
26+
}
27+
};
28+
629
export default class CallExpression extends BaseJSNode<ESTree.CallExpression> {
730
public run(): any {
831
let exp_object: string | undefined;
@@ -32,14 +55,14 @@ export default class CallExpression extends BaseJSNode<ESTree.CallExpression> {
3255
}
3356

3457
if (this.node.callee.type === 'MemberExpression') {
35-
if (Builtins.has(this.node, this.visitor)) {
36-
return Builtins.execute(this.node, this.visitor);
37-
}
38-
3958
const obj = this.visitor.visitNode(this.node.callee.object);
4059
const prop = this.node.callee.computed ? this.visitor.visitNode(this.node.callee.property) : this.visitor.getName(this.node.callee.property);
4160
const args = this.node.arguments.map((arg) => this.visitor.visitNode(arg));
4261

62+
if (this.node.callee.type === 'MemberExpression' && prop in builtins) {
63+
return builtins[prop](args, obj, this.visitor);
64+
}
65+
4366
if (!obj)
4467
this.#throwError();
4568

@@ -100,50 +123,4 @@ export default class CallExpression extends BaseJSNode<ESTree.CallExpression> {
100123
}
101124
return '<unknown>';
102125
}
103-
}
104-
105-
class Builtins {
106-
static builtins: { [key: string]: any } = {
107-
// Override the forEach method so that the "this" arg is set correctly
108-
forEach: (node: ESTree.CallExpression, visitor: Visitor) => {
109-
const args = node.arguments.map((arg) => visitor.visitNode(arg));
110-
111-
if (node.callee.type === 'MemberExpression') {
112-
const arr = visitor.visitNode(node.callee.object);
113-
114-
// Set forEach's “this” arg
115-
if (args.length > 1) {
116-
visitor.scope.set('_this', args.slice(-1)[0]);
117-
}
118-
119-
// Execute callback function
120-
let index = 0;
121-
122-
for (const element of arr) {
123-
args[0]([ element, index++, arr ]);
124-
}
125-
} else {
126-
console.warn('Unhandled callee type:', node.callee.type);
127-
}
128-
},
129-
// Also override the toString method so that it stringifies the correct object
130-
toString: (node: ESTree.CallExpression, visitor: Visitor) => {
131-
if (node.callee.type === 'MemberExpression') {
132-
return visitor.visitNode(node.callee.object).toString();
133-
}
134-
}
135-
};
136-
137-
static has(node: ESTree.CallExpression, visitor: Visitor): boolean {
138-
if (node.callee.type === 'MemberExpression') {
139-
return !!this.builtins?.[visitor.getName(node.callee.property) || ''];
140-
}
141-
return false;
142-
}
143-
144-
static execute(node: ESTree.CallExpression, visitor: Visitor) {
145-
if (node.callee.type === 'MemberExpression') {
146-
return this.builtins[visitor.getName(node.callee.property) || ''](node, visitor);
147-
}
148-
}
149126
}

0 commit comments

Comments
 (0)