typescript-to-lua
Version:
A generic TypeScript to Lua transpiler. Write your code in TypeScript and publish Lua!
207 lines • 11.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.transformCallExpression = void 0;
exports.validateArguments = validateArguments;
exports.transformArguments = transformArguments;
exports.transformCallAndArguments = transformCallAndArguments;
exports.transformContextualCallExpression = transformContextualCallExpression;
exports.getCalledExpression = getCalledExpression;
const ts = require("typescript");
const lua = require("../../LuaAST");
const builtins_1 = require("../builtins");
const assignment_validation_1 = require("../utils/assignment-validation");
const function_context_1 = require("../utils/function-context");
const lua_ast_1 = require("../utils/lua-ast");
const safe_names_1 = require("../utils/safe-names");
const typescript_1 = require("../utils/typescript");
const access_1 = require("./access");
const multi_1 = require("./language-extensions/multi");
const diagnostics_1 = require("../utils/diagnostics");
const expression_list_1 = require("./expression-list");
const preceding_statements_1 = require("../utils/preceding-statements");
const optional_chaining_1 = require("./optional-chaining");
const import_1 = require("./modules/import");
const call_extension_1 = require("./language-extensions/call-extension");
const identifier_1 = require("./identifier");
function validateArguments(context, params, signature) {
if (!signature || signature.parameters.length < params.length) {
return;
}
for (const [index, param] of params.entries()) {
const signatureParameter = signature.parameters[index];
if (signatureParameter.valueDeclaration !== undefined) {
const signatureType = context.checker.getTypeAtLocation(signatureParameter.valueDeclaration);
const paramType = context.checker.getTypeAtLocation(param);
(0, assignment_validation_1.validateAssignment)(context, param, paramType, signatureType, signatureParameter.name);
}
}
}
function transformArguments(context, params, signature, callContext) {
validateArguments(context, params, signature);
return (0, expression_list_1.transformExpressionList)(context, callContext ? [callContext, ...params] : params);
}
function transformCallWithArguments(context, callExpression, transformedArguments, argPrecedingStatements, callContext) {
let call = context.transformExpression(callExpression);
let transformedContext;
if (callContext) {
transformedContext = context.transformExpression(callContext);
}
if (argPrecedingStatements.length > 0) {
if (transformedContext) {
transformedContext = (0, expression_list_1.moveToPrecedingTemp)(context, transformedContext, callContext);
}
call = (0, expression_list_1.moveToPrecedingTemp)(context, call, callExpression);
context.addPrecedingStatements(argPrecedingStatements);
}
if (transformedContext) {
transformedArguments.unshift(transformedContext);
}
return [call, transformedArguments];
}
function transformCallAndArguments(context, callExpression, params, signature, callContext) {
const { precedingStatements: argPrecedingStatements, result: transformedArguments } = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => transformArguments(context, params, signature, callContext));
return transformCallWithArguments(context, callExpression, transformedArguments, argPrecedingStatements);
}
function transformElementAccessCall(context, left, transformedArguments, argPrecedingStatements) {
// Cache left-side if it has effects
// local ____self = context; return ____self[argument](parameters);
const selfIdentifier = lua.createIdentifier(context.createTempName("self"));
const callContext = context.transformExpression(left.expression);
const selfAssignment = lua.createVariableDeclarationStatement(selfIdentifier, callContext);
context.addPrecedingStatements(selfAssignment);
const argument = ts.isElementAccessExpression(left)
? (0, access_1.transformElementAccessArgument)(context, left)
: lua.createStringLiteral(left.name.text);
let index = lua.createTableIndexExpression(selfIdentifier, argument);
if (argPrecedingStatements.length > 0) {
// Cache index in temp if args had preceding statements
index = (0, expression_list_1.moveToPrecedingTemp)(context, index);
context.addPrecedingStatements(argPrecedingStatements);
}
return lua.createCallExpression(index, [selfIdentifier, ...transformedArguments]);
}
function transformContextualCallExpression(context, node, args) {
if (ts.isOptionalChain(node)) {
return (0, optional_chaining_1.transformOptionalChain)(context, node);
}
const left = ts.isCallExpression(node) ? getCalledExpression(node) : node.tag;
let { precedingStatements: argPrecedingStatements, result: transformedArguments } = (0, preceding_statements_1.transformInPrecedingStatementScope)(context, () => transformArguments(context, args));
if (ts.isPropertyAccessExpression(left) &&
ts.isIdentifier(left.name) &&
(0, safe_names_1.isValidLuaIdentifier)(left.name.text, context.options) &&
argPrecedingStatements.length === 0) {
// table:name()
const table = context.transformExpression(left.expression);
let name = left.name.text;
const symbol = context.checker.getSymbolAtLocation(left);
const customName = (0, identifier_1.getCustomNameFromSymbol)(context, symbol);
if (customName) {
name = customName;
}
return lua.createMethodCallExpression(table, lua.createIdentifier(name, left.name), transformedArguments, node);
}
else if (ts.isElementAccessExpression(left) || ts.isPropertyAccessExpression(left)) {
if ((0, typescript_1.isExpressionWithEvaluationEffect)(left.expression)) {
return transformElementAccessCall(context, left, transformedArguments, argPrecedingStatements);
}
else {
let expression;
[expression, transformedArguments] = transformCallWithArguments(context, left, transformedArguments, argPrecedingStatements, left.expression);
return lua.createCallExpression(expression, transformedArguments, node);
}
}
else if (ts.isIdentifier(left) || ts.isCallExpression(left)) {
const callContext = context.isStrict ? ts.factory.createNull() : ts.factory.createIdentifier("_G");
let expression;
[expression, transformedArguments] = transformCallWithArguments(context, left, transformedArguments, argPrecedingStatements, callContext);
return lua.createCallExpression(expression, transformedArguments, node);
}
else {
throw new Error(`Unsupported LeftHandSideExpression kind: ${ts.SyntaxKind[left.kind]}`);
}
}
function transformPropertyCall(context, node, calledMethod) {
const signature = context.checker.getResolvedSignature(node);
if (calledMethod.expression.kind === ts.SyntaxKind.SuperKeyword) {
// Super calls take the format of super.call(self,...)
const parameters = transformArguments(context, node.arguments, signature, ts.factory.createThis());
return lua.createCallExpression(context.transformExpression(node.expression), parameters, node);
}
if ((0, function_context_1.getCallContextType)(context, node) !== function_context_1.ContextType.Void) {
// table:name()
return transformContextualCallExpression(context, node, node.arguments);
}
else {
// table.name()
const [callPath, parameters] = transformCallAndArguments(context, node.expression, node.arguments, signature);
return lua.createCallExpression(callPath, parameters, node);
}
}
function transformElementCall(context, node) {
if ((0, function_context_1.getCallContextType)(context, node) !== function_context_1.ContextType.Void) {
// A contextual parameter must be given to this call expression
return transformContextualCallExpression(context, node, node.arguments);
}
else {
// No context
const [expression, parameters] = transformCallAndArguments(context, node.expression, node.arguments);
return lua.createCallExpression(expression, parameters, node);
}
}
const transformCallExpression = (node, context) => {
var _a;
const calledExpression = getCalledExpression(node);
if (calledExpression.kind === ts.SyntaxKind.ImportKeyword) {
return (0, import_1.transformImportExpression)(node, context);
}
if (ts.isOptionalChain(node)) {
return (0, optional_chaining_1.transformOptionalChain)(context, node);
}
const optionalContinuation = ts.isIdentifier(calledExpression)
? (0, optional_chaining_1.getOptionalContinuationData)(calledExpression)
: undefined;
const wrapResultInTable = (0, multi_1.isMultiReturnCall)(context, node) && (0, multi_1.shouldMultiReturnCallBeWrapped)(context, node);
const builtinOrExtensionResult = (_a = (0, builtins_1.transformBuiltinCallExpression)(context, node)) !== null && _a !== void 0 ? _a : (0, call_extension_1.transformLanguageExtensionCallExpression)(context, node);
if (builtinOrExtensionResult) {
if (optionalContinuation !== undefined) {
context.diagnostics.push((0, diagnostics_1.unsupportedBuiltinOptionalCall)(node));
}
return wrapResultInTable ? (0, lua_ast_1.wrapInTable)(builtinOrExtensionResult) : builtinOrExtensionResult;
}
if (ts.isPropertyAccessExpression(calledExpression)) {
const result = transformPropertyCall(context, node, calledExpression);
return wrapResultInTable ? (0, lua_ast_1.wrapInTable)(result) : result;
}
if (ts.isElementAccessExpression(calledExpression)) {
const result = transformElementCall(context, node);
return wrapResultInTable ? (0, lua_ast_1.wrapInTable)(result) : result;
}
const signature = context.checker.getResolvedSignature(node);
// Handle super calls properly
if (calledExpression.kind === ts.SyntaxKind.SuperKeyword) {
const parameters = transformArguments(context, node.arguments, signature, ts.factory.createThis());
return lua.createCallExpression(lua.createTableIndexExpression(context.transformExpression(ts.factory.createSuper()), lua.createStringLiteral("____constructor")), parameters, node);
}
let callPath;
let parameters;
const isContextualCall = (0, function_context_1.getCallContextType)(context, node) !== function_context_1.ContextType.Void;
if (!isContextualCall) {
[callPath, parameters] = transformCallAndArguments(context, calledExpression, node.arguments, signature);
}
else {
// if is optionalContinuation, context will be handled by transformOptionalChain.
const useGlobalContext = !context.isStrict && optionalContinuation === undefined;
const callContext = useGlobalContext ? ts.factory.createIdentifier("_G") : ts.factory.createNull();
[callPath, parameters] = transformCallAndArguments(context, calledExpression, node.arguments, signature, callContext);
}
const callExpression = lua.createCallExpression(callPath, parameters, node);
if (optionalContinuation && isContextualCall) {
optionalContinuation.contextualCall = callExpression;
}
return wrapResultInTable ? (0, lua_ast_1.wrapInTable)(callExpression) : callExpression;
};
exports.transformCallExpression = transformCallExpression;
function getCalledExpression(node) {
return ts.skipOuterExpressions(node.expression);
}
//# sourceMappingURL=call.js.map