Skip to content

Commit 1c6d52a

Browse files
authored
Inlining of map and set and detection when they are used (#159)
* Inlining of map and set and detection when they are used
1 parent bc073bb commit 1c6d52a

File tree

7 files changed

+155
-42
lines changed

7 files changed

+155
-42
lines changed

src/Compiler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ export function compileFilesWithOptions(fileNames: string[], options: CompilerOp
9595
});
9696

9797
// Copy lualib to target dir
98-
if (options.luaLibImport === LuaLibImportKind.Require) {
98+
if (options.luaLibImport === LuaLibImportKind.Require || options.luaLibImport === LuaLibImportKind.Always) {
9999
fs.copyFileSync(
100100
path.resolve(__dirname, "../dist/lualib/lualib_bundle.lua"),
101101
path.join(options.outDir, "lualib_bundle.lua")

src/Transpiler.ts

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,10 @@ export enum LuaLibFeature {
4444
}
4545

4646
export enum LuaLibImportKind {
47+
None = "none",
48+
Always = "always",
4749
Inline = "inline",
4850
Require = "require",
49-
None = "none",
5051
}
5152

5253
interface ExportInfo {
@@ -140,6 +141,11 @@ export abstract class LuaTranspiler {
140141
}
141142

142143
public importLuaLibFeature(feature: LuaLibFeature): void {
144+
// Add additional lib requirements
145+
if (feature === LuaLibFeature.Map || feature === LuaLibFeature.Set) {
146+
this.luaLibFeatureSet.add(LuaLibFeature.InstanceOf);
147+
}
148+
143149
// TODO inline imported features in output i option set
144150
this.luaLibFeatureSet.add(feature);
145151
}
@@ -177,18 +183,17 @@ export abstract class LuaTranspiler {
177183
"-- https://github.com/Perryvw/TypescriptToLua\n";
178184
}
179185
let result = header;
180-
if (this.options.luaLibImport === LuaLibImportKind.Require) {
186+
187+
// Transpile content first to gather some info on dependencies
188+
let fileStatements = "";
189+
this.exportStack.push([]);
190+
this.sourceFile.statements.forEach(s => fileStatements += this.transpileNode(s));
191+
192+
if ((this.options.luaLibImport === LuaLibImportKind.Require && this.luaLibFeatureSet.size > 0)
193+
|| this.options.luaLibImport === LuaLibImportKind.Always) {
181194
// require helper functions
182195
result += `require("lualib_bundle")\n`;
183196
}
184-
if (this.isModule) {
185-
// Shadow exports if it already exists
186-
result += "local exports = exports or {}\n";
187-
}
188-
189-
// Transpile content statements
190-
this.exportStack.push([]);
191-
this.sourceFile.statements.forEach(s => result += this.transpileNode(s));
192197

193198
// Inline lualib features
194199
if (this.options.luaLibImport === LuaLibImportKind.Inline) {
@@ -199,6 +204,14 @@ export abstract class LuaTranspiler {
199204
}
200205
}
201206

207+
if (this.isModule) {
208+
// Shadow exports if it already exists
209+
result += "local exports = exports or {}\n";
210+
}
211+
212+
// Add file systems after imports since order matters in Lua
213+
result += fileStatements;
214+
202215
// Exports
203216
result += this.makeExports();
204217

@@ -935,6 +948,8 @@ export abstract class LuaTranspiler {
935948
const name = this.transpileExpression(node.expression);
936949
const params = node.arguments ? this.transpileArguments(node.arguments, ts.createTrue()) : "true";
937950

951+
this.checkForLuaLibType(this.checker.getTypeAtLocation(node));
952+
938953
return `${name}.new(${params})`;
939954
}
940955

@@ -1146,6 +1161,8 @@ export abstract class LuaTranspiler {
11461161
}
11471162
}
11481163

1164+
this.checkForLuaLibType(type);
1165+
11491166
// Do not output path for member only enums
11501167
if (tsHelper.isCompileMembersOnlyEnum(type, this.checker)) {
11511168
return property;
@@ -1681,4 +1698,17 @@ export abstract class LuaTranspiler {
16811698

16821699
return result;
16831700
}
1701+
1702+
public checkForLuaLibType(type: ts.Type): void {
1703+
if (type.symbol) {
1704+
switch (this.checker.getFullyQualifiedName(type.symbol)) {
1705+
case "Map":
1706+
this.importLuaLibFeature(LuaLibFeature.Map);
1707+
return;
1708+
case "Set":
1709+
this.importLuaLibFeature(LuaLibFeature.Set);
1710+
return;
1711+
}
1712+
}
1713+
}
16841714
}

test/runner.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { TestRunner, TestSet } from "alsatian";
22
import { TapBark } from "tap-bark";
33

4+
import * as fs from "fs";
5+
import * as path from "path";
6+
47
// create test set
58
const testSet = TestSet.create();
69

@@ -10,6 +13,12 @@ testSet.addTestsFromFiles("./test/**/*.spec.ts");
1013
// create a test runner
1114
const testRunner = new TestRunner();
1215

16+
// Copy lualib to project root
17+
fs.copyFileSync(
18+
path.resolve(__dirname, "../dist/lualib/lualib_bundle.lua"),
19+
"lualib_bundle.lua"
20+
);
21+
1322
// setup the output
1423
testRunner.outputStream
1524
// this will use alsatian's default output if you remove this
@@ -19,8 +28,14 @@ testRunner.outputStream
1928
.pipe(process.stdout);
2029

2130
// run the test set
22-
testRunner.run(testSet);
31+
testRunner.run(testSet)
2332
// this will be called after all tests have been run
24-
// .then((results) => done())
33+
.then(result => {
34+
// Remove lualib bundle again
35+
fs.unlinkSync("lualib_bundle.lua");
36+
})
2537
// this will be called if there was a problem
26-
// .catch((error) => doSomethingWith(error));
38+
.catch(error => {
39+
// Remove lualib bundle again
40+
fs.unlinkSync("lualib_bundle.lua");
41+
});

test/src/util.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ import { Expect } from "alsatian";
55

66
import { CompilerOptions } from "../../src/CommandLineParser";
77
import { createTranspiler } from "../../src/Compiler";
8-
import { LuaTarget, LuaTranspiler, TranspileError } from "../../src/Transpiler";
8+
import { LuaLibImportKind, LuaTarget, LuaTranspiler, TranspileError } from "../../src/Transpiler";
99

1010
import {lauxlib, lua, lualib, to_jsstring, to_luastring } from "fengari";
1111

1212
const fs = require("fs");
1313

1414
const libSource = fs.readFileSync(path.join(path.dirname(require.resolve("typescript")), "lib.es6.d.ts")).toString();
1515

16-
export function transpileString(str: string, options: CompilerOptions = { luaLibImport: "none", luaTarget: LuaTarget.Lua53 }): string {
16+
export function transpileString(str: string, options: CompilerOptions = { luaLibImport: LuaLibImportKind.Require, luaTarget: LuaTarget.Lua53 }): string {
1717
const compilerHost = {
1818
directoryExists: () => true,
1919
fileExists: (fileName): boolean => true,
@@ -114,8 +114,6 @@ export function transpileAndExecute(ts: string): any {
114114
return executeLua(transpileString(ts));
115115
}
116116

117-
const tslualib = fs.readFileSync("dist/lualib/lualib_bundle.lua") + "\n";
118-
119117
const jsonlib = fs.readFileSync("test/src/json.lua") + "\n";
120118

121-
export const minimalTestLib = tslualib + jsonlib;
119+
export const minimalTestLib = jsonlib;

test/unit/lualib/inlining.spec.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { Expect, Test, TestCase } from "alsatian";
2+
import * as util from "../../src/util";
3+
4+
import { LuaLibImportKind, LuaTarget } from "../../../src/Transpiler";
5+
6+
export class InliningTests {
7+
@Test("map constructor")
8+
public mapConstructor(): void {
9+
const lua = util.transpileString(`let mymap = new Map(); return mymap.size;`,
10+
{ luaLibImport: LuaLibImportKind.Inline, luaTarget: LuaTarget.Lua53 });
11+
const result = util.executeLua(lua);
12+
13+
Expect(result).toBe(0);
14+
}
15+
16+
@Test("map foreach keys")
17+
public mapForEachKeys(): void {
18+
const lua = util.transpileString(
19+
`let mymap = new Map([[5, 2],[6, 3],[7, 4]]);
20+
let count = 0;
21+
mymap.forEach((value, key) => { count += key; });
22+
return count;`,
23+
{ luaLibImport: LuaLibImportKind.Inline, luaTarget: LuaTarget.Lua53 });
24+
25+
const result = util.executeLua(lua);
26+
Expect(result).toBe(18);
27+
}
28+
29+
@Test("set constructor")
30+
public setConstructor(): void {
31+
const lua = util.transpileString(`class abc {} let def = new abc(); let myset = new Set(); return myset.size;`,
32+
{ luaLibImport: LuaLibImportKind.Inline, luaTarget: LuaTarget.Lua53 });
33+
const result = util.executeLua(lua);
34+
35+
Expect(result).toBe(0);
36+
}
37+
38+
@Test("set foreach keys")
39+
public setForEachKeys(): void {
40+
const lua = util.transpileString(
41+
`let myset = new Set([2, 3, 4]);
42+
let count = 0;
43+
myset.forEach((value, key) => { count += key; });
44+
return count;`,
45+
{ luaLibImport: LuaLibImportKind.Inline, luaTarget: LuaTarget.Lua53 });
46+
47+
const result = util.executeLua(lua);
48+
Expect(result).toBe(9);
49+
}
50+
}

test/unit/lualib/map.spec.ts

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ import * as util from "../../src/util";
33

44
export class MapTests {
55
@Test("map constructor")
6-
public mapConstructor() {
6+
public mapConstructor(): void {
77
const lua = util.transpileString(`let mymap = new Map(); return mymap.size;`);
88
const result = util.executeLua(lua);
99

1010
Expect(result).toBe(0);
1111
}
1212

1313
@Test("map iterable constructor")
14-
public mapIterableConstructor() {
14+
public mapIterableConstructor(): void {
1515
const lua = util.transpileString(`let mymap = new Map([["a", "c"],["b", "d"]]);
1616
return mymap.has("a") && mymap.has("b");`);
1717
const result = util.executeLua(lua);
@@ -20,7 +20,7 @@ export class MapTests {
2020
}
2121

2222
@Test("map iterable constructor map")
23-
public mapIterableConstructor2() {
23+
public mapIterableConstructor2(): void {
2424
const lua = util.transpileString(`let mymap = new Map(new Map([["a", "c"],["b", "d"]]));
2525
return mymap.has("a") && mymap.has("b");`);
2626
const result = util.executeLua(lua);
@@ -29,7 +29,7 @@ export class MapTests {
2929
}
3030

3131
@Test("map clear")
32-
public mapClear() {
32+
public mapClear(): void {
3333
const mapTS = `let mymap = new Map([["a", "c"],["b", "d"]]); mymap.clear();`;
3434
const lua = util.transpileString(mapTS + `return mymap.size;`);
3535
const size = util.executeLua(lua);
@@ -41,15 +41,15 @@ export class MapTests {
4141
}
4242

4343
@Test("map delete")
44-
public mapDelete() {
44+
public mapDelete(): void {
4545
const mapTS = `let mymap = new Map([["a", "c"],["b", "d"]]); mymap.delete("a");`;
4646
const lua = util.transpileString(mapTS + `return mymap.has("b") && !mymap.has("a");`);
4747
const contains = util.executeLua(lua);
4848
Expect(contains).toBe(true);
4949
}
5050

5151
@Test("map entries")
52-
public mapEntries() {
52+
public mapEntries(): void {
5353
const lua = util.transpileString(`let mymap = new Map([[5, 2],[6, 3],[7, 4]]);
5454
let count = 0;
5555
for (var [key, value] of mymap.entries()) { count += key + value; }
@@ -59,7 +59,7 @@ export class MapTests {
5959
}
6060

6161
@Test("map foreach")
62-
public mapForEach() {
62+
public mapForEach(): void {
6363
const lua = util.transpileString(
6464
`let mymap = new Map([["a", 2],["b", 3],["c", 4]]);
6565
let count = 0;
@@ -72,7 +72,7 @@ export class MapTests {
7272
}
7373

7474
@Test("map foreach keys")
75-
public mapForEachKeys() {
75+
public mapForEachKeys(): void {
7676
const lua = util.transpileString(
7777
`let mymap = new Map([[5, 2],[6, 3],[7, 4]]);
7878
let count = 0;
@@ -85,42 +85,42 @@ export class MapTests {
8585
}
8686

8787
@Test("map get")
88-
public mapGet() {
88+
public mapGet(): void {
8989
const lua = util.transpileString(`let mymap = new Map([["a", "c"],["b", "d"]]); return mymap.get("a");`);
9090
const result = util.executeLua(lua);
9191
Expect(result).toBe("c");
9292
}
9393

9494
@Test("map get missing")
95-
public mapGetMissing() {
95+
public mapGetMissing(): void {
9696
const lua = util.transpileString(`let mymap = new Map([["a", "c"],["b", "d"]]); return mymap.get("c");`);
9797
const result = util.executeLua(lua);
9898
Expect(result).toBe(null);
9999
}
100100

101101
@Test("map has")
102-
public mapHas() {
102+
public mapHas(): void {
103103
const lua = util.transpileString(`let mymap = new Map([["a", "c"]]); return mymap.has("a");`);
104104
const contains = util.executeLua(lua);
105105
Expect(contains).toBe(true);
106106
}
107107

108108
@Test("map has false")
109-
public mapHasFalse() {
109+
public mapHasFalse(): void {
110110
const lua = util.transpileString(`let mymap = new Map(); return mymap.has("a");`);
111111
const contains = util.executeLua(lua);
112112
Expect(contains).toBe(false);
113113
}
114114

115115
@Test("map has null")
116-
public mapHasNull() {
116+
public mapHasNull(): void {
117117
const lua = util.transpileString(`let mymap = new Map([["a", "c"]]); return mymap.has(null);`);
118118
const contains = util.executeLua(lua);
119119
Expect(contains).toBe(false);
120120
}
121121

122122
@Test("map keys")
123-
public mapKeys() {
123+
public mapKeys(): void {
124124
const lua = util.transpileString(`let mymap = new Map([[5, 2],[6, 3],[7, 4]]);
125125
let count = 0;
126126
for (var key of mymap.keys()) { count += key; }
@@ -130,7 +130,7 @@ export class MapTests {
130130
}
131131

132132
@Test("map set")
133-
public mapSet() {
133+
public mapSet(): void {
134134
const mapTS = `let mymap = new Map(); mymap.set("a", 5);`;
135135
const lua = util.transpileString(mapTS + `return mymap.has("a");`);
136136
const has = util.executeLua(lua);
@@ -142,7 +142,7 @@ export class MapTests {
142142
}
143143

144144
@Test("map values")
145-
public mapValues() {
145+
public mapValues(): void {
146146
const lua = util.transpileString(`let mymap = new Map([[5, 2],[6, 3],[7, 4]]);
147147
let count = 0;
148148
for (var value of mymap.values()) { count += value; }
@@ -152,7 +152,7 @@ export class MapTests {
152152
}
153153

154154
@Test("map size")
155-
public mapSize() {
155+
public mapSize(): void {
156156
Expect(util.transpileAndExecute(`let m = new Map(); return m.size;`)).toBe(0);
157157
Expect(util.transpileAndExecute(`let m = new Map(); m.set(1,3); return m.size;`)).toBe(1);
158158
Expect(util.transpileAndExecute(`let m = new Map([[1,2],[3,4]]); return m.size;`)).toBe(2);

0 commit comments

Comments
 (0)