Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/LuaPrinter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -742,7 +742,9 @@ export class LuaPrinter {
public printMethodCallExpression(expression: lua.MethodCallExpression): SourceNode {
const chunks = [];

const prefix = this.printExpression(expression.prefixExpression);
const prefix = lua.isStringLiteral(expression.prefixExpression)
? this.printExpression(lua.createParenthesizedExpression(expression.prefixExpression))
: this.printExpression(expression.prefixExpression);

const name = this.printIdentifier(expression.name);

Expand Down
8 changes: 2 additions & 6 deletions src/transformation/builtins/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@ import * as lua from "../../LuaAST";
import { TransformationContext } from "../context";
import { UnsupportedProperty } from "../utils/errors";
import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib";
import { isExplicitArrayType } from "../utils/typescript";
import { PropertyCallExpression, transformArguments } from "../visitors/call";

export function transformArrayPrototypeCall(
context: TransformationContext,
node: PropertyCallExpression
): lua.CallExpression | undefined {
): lua.CallExpression {
const expression = node.expression;
const ownerType = context.checker.getTypeAtLocation(expression.expression);
const signature = context.checker.getResolvedSignature(node);
const params = transformArguments(context, node.arguments, signature);
const caller = context.transformExpression(expression.expression);
Expand Down Expand Up @@ -79,9 +77,7 @@ export function transformArrayPrototypeCall(
case "flatMap":
return transformLuaLibFunction(context, LuaLibFeature.ArrayFlatMap, node, caller, ...params);
default:
if (isExplicitArrayType(context, ownerType)) {
throw UnsupportedProperty("array", expressionName, node);
}
throw UnsupportedProperty("array", expressionName, node);
}
}

Expand Down
22 changes: 13 additions & 9 deletions src/transformation/builtins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ import { assume } from "../../utils";
import { TransformationContext } from "../context";
import { importLuaLibFeature, LuaLibFeature } from "../utils/lualib";
import { getIdentifierSymbolId } from "../utils/symbols";
import { isArrayType, isFunctionType, isNumberType, isStandardLibraryType, isStringType } from "../utils/typescript";
import {
hasStandardLibrarySignature,
isArrayType,
isFunctionType,
isNumberType,
isStandardLibraryType,
isStringType,
} from "../utils/typescript";
import { PropertyCallExpression } from "../visitors/call";
import { checkForLuaLibType } from "../visitors/class/new";
import { transformArrayProperty, transformArrayPrototypeCall } from "./array";
Expand Down Expand Up @@ -85,22 +92,19 @@ export function transformBuiltinCallExpression(
}
}

if (isStringType(context, ownerType)) {
if (isStringType(context, ownerType) && hasStandardLibrarySignature(context, node)) {
return transformStringPrototypeCall(context, node);
}

if (isNumberType(context, ownerType)) {
if (isNumberType(context, ownerType) && hasStandardLibrarySignature(context, node)) {
return transformNumberPrototypeCall(context, node);
}

if (isArrayType(context, ownerType)) {
const result = transformArrayPrototypeCall(context, node);
if (result) {
return result;
}
if (isArrayType(context, ownerType) && hasStandardLibrarySignature(context, node)) {
return transformArrayPrototypeCall(context, node);
}

if (isFunctionType(context, ownerType)) {
if (isFunctionType(context, ownerType) && hasStandardLibrarySignature(context, node)) {
return transformFunctionPrototypeCall(context, node);
}

Expand Down
32 changes: 0 additions & 32 deletions src/transformation/builtins/string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { UnsupportedProperty } from "../utils/errors";
import { createExpressionPlusOne } from "../utils/lua-ast";
import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib";
import { PropertyCallExpression, transformArguments } from "../visitors/call";
import { transformIdentifier } from "../visitors/identifier";

function createStringCall(methodName: string, tsOriginal: ts.Node, ...params: lua.Expression[]): lua.CallExpression {
const stringIdentifier = lua.createIdentifier("string");
Expand Down Expand Up @@ -124,37 +123,6 @@ export function transformStringPrototypeCall(
return transformLuaLibFunction(context, LuaLibFeature.StringPadStart, node, caller, ...params);
case "padEnd":
return transformLuaLibFunction(context, LuaLibFeature.StringPadEnd, node, caller, ...params);

case "byte":
case "char":
case "dump":
case "find":
case "format":
case "gmatch":
case "gsub":
case "len":
case "lower":
case "match":
case "pack":
case "packsize":
case "rep":
case "reverse":
case "sub":
case "unpack":
case "upper":
// Allow lua's string instance methods
let stringVariable = context.transformExpression(expression.expression);
if (ts.isStringLiteralLike(expression.expression)) {
// "foo":method() needs to be ("foo"):method()
stringVariable = lua.createParenthesizedExpression(stringVariable);
}

return lua.createMethodCallExpression(
stringVariable,
transformIdentifier(context, expression.name),
params,
node
);
default:
throw UnsupportedProperty("string", expressionName, node);
}
Expand Down
23 changes: 18 additions & 5 deletions src/transformation/utils/typescript/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ export function isFirstDeclaration(context: TransformationContext, node: ts.Vari
return firstDeclaration === node;
}

function isStandardLibraryDeclaration(context: TransformationContext, declaration: ts.Declaration): boolean {
const sourceFile = declaration.getSourceFile();
if (!sourceFile) {
return false;
}

return context.program.isSourceFileDefaultLibrary(sourceFile);
}

export function isStandardLibraryType(
context: TransformationContext,
type: ts.Type,
Expand All @@ -59,12 +68,16 @@ export function isStandardLibraryType(
return true;
}

const sourceFile = declaration.getSourceFile();
if (!sourceFile) {
return false;
}
return isStandardLibraryDeclaration(context, declaration);
}

return context.program.isSourceFileDefaultLibrary(sourceFile);
export function hasStandardLibrarySignature(
context: TransformationContext,
callExpression: ts.CallExpression
): boolean {
const signature = context.checker.getResolvedSignature(callExpression);

return signature && signature.declaration ? isStandardLibraryDeclaration(context, signature.declaration) : false;
}

export function inferAssignedType(context: TransformationContext, expression: ts.Expression): ts.Type {
Expand Down
2 changes: 1 addition & 1 deletion src/transformation/utils/typescript/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function isNumberType(context: TransformationContext, type: ts.Type): boo
return isTypeWithFlags(context, type, ts.TypeFlags.Number | ts.TypeFlags.NumberLike | ts.TypeFlags.NumberLiteral);
}

export function isExplicitArrayType(context: TransformationContext, type: ts.Type): boolean {
function isExplicitArrayType(context: TransformationContext, type: ts.Type): boolean {
if (type.symbol) {
const baseConstraint = context.checker.getBaseConstraintOfType(type);
if (baseConstraint && baseConstraint !== type) {
Expand Down
6 changes: 0 additions & 6 deletions test/unit/builtins/loading.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,4 @@ describe("Unknown builtin property", () => {
.disableSemanticCheck()
.expectToHaveDiagnosticOfError(UnsupportedProperty("Math", "unknownProperty", util.nodeStub));
});

test("function call", () => {
util.testExpression`[].unknownFunction()`
.disableSemanticCheck()
.expectToHaveDiagnosticOfError(UnsupportedProperty("array", "unknownFunction", util.nodeStub));
});
});
7 changes: 7 additions & 0 deletions test/unit/builtins/numbers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,10 @@ test.each(cases)("isNaN(%p)", value => {
test.each(cases)("isFinite(%p)", value => {
util.testExpressionTemplate`isFinite(${value} as any)`.expectToMatchJsResult();
});

test("number intersected method", () => {
util.testFunction`
type Vector = number & { normalize(): Vector };
return ({ normalize: () => 3 } as Vector).normalize();
`.expectToMatchJsResult();
});
14 changes: 7 additions & 7 deletions test/unit/builtins/string.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
import { UnsupportedProperty } from "../../../src/transformation/utils/errors";
import * as util from "../../util";

test("Unsupported string function", () => {
util.testExpression`"test".testThisIsNoMember()`
.disableSemanticCheck()
.expectToHaveDiagnosticOfError(UnsupportedProperty("string", "testThisIsNoMember", util.nodeStub));
});

test("Supported lua string function", () => {
const tsHeader = `
declare global {
Expand Down Expand Up @@ -287,3 +280,10 @@ describe.each(["trim", "trimEnd", "trimRight", "trimStart", "trimLeft"])("string
util.testExpression`${util.formatCode(testString)}.${trim}()`.expectToMatchJsResult();
});
});

test("string intersected method", () => {
util.testFunction`
type Vector = string & { abc(): Vector };
return ({ abc: () => "a" } as Vector).abc();
`.expectToMatchJsResult();
});