From 349de6012995762c67f793592a7fe796509bfbc0 Mon Sep 17 00:00:00 2001 From: dcodeIO Date: Sat, 3 Feb 2018 02:36:20 +0100 Subject: [PATCH] First pass on a programmatic asc API --- .gitattributes | 1 + README.md | 6 +- bin/asc | 2 +- bin/asc.js | 772 ++++++++++++++++++++++++++----------------------- 4 files changed, 408 insertions(+), 373 deletions(-) create mode 100644 .gitattributes 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]; +}