diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..6106a94d
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+bin/asc text eol=lf
diff --git a/README.md b/README.md
index 71e546c7..c1ed829d 100644
--- a/README.md
+++ b/README.md
@@ -19,13 +19,13 @@ A few early examples to get an idea:
Exposes WebAssembly's i64 operations to JavaScript using 32-bit integers (low and high bits).
* **[PSON decoder](./examples/pson)**
- A PSON decoder implemented in AssemblyScript.
+ A simple decoder for the PSON binary format.
* **[TLSF memory allocator](./examples/tlsf)**
- An implementation of the TLSF memory allocator in AssemblyScript.
+ An implementation of the TLSF memory allocator.
* **[μgc garbage collector](./examples/ugc)**
- A port of μgc to AssemblyScript.
+ A port of the μgc garbage collector library.
Or browse the [compiler tests](./tests/compiler) for a more in-depth overview of what's supported already. One of them is a [showcase](./tests/compiler/showcase.ts).
diff --git a/bin/asc b/bin/asc
index 250a511a..c0fcce22 100755
--- a/bin/asc
+++ b/bin/asc
@@ -1,2 +1,2 @@
#!/usr/bin/env node
-require("./asc.js");
+require("./asc.js").main(process.argv.slice(2));
diff --git a/bin/asc.js b/bin/asc.js
index 1982ea12..5bd60ae4 100644
--- a/bin/asc.js
+++ b/bin/asc.js
@@ -1,429 +1,463 @@
-#!/usr/bin/env node
const fs = require("fs");
const path = require("path");
-const minimist = require("minimist");
-const glob = require("glob");
-const { SourceMapConsumer, SourceMapGenerator } = require("source-map");
+// Use distribution files if present, otherwise run the sources directly
var assemblyscript;
var isDev = true;
try {
assemblyscript = require("../dist/assemblyscript.js");
- try { require("source-map-support").install(); } catch (e) {} // optional
isDev = false;
+ try { require("source-map-support").install(); } catch (e) {} // optional
} catch (e) {
require("ts-node").register({ project: require("path").join(__dirname, "..", "src") });
require("../src/glue/js");
assemblyscript = require("../src");
}
+// Common constants
+const VERSION = require("../package.json").version + (isDev ? "-dev" : "");
+const OPTIONS = require("./asc.json");
const SOURCEMAP_ROOT = "assemblyscript:///";
const LIBRARY_PREFIX = assemblyscript.LIBRARY_PREFIX;
+const DEFAULT_OPTIMIZE_LEVEL = 2;
+const DEFAULT_SHRINK_LEVEL = 1;
-const conf = require("./asc.json");
-const opts = {};
+exports.VERSION = VERSION;
-Object.keys(conf).forEach(key => {
- var opt = conf[key];
- if (opt.aliases)
- (opts.alias || (opts.alias = {}))[key] = opt.aliases;
- if (opt.default !== undefined)
- (opts.default || (opts.default = {}))[key] = opt.default;
- if (opt.type === "string")
- (opts.string || (opts.string = [])).push(key);
- else if (opt.type === "boolean")
- (opts.boolean || (opts.boolean = [])).push(key);
-});
+function main(argv, callback) {
+ const args = parseArguments(argv);
+ const indent = 24;
-const args = minimist(process.argv.slice(2), opts);
-const indent = 24;
-
-var version = require("../package.json").version;
-if (isDev) version += "-dev";
-
-if (args.version) {
- console.log([
- "Version " + version
- ].join("\n"));
- process.exit(0);
-}
-
-if (args.help || args._.length < 1) {
- const options = [];
- Object.keys(conf).forEach(name => {
- var option = conf[name];
- var text = " ";
- text += "--" + name;
- if (option.aliases && option.aliases[0].length === 1)
- text += ", -" + option.aliases[0];
- while (text.length < indent)
- text += " ";
- if (Array.isArray(option.desc)) {
- options.push(text + option.desc[0] + option.desc.slice(1).map(line => {
- for (var i = 0; i < indent; ++i)
- line = " " + line;
- return "\n" + line;
- }).join(""));
- } else
- options.push(text + option.desc);
- });
- console.log([
- "Version " + version,
- "Syntax: asc [entryFile ...] [options]",
- "",
- "Examples: asc hello.ts",
- " asc hello.ts -b hello.wasm -t hello.wast",
- " asc hello1.ts hello2.ts -b -O > hello.wasm",
- "",
- "Options:"
- ].concat(options).join("\n"));
- process.exit(args.help ? 0 : 1);
-}
-
-var parser = null;
-
-var readTime = 0;
-var readCount = 0;
-var writeTime = 0;
-var writeCount = 0;
-var parseTime = 0;
-var compileTime = 0;
-var validateTime = 0;
-var optimizeTime = 0;
-
-function measure(fn) {
- var start = process.hrtime();
- fn();
- var times = process.hrtime(start);
- return times[0] * 1e9 + times[1];
-}
-
-function checkDiagnostics(parser) {
- var diagnostic;
- var hasErrors = false;
-
- while ((diagnostic = assemblyscript.nextDiagnostic(parser)) != null) {
- console.error(assemblyscript.formatDiagnostic(diagnostic, process.stderr.isTTY, true));
- if (assemblyscript.isError(diagnostic))
- hasErrors = true;
+ // Just print the version if requested
+ if (args.version) {
+ console.log("Version " + VERSION);
+ if (callback) return callback(null);
+ process.exit(0);
}
- if (hasErrors)
- process.exit(1);
-}
-// Include standard library if --noLib isn't set
-var libDirs = args.noLib ? [] : [ path.join(__dirname, "..", "std", "assembly") ];
+ // Print the help message if requested or no source files are provided
+ if (args.help || args._.length < 1) {
+ const options = [];
+ Object.keys(OPTIONS).forEach(name => {
+ var option = OPTIONS[name];
+ var text = " ";
+ text += "--" + name;
+ if (option.aliases && option.aliases[0].length === 1)
+ text += ", -" + option.aliases[0];
+ while (text.length < indent)
+ text += " ";
+ if (Array.isArray(option.desc)) {
+ options.push(text + option.desc[0] + option.desc.slice(1).map(line => {
+ for (var i = 0; i < indent; ++i)
+ line = " " + line;
+ return "\n" + line;
+ }).join(""));
+ } else
+ options.push(text + option.desc);
+ });
-// Include custom library components (with or without stdlib)
-if (args.lib) {
- if (Array.isArray(args.lib))
- Array.prototype.push.apply(libDirs, args.lib.map(dir));
- else
- libDirs.push(args.lib);
-}
+ (args.help ? console.log : console.error)([
+ "Version " + VERSION,
+ "Syntax: asc [entryFile ...] [options]",
+ "",
+ "Examples: asc hello.ts",
+ " asc hello.ts -b hello.wasm -t hello.wast",
+ " asc hello1.ts hello2.ts -b -O > hello.wasm",
+ "",
+ "Options:"
+ ].concat(options).join("\n"));
+ if (callback) return callback(args.help ? null : Error("usage"));
+ process.exit(args.help ? 0 : 1);
+ }
-libDirs.forEach(libDir => {
- var notIoTime = 0;
- readTime += measure(() => {
- glob.sync("*.ts", { cwd: libDir }).forEach(file => {
- var nextText = fs.readFileSync(path.join(libDir, file), { encoding: "utf8" });
- ++readCount;
- var time = measure(() => {
- parser = assemblyscript.parseFile(nextText, LIBRARY_PREFIX + file, parser, false);
+ // Record compilation times
+ var stats = createStats();
+
+ // Include standard library if --noLib isn't set
+ const libDirs = args.noLib ? [] : [ path.join(__dirname, "..", "std", "assembly") ];
+
+ // Include custom library components (with or without stdlib)
+ if (args.lib) {
+ if (Array.isArray(args.lib))
+ Array.prototype.push.apply(libDirs, args.lib.map(dir));
+ else
+ libDirs.push(args.lib);
+ }
+
+ // Begin parsing
+ var parser = null;
+
+ // Include library components
+ libDirs.forEach(libDir => {
+ var notReadTime = 0;
+ stats.readTime += measure(() => {
+ require("glob").sync("*.ts", { cwd: libDir }).forEach(file => {
+ var nextText = fs.readFileSync(path.join(libDir, file), { encoding: "utf8" });
+ ++stats.readCount;
+ var time = measure(() => {
+ parser = assemblyscript.parseFile(nextText, LIBRARY_PREFIX + file, parser, false);
+ });
+ stats.parseTime += time;
+ notReadTime += time;
});
- parseTime += time;
- notIoTime += time;
- });
- }) - notIoTime;
-});
+ }) - notReadTime;
+ });
-// Include entry files
-args._.forEach(filename => {
- var entryPath = filename.replace(/\\/g, "/").replace(/(\.ts|\/)$/, "");
- var entryDir = path.dirname(entryPath);
- var entryText;
+ // Include entry files
+ for (let i = 0, k = args._.length; i < k; ++i) {
+ const filename = args._[i];
- try {
- readTime += measure(() => {
- entryText = fs.readFileSync(entryPath + ".ts", { encoding: "utf8" });
- entryPath += ".ts";
- });
- ++readCount;
- } catch (e) {
+ let entryPath = filename.replace(/\\/g, "/").replace(/(\.ts|\/)$/, "");
+ let entryText;
+
+ // Try entryPath.ts, then entryPath/index.ts
try {
- readTime += measure(() => {
- entryText = fs.readFileSync(entryPath + "/index.ts", { encoding: "utf8" });
- entryPath += "/index.ts";
+ stats.readTime += measure(() => {
+ entryText = fs.readFileSync(entryPath + ".ts", { encoding: "utf8" });
+ entryPath += ".ts";
});
- ++readCount;
- entryPath = entryPath + "/index";
+ ++stats.readCount;
} catch (e) {
- console.error("File '" + entryPath + ".ts' not found.");
- process.exit(1);
+ try {
+ stats.readTime += measure(() => {
+ entryText = fs.readFileSync(entryPath + "/index.ts", { encoding: "utf8" });
+ entryPath += "/index.ts";
+ });
+ ++stats.readCount;
+ entryPath = entryPath + "/index";
+ } catch (e) {
+ console.error("File '" + entryPath + ".ts' not found.");
+ if (callback) return callback(Error("file not found"));
+ process.exit(1);
+ }
}
- }
- var nextFile;
- var nextText;
+ stats.parseTime += measure(() => {
+ parser = assemblyscript.parseFile(entryText, entryPath, parser, true);
+ });
- // Load entry text
- parseTime += measure(() => {
- parser = assemblyscript.parseFile(entryText, entryPath, parser, true);
- });
+ let nextFile;
+ let nextText;
- while ((nextFile = parser.nextFile()) != null) {
- var found = false;
- if (nextFile.startsWith(LIBRARY_PREFIX)) {
- for (var i = 0; i < libDirs.length; ++i) {
- readTime += measure(() => {
+ while ((nextFile = parser.nextFile()) != null) {
+ let found = false;
+
+ // Load library file if explicitly requested
+ if (nextFile.startsWith(LIBRARY_PREFIX)) {
+ for (let i = 0; i < libDirs.length; ++i) {
+ stats.readTime += measure(() => {
+ try {
+ nextText = fs.readFileSync(path.join(libDirs[i], nextFile.substring(4) + ".ts"), { encoding: "utf8" });
+ nextFile = nextFile + ".ts";
+ found = true;
+ } catch (e) {}
+ });
+ ++stats.readCount;
+ if (found)
+ break;
+ }
+
+ // Otherwise try nextFile.ts, nextFile/index.ts, (lib)/nextFile.ts
+ } else {
+ stats.readTime += measure(() => {
try {
- nextText = fs.readFileSync(path.join(libDirs[i], nextFile.substring(4) + ".ts"), { encoding: "utf8" });
+ nextText = fs.readFileSync(nextFile + ".ts", { encoding: "utf8" });
nextFile = nextFile + ".ts";
found = true;
} catch (e) {}
});
- ++readCount;
- if (found)
- break;
- }
- } else {
- readTime += measure(() => {
- try {
- nextText = fs.readFileSync(nextFile + ".ts", { encoding: "utf8" });
- nextFile = nextFile + ".ts";
- found = true;
- } catch (e) {}
- });
- ++readCount;
- if (!found) {
- readTime += measure(() => {
- try {
- nextText = fs.readFileSync(nextFile + "/index.ts", { encoding: "utf8" });
- nextFile = nextFile + "/index.ts";
- found = true;
- } catch (e) {}
- });
- ++readCount;
- }
- }
- if (!found) {
- console.error("Imported file '" + nextFile + ".ts' not found.");
- process.exit(1);
- }
- parseTime += measure(() => {
- assemblyscript.parseFile(nextText, nextFile, parser);
- });
- }
- checkDiagnostics(parser);
-});
-
-var options = assemblyscript.createOptions();
-assemblyscript.setTarget(options, 0);
-assemblyscript.setNoTreeShaking(options, args.noTreeShaking);
-assemblyscript.setNoAssert(options, args.noAssert);
-assemblyscript.setNoMemory(options, args.noMemory);
-assemblyscript.setSourceMap(options, args.sourceMap != null);
-
-var module;
-compileTime += measure(() => {
- module = assemblyscript.compile(parser, options);
-});
-checkDiagnostics(parser);
-
-if (args.validate)
- validateTime += measure(() => {
- if (!module.validate()) {
- module.dispose();
- console.error("Validation failed");
- process.exit(1);
- }
- });
-
-if (args.trapMode === "clamp")
- optimizeTime += measure(() => {
- module.runPasses([ "trap-mode-clamp" ]);
- });
-else if (args.trapMode === "js")
- optimizeTime += measure(() => {
- module.runPasses([ "trap-mode-js" ]);
- });
-else if (args.trapMode !== "allow") {
- console.error("Unsupported trap mode: " + args.trapMode);
- process.exit(1);
-}
-
-var defaultOptimizeLevel = 2;
-var defaultShrinkLevel = 1;
-
-var optimizeLevel = -1;
-var shrinkLevel = 0;
-var debugInfo = !args.noDebug;
-var runPasses = [];
-
-if (args.optimize !== false) {
- if (typeof args.optimize === "number")
- optimizeLevel = args.optimize;
- else if (args["0"])
- optimizeLevel = 0;
- else if (args["1"])
- optimizeLevel = 1;
- else if (args["2"])
- optimizeLevel = 2;
- else if (args["3"])
- optimizeLevel = 3;
- else if (args.optimize === true) {
- optimizeLevel = defaultOptimizeLevel;
- shrinkLevel = defaultShrinkLevel;
- } else
- optimizeLevel = 0;
-}
-if (args["s"])
- shrinkLevel = 1;
-else if (args["z"])
- shrinkLevel = 2;
-
-// Check explicit levels
-if (typeof args.optimizeLevel === "number")
- optimizeLevel = args.optimizeLevel;
-if (typeof args.shrinkLevel === "number")
- shrinkLevel = args.shrinkLevel;
-
-// Check additional passes
-if (args.runPasses) {
- if (typeof args.runPasses === "string")
- args.runPasses = args.runPasses.split(",");
- if (args.runPasses.length)
- args.runPasses.forEach(pass => {
- if (runPasses.indexOf(pass) < 0)
- runPasses.push(pass);
- });
-}
-
-module.setOptimizeLevel(optimizeLevel);
-module.setShrinkLevel(shrinkLevel);
-module.setDebugInfo(debugInfo);
-
-if (optimizeLevel >= 0)
- optimizeTime += measure(() => {
- module.optimize();
- });
-if (runPasses.length)
- optimizeTime += measure(() => {
- module.runPasses(runPasses.map(pass => pass.trim()));
- });
-
-function processSourceMap(sourceMap, sourceMapURL) {
- var json = JSON.parse(sourceMap);
- json.sources = json.sources.map(name => SOURCEMAP_ROOT + name);
- var libPrefix = SOURCEMAP_ROOT + LIBRARY_PREFIX;
- return SourceMapConsumer.with(json, sourceMapURL, consumer => {
- var generator = SourceMapGenerator.fromSourceMap(consumer);
- json.sources.forEach(name => {
- var text, found = false;
- if (name.startsWith(libPrefix)) {
- for (var i = 0, k = libDirs.length; i < k; ++i) {
- readTime += measure(() => {
+ ++stats.readCount;
+ if (!found) {
+ stats.readTime += measure(() => {
try {
- text = fs.readFileSync(path.join(libDirs[i], name.substring(libPrefix.length)), { encoding: "utf8" });
+ nextText = fs.readFileSync(nextFile + "/index.ts", { encoding: "utf8" });
+ nextFile = nextFile + "/index.ts";
found = true;
} catch (e) {}
});
- ++readCount;
+ ++stats.readCount;
+ }
+ }
+ if (!found) {
+ console.error("Imported file '" + nextFile + ".ts' not found.");
+ process.exit(1);
+ }
+ stats.parseTime += measure(() => {
+ assemblyscript.parseFile(nextText, nextFile, parser);
+ });
+ }
+ if (checkDiagnostics(parser)) {
+ if (callback) return callback(Error("compilation error"));
+ process.exit(1);
+ }
+ }
+
+ // Begin compilation
+ const options = assemblyscript.createOptions();
+ assemblyscript.setTarget(options, 0);
+ assemblyscript.setNoTreeShaking(options, args.noTreeShaking);
+ assemblyscript.setNoAssert(options, args.noAssert);
+ assemblyscript.setNoMemory(options, args.noMemory);
+ assemblyscript.setSourceMap(options, args.sourceMap != null);
+
+ var module;
+ stats.compileTime += measure(() => {
+ module = assemblyscript.compile(parser, options);
+ });
+ if (checkDiagnostics(parser)) {
+ if (module) module.dispose();
+ if (callback) return callback(Error("errored"));
+ process.exit(1);
+ }
+
+ // Validate the module if requested
+ if (args.validate) {
+ stats.validateTime += measure(() => {
+ if (!module.validate()) {
+ module.dispose();
+ if (callback) return callback(Error("validation failed"));
+ console.error("Validation failed");
+ process.exit(1);
+ }
+ });
+ }
+
+ // Set Binaryen-specific options
+ if (args.trapMode === "clamp") {
+ stats.optimizeTime += measure(() => module.runPasses([ "trap-mode-clamp" ]));
+ } else if (args.trapMode === "js") {
+ stats.optimizeTime += measure(() => module.runPasses([ "trap-mode-js" ]));
+ } else if (args.trapMode !== "allow") {
+ module.dispose();
+ console.error("Unsupported trap mode");
+ if (callback) return callback(Error("unsupported trap mode"));
+ process.exit(1);
+ }
+
+ var optimizeLevel = -1;
+ var shrinkLevel = 0;
+ var debugInfo = !args.noDebug;
+
+ if (args.optimize !== false) {
+ if (typeof args.optimize === "number")
+ optimizeLevel = args.optimize;
+ else if (args["0"])
+ optimizeLevel = 0;
+ else if (args["1"])
+ optimizeLevel = 1;
+ else if (args["2"])
+ optimizeLevel = 2;
+ else if (args["3"])
+ optimizeLevel = 3;
+ else if (args.optimize === true) {
+ optimizeLevel = DEFAULT_OPTIMIZE_LEVEL;
+ shrinkLevel = DEFAULT_SHRINK_LEVEL;
+ } else
+ optimizeLevel = 0;
+ }
+ if (args["s"])
+ shrinkLevel = 1;
+ else if (args["z"])
+ shrinkLevel = 2;
+
+ if (typeof args.optimizeLevel === "number")
+ optimizeLevel = args.optimizeLevel;
+ if (typeof args.shrinkLevel === "number")
+ shrinkLevel = args.shrinkLevel;
+ else if (args.shrinkLevel === "s")
+ shrinkLevel = 1;
+ else if (args.shrinkLevel === "z")
+ shrinkLevel = 2;
+
+ module.setOptimizeLevel(optimizeLevel);
+ module.setShrinkLevel(shrinkLevel);
+ module.setDebugInfo(debugInfo);
+
+ var runPasses = [];
+ if (args.runPasses) {
+ if (typeof args.runPasses === "string")
+ args.runPasses = args.runPasses.split(",");
+ if (args.runPasses.length)
+ args.runPasses.forEach(pass => {
+ if (runPasses.indexOf(pass) < 0)
+ runPasses.push(pass);
+ });
+ }
+
+ // Optimize the module if requested
+ if (optimizeLevel >= 0)
+ stats.optimizeTime += measure(() => module.optimize());
+
+ // Run additional passes if requested
+ if (runPasses.length)
+ stats.optimizeTime += measure(() => module.runPasses(runPasses.map(pass => pass.trim())));
+
+ // Prepare output
+ if (!args.noEmit) {
+ var hasOutput = false;
+
+ if (args.outFile != null) {
+ if (/\.wast$/.test(args.outFile) && args.textFile == null)
+ args.textFile = args.outFile;
+ else if (/\.js$/.test(args.outFile) && args.asmjsFile == null)
+ args.asmjsFile = args.outFile;
+ else if (args.binaryFile == null)
+ args.binaryFile = args.outFile;
+ }
+
+ if (args.binaryFile != null && args.binaryFile.length) {
+ var sourceMapURL = args.sourceMap != null
+ ? args.sourceMap.length
+ ? args.sourceMap
+ : path.basename(args.binaryFile) + ".map"
+ : null;
+ var binary;
+ stats.writeTime += measure(() => {
+ // FIXME: 'not a valid URL' in FF (wants http(s)://.../url)
+ binary = module.toBinary(sourceMapURL);
+ fs.writeFileSync(args.binaryFile, binary.output);
+ });
+ ++stats.writeCount;
+ if (binary.sourceMap != null) {
+ postProcessSourceMap(binary.sourceMap, sourceMapURL, libDirs, stats).then(sourceMap => {
+ stats.writeTime += measure(() => {
+ fs.writeFileSync(path.join(path.dirname(args.binaryFile), path.basename(sourceMapURL)), sourceMap, { encoding: "utf8" });
+ }, err => {
+ throw err;
+ });
+ ++stats.writeCount;
+ }).catch(err => {
+ // FIXME: as this is asynchronous, we cannot properly terminate main
+ console.error(err);
+ });
+ }
+ hasOutput = true;
+ }
+
+ if (args.textFile != null && args.textFile.length) {
+ stats.writeTime += measure(() => fs.writeFileSync(args.textFile, module.toText(), { encoding: "utf8" }));
+ ++stats.writeCount;
+ hasOutput = true;
+ }
+
+ if (args.asmjsFile != null && args.asmjsFile.length) {
+ stats.writeTime += measure(() => fs.writeFileSync(args.asmjsFile, module.toAsmjs(), { encoding: "utf8" }));
+ ++stats.writeCount;
+ hasOutput = true;
+ }
+
+ if (!hasOutput) {
+ if (args.binaryFile === "") {
+ stats.writeTime += measure(() => process.stdout.write(Buffer.from(module.toBinary().output)));
+ ++stats.writeCount;
+ } else if (args.asmjsFile === "") {
+ stats.writeTime += measure(() => module.printAsmjs());
+ ++stats.writeCount;
+ } else {
+ stats.writeTime += measure(() => module.print());
+ ++stats.writeCount;
+ }
+ }
+ }
+
+ module.dispose();
+
+ if (args.measure) process.on("beforeExit", () => console.error([
+ "I/O Read : " + (stats.readTime ? (stats.readTime / 1e6).toFixed(3) + " ms (" + stats.readCount + " files)" : "N/A"),
+ "I/O Write : " + (stats.writeTime ? (stats.writeTime / 1e6).toFixed(3) + " ms (" + stats.writeCount + " files)" : "N/A"),
+ "Parse : " + (stats.parseTime ? (stats.parseTime / 1e6).toFixed(3) + " ms" : "N/A"),
+ "Compile : " + (stats.compileTime ? (stats.compileTime / 1e6).toFixed(3) + " ms" : "N/A"),
+ "Validate : " + (stats.validateTime ? (stats.validateTime / 1e6).toFixed(3) + " ms" : "N/A"),
+ "Optimize : " + (stats.optimizeTime ? (stats.optimizeTime / 1e6).toFixed(3) + " ms" : "N/A")
+ ].join("\n")));
+}
+
+exports.main = main;
+
+function parseArguments(argv) {
+ const opts = {};
+ Object.keys(OPTIONS).forEach(key => {
+ const opt = OPTIONS[key];
+ if (opt.aliases)
+ (opts.alias || (opts.alias = {}))[key] = opt.aliases;
+ if (opt.default !== undefined)
+ (opts.default || (opts.default = {}))[key] = opt.default;
+ if (opt.type === "string")
+ (opts.string || (opts.string = [])).push(key);
+ else if (opt.type === "boolean")
+ (opts.boolean || (opts.boolean = [])).push(key);
+ });
+ return require("minimist")(argv, opts);
+}
+
+exports.parseArguments = parseArguments;
+
+function checkDiagnostics(parser) {
+ var diagnostic;
+ var hasErrors = false;
+ while ((diagnostic = assemblyscript.nextDiagnostic(parser)) != null) {
+ console.error(assemblyscript.formatDiagnostic(diagnostic, process.stderr.isTTY, true));
+ if (assemblyscript.isError(diagnostic)) hasErrors = true;
+ }
+ return hasErrors;
+}
+
+exports.checkDiagnostics = checkDiagnostics;
+
+function postProcessSourceMap(sourceMap, sourceMapURL, libDirs, stats) {
+ const { SourceMapConsumer, SourceMapGenerator } = require("source-map");
+ const json = JSON.parse(sourceMap);
+ json.sourceRoot = SOURCEMAP_ROOT;
+ return SourceMapConsumer.with(json, undefined, consumer => {
+ const generator = SourceMapGenerator.fromSourceMap(consumer);
+ json.sources.forEach(name => {
+ var text, found = false;
+ if (name.startsWith(LIBRARY_PREFIX)) {
+ for (var i = 0, k = libDirs.length; i < k; ++i) {
+ stats.readTime += measure(() => {
+ try {
+ text = fs.readFileSync(path.join(libDirs[i], name.substring(LIBRARY_PREFIX.length)), { encoding: "utf8" });
+ found = true;
+ } catch (e) {}
+ });
+ ++stats.readCount;
}
} else {
- readTime += measure(() => {
+ stats.readTime += measure(() => {
try {
- text = fs.readFileSync(name.substring(SOURCEMAP_ROOT.length), { encoding: "utf8" });
+ text = fs.readFileSync(name, { encoding: "utf8" });
found = true;
} catch (e) {}
});
- ++readCount;
+ ++stats.readCount;
}
if (found)
generator.setSourceContent(name, text);
else
console.error("No source content found for file '" + name + "'.");
});
- return generator.toString();
+ return Promise.resolve(generator.toString());
});
}
-if (!args.noEmit) {
- var hasOutput = false;
+exports.processSourceMap = postProcessSourceMap;
- if (args.outFile != null) {
- if (/\.wast$/.test(args.outFile) && args.textFile == null)
- args.textFile = args.outFile;
- else if (/\.js$/.test(args.outFile) && args.asmjsFile == null)
- args.asmjsFile = args.outFile;
- else if (args.binaryFile == null)
- args.binaryFile = args.outFile;
- }
- if (args.binaryFile != null && args.binaryFile.length) {
- var sourceMapURL = args.sourceMap != null
- ? args.sourceMap.length
- ? args.sourceMap
- : path.basename(args.binaryFile) + ".map"
- : null;
- var binary;
- writeTime += measure(() => {
- // FIXME: 'not a valid URL' in FF (wants http(s)://.../url)
- binary = module.toBinary(sourceMapURL);
- fs.writeFileSync(args.binaryFile, binary.output);
- });
- ++writeCount;
- if (binary.sourceMap != null)
- processSourceMap(binary.sourceMap).then(sourceMap => {
- writeTime += measure(() => {
- fs.writeFileSync(path.join(path.dirname(args.binaryFile), path.basename(sourceMapURL)), sourceMap, { encoding: "utf8" });
- }, err => {
- throw err;
- });
- ++writeCount;
- });
- hasOutput = true;
- }
- if (args.textFile != null && args.textFile.length) {
- writeTime += measure(() => {
- fs.writeFileSync(args.textFile, module.toText(), { encoding: "utf8" });
- });
- ++writeCount;
- hasOutput = true;
- }
- if (args.asmjsFile != null && args.asmjsFile.length) {
- writeTime += measure(() => {
- fs.writeFileSync(args.asmjsFile, module.toAsmjs(), { encoding: "utf8" });
- });
- ++writeCount;
- hasOutput = true;
- }
- if (!hasOutput) {
- if (args.binaryFile === "") {
- writeTime += measure(() => {
- process.stdout.write(Buffer.from(module.toBinary()));
- });
- ++writeCount;
- } else if (args.asmjsFile === "") {
- writeTime += measure(() => {
- module.printAsmjs();
- });
- ++writeCount;
- } else {
- writeTime += measure(() => {
- module.print();
- });
- ++writeCount;
- }
- }
+function createStats() {
+ return {
+ readTime: 0,
+ readCount: 0,
+ writeTime: 0,
+ writeCount: 0,
+ parseTime: 0,
+ compileTime: 0,
+ validateTime: 0,
+ optimizeTime: 0
+ };
}
-module.dispose();
-
-if (args.measure) process.on("beforeExit", () => console.error([
- "I/O Read : " + (readTime ? (readTime / 1e6).toFixed(3) + " ms (" + readCount + " files)" : "N/A"),
- "I/O Write : " + (writeTime ? (writeTime / 1e6).toFixed(3) + " ms (" + writeCount + " files)" : "N/A"),
- "Parse : " + (parseTime ? (parseTime / 1e6).toFixed(3) + " ms" : "N/A"),
- "Compile : " + (compileTime ? (compileTime / 1e6).toFixed(3) + " ms" : "N/A"),
- "Validate : " + (validateTime ? (validateTime / 1e6).toFixed(3) + " ms" : "N/A"),
- "Optimize : " + (optimizeTime ? (optimizeTime / 1e6).toFixed(3) + " ms" : "N/A")
-].join("\n")));
+function measure(fn) {
+ const start = process.hrtime();
+ fn();
+ const times = process.hrtime(start);
+ return times[0] * 1e9 + times[1];
+}