use :node;
var Node = module.require('../Node').Node,
CallExpression = module.require('./CallExpression').CallExpression;
fn NullCheckCallExpression(callee, args)
extends Node { this.type = 'NullCheckCallExpression'; this.callee = callee; this.callee.parent = this; this.args = args; for arg in args { arg.parent = this; }
}
NullCheckCallExpression.prototype.codegen = () -> {
if !super.codegen() { return; } var calleeType = this.callee.type; this.callee = this.callee.codegen(); var args = this.args; for arg, i in args { var isSplat = args[i].type == "SplatExpression"; args[i] = arg.codegen(); args[i].codeGenerated = true; if isSplat { args[i].__splat = true; } } // If the callee has a function call (e.g: a().b) // then store its value in a separate variable to avoid // calling the function twice. if this.callee.hasCallExpression?() { var context = this.getContext(); var id = { "type": "Identifier", "name": NullCheckCallExpression.getNextVariableName(), "codeGenerated": true }; context.node.body.splice(context.position + (NullCheckCallExpression.nullCheckIndex - 2), 0, { "type": "VariableDeclaration", "declarations": [ { "type": "VariableDeclarator", "id": id, "init": this.callee } ], "kind": "let", "codeGenerated": true }); this.callee = id; } // Create a basic typeof callee !== "function" check var test = { "type": "BinaryExpression", "operator": "===", "left": { "type": "UnaryExpression", "operator": "typeof", "argument": this.callee, "prefix": true }, "right": { "type": "Literal", "value": "function", "raw": "\"function\"" } }; var argument = test.left.argument; // If we are null propagating (?.), then make sure to // add the null propagating condition if calleeType == 'NullPropagatingExpression' { argument = argument.consequent; test.left.argument = argument; test = { "type": "LogicalExpression", "operator": "&&", "left": this.callee.test, "right": test }; } argument.codeGenerated = true; var consequent = new CallExpression(argument, args).codegen(); if this.parent.type == 'ExpressionStatement' { this.parent.type = 'IfStatement'; this.parent.test = test; this.parent.consequent = { type: 'BlockStatement', body: [ { type: 'ExpressionStatement', expression: consequent } ] }; this.parent.alternate = null; } else { this.type = 'ConditionalExpression'; this.test = test; this.consequent = consequent; this.alternate = { "type": "UnaryExpression", "operator": "void", "argument": { "type": "Literal", "value": 0, "raw": "0" }, "prefix": true }; } return this;
};
NullCheckCallExpression.prototype.hasCallExpression = () -> true;
NullCheckCallExpression.getNextVariableName = () -> {
if !this.nullCheckIndex? { this.nullCheckIndex = 0; } return "nullCheck" + this.nullCheckIndex++;
};
NullCheckCallExpression.resetVariableNames = () -> {
this.nullCheckIndex = 0;
};
exports.NullCheckCallExpression = NullCheckCallExpression;