diff --git a/.gitignore b/.gitignore index 57082f26a3..2efb8995ec 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ out/ raw/ .history *.backup -.vscode \ No newline at end of file +.vscode +.idea diff --git a/NOTICE b/NOTICE index 8e47e0290f..1a15f844c7 100644 --- a/NOTICE +++ b/NOTICE @@ -30,6 +30,7 @@ under the licensing terms detailed in LICENSE: * Gabor Greif * Martin Fredriksson * forcepusher +* Piotr OleÅ› Portions of this software are derived from third-party works licensed under the following terms: diff --git a/cli/asc.d.ts b/cli/asc.d.ts index 7957ac7c2f..ecf3c9589c 100644 --- a/cli/asc.d.ts +++ b/cli/asc.d.ts @@ -55,6 +55,38 @@ export interface MemoryStream extends OutputStream { toString(): string; } +/** A subset of Source interface from assemblyscript package */ +export interface Source { + /** Normalized path with file extension. */ + normalizedPath: string; +} + +/** A subset of Range interface from assemblyscript package */ +export interface Range { + /** Range start char index in a source */ + start: number; + /** Range end char index in a source */ + end: number; + source: Source; +} + +/** A subset of DiagnosticMessage interface from assemblyscript package */ +export interface DiagnosticMessage { + /** Message code. */ + code: number; + /** Message category. */ + category: number; + /** Message text. */ + message: string; + /** Respective source range, if any. */ + range: Range | null; + /** Related range, if any. */ + relatedRange: Range | null; +} + +/** A function that handles diagnostic */ +type DiagnosticReporter = (diagnostic: DiagnosticMessage) => void; + /** Compiler options. */ export interface CompilerOptions { /** Prints just the compiler's version and exits. */ @@ -157,6 +189,8 @@ export interface APIOptions { writeFile?: (filename: string, contents: Uint8Array, baseDir: string) => void; /** Lists all files within a directory. */ listFiles?: (dirname: string, baseDir: string) => string[] | null; + /** Handles diagnostic messages. */ + reportDiagnostic?: DiagnosticReporter; } /** Convenience function that parses and compiles source strings directly. */ @@ -176,7 +210,7 @@ export function main(argv: string[], options: APIOptions, callback?: (err: Error export function main(argv: string[], callback?: (err: Error | null) => number): number; /** Checks diagnostics emitted so far for errors. */ -export function checkDiagnostics(emitter: Record, stderr?: OutputStream): boolean; +export function checkDiagnostics(emitter: Record, stderr?: OutputStream, reportDiagnostic?: DiagnosticReporter): boolean; /** An object of stats for the current task. */ export interface Stats { diff --git a/cli/asc.js b/cli/asc.js index e288bda790..a3c31ec163 100644 --- a/cli/asc.js +++ b/cli/asc.js @@ -129,6 +129,20 @@ function loadAssemblyScriptWasm(binaryPath) { return exports; } +/** + * Wraps object in WASM environment, returns untouched otherwise. + * @template T + * @param {number | T} ptrOrObj Pointer in WASM environment, object otherwise + * @param {typeof T} type Object type that provides wrap method in WASM environment + * @returns {T | null} + */ +function __wrap(ptrOrObj, type) { + if (typeof ptrOrObj === "number") { + return ptrOrObj === 0 ? null : type.wrap(ptrOrObj); + } + return ptrOrObj; +} + var assemblyscript, __newString, __getString, __pin, __unpin, __collect; function loadAssemblyScript() { @@ -702,7 +716,7 @@ exports.main = function main(argv, options, callback) { }); } } - var numErrors = checkDiagnostics(program, stderr); + var numErrors = checkDiagnostics(program, stderr, options.reportDiagnostic); if (numErrors) { const err = Error(numErrors + " parse error(s)"); err.stack = err.message; // omit stack @@ -816,7 +830,7 @@ exports.main = function main(argv, options, callback) { }; } }); - var numErrors = checkDiagnostics(program, stderr); + var numErrors = checkDiagnostics(program, stderr, options.reportDiagnostic); if (numErrors) { if (module) module.dispose(); const err = Error(numErrors + " compile error(s)"); @@ -1155,7 +1169,7 @@ function getAsconfig(file, baseDir, readFile) { exports.getAsconfig = getAsconfig; /** Checks diagnostics emitted so far for errors. */ -function checkDiagnostics(program, stderr) { +function checkDiagnostics(program, stderr, reportDiagnostic) { var numErrors = 0; do { let diagnosticPtr = assemblyscript.nextDiagnostic(program); @@ -1167,6 +1181,33 @@ function checkDiagnostics(program, stderr) { EOL + EOL ); } + if (reportDiagnostic) { + const diagnostic = __wrap(diagnosticPtr, assemblyscript.DiagnosticMessage); + const range = __wrap(diagnostic.range, assemblyscript.Range); + const relatedRange = __wrap(diagnostic.relatedRange, assemblyscript.Range); + const rangeSource = range ? __wrap(range.source, assemblyscript.Source) : null; + const relatedRangeSource = relatedRange ? __wrap(relatedRange.source, assemblyscript.Source) : null; + + reportDiagnostic({ + message: __getString(diagnostic.message), + code: diagnostic.code, + category: diagnostic.category, + range: range ? { + start: range.start, + end: range.end, + source: rangeSource ? { + normalizedPath: __getString(rangeSource.normalizedPath) + } : null, + } : null, + relatedRange: relatedRange ? { + start: relatedRange.start, + end: relatedRange.end, + source: relatedRangeSource ? { + normalizedPath: __getString(relatedRangeSource.normalizedPath) + } : null + } : null + }); + } if (assemblyscript.isError(diagnosticPtr)) ++numErrors; __unpin(diagnosticPtr); } while (true);