jintr
Version:
A tiny JavaScript interpreter written in TypeScript.
119 lines (118 loc) • 5.67 kB
JavaScript
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _CallExpression_instances, _CallExpression_throwError, _CallExpression_getCalleeString;
import BaseJSNode from './BaseJSNode.js';
import { JinterError } from '../utils/index.js';
const builtins = {
// Override the forEach method so that the "this" arg is set correctly
forEach: (args, target, visitor) => {
const arr = target;
// Set forEach's “this” arg
if (args.length > 1) {
visitor.scope.set('_this', args.slice(-1)[0]);
}
// Execute callback function
let index = 0;
for (const element of arr) {
args[0]([element, index++, arr]);
}
},
// Also override the toString method so that it stringifies the correct object
toString: (_args, target) => {
return target.toString();
}
};
export default class CallExpression extends BaseJSNode {
constructor() {
super(...arguments);
_CallExpression_instances.add(this);
}
run() {
let exp_object;
let exp_property;
if (this.node.callee.type === 'MemberExpression') {
exp_object = this.visitor.getName(this.node.callee.object);
exp_property = this.visitor.getName(this.node.callee.property);
}
else if (this.node.callee.type === 'Identifier') {
exp_property = this.node.callee.name;
}
// Obj.fn(...);
if (exp_object && this.visitor.listeners[exp_object]) {
const cb = this.visitor.listeners[exp_object](this.node, this.visitor);
if (cb !== '__continue_exec') {
return cb;
}
}
// ?.fn(...);
if (exp_property && exp_property !== 'toString' && this.visitor.listeners[exp_property]) {
const cb = this.visitor.listeners[exp_property](this.node, this.visitor);
if (cb !== '__continue_exec') {
return cb;
}
}
if (this.node.callee.type === 'MemberExpression') {
const obj = this.visitor.visitNode(this.node.callee.object);
const prop = this.node.callee.computed ? this.visitor.visitNode(this.node.callee.property) : this.visitor.getName(this.node.callee.property);
const args = this.node.arguments.map((arg) => this.visitor.visitNode(arg));
if (prop in builtins) {
return builtins[prop](args, obj, this.visitor);
}
if (!obj)
__classPrivateFieldGet(this, _CallExpression_instances, "m", _CallExpression_throwError).call(this);
if (typeof obj[prop] !== 'function')
__classPrivateFieldGet(this, _CallExpression_instances, "m", _CallExpression_throwError).call(this);
if (obj[prop].toString().includes('[native code]'))
return obj[prop](...args);
return obj[prop](args);
}
const fn = this.visitor.visitNode(this.node.callee);
const args = this.node.arguments.map((arg) => this.visitor.visitNode(arg));
if (typeof fn !== 'function')
__classPrivateFieldGet(this, _CallExpression_instances, "m", _CallExpression_throwError).call(this);
return fn(args);
}
}
_CallExpression_instances = new WeakSet(), _CallExpression_throwError = function _CallExpression_throwError() {
if (this.node.callee.type === 'MemberExpression' || this.node.callee.type === 'Identifier') {
const callee_string = __classPrivateFieldGet(this, _CallExpression_instances, "m", _CallExpression_getCalleeString).call(this, this.node.callee);
throw new JinterError(`${callee_string} is not a function`);
}
else if (this.node.callee.type === 'SequenceExpression') {
const call = [];
const items = [];
call.push('(');
this.node.callee.expressions.forEach((expr) => {
if (expr.type === 'Literal') {
items.push(expr.raw || '');
}
else if (expr.type === 'Identifier') {
items.push(expr.name);
}
else if (expr.type === 'MemberExpression') {
if (expr.computed) {
items.push(`${this.visitor.getName(expr.object)}[${this.visitor.getName(expr.property) || '...'}]`);
}
else {
items.push(`${this.visitor.getName(expr.object)}.${this.visitor.getName(expr.property)}`);
}
}
});
call.push(items.join(', '));
call.push(')');
throw new JinterError(`${call.join('')} is not a function`);
}
}, _CallExpression_getCalleeString = function _CallExpression_getCalleeString(node) {
if (node.type === 'Identifier') {
return node.name;
}
else if (node.type === 'MemberExpression') {
const object_string = __classPrivateFieldGet(this, _CallExpression_instances, "m", _CallExpression_getCalleeString).call(this, node.object);
const property_string = node.computed ? `[${this.visitor.getName(node.property) || '...'}]` : `.${this.visitor.getName(node.property)}`;
return `${object_string}${property_string}`;
}
return '<unknown>';
};