Skip to content

Commit 77b0929

Browse files
authored
Added override for traceback (#490)
* Added override for traceback * Improved sourcemap override * Removed obsolete argument * put traceback override at start of the file after headers * changed 2 underscore identifiers * Added test for sourceMapTraceback * don't enforce prettier linting * use debug.getinfo for file names * Trying to diagnose test issue * No longer check filename in sourcemap test * Another stab at fixing tests
1 parent aa8fede commit 77b0929

File tree

11 files changed

+154
-49
lines changed

11 files changed

+154
-49
lines changed

build_lualib.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ compile([
2020
"./src/lualib",
2121
"--noHeader",
2222
"true",
23-
...glob.sync("./src/lualib/*.ts"),
23+
...glob.sync("./src/lualib/**/*.ts"),
2424
]);
2525

2626
if (fs.existsSync(bundlePath)) {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"build-lualib": "ts-node ./build_lualib.ts",
2222
"pretest": "ts-node --transpile-only ./build_lualib.ts",
2323
"test": "jest",
24-
"lint": "npm run lint:tslint && npm run lint:prettier",
24+
"lint": "npm run lint:tslint",
2525
"lint:prettier": "prettier --check **/*.{js,ts,yml,json}",
2626
"lint:tslint": "tslint -p . && tslint -p test && tslint src/lualib/*.ts",
2727
"release-major": "npm version major",

src/CommandLineParser.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ const optionDeclarations: {[key: string]: CLIOption<any>} = {
5252
describe: "Disables hoisting.",
5353
type: "boolean",
5454
} as CLIOption<boolean>,
55+
sourceMapTraceback: {
56+
default: false,
57+
describe: "Applies the source map to show source TS files and lines in error tracebacks.",
58+
type: "boolean",
59+
} as CLIOption<boolean>,
5560
};
5661

5762
export const { version } = require("../package.json");

src/CompilerOptions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export interface CompilerOptions extends ts.CompilerOptions {
55
luaTarget?: LuaTarget;
66
luaLibImport?: LuaLibImportKind;
77
noHoisting?: boolean;
8+
sourceMapTraceback?: boolean;
89
}
910

1011
export enum LuaLibImportKind {

src/LuaLib.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export enum LuaLibFeature {
3434
Set = "Set",
3535
WeakMap = "WeakMap",
3636
WeakSet = "WeakSet",
37+
SourceMapTraceBack = "SourceMapTraceBack",
3738
StringReplace = "StringReplace",
3839
StringSplit = "StringSplit",
3940
StringConcat = "StringConcat",

src/LuaPrinter.ts

Lines changed: 37 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -50,50 +50,45 @@ export class LuaPrinter {
5050
this.currentIndent = "";
5151
}
5252

53-
public print(block: tstl.Block, luaLibFeatures?: Set<LuaLibFeature>, sourceFile?: string): string {
54-
if (this.options.inlineSourceMap === true) {
55-
const rootSourceNode = this.printImplementation(block, luaLibFeatures, sourceFile);
53+
public print(block: tstl.Block, luaLibFeatures?: Set<LuaLibFeature>, sourceFile?: string): [string, string] {
54+
// Add traceback lualib if sourcemap traceback option is enabled
55+
if (this.options.sourceMapTraceback) {
56+
if (luaLibFeatures === undefined) {
57+
luaLibFeatures = new Set();
58+
}
59+
luaLibFeatures.add(LuaLibFeature.SourceMapTraceBack);
60+
}
5661

57-
const codeWithMap = rootSourceNode
58-
// TODO is the file: part really required? and should this be handled in the printer?
59-
.toStringWithSourceMap({file: path.basename(sourceFile, path.extname(sourceFile)) + ".lua"});
62+
const rootSourceNode = this.printImplementation(block, luaLibFeatures, sourceFile);
6063

61-
let inlineSourceMap = this.printInlineSourceMap(codeWithMap.map);
64+
const codeWithSourceMap = rootSourceNode
65+
// TODO is the file: part really required? and should this be handled in the printer?
66+
.toStringWithSourceMap({file: path.basename(sourceFile, path.extname(sourceFile)) + ".lua"});
6267

63-
// TODO: Put this behind a compiler option?
64-
const stackTraceOverride = this.printStackTraceOverride(rootSourceNode);
65-
inlineSourceMap = stackTraceOverride + inlineSourceMap;
68+
let codeResult = codeWithSourceMap.code;
6669

67-
return codeWithMap.code + "\n" + inlineSourceMap;
68-
} else {
69-
return this.printImplementation(block, luaLibFeatures, sourceFile).toString();
70+
if (this.options.inlineSourceMap) {
71+
codeResult += "\n" + this.printInlineSourceMap(codeWithSourceMap.map);
7072
}
71-
}
72-
73-
public printWithSourceMap(
74-
block: tstl.Block,
75-
luaLibFeatures?: Set<LuaLibFeature>,
76-
sourceFile?: string): [string, string] {
77-
78-
const codeWithMap =
79-
this.printImplementation(block, luaLibFeatures, sourceFile)
80-
// TODO is the file: part really required? and should this be handled in the printer?
81-
.toStringWithSourceMap({file: path.basename(sourceFile, path.extname(sourceFile)) + ".lua"});
8273

74+
if (this.options.sourceMapTraceback) {
75+
const stackTraceOverride = this.printStackTraceOverride(rootSourceNode);
76+
codeResult = codeResult.replace("{#SourceMapTraceback}", stackTraceOverride);
77+
}
8378

84-
return [codeWithMap.code, codeWithMap.map.toString()];
79+
return [codeResult, codeWithSourceMap.map.toString()];
8580
}
8681

8782
private printInlineSourceMap(sourceMap: SourceMapGenerator): string {
8883
const map = sourceMap.toString();
8984
const base64Map = Buffer.from(map).toString('base64');
9085

91-
return "//# sourceMappingURL=data:application/json;base64," + base64Map;
86+
return `//# sourceMappingURL=data:application/json;base64,${base64Map}\n`;
9287
}
9388

9489
private printStackTraceOverride(rootNode: SourceNode): string {
9590
let line = 1;
96-
const map = {};
91+
const map: {[line: number]: number} = {};
9792
rootNode.walk((chunk, mappedPosition) => {
9893
if (mappedPosition.line !== undefined && mappedPosition.line > 0) {
9994
if (map[line] === undefined) {
@@ -104,8 +99,15 @@ export class LuaPrinter {
10499
}
105100
line += chunk.split("\n").length - 1;
106101
});
107-
console.log(map);
108-
return "";
102+
103+
const mapItems = [];
104+
for (const lineNr in map) {
105+
mapItems.push(`["${lineNr}"] = ${map[lineNr]}`);
106+
}
107+
108+
const mapString = "{" + mapItems.join(",") + "}";
109+
110+
return `__TS__SourceMapTraceBack(debug.getinfo(1).short_src, ${mapString});`;
109111
}
110112

111113
private printImplementation(
@@ -136,9 +138,13 @@ export class LuaPrinter {
136138

137139
this.sourceFile = path.basename(sourceFile);
138140

139-
const blockNode = this.createSourceNode(block, this.printBlock(block));
141+
if (this.options.sourceMapTraceback) {
142+
header += "{#SourceMapTraceback}\n";
143+
}
144+
145+
const fileBlockNode = this.createSourceNode(block, this.printBlock(block));
140146

141-
return this.concatNodes(header, blockNode);
147+
return this.concatNodes(header, fileBlockNode);
142148
}
143149

144150
private pushIndent(): void {

src/LuaTranspiler.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,21 +141,23 @@ export class LuaTranspiler {
141141
// Transform AST
142142
const [luaAST, lualibFeatureSet] = this.luaTransformer.transformSourceFile(sourceFile);
143143
// Print AST
144-
return this.luaPrinter.print(luaAST, lualibFeatureSet, sourceFile.fileName);
144+
const [code, sourceMap] = this.luaPrinter.print(luaAST, lualibFeatureSet, sourceFile.fileName);
145+
return code;
145146
}
146147

147148
public transpileSourceFileWithSourceMap(sourceFile: ts.SourceFile): [string, string] {
148149
// Transform AST
149150
const [luaAST, lualibFeatureSet] = this.luaTransformer.transformSourceFile(sourceFile);
150151
// Print AST
151-
return this.luaPrinter.printWithSourceMap(luaAST, lualibFeatureSet, sourceFile.fileName);
152+
return this.luaPrinter.print(luaAST, lualibFeatureSet, sourceFile.fileName);
152153
}
153154

154155
public transpileSourceFileKeepAST(sourceFile: ts.SourceFile): [tstl.Block, string] {
155156
// Transform AST
156157
const [luaAST, lualibFeatureSet] = this.luaTransformer.transformSourceFile(sourceFile);
157158
// Print AST
158-
return [luaAST, this.luaPrinter.print(luaAST, lualibFeatureSet, sourceFile.fileName)];
159+
const [code, sourceMap] = this.luaPrinter.print(luaAST, lualibFeatureSet, sourceFile.fileName);
160+
return [luaAST, code];
159161
}
160162

161163
public reportDiagnostic(diagnostic: ts.Diagnostic): void {

src/lualib/SourceMapTraceBack.ts

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,32 @@ declare const debug: {
22
traceback: (this: void, ...args: any[]) => string;
33
};
44

5-
declare function getfenv(obj: any): {[key: string]: any};
5+
type TraceBackFunction = (this: void, thread?: any, message?: string, level?: number) => string;
66

7-
function __TS__SourceMapTraceBack(fileName: string, sourceMap: {[line: number]: number}): void {
8-
getfenv(1)["traceback"] = getfenv(1)["traceback"] || {};
9-
getfenv(1)["traceback"][fileName] = getfenv(1)["traceback"][fileName] || debug.traceback;
10-
debug.traceback = (...args: any[]) => {
11-
let trace = getfenv(1)["traceback"][fileName](...args);
7+
declare const _G: {[key: string]: any} & {__TS__originalTraceback: TraceBackFunction};
128

13-
const matches = string.gmatch(trace, `${fileName}.lua:(%d+)`);
14-
for (const match in matches) {
15-
trace = string.gsub(trace, `${fileName}.lua:${match}`, `${fileName}.ts:${sourceMap[match] || "??"}`);
16-
}
9+
// TODO: In the future, change this to __TS__RegisterFileInfo and provide tstl interface to
10+
// get some metadata about transpilation.
11+
function __TS__SourceMapTraceBack(this: void, fileName: string, sourceMap: {[line: number]: number}): void {
12+
_G["__TS__sourcemap"] = _G["__TS__sourcemap"] || {};
13+
_G["__TS__sourcemap"][fileName] = sourceMap;
1714

18-
return trace;
19-
};
15+
if (_G.__TS__originalTraceback === undefined) {
16+
_G.__TS__originalTraceback = debug.traceback;
17+
debug.traceback = (thread, message, level) => {
18+
const trace = _G["__TS__originalTraceback"](thread, message, level);
19+
const [result, occurrences] = string.gsub(
20+
trace,
21+
"(%S+).lua:(%d+)",
22+
(file, line) => {
23+
if (_G["__TS__sourcemap"][file + ".lua"] && _G["__TS__sourcemap"][file + ".lua"][line]) {
24+
return `${file}.ts:${_G["__TS__sourcemap"][file + ".lua"][line]}`;
25+
}
26+
return `${file}.lua:${line}`;
27+
}
28+
);
29+
30+
return result;
31+
};
32+
}
2033
}
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
/** @luaIterator */
2-
interface GMatchResult extends Iterable<string> { }
2+
interface GMatchResult extends Array<string> { }
33

4+
/** @noSelf */
45
declare namespace string {
56
/** @tupleReturn */
67
function gsub(source: string, searchValue: string, replaceValue: string): [string, number];
8+
/** @tupleReturn */
9+
function gsub(source: string, searchValue: string, replaceValue: (...groups: string[]) => string): [string, number];
710

811
function gmatch(haystack: string, pattern: string): GMatchResult;
912
}

test/unit/compiler/configuration/mixed/index.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ test("tsconfig.json mixed with cmd line args", () => {
3232
noHeader: false,
3333
project: tsConfigPath,
3434
noHoisting: false,
35+
sourceMapTraceback: false,
3536
} as CompilerOptions);
3637
} else {
3738
expect(parsedArgs.isValid).toBeTruthy();

0 commit comments

Comments
 (0)