Fix asc bundle with webpack 4, see #36; Function expression progress

This commit is contained in:
dcodeIO 2018-03-03 18:38:38 +01:00
parent a5e31200d3
commit f754b24819
14 changed files with 366 additions and 244 deletions

View File

@ -19,8 +19,8 @@ The API accepts the same options as the CLI but also lets you override stdout an
const asc = require("assemblyscript/bin/asc.js");
asc.main([
"myModule.ts",
"-b", "myModule.wasm",
"-O",
"--binaryFile", "myModule.wasm",
"--optimize",
"--sourceMap",
"--measure"
], {

View File

@ -1,4 +1,5 @@
#!/usr/bin/env node
const asc = module.exports = require("./asc.js");
if (/\basc$/.test(process.argv[1]))
if (/\basc$/.test(process.argv[1])) {
process.exitCode = asc.main(process.argv.slice(2));
}

View File

@ -1,4 +1,13 @@
//////////////////////// Compiler frontend for node.js /////////////////////////
/**
* @file Compiler frontend for node.js
*
* Uses the low-level API exported from src/index.ts so it works with the compiler compiled to
* JavaScript as well as the compiler compiled to WebAssembly (eventually). Runs the sources
* directly through ts-node if distribution files are not present (indicated by a `-dev` version).
*
* Can also be packaged as a bundle suitable for in-browser use with the standard library injected
* in the build step. See dist/asc.js for the bundle and webpack.config.js for building details.
*/
const fs = require("fs");
const path = require("path");
@ -6,24 +15,24 @@ const os = require("os");
// Use distribution files if present, otherwise run the sources directly
var assemblyscript, isDev;
try {
assemblyscript = require("../dist/assemblyscript.js");
isDev = false;
try { require("source-map-support").install(); } catch (e) {} // optional
} catch (e) {
(() => {
try {
require("ts-node").register({
project: path.join(__dirname, "..", "src", "tsconfig.json")
});
require("../src/glue/js");
assemblyscript = require("../src");
isDev = true;
} catch (e) {
// last resort: same directory CommonJS
assemblyscript = require("./assemblyscript");
assemblyscript = require("../dist/assemblyscript.js");
isDev = false;
try { require("source-map-support").install(); } catch (e) {/* optional */}
} catch (e) {
try {
require("ts-node").register({ project: path.join(__dirname, "..", "src", "tsconfig.json") });
require("../src/glue/js");
assemblyscript = require("../src");
isDev = true;
} catch (e) {
// last resort: same directory CommonJS
assemblyscript = eval("require('./assemblyscript')");
isDev = false;
}
}
}
})();
/** Whether this is a webpack bundle or not. */
exports.isBundle = typeof BUNDLE_VERSION === "string";
@ -32,8 +41,7 @@ exports.isBundle = typeof BUNDLE_VERSION === "string";
exports.isDev = isDev;
/** AssemblyScript veresion. */
exports.version = exports.isBundle ? BUNDLE_VERSION
: require("../package.json").version;
exports.version = exports.isBundle ? BUNDLE_VERSION : require("../package.json").version;
/** Available options. */
exports.options = require("./asc.json");
@ -61,8 +69,9 @@ exports.main = function main(argv, options, callback) {
if (typeof options === "function") {
callback = options;
options = {};
} else if (!options)
} else if (!options) {
options = {};
}
const stdout = options.stdout || process.stdout;
const stderr = options.stderr || process.stderr;
@ -72,17 +81,12 @@ exports.main = function main(argv, options, callback) {
const stats = options.stats || createStats();
// All of the above must be specified in browser environments
if (!stdout)
throw Error("'options.stdout' must be specified");
if (!stderr)
throw Error("'options.stderr' must be specified");
if (!stdout) throw Error("'options.stdout' must be specified");
if (!stderr) throw Error("'options.stderr' must be specified");
if (!fs.readFileSync) {
if (readFile === readFileNode)
throw Error("'options.readFile' must be specified");
if (writeFile === writeFileNode)
throw Error("'options.writeFile' must be specified");
if (listFiles === listFilesNode)
throw Error("'options.listFiles' must be specified");
if (readFile === readFileNode) throw Error("'options.readFile' must be specified");
if (writeFile === writeFileNode) throw Error("'options.writeFile' must be specified");
if (listFiles === listFilesNode) throw Error("'options.listFiles' must be specified");
}
const args = parseArguments(argv);
@ -110,18 +114,22 @@ exports.main = function main(argv, options, callback) {
var option = exports.options[name];
var text = " ";
text += "--" + name;
if (option.aliases && option.aliases[0].length === 1)
if (option.aliases && option.aliases[0].length === 1) {
text += ", -" + option.aliases[0];
while (text.length < indent)
}
while (text.length < indent) {
text += " ";
}
if (Array.isArray(option.desc)) {
opts.push(text + option.desc[0] + option.desc.slice(1).map(line => {
for (let i = 0; i < indent; ++i)
for (let i = 0; i < indent; ++i) {
line = " " + line;
}
return os.EOL + line;
}).join(""));
} else
} else {
opts.push(text + option.desc);
}
});
(args.help ? stdout : stderr).write([
@ -146,8 +154,9 @@ exports.main = function main(argv, options, callback) {
// Include custom library components (with or without stdlib)
if (args.lib) {
if (typeof args.lib === "string")
if (typeof args.lib === "string") {
args.lib = args.lib.split(",");
}
Array.prototype.push.apply(libDirs, args.lib.map(trim));
}
@ -164,12 +173,14 @@ exports.main = function main(argv, options, callback) {
let sourceText = readFile(path.join(baseDir, sourcePath) + ".ts");
if (sourceText === null) {
sourceText = readFile(path.join(baseDir, sourcePath, "index.ts"));
if (sourceText === null)
if (sourceText === null) {
return callback(Error("Entry file '" + sourcePath + ".ts' not found."));
else
} else {
sourcePath += "/index.ts";
} else
}
} else {
sourcePath += ".ts";
}
stats.parseCount++;
stats.parseTime += measure(() => {
@ -182,9 +193,9 @@ exports.main = function main(argv, options, callback) {
// Load library file if explicitly requested
if (sourcePath.startsWith(exports.libraryPrefix)) {
for (let i = 0, k = libDirs.length; i < k; ++i) {
if (exports.libraryFiles.hasOwnProperty(sourcePath))
if (exports.libraryFiles.hasOwnProperty(sourcePath)) {
sourceText = exports.libraryFiles[sourcePath];
else {
} else {
sourceText = readFile(path.join(
libDirs[i],
sourcePath.substring(exports.libraryPrefix.length) + ".ts")
@ -205,9 +216,9 @@ exports.main = function main(argv, options, callback) {
for (let i = 0, k = libDirs.length; i < k; ++i) {
const dir = libDirs[i];
const key = exports.libraryPrefix + sourcePath;
if (exports.libraryFiles.hasOwnProperty(key))
if (exports.libraryFiles.hasOwnProperty(key)) {
sourceText = exports.libraryFiles[key];
else {
} else {
sourceText = readFile(path.join(dir, sourcePath + ".ts"));
if (sourceText !== null) {
sourcePath = exports.libraryPrefix + sourcePath + ".ts";
@ -215,30 +226,33 @@ exports.main = function main(argv, options, callback) {
}
}
}
if (sourceText === null)
if (sourceText === null) {
return callback(
Error("Import file '" + sourcePath + ".ts' not found.")
);
} else
}
} else {
sourcePath += "/index.ts";
} else
}
} else {
sourcePath += ".ts";
}
}
stats.parseCount++;
stats.parseTime += measure(() => {
assemblyscript.parseFile(sourceText, sourcePath, false, parser);
});
}
if (checkDiagnostics(parser, stderr))
if (checkDiagnostics(parser, stderr)) {
return callback(Error("Parse error"));
}
}
// Include (other) library components
var hasBundledLibrary = false;
if (!args.noLib)
Object.keys(exports.libraryFiles).forEach(libPath => {
if (libPath.lastIndexOf("/") >= exports.libraryPrefix.length)
return;
if (libPath.lastIndexOf("/") >= exports.libraryPrefix.length) return;
stats.parseCount++;
stats.parseTime += measure(() => {
parser = assemblyscript.parseFile(
@ -257,8 +271,9 @@ exports.main = function main(argv, options, callback) {
for (let j = 0, l = libFiles.length; j < l; ++j) {
let libPath = libFiles[j];
let libText = readFile(path.join(libDir, libPath));
if (libText === null)
if (libText === null) {
return callback(Error("Library file '" + libPath + "' not found."));
}
stats.parseCount++;
stats.parseTime += measure(() => {
parser = assemblyscript.parseFile(
@ -283,13 +298,15 @@ exports.main = function main(argv, options, callback) {
var module;
stats.compileCount++;
try {
stats.compileTime += measure(() => {
module = assemblyscript.compile(parser, compilerOptions);
});
} catch (e) {
return callback(e);
}
(() => {
try {
stats.compileTime += measure(() => {
module = assemblyscript.compile(parser, compilerOptions);
});
} catch (e) {
return callback(e);
}
})();
if (checkDiagnostics(parser, stderr)) {
if (module) module.dispose();
return callback(Error("Compile error"));
@ -327,35 +344,40 @@ exports.main = function main(argv, options, callback) {
var debugInfo = !args.noDebug;
if (args.optimize !== false) {
if (typeof args.optimize === "number")
if (typeof args.optimize === "number") {
optimizeLevel = args.optimize;
else if (args["0"])
} else if (args["0"]) {
optimizeLevel = 0;
else if (args["1"])
} else if (args["1"]) {
optimizeLevel = 1;
else if (args["2"])
} else if (args["2"]) {
optimizeLevel = 2;
else if (args["3"])
} else if (args["3"]) {
optimizeLevel = 3;
else if (args.optimize === true) {
} else if (args.optimize === true) {
optimizeLevel = exports.defaultOptimizeLevel;
shrinkLevel = exports.defaultShrinkLevel;
} 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")
if (args["s"]) {
shrinkLevel = 1;
else if (args.shrinkLevel === "z")
} 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 > 0 ? optimizeLevel : 0);
module.setShrinkLevel(shrinkLevel);
@ -363,13 +385,15 @@ exports.main = function main(argv, options, callback) {
var runPasses = [];
if (args.runPasses) {
if (typeof args.runPasses === "string")
if (typeof args.runPasses === "string") {
args.runPasses = args.runPasses.split(",");
if (args.runPasses.length)
}
if (args.runPasses.length) {
args.runPasses.forEach(pass => {
if (runPasses.indexOf(pass) < 0)
runPasses.push(pass);
});
}
}
// Optimize the module if requested
@ -393,12 +417,13 @@ exports.main = function main(argv, options, callback) {
let hasStdout = false;
if (args.outFile != null) {
if (/\.was?t$/.test(args.outFile) && args.textFile == null)
if (/\.was?t$/.test(args.outFile) && args.textFile == null) {
args.textFile = args.outFile;
else if (/\.js$/.test(args.outFile) && args.asmjsFile == null)
} else if (/\.js$/.test(args.outFile) && args.asmjsFile == null) {
args.asmjsFile = args.outFile;
else if (args.binaryFile == null)
} else if (args.binaryFile == null) {
args.binaryFile = args.outFile;
}
}
// Write binary
@ -437,12 +462,13 @@ exports.main = function main(argv, options, callback) {
);
if (text !== null) break;
}
} else
} else {
text = readFile(path.join(baseDir, name));
if (text === null)
}
if (text === null) {
return callback(Error("Source file '" + name + "' not found."));
if (!sourceMap.sourceContents)
sourceMap.sourceContents = [];
}
if (!sourceMap.sourceContents) sourceMap.sourceContents = [];
sourceMap.sourceContents[index] = text;
});
writeFile(path.join(
@ -501,8 +527,9 @@ exports.main = function main(argv, options, callback) {
}
module.dispose();
if (args.measure)
if (args.measure) {
printStats(stats, stderr);
}
return callback(null);
function readFileNode(filename) {
@ -522,10 +549,11 @@ exports.main = function main(argv, options, callback) {
try {
stats.writeCount++;
stats.writeTime += measure(() => {
if (typeof contents === "string")
if (typeof contents === "string") {
fs.writeFileSync(filename, contents, { encoding: "utf8" } );
else
} else {
fs.writeFileSync(filename, contents);
}
});
return true;
} catch (e) {
@ -551,10 +579,11 @@ exports.main = function main(argv, options, callback) {
writeStdout.used = true;
}
stats.writeTime += measure(() => {
if (typeof contents === "string")
if (typeof contents === "string") {
stdout.write(contents, { encoding: "utf8" });
else
} else {
stdout.write(contents);
}
});
}
}
@ -564,14 +593,17 @@ function parseArguments(argv) {
const opts = {};
Object.keys(exports.options).forEach(key => {
const opt = exports.options[key];
if (opt.aliases)
if (opt.aliases) {
(opts.alias || (opts.alias = {}))[key] = opt.aliases;
if (opt.default !== undefined)
}
if (opt.default !== undefined) {
(opts.default || (opts.default = {}))[key] = opt.default;
if (opt.type === "string")
}
if (opt.type === "string") {
(opts.string || (opts.string = [])).push(key);
else if (opt.type === "boolean")
} else if (opt.type === "boolean") {
(opts.boolean || (opts.boolean = [])).push(key);
}
});
return require("minimist")(argv, opts);
}
@ -587,8 +619,7 @@ function checkDiagnostics(emitter, stderr) {
assemblyscript.formatDiagnostic(diagnostic, stderr.isTTY, true) +
os.EOL + os.EOL
);
if (assemblyscript.isError(diagnostic))
hasErrors = true;
if (assemblyscript.isError(diagnostic)) hasErrors = true;
}
return hasErrors;
}
@ -617,8 +648,7 @@ function createStats() {
exports.createStats = createStats;
if (!process.hrtime)
process.hrtime = require("browser-process-hrtime");
if (!process.hrtime) process.hrtime = require("browser-process-hrtime");
/** Measures the execution time of the specified function. */
function measure(fn) {

2
dist/asc.js vendored

File diff suppressed because one or more lines are too long

2
dist/asc.js.map vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -38,7 +38,7 @@
"node": ">=8"
},
"scripts": {
"build": "webpack",
"build": "webpack --mode production",
"clean": "node scripts/clean",
"lint": "npm run lint:compiler && npm run lint:library",
"lint:compiler": "tslint -c tslint.json --project src --formatters-dir lib/tslint --format as",

View File

@ -294,7 +294,7 @@ export class Compiler extends DiagnosticEmitter {
var pages = i64_shr_u(i64_align(memoryOffset, 0x10000), i64_new(16, 0));
this.module.setMemory(
i64_low(pages),
Module.MAX_MEMORY_WASM32 /* TODO: not WASM64 compatible yet */,
Module.MAX_MEMORY_WASM32, // TODO: not WASM64 compatible yet
this.memorySegments,
this.options.target,
"memory"
@ -323,6 +323,7 @@ export class Compiler extends DiagnosticEmitter {
compileSourceByPath(normalizedPathWithoutExtension: string, reportNode: Node): void {
var sources = this.program.sources;
// try file.ts
var source: Source;
var expected = normalizedPathWithoutExtension + ".ts";
for (var i = 0, k = sources.length; i < k; ++i) {
@ -333,6 +334,7 @@ export class Compiler extends DiagnosticEmitter {
}
}
// try file/index.ts
expected = normalizedPathWithoutExtension + "/index.ts";
for (i = 0, k = sources.length; i < k; ++i) {
source = sources[i];
@ -342,6 +344,7 @@ export class Compiler extends DiagnosticEmitter {
}
}
// try (lib)/file.ts
expected = LIBRARY_PREFIX + normalizedPathWithoutExtension + ".ts";
for (i = 0, k = sources.length; i < k; ++i) {
source = sources[i];
@ -362,6 +365,7 @@ export class Compiler extends DiagnosticEmitter {
if (files.has(source.normalizedPath)) return;
files.add(source.normalizedPath);
// compile top-level statements
var noTreeShaking = this.options.noTreeShaking;
var isEntry = source.isEntry;
var startFunctionBody = this.startFunctionBody;
@ -451,35 +455,36 @@ export class Compiler extends DiagnosticEmitter {
// globals
compileGlobalDeclaration(declaration: VariableDeclaration): Global | null {
// look up the initialized program element
var element = this.program.elements.get(declaration.fileLevelInternalName);
if (!element || element.kind != ElementKind.GLOBAL) {
throw new Error("global expected");
}
if (!this.compileGlobal(<Global>element)) { // reports
return null;
}
if (!element || element.kind != ElementKind.GLOBAL) throw new Error("global expected");
if (!this.compileGlobal(<Global>element)) return null; // reports
return <Global>element;
}
compileGlobal(global: Global): bool {
if (global.is(ElementFlags.COMPILED) || global.is(ElementFlags.BUILTIN)) {
return true;
}
if (global.is(ElementFlags.COMPILED) || global.is(ElementFlags.BUILTIN)) return true;
global.set(ElementFlags.COMPILED); // ^ built-ins are compiled on use
var declaration = global.declaration;
var initExpr: ExpressionRef = 0;
if (global.type == Type.void) { // infer type
if (global.type == Type.void) { // type is void if not yet resolved or not annotated
// resolve now if annotated
if (declaration.type) {
var resolvedType = this.program.resolveType(declaration.type); // reports
if (!resolvedType) return false;
if (resolvedType == Type.void) {
this.error(
DiagnosticCode.Type_0_is_not_assignable_to_type_1,
declaration.type.range, "*", resolvedType.toString()
DiagnosticCode.Type_expected,
declaration.type.range
);
return false;
}
global.type = resolvedType;
// infer from initializer if not annotated
} else if (declaration.initializer) { // infer type using void/NONE for literal inference
initExpr = this.compileExpression( // reports
declaration.initializer,
@ -494,6 +499,8 @@ export class Compiler extends DiagnosticEmitter {
return false;
}
global.type = this.currentType;
// must either be annotated or have an initializer
} else {
this.error(
DiagnosticCode.Type_expected,
@ -505,7 +512,10 @@ export class Compiler extends DiagnosticEmitter {
var nativeType = global.type.toNativeType();
// handle imports
if (global.is(ElementFlags.DECLARED)) {
// constant global
if (global.is(ElementFlags.CONSTANT)) {
this.module.addGlobalImport(
global.internalName,
@ -517,6 +527,8 @@ export class Compiler extends DiagnosticEmitter {
);
global.set(ElementFlags.COMPILED);
return true;
// importing mutable globals is not supported in the MVP
} else {
this.error(
DiagnosticCode.Operation_not_supported,
@ -526,16 +538,26 @@ export class Compiler extends DiagnosticEmitter {
return false;
}
// the MVP does not yet support initializer expressions other than constant values (and
// get_globals), hence such initializations must be performed in the start function for now.
var initializeInStart = false;
// inlined constant can be compiled as-is
if (global.is(ElementFlags.INLINED)) {
initExpr = this.compileInlineConstant(global, global.type, true);
} else {
// evaluate initializer if present
if (declaration.initializer) {
if (!initExpr) {
initExpr = this.compileExpression(declaration.initializer, global.type);
}
// check if the initializer is constant
if (_BinaryenExpressionGetId(initExpr) != ExpressionId.Const) {
// if a constant global, check if the initializer becomes constant after precompute
if (global.is(ElementFlags.CONSTANT)) {
initExpr = this.precomputeExpressionRef(initExpr);
if (_BinaryenExpressionGetId(initExpr) != ExpressionId.Const) {
@ -549,17 +571,22 @@ export class Compiler extends DiagnosticEmitter {
initializeInStart = true;
}
}
// initialize to zero if there's no initializer
} else {
initExpr = global.type.toNativeZero(this.module);
}
}
var internalName = global.internalName;
if (initializeInStart) {
if (initializeInStart) { // initialize to mutable zero and set the actual value in start
this.module.addGlobal(internalName, nativeType, true, global.type.toNativeZero(this.module));
var setExpr = this.module.createSetGlobal(internalName, initExpr);
this.startFunctionBody.push(setExpr);
} else {
} else { // compile as-is
if (global.is(ElementFlags.CONSTANT)) {
var exprType = _BinaryenExpressionGetType(initExpr);
switch (exprType) {
@ -590,18 +617,18 @@ export class Compiler extends DiagnosticEmitter {
default:
throw new Error("concrete type expected");
}
global.set(ElementFlags.INLINED);
if (declaration.isTopLevel) { // might be re-exported
this.module.addGlobal(internalName, nativeType, !global.is(ElementFlags.CONSTANT), initExpr);
global.set(ElementFlags.INLINED); // inline the value from now on
if (declaration.isTopLevel) { // but keep the element if it might be re-exported
this.module.addGlobal(internalName, nativeType, false, initExpr);
}
if (declaration.range.source.isEntry && declaration.isTopLevelExport) {
this.module.addGlobalExport(global.internalName, declaration.programLevelInternalName);
}
} else {
} else /* mutable */ {
this.module.addGlobal(internalName, nativeType, !global.is(ElementFlags.CONSTANT), initExpr);
}
}
global.set(ElementFlags.COMPILED);
return true;
}
@ -615,8 +642,6 @@ export class Compiler extends DiagnosticEmitter {
compileEnum(element: Enum): bool {
if (element.is(ElementFlags.COMPILED)) return true;
// members might reference each other, triggering another compile
element.set(ElementFlags.COMPILED);
this.currentEnum = element;
@ -711,6 +736,7 @@ export class Compiler extends DiagnosticEmitter {
// functions
/** Compiles a function given its declaration. */
compileFunctionDeclaration(
declaration: FunctionDeclaration,
typeArguments: TypeNode[],
@ -728,13 +754,14 @@ export class Compiler extends DiagnosticEmitter {
);
}
/** Resolves the specified type arguments prior to compiling the resulting function instance. */
compileFunctionUsingTypeArguments(
prototype: FunctionPrototype,
typeArguments: TypeNode[],
contextualTypeArguments: Map<string,Type> | null,
reportNode: Node
): Function | null {
var instance = prototype.resolveInclTypeArguments( // reports
var instance = prototype.resolveUsingTypeArguments( // reports
typeArguments,
contextualTypeArguments,
reportNode
@ -743,6 +770,7 @@ export class Compiler extends DiagnosticEmitter {
return this.compileFunction(instance) ? instance : null;
}
/** Compiles a readily resolved function instance. */
compileFunction(instance: Function): bool {
if (instance.is(ElementFlags.COMPILED)) return true;
@ -1064,7 +1092,7 @@ export class Compiler extends DiagnosticEmitter {
contextualTypeArguments: Map<string,Type> | null = null,
alternativeReportNode: Node | null = null
): void {
var instance = prototype.resolveInclTypeArguments( // reports
var instance = prototype.resolveUsingTypeArguments( // reports
typeArguments,
contextualTypeArguments,
alternativeReportNode
@ -3029,6 +3057,14 @@ export class Compiler extends DiagnosticEmitter {
);
break;
case TypeKind.F32:
case TypeKind.F64:
this.error(
DiagnosticCode.The_0_operator_cannot_be_applied_to_type_1,
expression.range, Token.operatorToString(expression.operator), this.currentType.toString()
);
return this.module.createUnreachable();
case TypeKind.VOID:
this.error(
DiagnosticCode.Operation_not_supported,
@ -3097,6 +3133,14 @@ export class Compiler extends DiagnosticEmitter {
);
break;
case TypeKind.F32:
case TypeKind.F64:
this.error(
DiagnosticCode.The_0_operator_cannot_be_applied_to_type_1,
expression.range, Token.operatorToString(expression.operator), this.currentType.toString()
);
return this.module.createUnreachable();
case TypeKind.VOID:
this.error(
DiagnosticCode.Operation_not_supported,
@ -3780,76 +3824,67 @@ export class Compiler extends DiagnosticEmitter {
if (!resolved) return this.module.createUnreachable();
var element = resolved.element;
if (element.kind != ElementKind.FUNCTION_PROTOTYPE) {
this.error(
DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures,
expression.range, element.internalName
);
return this.module.createUnreachable();
}
var functionPrototype: FunctionPrototype;
var functionInstance: Function | null;
switch (element.kind) {
var functionPrototype = <FunctionPrototype>element;
var functionInstance: Function | null = null;
// TODO: generalize?
if (functionPrototype.is(ElementFlags.BUILTIN)) {
var resolvedTypeArguments: Type[] | null = null;
if (expression.typeArguments) {
var k = expression.typeArguments.length;
resolvedTypeArguments = new Array<Type>(k);
for (var i = 0; i < k; ++i) {
var resolvedType = this.program.resolveType( // reports
expression.typeArguments[i],
this.currentFunction.contextualTypeArguments,
true
// direct function call
case ElementKind.FUNCTION_PROTOTYPE:
functionPrototype = <FunctionPrototype>element;
if (functionPrototype.is(ElementFlags.BUILTIN)) {
let expr = compileBuiltinCall( // reports
this,
functionPrototype,
functionPrototype.resolveBuiltinTypeArguments(
expression.typeArguments,
this.currentFunction.contextualTypeArguments
),
expression.arguments,
contextualType,
expression
);
if (!resolvedType) return this.module.createUnreachable();
resolvedTypeArguments[i] = resolvedType;
if (!expr) {
this.error(
DiagnosticCode.Operation_not_supported,
expression.range
);
return this.module.createUnreachable();
}
return expr;
} else {
functionInstance = functionPrototype.resolveUsingTypeArguments( // reports
expression.typeArguments,
this.currentFunction.contextualTypeArguments,
expression
);
if (!functionInstance) return this.module.createUnreachable();
}
}
var expr = compileBuiltinCall(
this,
functionPrototype,
resolvedTypeArguments,
expression.arguments,
contextualType,
expression
);
if (!expr) {
break;
// indirect function call
// TODO: currently these are bound to distinct functions, not types.
case ElementKind.LOCAL:
case ElementKind.GLOBAL:
case ElementKind.FIELD:
if ((<VariableLikeElement>element).type.isFunction) {
functionInstance = <Function>(<VariableLikeElement>element).type.functionType;
} else {
this.error(
DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures,
expression.range, (<VariableLikeElement>element).type.toString()
);
return this.module.createUnreachable();
}
break;
default:
this.error(
DiagnosticCode.Operation_not_supported,
expression.range
DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures,
expression.range, element.internalName
);
return this.module.createUnreachable();
}
return expr;
}
// TODO: infer type arguments from parameter types if omitted
functionInstance = functionPrototype.resolveInclTypeArguments( // reports
expression.typeArguments,
this.currentFunction.contextualTypeArguments,
expression
);
if (!functionInstance) return this.module.createUnreachable();
// TODO: generalize? (see above)
/* if (functionInstance.is(ElementFlags.BUILTIN)) {
var expr = compileBuiltinCall(
this,
functionPrototype,
functionInstance.typeArguments,
expression.arguments,
contextualType,
expression
);
if (!expr) {
this.error(DiagnosticCode.Operation_not_supported, expression.range);
return this.module.createUnreachable();
}
return expr;
} */
var numArguments = expression.arguments.length;
var numArgumentsInclThis = functionInstance.instanceMethodOf
? numArguments + 1
@ -3861,7 +3896,7 @@ export class Compiler extends DiagnosticEmitter {
assert(resolved.targetExpression != null);
args[argumentIndex++] = <Expression>resolved.targetExpression;
}
for (i = 0; i < numArguments; ++i) {
for (var i = 0; i < numArguments; ++i) {
args[argumentIndex++] = expression.arguments[i];
}
return this.compileCall(functionInstance, args, expression);
@ -4413,7 +4448,7 @@ export class Compiler extends DiagnosticEmitter {
if (resolved) {
if (resolved.element.kind == ElementKind.CLASS_PROTOTYPE) {
var prototype = <ClassPrototype>resolved.element;
var instance = prototype.resolveInclTypeArguments( // reports
var instance = prototype.resolveUsingTypeArguments( // reports
expression.typeArguments,
null,
expression

View File

@ -1347,7 +1347,7 @@ export class Program extends DiagnosticEmitter {
if ((element = this.elements.get(localName)) || (element = this.elements.get(globalName))) {
switch (element.kind) {
case ElementKind.CLASS_PROTOTYPE:
var instance = (<ClassPrototype>element).resolveInclTypeArguments(
var instance = (<ClassPrototype>element).resolveUsingTypeArguments(
node.typeArguments,
contextualTypeArguments,
null
@ -1402,7 +1402,7 @@ export class Program extends DiagnosticEmitter {
return null;
}
/** Resolves an array of type parameters to concrete types. */
/** Resolves an array of type arguments to concrete types. */
resolveTypeArguments(
typeParameters: TypeParameter[],
typeArgumentNodes: TypeNode[] | null,
@ -1511,7 +1511,10 @@ export class Program extends DiagnosticEmitter {
): ResolvedElement | null {
// start by resolving the lhs target (expression before the last dot)
var targetExpression = propertyAccess.expression;
resolvedElement = this.resolveExpression(targetExpression, contextualFunction); // reports
resolvedElement = this.resolveExpression( // reports
targetExpression,
contextualFunction
);
if (!resolvedElement) return null;
var target = resolvedElement.element;
@ -1592,10 +1595,16 @@ export class Program extends DiagnosticEmitter {
return null;
}
resolveElementAccess(elementAccess: ElementAccessExpression, contextualFunction: Function): ResolvedElement | null {
resolveElementAccess(
elementAccess: ElementAccessExpression,
contextualFunction: Function
): ResolvedElement | null {
// start by resolving the lhs target (expression before the last dot)
var targetExpression = elementAccess.expression;
resolvedElement = this.resolveExpression(targetExpression, contextualFunction);
resolvedElement = this.resolveExpression(
targetExpression,
contextualFunction
);
if (!resolvedElement) return null;
var target = resolvedElement.element;
switch (target.kind) {
@ -1625,7 +1634,10 @@ export class Program extends DiagnosticEmitter {
return null;
}
resolveExpression(expression: Expression, contextualFunction: Function): ResolvedElement | null {
resolveExpression(
expression: Expression,
contextualFunction: Function
): ResolvedElement | null {
var classType: Class | null;
while (expression.kind == NodeKind.PARENTHESIZED) {
@ -1672,17 +1684,26 @@ export class Program extends DiagnosticEmitter {
return this.resolveIdentifier(<IdentifierExpression>expression, contextualFunction);
case NodeKind.PROPERTYACCESS:
return this.resolvePropertyAccess(<PropertyAccessExpression>expression, contextualFunction);
return this.resolvePropertyAccess(
<PropertyAccessExpression>expression,
contextualFunction
);
case NodeKind.ELEMENTACCESS:
return this.resolveElementAccess(<ElementAccessExpression>expression, contextualFunction);
return this.resolveElementAccess(
<ElementAccessExpression>expression,
contextualFunction
);
case NodeKind.CALL:
var resolved = this.resolveExpression((<CallExpression>expression).expression, contextualFunction);
var resolved = this.resolveExpression(
(<CallExpression>expression).expression,
contextualFunction
);
if (resolved) {
var element = resolved.element;
if (element && element.kind == ElementKind.FUNCTION_PROTOTYPE) {
var instance = (<FunctionPrototype>element).resolveInclTypeArguments(
var instance = (<FunctionPrototype>element).resolveUsingTypeArguments(
(<CallExpression>expression).typeArguments,
null,
expression
@ -2130,6 +2151,7 @@ export class FunctionPrototype extends Element {
}
}
/** Resolves this prototype to an instance using the specified concrete type arguments. */
resolve(
functionTypeArguments: Type[] | null = null,
contextualTypeArguments: Map<string,Type> | null = null
@ -2226,7 +2248,28 @@ export class FunctionPrototype extends Element {
return instance;
}
resolveInclTypeArguments(
/** Resolves this prototype partially by applying the specified inherited class type arguments. */
resolvePartial(classTypeArguments: Type[] | null): FunctionPrototype | null {
if (!this.classPrototype) {
throw new Error("partially resolved instance method must reference its class prototype");
}
if (classTypeArguments && classTypeArguments.length) {
var partialPrototype = new FunctionPrototype(
this.program,
this.simpleName,
this.internalName,
this.declaration,
this.classPrototype
);
partialPrototype.flags = this.flags;
partialPrototype.classTypeArguments = classTypeArguments;
return partialPrototype;
}
return this; // no need to clone
}
/** Resolves the specified type arguments prior to resolving this prototype to an instance. */
resolveUsingTypeArguments(
typeArgumentNodes: TypeNode[] | null,
contextualTypeArguments: Map<string,Type> | null,
reportNode: Node
@ -2245,23 +2288,27 @@ export class FunctionPrototype extends Element {
return this.resolve(resolvedTypeArguments, contextualTypeArguments);
}
resolvePartial(classTypeArguments: Type[] | null): FunctionPrototype | null {
if (!this.classPrototype) {
throw new Error("partially resolved instance method must reference its class prototype");
/** Resolves the type arguments to use when compiling a built-in call. Must be a built-in. */
resolveBuiltinTypeArguments(
typeArgumentNodes: TypeNode[] | null,
contextualTypeArguments: Map<string,Type> | null
): Type[] | null {
assert(this.is(ElementFlags.BUILTIN));
var resolvedTypeArguments: Type[] | null = null;
if (typeArgumentNodes) {
var k = typeArgumentNodes.length;
resolvedTypeArguments = new Array<Type>(k);
for (var i = 0; i < k; ++i) {
var resolvedType = this.program.resolveType( // reports
typeArgumentNodes[i],
contextualTypeArguments,
true
);
if (!resolvedType) return null;
resolvedTypeArguments[i] = resolvedType;
}
}
if (classTypeArguments && classTypeArguments.length) {
var partialPrototype = new FunctionPrototype(
this.program,
this.simpleName,
this.internalName,
this.declaration,
this.classPrototype
);
partialPrototype.flags = this.flags;
partialPrototype.classTypeArguments = classTypeArguments;
return partialPrototype;
}
return this; // no need to clone
return resolvedTypeArguments;
}
toString(): string { return this.simpleName; }
@ -2650,7 +2697,11 @@ export class ClassPrototype extends Element {
}
}
resolve(typeArguments: Type[] | null, contextualTypeArguments: Map<string,Type> | null = null): Class | null {
/** Resolves this prototype to an instance using the specified concrete type arguments. */
resolve(
typeArguments: Type[] | null,
contextualTypeArguments: Map<string,Type> | null = null
): Class | null {
var instanceKey = typeArguments ? typesToString(typeArguments) : "";
var instance = this.instances.get(instanceKey);
if (instance) return instance;
@ -2790,7 +2841,8 @@ export class ClassPrototype extends Element {
return instance;
}
resolveInclTypeArguments(
/** Resolves the specified type arguments prior to resolving this prototype to an instance. */
resolveUsingTypeArguments(
typeArgumentNodes: TypeNode[] | null,
contextualTypeArguments: Map<string,Type> | null,
alternativeReportNode: Node | null

View File

@ -5,11 +5,24 @@
(elem (i32.const 0) $start~anonymous|0 $start~anonymous|0 $start~someName|2)
(memory $0 1)
(export "memory" (memory $0))
(start $start~someName|2)
(start $start)
(func $start~anonymous|0 (; 0 ;) (type $ii) (param $0 i32) (result i32)
(get_local $0)
)
(func $start~someName|2 (; 1 ;) (type $v)
(nop)
)
(func $start (; 2 ;) (type $v)
(drop
(call $start~anonymous|0
(i32.const 1)
)
)
(drop
(call $start~anonymous|0
(i32.const 2)
)
)
(call $start~someName|2)
)
)

View File

@ -1,11 +1,11 @@
var f1 = function(a: i32): i32 {
return a;
};
f1;
f1(1);
var f2 = (a: i32): i32 => {
return a;
};
f2;
f2(2);
var f3 = function someName(): void {
};
f3;
f3();

View File

@ -24,13 +24,15 @@
)
(func $start (; 3 ;) (type $v)
(drop
(get_global $function-expression/f1)
(call $start~anonymous|0
(i32.const 1)
)
)
(drop
(get_global $function-expression/f2)
)
(drop
(get_global $function-expression/f3)
(call $start~anonymous|1
(i32.const 2)
)
)
(call $start~someName|2)
)
)

View File

@ -4,7 +4,6 @@ const webpack = require("webpack");
// Build the C-like library
const lib = {
mode: "production",
entry: [ "./src/glue/js", "./src/index.ts" ],
module: {
rules: [
@ -23,10 +22,8 @@ const lib = {
filename: "assemblyscript.js",
path: path.resolve(__dirname, "dist"),
library: "assemblyscript",
libraryTarget: "umd"
},
optimization: {
minimize: true
libraryTarget: "umd",
globalObject: "typeof self !== 'undefined' ? self : this"
},
devtool: "source-map",
performance: {
@ -36,16 +33,10 @@ const lib = {
// Build asc for browser usage
const bin = {
mode: "production",
context: path.join(__dirname, "bin"),
entry: [ "./asc.js" ],
externals: [{
"../dist/assemblyscript.js": {
commonjs: "assemblyscript",
commonjs2: "assemblyscript",
amd: "assemblyscript",
root: "_"
}
"../dist/assemblyscript.js": "assemblyscript"
}],
node: {
"fs": "empty",
@ -57,10 +48,8 @@ const bin = {
filename: "asc.js",
path: path.resolve(__dirname, "dist"),
library: "asc",
libraryTarget: "umd"
},
optimization: {
minimize: true
libraryTarget: "umd",
globalObject: "typeof self !== 'undefined' ? self : this"
},
devtool: "source-map",
plugins: [
@ -79,7 +68,7 @@ const bin = {
},
__dirname: JSON.stringify(".")
}),
new webpack.IgnorePlugin(/\.\/src|package\.json|^(\.\/assemblyscript|ts\-node|glob|source\-map\-support)$/),
new webpack.IgnorePlugin(/\.\/src|package\.json|^(ts\-node|glob|source\-map\-support)$/),
]
};