X Tutup
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
6 changes: 6 additions & 0 deletions src/Decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ export class Decorator {
return DecoratorKind.NoClassOr;
case "luaiterator":
return DecoratorKind.LuaIterator;
case "noself":
return DecoratorKind.NoSelf;
case "noselfinfile":
return DecoratorKind.NoSelfInFile;
}

return undefined;
Expand All @@ -50,4 +54,6 @@ export enum DecoratorKind {
TupleReturn = "TupleReturn",
NoClassOr = "NoClassOr",
LuaIterator = "LuaIterator",
NoSelf = "NoSelf",
NoSelfInFile = "NoSelfInFile",
}
34 changes: 14 additions & 20 deletions src/LuaTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1454,16 +1454,13 @@ export class LuaTransformer {
private transformGeneratorFunction(
parameters: ts.NodeArray<ts.ParameterDeclaration>,
body: ts.Block,
transformedParameters: tstl.Identifier[],
dotsLiteral: tstl.DotsLiteral,
spreadIdentifier?: tstl.Identifier
): [tstl.Statement[], Scope]
{
this.importLuaLibFeature(LuaLibFeature.Symbol);
const [functionBody, functionScope] = this.transformFunctionBody(
parameters,
body,
spreadIdentifier
body
);

const coroutineIdentifier = tstl.createIdentifier("____co");
Expand All @@ -1478,12 +1475,7 @@ export class LuaTransformer {
tstl.createTableIndexExpression(tstl.createIdentifier("coroutine"),
tstl.createStringLiteral("create")
),
[tstl.createFunctionExpression(
tstl.createBlock(functionBody),
transformedParameters,
dotsLiteral,
spreadIdentifier),
]
[tstl.createFunctionExpression(tstl.createBlock(functionBody))]
)
);

Expand Down Expand Up @@ -1586,6 +1578,12 @@ export class LuaTransformer {
//return ____it
tstl.createReturnStatement([itIdentifier]),
];

if (spreadIdentifier) {
const spreadTable = this.wrapInTable(tstl.createDotsLiteral());
block.unshift(tstl.createVariableDeclarationStatement(spreadIdentifier, spreadTable));
}

return [block, functionScope];
}

