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/LuaLib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export enum LuaLibFeature {
ArrayFlat = "ArrayFlat",
ArrayFlatMap = "ArrayFlatMap",
ArraySetLength = "ArraySetLength",
Class = "Class",
ClassIndex = "ClassIndex",
ClassNewIndex = "ClassNewIndex",
Decorate = "Decorate",
Expand All @@ -36,6 +37,7 @@ export enum LuaLibFeature {
InstanceOfObject = "InstanceOfObject",
Iterator = "Iterator",
Map = "Map",
New = "New",
NewIndex = "NewIndex",
Number = "Number",
NumberIsFinite = "NumberIsFinite",
Expand Down Expand Up @@ -67,7 +69,7 @@ export enum LuaLibFeature {
const luaLibDependencies: { [lib in LuaLibFeature]?: LuaLibFeature[] } = {
ArrayFlat: [LuaLibFeature.ArrayConcat],
ArrayFlatMap: [LuaLibFeature.ArrayConcat],
Error: [LuaLibFeature.FunctionCall],
Error: [LuaLibFeature.New, LuaLibFeature.FunctionCall],
InstanceOf: [LuaLibFeature.Symbol],
Iterator: [LuaLibFeature.Symbol],
ObjectFromEntries: [LuaLibFeature.Iterator, LuaLibFeature.Symbol],
Expand Down
8 changes: 8 additions & 0 deletions src/lualib/Class.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
function __TS__Class(): LuaClass {
const c = {} as LuaClass;
c.__index = c;
c.prototype = {};
c.prototype.__index = c.prototype;
c.prototype.constructor = c;
return c;
}
5 changes: 5 additions & 0 deletions src/lualib/New.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function __TS__New(this: void, target: LuaClass, ...args: Vararg<any>): any {
const instance: any = setmetatable({}, target.prototype);
instance.____constructor(...args);
return instance;
}
2 changes: 2 additions & 0 deletions src/lualib/declarations/tstl.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ interface LuaClass {
____super?: LuaClass;
____getters?: { [key: string]: (self: LuaClass) => any };
____setters?: { [key: string]: (self: LuaClass, val: any) => void };
__index?: any;
}

interface LuaObject {
constructor: LuaClass;
____getters?: { [key: string]: (self: LuaObject) => any };
____setters?: { [key: string]: (self: LuaObject, val: any) => void };
__index?: any;
}
3 changes: 0 additions & 3 deletions src/transformation/utils/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ export const InvalidInstanceOfExtension = (node: ts.Node) =>

export const InvalidJsonFileContent = (node: ts.Node) => new TranspileError("Invalid JSON file content", node);

export const ForbiddenStaticClassPropertyName = (node: ts.Node, name: string) =>
new TranspileError(`Cannot use "${name}" as a static class property or method name.`, node);

export const MissingClassName = (node: ts.Node) => new TranspileError(`Class declarations must have a name.`, node);

export const MissingForOfVariables = (node: ts.Node) =>
Expand Down
2 changes: 1 addition & 1 deletion src/transformation/utils/lualib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export function importLuaLibFeature(context: TransformationContext, feature: Lua
export function transformLuaLibFunction(
context: TransformationContext,
feature: LuaLibFeature,
tsParent?: ts.Expression,
tsParent?: ts.Node,
...params: lua.Expression[]
): lua.CallExpression {
importLuaLibFeature(context, feature);
Expand Down
24 changes: 6 additions & 18 deletions src/transformation/visitors/class/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { FunctionVisitor, TransformationContext } from "../../context";
import { AnnotationKind, getTypeAnnotations } from "../../utils/annotations";
import {
ForbiddenLuaTableNonDeclaration,
ForbiddenStaticClassPropertyName,
InvalidExportsExtension,
InvalidExtendsExtension,
InvalidExtendsLuaTable,
Expand Down Expand Up @@ -80,16 +79,13 @@ export function transformClassDeclaration(
} else if (classDeclaration.name !== undefined) {
className = transformIdentifier(context, classDeclaration.name);
classNameText = classDeclaration.name.text;
} else if (hasDefaultExportModifier(classDeclaration)) {
const left = createExportedIdentifier(context, createDefaultExportIdentifier(classDeclaration));
const right = transformClassAsExpression(classDeclaration, context, true);

return lua.createAssignmentStatement(left, right, classDeclaration);
} else {
const isDefaultExport = hasDefaultExportModifier(classDeclaration);
if (isDefaultExport) {
const left = createExportedIdentifier(context, createDefaultExportIdentifier(classDeclaration));
const right = transformClassAsExpression(classDeclaration, context, true);

return lua.createAssignmentStatement(left, right, classDeclaration);
} else {
throw MissingClassName(classDeclaration);
}
throw MissingClassName(classDeclaration);
}

const annotations = getTypeAnnotations(context, context.checker.getTypeAtLocation(classDeclaration));
Expand Down Expand Up @@ -136,14 +132,6 @@ export function transformClassDeclaration(
throw ForbiddenLuaTableNonDeclaration(classDeclaration);
}

for (const member of classDeclaration.members) {
if (member.name && (ts.isStringLiteral(member.name) || ts.isIdentifier(member.name))) {
if (isStaticNode(member) && member.name.text === "new") {
throw ForbiddenStaticClassPropertyName(member, member.name.text);
}
}
}

// Get all properties with value
const properties = classDeclaration.members.filter(ts.isPropertyDeclaration).filter(member => member.initializer);

Expand Down
4 changes: 2 additions & 2 deletions src/transformation/visitors/class/new.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as lua from "../../../LuaAST";
import { FunctionVisitor, TransformationContext } from "../../context";
import { AnnotationKind, getTypeAnnotations } from "../../utils/annotations";
import { InvalidAnnotationArgumentNumber, InvalidNewExpressionOnExtension } from "../../utils/errors";
import { importLuaLibFeature, LuaLibFeature } from "../../utils/lualib";
import { importLuaLibFeature, LuaLibFeature, transformLuaLibFunction } from "../../utils/lualib";
import { transformArguments } from "../call";
import { transformLuaTableNewExpression } from "../lua-table";

Expand Down Expand Up @@ -82,5 +82,5 @@ export const transformNewExpression: FunctionVisitor<ts.NewExpression> = (node,
);
}

return lua.createCallExpression(lua.createTableIndexExpression(name, lua.createStringLiteral("new")), params, node);
return transformLuaLibFunction(context, LuaLibFeature.New, node, name, ...params);
};
107 changes: 15 additions & 92 deletions src/transformation/visitors/class/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@ import {
getIdentifierExportScope,
hasDefaultExportModifier,
} from "../../utils/export";
import {
createExportsIdentifier,
createLocalOrExportedOrGlobalDeclaration,
createSelfIdentifier,
} from "../../utils/lua-ast";
import { createExportsIdentifier, createLocalOrExportedOrGlobalDeclaration } from "../../utils/lua-ast";
import { importLuaLibFeature, LuaLibFeature, transformLuaLibFunction } from "../../utils/lualib";
import { hasMemberInClassOrAncestor } from "./members/accessors";
import { getExtendedTypeNode, isStaticNode } from "./utils";
Expand All @@ -27,20 +23,19 @@ export function createClassSetup(
): lua.Statement[] {
const result: lua.Statement[] = [];

// [____exports.]className = {}
const classTable: lua.Expression = lua.createTableExpression();

const isDefaultExport = hasDefaultExportModifier(statement);
// __TS__Class()
const classInitializer = transformLuaLibFunction(context, LuaLibFeature.Class, statement);

const defaultExportLeftHandSide = isDefaultExport
const defaultExportLeftHandSide = hasDefaultExportModifier(statement)
? lua.createTableIndexExpression(createExportsIdentifier(), createDefaultExportStringLiteral(statement))
: undefined;

const classVar = defaultExportLeftHandSide
? [lua.createAssignmentStatement(defaultExportLeftHandSide, classTable, statement)]
: createLocalOrExportedOrGlobalDeclaration(context, className, classTable, statement);

result.push(...classVar);
// [____exports.]className = __TS__Class()
if (defaultExportLeftHandSide) {
result.push(lua.createAssignmentStatement(defaultExportLeftHandSide, classInitializer, statement));
} else {
result.push(...createLocalOrExportedOrGlobalDeclaration(context, className, classInitializer, statement));
}

if (defaultExportLeftHandSide) {
// local localClassName = ____exports.default
Expand Down Expand Up @@ -79,14 +74,6 @@ export function createClassSetup(
importLuaLibFeature(context, LuaLibFeature.ClassIndex);
}

// localClassName.__index = localClassName
const classIndex = lua.createTableIndexExpression(
lua.cloneIdentifier(localClassName),
lua.createStringLiteral("__index")
);
const assignClassIndex = lua.createAssignmentStatement(classIndex, lua.cloneIdentifier(localClassName), statement);
result.push(assignClassIndex);

// localClassName.____setters = {}
if (statement.members.some(m => ts.isSetAccessor(m) && isStaticNode(m))) {
const classSetters = lua.createTableIndexExpression(
Expand All @@ -99,12 +86,9 @@ export function createClassSetup(
importLuaLibFeature(context, LuaLibFeature.ClassNewIndex);
}

// localClassName.prototype = {}
// localClassName.prototype
const createClassPrototype = () =>
lua.createTableIndexExpression(lua.cloneIdentifier(localClassName), lua.createStringLiteral("prototype"));
const classPrototypeTable = lua.createTableExpression();
const assignClassPrototype = lua.createAssignmentStatement(createClassPrototype(), classPrototypeTable, statement);
result.push(assignClassPrototype);

// localClassName.prototype.____getters = {}
if (statement.members.some(m => ts.isGetAccessor(m) && !isStaticNode(m))) {
Expand All @@ -120,23 +104,15 @@ export function createClassSetup(
result.push(assignClassPrototypeGetters);
}

const classPrototypeIndex = lua.createTableIndexExpression(
createClassPrototype(),
lua.createStringLiteral("__index")
);
if (hasMemberInClassOrAncestor(context, statement, m => ts.isGetAccessor(m) && !isStaticNode(m))) {
// localClassName.prototype.__index = __TS__Index(localClassName.prototype)
const assignClassPrototypeIndex = lua.createAssignmentStatement(
classPrototypeIndex,
transformLuaLibFunction(context, LuaLibFeature.Index, undefined, createClassPrototype()),
statement
const classPrototypeIndex = lua.createTableIndexExpression(
createClassPrototype(),
lua.createStringLiteral("__index")
);
result.push(assignClassPrototypeIndex);
} else {
// localClassName.prototype.__index = localClassName.prototype
const assignClassPrototypeIndex = lua.createAssignmentStatement(
classPrototypeIndex,
createClassPrototype(),
transformLuaLibFunction(context, LuaLibFeature.Index, undefined, createClassPrototype()),
statement
);
result.push(assignClassPrototypeIndex);
Expand Down Expand Up @@ -170,18 +146,6 @@ export function createClassSetup(
result.push(assignClassPrototypeIndex);
}

// localClassName.prototype.constructor = localClassName
const classPrototypeConstructor = lua.createTableIndexExpression(
createClassPrototype(),
lua.createStringLiteral("constructor")
);
const assignClassPrototypeConstructor = lua.createAssignmentStatement(
classPrototypeConstructor,
lua.cloneIdentifier(localClassName),
statement
);
result.push(assignClassPrototypeConstructor);

const hasStaticGetters = hasMemberInClassOrAncestor(
context,
statement,
Expand Down Expand Up @@ -303,46 +267,5 @@ export function createClassSetup(
result.push(setClassMetatable);
}

const newFuncStatements: lua.Statement[] = [];

// local self = setmetatable({}, localClassName.prototype)
const assignSelf = lua.createVariableDeclarationStatement(
createSelfIdentifier(),
lua.createCallExpression(lua.createIdentifier("setmetatable"), [
lua.createTableExpression(),
createClassPrototype(),
]),
statement
);
newFuncStatements.push(assignSelf);

// self:____constructor(...)
const callConstructor = lua.createExpressionStatement(
lua.createMethodCallExpression(createSelfIdentifier(), lua.createIdentifier("____constructor"), [
lua.createDotsLiteral(),
]),
statement
);
newFuncStatements.push(callConstructor);

// return self
const returnSelf = lua.createReturnStatement([createSelfIdentifier()], statement);
newFuncStatements.push(returnSelf);

// function localClassName.new(construct, ...) ... end
// or function export.localClassName.new(construct, ...) ... end
const newFunc = lua.createAssignmentStatement(
lua.createTableIndexExpression(lua.cloneIdentifier(localClassName), lua.createStringLiteral("new")),
lua.createFunctionExpression(
lua.createBlock(newFuncStatements),
undefined,
lua.createDotsLiteral(),
undefined,
lua.FunctionExpressionFlags.Declaration
),
statement
);
result.push(newFunc);

return result;
}
52 changes: 10 additions & 42 deletions test/translation/__snapshots__/transformation.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,9 @@ end"
`;

exports[`Transformation (classPureAbstract) 1`] = `
"ClassB = {}
"require(\\"lualib_bundle\\");
ClassB = __TS__Class()
ClassB.name = \\"ClassB\\"
ClassB.__index = ClassB
ClassB.prototype = {}
ClassB.prototype.__index = ClassB.prototype
ClassB.prototype.constructor = ClassB
function ClassB.new(...)
local self = setmetatable({}, ClassB.prototype)
self:____constructor(...)
return self
end
function ClassB.prototype.____constructor(self)
end"
`;
Expand Down Expand Up @@ -82,17 +74,9 @@ return ____exports"
`;

exports[`Transformation (methodRestArguments) 1`] = `
"MyClass = {}
"require(\\"lualib_bundle\\");
MyClass = __TS__Class()
MyClass.name = \\"MyClass\\"
MyClass.__index = MyClass
MyClass.prototype = {}
MyClass.prototype.__index = MyClass.prototype
MyClass.prototype.constructor = MyClass
function MyClass.new(...)
local self = setmetatable({}, MyClass.prototype)
self:____constructor(...)
return self
end
function MyClass.prototype.____constructor(self)
end
function MyClass.prototype.varargsFunction(self, a, ...)
Expand All @@ -106,38 +90,22 @@ return ____exports"
`;

exports[`Transformation (modulesClassExport) 1`] = `
"local ____exports = {}
____exports.TestClass = {}
"require(\\"lualib_bundle\\");
local ____exports = {}
____exports.TestClass = __TS__Class()
local TestClass = ____exports.TestClass
TestClass.name = \\"TestClass\\"
TestClass.__index = TestClass
TestClass.prototype = {}
TestClass.prototype.__index = TestClass.prototype
TestClass.prototype.constructor = TestClass
function TestClass.new(...)
local self = setmetatable({}, TestClass.prototype)
self:____constructor(...)
return self
end
function TestClass.prototype.____constructor(self)
end
return ____exports"
`;

exports[`Transformation (modulesClassWithMemberExport) 1`] = `
"local ____exports = {}
____exports.TestClass = {}
"require(\\"lualib_bundle\\");
local ____exports = {}
____exports.TestClass = __TS__Class()
local TestClass = ____exports.TestClass
TestClass.name = \\"TestClass\\"
TestClass.__index = TestClass
TestClass.prototype = {}
TestClass.prototype.__index = TestClass.prototype
TestClass.prototype.constructor = TestClass
function TestClass.new(...)
local self = setmetatable({}, TestClass.prototype)
self:____constructor(...)
return self
end
function TestClass.prototype.____constructor(self)
end
function TestClass.prototype.memberFunc(self)
Expand Down
Loading