Expand All @@ -1606,8 +1604,6 @@ export class LuaTransformer {
? this.transformGeneratorFunction(
functionDeclaration.parameters,
functionDeclaration.body,
params,
dotsLiteral,
restParamName
)
: this.transformFunctionBody(
Expand Down Expand Up @@ -3131,14 +3127,12 @@ export class LuaTransformer {
const callPath = this.transformExpression(node.expression);
const signatureDeclaration = signature && signature.getDeclaration();
if (signatureDeclaration
&& !ts.isPropertyAccessExpression(node.expression)
&& tsHelper.getDeclarationContextType(signatureDeclaration, this.checker) === ContextType.NonVoid
&& !ts.isElementAccessExpression(node.expression))
&& tsHelper.getDeclarationContextType(signatureDeclaration, this.checker) === ContextType.Void)
{
parameters = this.transformArguments(node.arguments, signature);
} else {
const context = this.isStrict ? ts.createNull() : ts.createIdentifier("_G");
parameters = this.transformArguments(node.arguments, signature, context);
} else {
parameters = this.transformArguments(node.arguments, signature);
}

const expressionType = this.checker.getTypeAtLocation(node.expression);
Expand Down Expand Up @@ -3885,7 +3879,7 @@ export class LuaTransformer {
const expression = node.expression as ts.PropertyAccessExpression;
const callerType = this.checker.getTypeAtLocation(expression.expression);
if (tsHelper.getFunctionContextType(callerType, this.checker) === ContextType.Void) {
throw TSTLErrors.UnsupportedMethodConversion(node);
throw TSTLErrors.UnsupportedSelfFunctionConversion(node);
}
const params = this.transformArguments(node.arguments);
const caller = this.transformExpression(expression.expression);
Expand Down Expand Up @@ -4326,9 +4320,9 @@ export class LuaTransformer {
throw TSTLErrors.UnsupportedOverloadAssignment(node, toName);
} else if (fromContext !== toContext && fromContext !== ContextType.None && toContext !== ContextType.None) {
if (toContext === ContextType.Void) {
throw TSTLErrors.UnsupportedFunctionConversion(node, toName);
throw TSTLErrors.UnsupportedNoSelfFunctionConversion(node, toName);
} else {
throw TSTLErrors.UnsupportedMethodConversion(node, toName);
throw TSTLErrors.UnsupportedSelfFunctionConversion(node, toName);
}
}

Expand Down
72 changes: 59 additions & 13 deletions src/TSHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,21 @@ export class TSHelper {
return decMap;
}

public static getCustomFileDirectives(file: ts.SourceFile): Map<DecoratorKind, Decorator> {
const decMap = new Map<DecoratorKind, Decorator>();
if (file.statements.length > 0) {
const tags = ts.getJSDocTags(file.statements[0]);
for (const tag of tags) {
const tagName = tag.tagName.escapedText as string;
if (Decorator.isValid(tagName)) {
const dec = new Decorator(tagName, tag.comment ? tag.comment.split(" ") : []);
decMap.set(dec.kind, dec);
}
}
}
return decMap;
}

// Search up until finding a node satisfying the callback
public static findFirstNodeAbove<T extends ts.Node>(node: ts.Node, callback: (n: ts.Node) => n is T): T {
let current = node;
Expand Down Expand Up @@ -559,6 +574,24 @@ export class TSHelper {
return signatureDeclarations;
}

public static hasNoSelfAncestor(declaration: ts.Declaration, checker: ts.TypeChecker): boolean {
const scopeDeclaration = TSHelper.findFirstNodeAbove(
declaration,
(n): n is ts.SourceFile | ts.ModuleDeclaration => ts.isSourceFile(n) || ts.isModuleDeclaration(n)
);
if (!scopeDeclaration) {
return false;
}
if (ts.isSourceFile(scopeDeclaration)) {
return TSHelper.getCustomFileDirectives(scopeDeclaration).has(DecoratorKind.NoSelfInFile);
}
const scopeType = checker.getTypeAtLocation(scopeDeclaration);
if (scopeType && TSHelper.getCustomDecorators(scopeType, checker).has(DecoratorKind.NoSelf)) {
return true;
}
return TSHelper.hasNoSelfAncestor(scopeDeclaration, checker);
}

public static getDeclarationContextType(
signatureDeclaration: ts.SignatureDeclaration,
checker: ts.TypeChecker
Expand All @@ -571,22 +604,35 @@ export class TSHelper {
? ContextType.Void
: ContextType.NonVoid;
}
if (ts.isMethodDeclaration(signatureDeclaration) || ts.isMethodSignature(signatureDeclaration)) {
// Method
return ContextType.NonVoid;
}
if (ts.isPropertySignature(signatureDeclaration.parent)
|| ts.isPropertyDeclaration(signatureDeclaration.parent)
|| ts.isPropertyAssignment(signatureDeclaration.parent)) {
// Lambda property

if (ts.isMethodSignature(signatureDeclaration)
|| ts.isMethodDeclaration(signatureDeclaration)
|| ts.isConstructSignatureDeclaration(signatureDeclaration)
|| ts.isConstructorDeclaration(signatureDeclaration)
|| (signatureDeclaration.parent && ts.isPropertyDeclaration(signatureDeclaration.parent))
|| (signatureDeclaration.parent && ts.isPropertySignature(signatureDeclaration.parent)))
{
// Class/interface methods only respect @noSelf on their parent
const scopeDeclaration = TSHelper.findFirstNodeAbove(
signatureDeclaration,
(n): n is ts.ClassLikeDeclaration | ts.InterfaceDeclaration =>
ts.isClassDeclaration(n)
|| ts.isClassExpression(n)
|| ts.isInterfaceDeclaration(n)
);
const scopeType = checker.getTypeAtLocation(scopeDeclaration);
if (scopeType && TSHelper.getCustomDecorators(scopeType, checker).has(DecoratorKind.NoSelf)) {
return ContextType.Void;
}
return ContextType.NonVoid;
}
if (ts.isBinaryExpression(signatureDeclaration.parent)) {
// Function expression: check type being assigned to
return TSHelper.getFunctionContextType(
checker.getTypeAtLocation(signatureDeclaration.parent.left), checker);

// Walk up to find @noSelf or @noSelfOnFile
if (TSHelper.hasNoSelfAncestor(signatureDeclaration, checker)) {
return ContextType.Void;
}
return ContextType.Void;

return ContextType.NonVoid;
}

public static reduceContextTypes(contexts: ContextType[]): ContextType {
Expand Down
30 changes: 14 additions & 16 deletions src/TSTLErrors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,46 +78,44 @@ export class TSTLErrors {
public static UnsupportedForTarget = (functionality: string, version: string, node: ts.Node) =>
new TranspileError(`${functionality} is/are not supported for target Lua ${version}.`, node);

public static UnsupportedFunctionConversion = (node: ts.Node, name?: string) => {
public static UnsupportedNoSelfFunctionConversion = (node: ts.Node, name?: string) => {
if (name) {
return new TranspileError(
`Unsupported conversion from method to function "${name}". ` +
`To fix, wrap the method in an arrow function.`,
`Unable to convert function with a 'this' parameter to function "${name}" with no 'this'. ` +
`To fix, wrap in an arrow function, or declare with 'this: void'.`,
node);
} else {
return new TranspileError(
`Unsupported conversion from method to function. ` +
`To fix, wrap the method in an arrow function.`,
`Unable to convert function with a 'this' parameter to function with no 'this'. ` +
`To fix, wrap in an arrow function, or declare with 'this: void'.`,
node);
}
};

public static UnsupportedMethodConversion = (node: ts.Node, name?: string) => {
public static UnsupportedSelfFunctionConversion = (node: ts.Node, name?: string) => {
if (name) {
return new TranspileError(
`Unsupported conversion from function to method "${name}". ` +
`To fix, wrap the function in an arrow function or declare the function with` +
` an explicit 'this' parameter.`,
`Unable to convert function with no 'this' parameter to function "${name}" with 'this'. ` +
`To fix, wrap in an arrow function or declare with 'this: any'.`,
node);
} else {
return new TranspileError(
`Unsupported conversion from function to method. ` +
`To fix, wrap the function in an arrow function or declare the function with` +
` an explicit 'this' parameter.`,
`Unable to convert function with no 'this' parameter to function with 'this'. ` +
`To fix, wrap in an arrow function or declare with 'this: any'.`,
node);
}
};

public static UnsupportedOverloadAssignment = (node: ts.Node, name?: string) => {
if (name) {
return new TranspileError(
`Unsupported assignment of mixed function/method overload to "${name}". ` +
`Overloads should either be all functions or all methods, but not both.`,
`Unsupported assignment of function with different overloaded types for 'this' to "${name}". ` +
`Overloads should all have the same type for 'this'.`,
node);
} else {
return new TranspileError(
`Unsupported assignment of mixed function/method overload. ` +
`Overloads should either be all functions or all methods, but not both.`,
`Unsupported assignment of function with different overloaded types for 'this'. ` +
`Overloads should all have the same type for 'this'.`,
node);
}
};
Expand Down
6 changes: 3 additions & 3 deletions src/lualib/ArrayConcat.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
declare function pcall(func: () => any): any;
declare function type(val: any): string;
declare function pcall(this: void, func: () => any): any;
declare function type(this: void, val: any): string;

function __TS__ArrayConcat(arr1: any[], ...args: any[]): any[] {
function __TS__ArrayConcat(this: void, arr1: any[], ...args: any[]): any[] {
const out: any[] = [];
for (const val of arr1) {
out[out.length] = val;
Expand Down
4 changes: 3 additions & 1 deletion src/lualib/ArrayEvery.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
function __TS__ArrayEvery<T>(arr: T[], callbackfn: (value: T, index?: number, array?: any[]) => boolean): boolean {
function __TS__ArrayEvery<T>(this: void, arr: T[], callbackfn: (value: T, index?: number, array?: any[]) => boolean)
: boolean
{
for (let i = 0; i < arr.length; i++) {
if (!callbackfn(arr[i], i, arr)) {
return false;
Expand Down
4 changes: 3 additions & 1 deletion src/lualib/ArrayFilter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
function __TS__ArrayFilter<T>(arr: T[], callbackfn: (value: T, index?: number, array?: any[]) => boolean): T[] {
function __TS__ArrayFilter<T>(this: void, arr: T[], callbackfn: (value: T, index?: number, array?: any[]) => boolean)
: T[]
{
const result: T[] = [];
for (let i = 0; i < arr.length; i++) {
if (callbackfn(arr[i], i, arr)) {
Expand Down
3 changes: 2 additions & 1 deletion src/lualib/ArrayFindIndex.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
function __TS__ArrayFindIndex<T>(
this: void,
arr: T[],
callbackFn: (this: void, element: T, index?: number, array?: T[]) => boolean
callbackFn: (element: T, index?: number, array?: T[]) => boolean
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this callback inherits the void context from the function definition? Maybe it's me, but this doesn't seem very intuitive to me.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's not inherited. I set the callback to take as self, so regularly defined functions can be passed in here. Otherwise any non-expression functions used as callbacks would need the this: void which would get annoying.

): number {
for (let i = 0, len = arr.length; i < len; i++) {
if (callbackFn(arr[i], i, arr)) {
Expand Down
4 changes: 3 additions & 1 deletion src/lualib/ArrayForEach.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
function __TS__ArrayForEach<T>(arr: T[], callbackFn: (value: T, index?: number, array?: any[]) => any): void {
function __TS__ArrayForEach<T>(this: void, arr: T[], callbackFn: (value: T, index?: number, array?: any[]) => any)
: void
{
for (let i = 0; i < arr.length; i++) {
callbackFn(arr[i], i, arr);
}
Expand Down
2 changes: 1 addition & 1 deletion src/lualib/ArrayIndexOf.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function __TS__ArrayIndexOf<T>(arr: T[], searchElement: T, fromIndex?: number): number {
function __TS__ArrayIndexOf<T>(this: void, arr: T[], searchElement: T, fromIndex?: number): number {
const len = arr.length;
if (len === 0) {
return -1;
Expand Down
2 changes: 1 addition & 1 deletion src/lualib/ArrayMap.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function __TS__ArrayMap<T, U>(arr: T[], callbackfn: (value: T, index?: number, array?: T[]) => U): U[] {
function __TS__ArrayMap<T, U>(this: void, arr: T[], callbackfn: (value: T, index?: number, array?: T[]) => U): U[] {
const newArray: U[] = [];
for (let i = 0; i < arr.length; i++) {
newArray[i] = callbackfn(arr[i], i, arr);
Expand Down
2 changes: 1 addition & 1 deletion src/lualib/ArrayPush.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function __TS__ArrayPush<T>(arr: T[], ...items: T[]): number {
function __TS__ArrayPush<T>(this: void, arr: T[], ...items: T[]): number {
for (const item of items) {
arr[arr.length] = item;
}
Expand Down
2 changes: 1 addition & 1 deletion src/lualib/ArrayReverse.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function __TS__ArrayReverse(arr: any[]): any[] {
function __TS__ArrayReverse(this: void, arr: any[]): any[] {
let i = 0;
let j = arr.length - 1;
while (i < j) {
Expand Down
4 changes: 2 additions & 2 deletions src/lualib/ArrayShift.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
declare namespace table {
function remove<T>(arr: T[], idx: number): T;
function remove<T>(this: void, arr: T[], idx: number): T;
}
function __TS__ArrayShift<T>(arr: T[]): T {
function __TS__ArrayShift<T>(this: void, arr: T[]): T {
return table.remove(arr, 1);
}
2 changes: 1 addition & 1 deletion src/lualib/ArraySlice.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// https://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf 22.1.3.23
function __TS__ArraySlice<T>(list: T[], first: number, last: number): T[] {
function __TS__ArraySlice<T>(this: void, list: T[], first: number, last: number): T[] {
const len = list.length;

let k: number;
Expand Down
4 changes: 3 additions & 1 deletion src/lualib/ArraySome.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
function __TS__ArraySome<T>(arr: T[], callbackfn: (value: T, index?: number, array?: any[]) => boolean): boolean {
function __TS__ArraySome<T>(this: void, arr: T[], callbackfn: (value: T, index?: number, array?: any[]) => boolean)
: boolean
{
for (let i = 0; i < arr.length; i++) {
if (callbackfn(arr[i], i, arr)) {
return true;
Expand Down
4 changes: 2 additions & 2 deletions src/lualib/ArraySort.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
declare namespace table {
function sort<T>(arr: T[], compareFn?: (a: T, b: T) => boolean): void;
function sort<T>(this: void, arr: T[], compareFn?: (this: void, a: T, b: T) => boolean): void;
}
function __TS__ArraySort<T>(arr: T[], compareFn?: (a: T, b: T) => number): T[] {
function __TS__ArraySort<T>(this: void, arr: T[], compareFn?: (a: T, b: T) => number): T[] {
if (compareFn !== undefined) {
table.sort(arr, (a, b) => compareFn(a, b) < 0);
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/lualib/ArraySplice.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function __TS__ArraySplice<T>(list: T[], start: number, deleteCount: number, ...items: T[]): T[] {
function __TS__ArraySplice<T>(this: void, list: T[], start: number, deleteCount: number, ...items: T[]): T[] {

const len = list.length;

Expand Down
4 changes: 2 additions & 2 deletions src/lualib/ArrayUnshift.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
declare namespace table {
function insert<T>(arr: T[], idx: number, val: T): void;
function insert<T>(this: void, arr: T[], idx: number, val: T): void;
}
function __TS__ArrayUnshift<T>(arr: T[], ...items: T[]): number {
function __TS__ArrayUnshift<T>(this: void, arr: T[], ...items: T[]): number {
for (let i = items.length - 1; i >= 0; --i) {
table.insert(arr, 1, items[i]);
}
Expand Down
Loading
X Tutup