mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-26 07:22:21 +00:00
Fix asc bundle with webpack 4, see #36; Function expression progress
This commit is contained in:
parent
a5e31200d3
commit
f754b24819
@ -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");
|
const asc = require("assemblyscript/bin/asc.js");
|
||||||
asc.main([
|
asc.main([
|
||||||
"myModule.ts",
|
"myModule.ts",
|
||||||
"-b", "myModule.wasm",
|
"--binaryFile", "myModule.wasm",
|
||||||
"-O",
|
"--optimize",
|
||||||
"--sourceMap",
|
"--sourceMap",
|
||||||
"--measure"
|
"--measure"
|
||||||
], {
|
], {
|
||||||
|
3
bin/asc
3
bin/asc
@ -1,4 +1,5 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
const asc = module.exports = require("./asc.js");
|
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));
|
process.exitCode = asc.main(process.argv.slice(2));
|
||||||
|
}
|
||||||
|
216
bin/asc.js
216
bin/asc.js
@ -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 fs = require("fs");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
@ -6,24 +15,24 @@ const os = require("os");
|
|||||||
|
|
||||||
// Use distribution files if present, otherwise run the sources directly
|
// Use distribution files if present, otherwise run the sources directly
|
||||||
var assemblyscript, isDev;
|
var assemblyscript, isDev;
|
||||||
try {
|
(() => {
|
||||||
assemblyscript = require("../dist/assemblyscript.js");
|
|
||||||
isDev = false;
|
|
||||||
try { require("source-map-support").install(); } catch (e) {} // optional
|
|
||||||
} catch (e) {
|
|
||||||
try {
|
try {
|
||||||
require("ts-node").register({
|
assemblyscript = require("../dist/assemblyscript.js");
|
||||||
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");
|
|
||||||
isDev = false;
|
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. */
|
/** Whether this is a webpack bundle or not. */
|
||||||
exports.isBundle = typeof BUNDLE_VERSION === "string";
|
exports.isBundle = typeof BUNDLE_VERSION === "string";
|
||||||
@ -32,8 +41,7 @@ exports.isBundle = typeof BUNDLE_VERSION === "string";
|
|||||||
exports.isDev = isDev;
|
exports.isDev = isDev;
|
||||||
|
|
||||||
/** AssemblyScript veresion. */
|
/** AssemblyScript veresion. */
|
||||||
exports.version = exports.isBundle ? BUNDLE_VERSION
|
exports.version = exports.isBundle ? BUNDLE_VERSION : require("../package.json").version;
|
||||||
: require("../package.json").version;
|
|
||||||
|
|
||||||
/** Available options. */
|
/** Available options. */
|
||||||
exports.options = require("./asc.json");
|
exports.options = require("./asc.json");
|
||||||
@ -61,8 +69,9 @@ exports.main = function main(argv, options, callback) {
|
|||||||
if (typeof options === "function") {
|
if (typeof options === "function") {
|
||||||
callback = options;
|
callback = options;
|
||||||
options = {};
|
options = {};
|
||||||
} else if (!options)
|
} else if (!options) {
|
||||||
options = {};
|
options = {};
|
||||||
|
}
|
||||||
|
|
||||||
const stdout = options.stdout || process.stdout;
|
const stdout = options.stdout || process.stdout;
|
||||||
const stderr = options.stderr || process.stderr;
|
const stderr = options.stderr || process.stderr;
|
||||||
@ -72,17 +81,12 @@ exports.main = function main(argv, options, callback) {
|
|||||||
const stats = options.stats || createStats();
|
const stats = options.stats || createStats();
|
||||||
|
|
||||||
// All of the above must be specified in browser environments
|
// All of the above must be specified in browser environments
|
||||||
if (!stdout)
|
if (!stdout) throw Error("'options.stdout' must be specified");
|
||||||
throw Error("'options.stdout' must be specified");
|
if (!stderr) throw Error("'options.stderr' must be specified");
|
||||||
if (!stderr)
|
|
||||||
throw Error("'options.stderr' must be specified");
|
|
||||||
if (!fs.readFileSync) {
|
if (!fs.readFileSync) {
|
||||||
if (readFile === readFileNode)
|
if (readFile === readFileNode) throw Error("'options.readFile' must be specified");
|
||||||
throw Error("'options.readFile' must be specified");
|
if (writeFile === writeFileNode) throw Error("'options.writeFile' must be specified");
|
||||||
if (writeFile === writeFileNode)
|
if (listFiles === listFilesNode) throw Error("'options.listFiles' must be specified");
|
||||||
throw Error("'options.writeFile' must be specified");
|
|
||||||
if (listFiles === listFilesNode)
|
|
||||||
throw Error("'options.listFiles' must be specified");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const args = parseArguments(argv);
|
const args = parseArguments(argv);
|
||||||
@ -110,18 +114,22 @@ exports.main = function main(argv, options, callback) {
|
|||||||
var option = exports.options[name];
|
var option = exports.options[name];
|
||||||
var text = " ";
|
var text = " ";
|
||||||
text += "--" + name;
|
text += "--" + name;
|
||||||
if (option.aliases && option.aliases[0].length === 1)
|
if (option.aliases && option.aliases[0].length === 1) {
|
||||||
text += ", -" + option.aliases[0];
|
text += ", -" + option.aliases[0];
|
||||||
while (text.length < indent)
|
}
|
||||||
|
while (text.length < indent) {
|
||||||
text += " ";
|
text += " ";
|
||||||
|
}
|
||||||
if (Array.isArray(option.desc)) {
|
if (Array.isArray(option.desc)) {
|
||||||
opts.push(text + option.desc[0] + option.desc.slice(1).map(line => {
|
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;
|
line = " " + line;
|
||||||
|
}
|
||||||
return os.EOL + line;
|
return os.EOL + line;
|
||||||
}).join(""));
|
}).join(""));
|
||||||
} else
|
} else {
|
||||||
opts.push(text + option.desc);
|
opts.push(text + option.desc);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
(args.help ? stdout : stderr).write([
|
(args.help ? stdout : stderr).write([
|
||||||
@ -146,8 +154,9 @@ exports.main = function main(argv, options, callback) {
|
|||||||
|
|
||||||
// Include custom library components (with or without stdlib)
|
// Include custom library components (with or without stdlib)
|
||||||
if (args.lib) {
|
if (args.lib) {
|
||||||
if (typeof args.lib === "string")
|
if (typeof args.lib === "string") {
|
||||||
args.lib = args.lib.split(",");
|
args.lib = args.lib.split(",");
|
||||||
|
}
|
||||||
Array.prototype.push.apply(libDirs, args.lib.map(trim));
|
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");
|
let sourceText = readFile(path.join(baseDir, sourcePath) + ".ts");
|
||||||
if (sourceText === null) {
|
if (sourceText === null) {
|
||||||
sourceText = readFile(path.join(baseDir, sourcePath, "index.ts"));
|
sourceText = readFile(path.join(baseDir, sourcePath, "index.ts"));
|
||||||
if (sourceText === null)
|
if (sourceText === null) {
|
||||||
return callback(Error("Entry file '" + sourcePath + ".ts' not found."));
|
return callback(Error("Entry file '" + sourcePath + ".ts' not found."));
|
||||||
else
|
} else {
|
||||||
sourcePath += "/index.ts";
|
sourcePath += "/index.ts";
|
||||||
} else
|
}
|
||||||
|
} else {
|
||||||
sourcePath += ".ts";
|
sourcePath += ".ts";
|
||||||
|
}
|
||||||
|
|
||||||
stats.parseCount++;
|
stats.parseCount++;
|
||||||
stats.parseTime += measure(() => {
|
stats.parseTime += measure(() => {
|
||||||
@ -182,9 +193,9 @@ exports.main = function main(argv, options, callback) {
|
|||||||
// Load library file if explicitly requested
|
// Load library file if explicitly requested
|
||||||
if (sourcePath.startsWith(exports.libraryPrefix)) {
|
if (sourcePath.startsWith(exports.libraryPrefix)) {
|
||||||
for (let i = 0, k = libDirs.length; i < k; ++i) {
|
for (let i = 0, k = libDirs.length; i < k; ++i) {
|
||||||
if (exports.libraryFiles.hasOwnProperty(sourcePath))
|
if (exports.libraryFiles.hasOwnProperty(sourcePath)) {
|
||||||
sourceText = exports.libraryFiles[sourcePath];
|
sourceText = exports.libraryFiles[sourcePath];
|
||||||
else {
|
} else {
|
||||||
sourceText = readFile(path.join(
|
sourceText = readFile(path.join(
|
||||||
libDirs[i],
|
libDirs[i],
|
||||||
sourcePath.substring(exports.libraryPrefix.length) + ".ts")
|
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) {
|
for (let i = 0, k = libDirs.length; i < k; ++i) {
|
||||||
const dir = libDirs[i];
|
const dir = libDirs[i];
|
||||||
const key = exports.libraryPrefix + sourcePath;
|
const key = exports.libraryPrefix + sourcePath;
|
||||||
if (exports.libraryFiles.hasOwnProperty(key))
|
if (exports.libraryFiles.hasOwnProperty(key)) {
|
||||||
sourceText = exports.libraryFiles[key];
|
sourceText = exports.libraryFiles[key];
|
||||||
else {
|
} else {
|
||||||
sourceText = readFile(path.join(dir, sourcePath + ".ts"));
|
sourceText = readFile(path.join(dir, sourcePath + ".ts"));
|
||||||
if (sourceText !== null) {
|
if (sourceText !== null) {
|
||||||
sourcePath = exports.libraryPrefix + sourcePath + ".ts";
|
sourcePath = exports.libraryPrefix + sourcePath + ".ts";
|
||||||
@ -215,30 +226,33 @@ exports.main = function main(argv, options, callback) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sourceText === null)
|
if (sourceText === null) {
|
||||||
return callback(
|
return callback(
|
||||||
Error("Import file '" + sourcePath + ".ts' not found.")
|
Error("Import file '" + sourcePath + ".ts' not found.")
|
||||||
);
|
);
|
||||||
} else
|
}
|
||||||
|
} else {
|
||||||
sourcePath += "/index.ts";
|
sourcePath += "/index.ts";
|
||||||
} else
|
}
|
||||||
|
} else {
|
||||||
sourcePath += ".ts";
|
sourcePath += ".ts";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
stats.parseCount++;
|
stats.parseCount++;
|
||||||
stats.parseTime += measure(() => {
|
stats.parseTime += measure(() => {
|
||||||
assemblyscript.parseFile(sourceText, sourcePath, false, parser);
|
assemblyscript.parseFile(sourceText, sourcePath, false, parser);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (checkDiagnostics(parser, stderr))
|
if (checkDiagnostics(parser, stderr)) {
|
||||||
return callback(Error("Parse error"));
|
return callback(Error("Parse error"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Include (other) library components
|
// Include (other) library components
|
||||||
var hasBundledLibrary = false;
|
var hasBundledLibrary = false;
|
||||||
if (!args.noLib)
|
if (!args.noLib)
|
||||||
Object.keys(exports.libraryFiles).forEach(libPath => {
|
Object.keys(exports.libraryFiles).forEach(libPath => {
|
||||||
if (libPath.lastIndexOf("/") >= exports.libraryPrefix.length)
|
if (libPath.lastIndexOf("/") >= exports.libraryPrefix.length) return;
|
||||||
return;
|
|
||||||
stats.parseCount++;
|
stats.parseCount++;
|
||||||
stats.parseTime += measure(() => {
|
stats.parseTime += measure(() => {
|
||||||
parser = assemblyscript.parseFile(
|
parser = assemblyscript.parseFile(
|
||||||
@ -257,8 +271,9 @@ exports.main = function main(argv, options, callback) {
|
|||||||
for (let j = 0, l = libFiles.length; j < l; ++j) {
|
for (let j = 0, l = libFiles.length; j < l; ++j) {
|
||||||
let libPath = libFiles[j];
|
let libPath = libFiles[j];
|
||||||
let libText = readFile(path.join(libDir, libPath));
|
let libText = readFile(path.join(libDir, libPath));
|
||||||
if (libText === null)
|
if (libText === null) {
|
||||||
return callback(Error("Library file '" + libPath + "' not found."));
|
return callback(Error("Library file '" + libPath + "' not found."));
|
||||||
|
}
|
||||||
stats.parseCount++;
|
stats.parseCount++;
|
||||||
stats.parseTime += measure(() => {
|
stats.parseTime += measure(() => {
|
||||||
parser = assemblyscript.parseFile(
|
parser = assemblyscript.parseFile(
|
||||||
@ -283,13 +298,15 @@ exports.main = function main(argv, options, callback) {
|
|||||||
|
|
||||||
var module;
|
var module;
|
||||||
stats.compileCount++;
|
stats.compileCount++;
|
||||||
try {
|
(() => {
|
||||||
stats.compileTime += measure(() => {
|
try {
|
||||||
module = assemblyscript.compile(parser, compilerOptions);
|
stats.compileTime += measure(() => {
|
||||||
});
|
module = assemblyscript.compile(parser, compilerOptions);
|
||||||
} catch (e) {
|
});
|
||||||
return callback(e);
|
} catch (e) {
|
||||||
}
|
return callback(e);
|
||||||
|
}
|
||||||
|
})();
|
||||||
if (checkDiagnostics(parser, stderr)) {
|
if (checkDiagnostics(parser, stderr)) {
|
||||||
if (module) module.dispose();
|
if (module) module.dispose();
|
||||||
return callback(Error("Compile error"));
|
return callback(Error("Compile error"));
|
||||||
@ -327,35 +344,40 @@ exports.main = function main(argv, options, callback) {
|
|||||||
var debugInfo = !args.noDebug;
|
var debugInfo = !args.noDebug;
|
||||||
|
|
||||||
if (args.optimize !== false) {
|
if (args.optimize !== false) {
|
||||||
if (typeof args.optimize === "number")
|
if (typeof args.optimize === "number") {
|
||||||
optimizeLevel = args.optimize;
|
optimizeLevel = args.optimize;
|
||||||
else if (args["0"])
|
} else if (args["0"]) {
|
||||||
optimizeLevel = 0;
|
optimizeLevel = 0;
|
||||||
else if (args["1"])
|
} else if (args["1"]) {
|
||||||
optimizeLevel = 1;
|
optimizeLevel = 1;
|
||||||
else if (args["2"])
|
} else if (args["2"]) {
|
||||||
optimizeLevel = 2;
|
optimizeLevel = 2;
|
||||||
else if (args["3"])
|
} else if (args["3"]) {
|
||||||
optimizeLevel = 3;
|
optimizeLevel = 3;
|
||||||
else if (args.optimize === true) {
|
} else if (args.optimize === true) {
|
||||||
optimizeLevel = exports.defaultOptimizeLevel;
|
optimizeLevel = exports.defaultOptimizeLevel;
|
||||||
shrinkLevel = exports.defaultShrinkLevel;
|
shrinkLevel = exports.defaultShrinkLevel;
|
||||||
} else
|
} else
|
||||||
optimizeLevel = 0;
|
optimizeLevel = 0;
|
||||||
}
|
}
|
||||||
if (args["s"])
|
|
||||||
shrinkLevel = 1;
|
|
||||||
else if (args["z"])
|
|
||||||
shrinkLevel = 2;
|
|
||||||
|
|
||||||
if (typeof args.optimizeLevel === "number")
|
if (args["s"]) {
|
||||||
optimizeLevel = args.optimizeLevel;
|
|
||||||
if (typeof args.shrinkLevel === "number")
|
|
||||||
shrinkLevel = args.shrinkLevel;
|
|
||||||
else if (args.shrinkLevel === "s")
|
|
||||||
shrinkLevel = 1;
|
shrinkLevel = 1;
|
||||||
else if (args.shrinkLevel === "z")
|
} else if (args["z"]) {
|
||||||
shrinkLevel = 2;
|
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.setOptimizeLevel(optimizeLevel > 0 ? optimizeLevel : 0);
|
||||||
module.setShrinkLevel(shrinkLevel);
|
module.setShrinkLevel(shrinkLevel);
|
||||||
@ -363,13 +385,15 @@ exports.main = function main(argv, options, callback) {
|
|||||||
|
|
||||||
var runPasses = [];
|
var runPasses = [];
|
||||||
if (args.runPasses) {
|
if (args.runPasses) {
|
||||||
if (typeof args.runPasses === "string")
|
if (typeof args.runPasses === "string") {
|
||||||
args.runPasses = args.runPasses.split(",");
|
args.runPasses = args.runPasses.split(",");
|
||||||
if (args.runPasses.length)
|
}
|
||||||
|
if (args.runPasses.length) {
|
||||||
args.runPasses.forEach(pass => {
|
args.runPasses.forEach(pass => {
|
||||||
if (runPasses.indexOf(pass) < 0)
|
if (runPasses.indexOf(pass) < 0)
|
||||||
runPasses.push(pass);
|
runPasses.push(pass);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optimize the module if requested
|
// Optimize the module if requested
|
||||||
@ -393,12 +417,13 @@ exports.main = function main(argv, options, callback) {
|
|||||||
let hasStdout = false;
|
let hasStdout = false;
|
||||||
|
|
||||||
if (args.outFile != null) {
|
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;
|
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;
|
args.asmjsFile = args.outFile;
|
||||||
else if (args.binaryFile == null)
|
} else if (args.binaryFile == null) {
|
||||||
args.binaryFile = args.outFile;
|
args.binaryFile = args.outFile;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write binary
|
// Write binary
|
||||||
@ -437,12 +462,13 @@ exports.main = function main(argv, options, callback) {
|
|||||||
);
|
);
|
||||||
if (text !== null) break;
|
if (text !== null) break;
|
||||||
}
|
}
|
||||||
} else
|
} else {
|
||||||
text = readFile(path.join(baseDir, name));
|
text = readFile(path.join(baseDir, name));
|
||||||
if (text === null)
|
}
|
||||||
|
if (text === null) {
|
||||||
return callback(Error("Source file '" + name + "' not found."));
|
return callback(Error("Source file '" + name + "' not found."));
|
||||||
if (!sourceMap.sourceContents)
|
}
|
||||||
sourceMap.sourceContents = [];
|
if (!sourceMap.sourceContents) sourceMap.sourceContents = [];
|
||||||
sourceMap.sourceContents[index] = text;
|
sourceMap.sourceContents[index] = text;
|
||||||
});
|
});
|
||||||
writeFile(path.join(
|
writeFile(path.join(
|
||||||
@ -501,8 +527,9 @@ exports.main = function main(argv, options, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.dispose();
|
module.dispose();
|
||||||
if (args.measure)
|
if (args.measure) {
|
||||||
printStats(stats, stderr);
|
printStats(stats, stderr);
|
||||||
|
}
|
||||||
return callback(null);
|
return callback(null);
|
||||||
|
|
||||||
function readFileNode(filename) {
|
function readFileNode(filename) {
|
||||||
@ -522,10 +549,11 @@ exports.main = function main(argv, options, callback) {
|
|||||||
try {
|
try {
|
||||||
stats.writeCount++;
|
stats.writeCount++;
|
||||||
stats.writeTime += measure(() => {
|
stats.writeTime += measure(() => {
|
||||||
if (typeof contents === "string")
|
if (typeof contents === "string") {
|
||||||
fs.writeFileSync(filename, contents, { encoding: "utf8" } );
|
fs.writeFileSync(filename, contents, { encoding: "utf8" } );
|
||||||
else
|
} else {
|
||||||
fs.writeFileSync(filename, contents);
|
fs.writeFileSync(filename, contents);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -551,10 +579,11 @@ exports.main = function main(argv, options, callback) {
|
|||||||
writeStdout.used = true;
|
writeStdout.used = true;
|
||||||
}
|
}
|
||||||
stats.writeTime += measure(() => {
|
stats.writeTime += measure(() => {
|
||||||
if (typeof contents === "string")
|
if (typeof contents === "string") {
|
||||||
stdout.write(contents, { encoding: "utf8" });
|
stdout.write(contents, { encoding: "utf8" });
|
||||||
else
|
} else {
|
||||||
stdout.write(contents);
|
stdout.write(contents);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -564,14 +593,17 @@ function parseArguments(argv) {
|
|||||||
const opts = {};
|
const opts = {};
|
||||||
Object.keys(exports.options).forEach(key => {
|
Object.keys(exports.options).forEach(key => {
|
||||||
const opt = exports.options[key];
|
const opt = exports.options[key];
|
||||||
if (opt.aliases)
|
if (opt.aliases) {
|
||||||
(opts.alias || (opts.alias = {}))[key] = opt.aliases;
|
(opts.alias || (opts.alias = {}))[key] = opt.aliases;
|
||||||
if (opt.default !== undefined)
|
}
|
||||||
|
if (opt.default !== undefined) {
|
||||||
(opts.default || (opts.default = {}))[key] = opt.default;
|
(opts.default || (opts.default = {}))[key] = opt.default;
|
||||||
if (opt.type === "string")
|
}
|
||||||
|
if (opt.type === "string") {
|
||||||
(opts.string || (opts.string = [])).push(key);
|
(opts.string || (opts.string = [])).push(key);
|
||||||
else if (opt.type === "boolean")
|
} else if (opt.type === "boolean") {
|
||||||
(opts.boolean || (opts.boolean = [])).push(key);
|
(opts.boolean || (opts.boolean = [])).push(key);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return require("minimist")(argv, opts);
|
return require("minimist")(argv, opts);
|
||||||
}
|
}
|
||||||
@ -587,8 +619,7 @@ function checkDiagnostics(emitter, stderr) {
|
|||||||
assemblyscript.formatDiagnostic(diagnostic, stderr.isTTY, true) +
|
assemblyscript.formatDiagnostic(diagnostic, stderr.isTTY, true) +
|
||||||
os.EOL + os.EOL
|
os.EOL + os.EOL
|
||||||
);
|
);
|
||||||
if (assemblyscript.isError(diagnostic))
|
if (assemblyscript.isError(diagnostic)) hasErrors = true;
|
||||||
hasErrors = true;
|
|
||||||
}
|
}
|
||||||
return hasErrors;
|
return hasErrors;
|
||||||
}
|
}
|
||||||
@ -617,8 +648,7 @@ function createStats() {
|
|||||||
|
|
||||||
exports.createStats = createStats;
|
exports.createStats = createStats;
|
||||||
|
|
||||||
if (!process.hrtime)
|
if (!process.hrtime) process.hrtime = require("browser-process-hrtime");
|
||||||
process.hrtime = require("browser-process-hrtime");
|
|
||||||
|
|
||||||
/** Measures the execution time of the specified function. */
|
/** Measures the execution time of the specified function. */
|
||||||
function measure(fn) {
|
function measure(fn) {
|
||||||
|
2
dist/asc.js
vendored
2
dist/asc.js
vendored
File diff suppressed because one or more lines are too long
2
dist/asc.js.map
vendored
2
dist/asc.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/assemblyscript.js
vendored
2
dist/assemblyscript.js
vendored
File diff suppressed because one or more lines are too long
2
dist/assemblyscript.js.map
vendored
2
dist/assemblyscript.js.map
vendored
File diff suppressed because one or more lines are too long
@ -38,7 +38,7 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "webpack",
|
"build": "webpack --mode production",
|
||||||
"clean": "node scripts/clean",
|
"clean": "node scripts/clean",
|
||||||
"lint": "npm run lint:compiler && npm run lint:library",
|
"lint": "npm run lint:compiler && npm run lint:library",
|
||||||
"lint:compiler": "tslint -c tslint.json --project src --formatters-dir lib/tslint --format as",
|
"lint:compiler": "tslint -c tslint.json --project src --formatters-dir lib/tslint --format as",
|
||||||
|
211
src/compiler.ts
211
src/compiler.ts
@ -294,7 +294,7 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
var pages = i64_shr_u(i64_align(memoryOffset, 0x10000), i64_new(16, 0));
|
var pages = i64_shr_u(i64_align(memoryOffset, 0x10000), i64_new(16, 0));
|
||||||
this.module.setMemory(
|
this.module.setMemory(
|
||||||
i64_low(pages),
|
i64_low(pages),
|
||||||
Module.MAX_MEMORY_WASM32 /* TODO: not WASM64 compatible yet */,
|
Module.MAX_MEMORY_WASM32, // TODO: not WASM64 compatible yet
|
||||||
this.memorySegments,
|
this.memorySegments,
|
||||||
this.options.target,
|
this.options.target,
|
||||||
"memory"
|
"memory"
|
||||||
@ -323,6 +323,7 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
compileSourceByPath(normalizedPathWithoutExtension: string, reportNode: Node): void {
|
compileSourceByPath(normalizedPathWithoutExtension: string, reportNode: Node): void {
|
||||||
var sources = this.program.sources;
|
var sources = this.program.sources;
|
||||||
|
|
||||||
|
// try file.ts
|
||||||
var source: Source;
|
var source: Source;
|
||||||
var expected = normalizedPathWithoutExtension + ".ts";
|
var expected = normalizedPathWithoutExtension + ".ts";
|
||||||
for (var i = 0, k = sources.length; i < k; ++i) {
|
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";
|
expected = normalizedPathWithoutExtension + "/index.ts";
|
||||||
for (i = 0, k = sources.length; i < k; ++i) {
|
for (i = 0, k = sources.length; i < k; ++i) {
|
||||||
source = sources[i];
|
source = sources[i];
|
||||||
@ -342,6 +344,7 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// try (lib)/file.ts
|
||||||
expected = LIBRARY_PREFIX + normalizedPathWithoutExtension + ".ts";
|
expected = LIBRARY_PREFIX + normalizedPathWithoutExtension + ".ts";
|
||||||
for (i = 0, k = sources.length; i < k; ++i) {
|
for (i = 0, k = sources.length; i < k; ++i) {
|
||||||
source = sources[i];
|
source = sources[i];
|
||||||
@ -362,6 +365,7 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
if (files.has(source.normalizedPath)) return;
|
if (files.has(source.normalizedPath)) return;
|
||||||
files.add(source.normalizedPath);
|
files.add(source.normalizedPath);
|
||||||
|
|
||||||
|
// compile top-level statements
|
||||||
var noTreeShaking = this.options.noTreeShaking;
|
var noTreeShaking = this.options.noTreeShaking;
|
||||||
var isEntry = source.isEntry;
|
var isEntry = source.isEntry;
|
||||||
var startFunctionBody = this.startFunctionBody;
|
var startFunctionBody = this.startFunctionBody;
|
||||||
@ -451,35 +455,36 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
// globals
|
// globals
|
||||||
|
|
||||||
compileGlobalDeclaration(declaration: VariableDeclaration): Global | null {
|
compileGlobalDeclaration(declaration: VariableDeclaration): Global | null {
|
||||||
|
// look up the initialized program element
|
||||||
var element = this.program.elements.get(declaration.fileLevelInternalName);
|
var element = this.program.elements.get(declaration.fileLevelInternalName);
|
||||||
if (!element || element.kind != ElementKind.GLOBAL) {
|
if (!element || element.kind != ElementKind.GLOBAL) throw new Error("global expected");
|
||||||
throw new Error("global expected");
|
if (!this.compileGlobal(<Global>element)) return null; // reports
|
||||||
}
|
|
||||||
if (!this.compileGlobal(<Global>element)) { // reports
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return <Global>element;
|
return <Global>element;
|
||||||
}
|
}
|
||||||
|
|
||||||
compileGlobal(global: Global): bool {
|
compileGlobal(global: Global): bool {
|
||||||
if (global.is(ElementFlags.COMPILED) || global.is(ElementFlags.BUILTIN)) {
|
if (global.is(ElementFlags.COMPILED) || global.is(ElementFlags.BUILTIN)) return true;
|
||||||
return true;
|
global.set(ElementFlags.COMPILED); // ^ built-ins are compiled on use
|
||||||
}
|
|
||||||
|
|
||||||
var declaration = global.declaration;
|
var declaration = global.declaration;
|
||||||
var initExpr: ExpressionRef = 0;
|
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) {
|
if (declaration.type) {
|
||||||
var resolvedType = this.program.resolveType(declaration.type); // reports
|
var resolvedType = this.program.resolveType(declaration.type); // reports
|
||||||
if (!resolvedType) return false;
|
if (!resolvedType) return false;
|
||||||
if (resolvedType == Type.void) {
|
if (resolvedType == Type.void) {
|
||||||
this.error(
|
this.error(
|
||||||
DiagnosticCode.Type_0_is_not_assignable_to_type_1,
|
DiagnosticCode.Type_expected,
|
||||||
declaration.type.range, "*", resolvedType.toString()
|
declaration.type.range
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
global.type = resolvedType;
|
global.type = resolvedType;
|
||||||
|
|
||||||
|
// infer from initializer if not annotated
|
||||||
} else if (declaration.initializer) { // infer type using void/NONE for literal inference
|
} else if (declaration.initializer) { // infer type using void/NONE for literal inference
|
||||||
initExpr = this.compileExpression( // reports
|
initExpr = this.compileExpression( // reports
|
||||||
declaration.initializer,
|
declaration.initializer,
|
||||||
@ -494,6 +499,8 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
global.type = this.currentType;
|
global.type = this.currentType;
|
||||||
|
|
||||||
|
// must either be annotated or have an initializer
|
||||||
} else {
|
} else {
|
||||||
this.error(
|
this.error(
|
||||||
DiagnosticCode.Type_expected,
|
DiagnosticCode.Type_expected,
|
||||||
@ -505,7 +512,10 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
|
|
||||||
var nativeType = global.type.toNativeType();
|
var nativeType = global.type.toNativeType();
|
||||||
|
|
||||||
|
// handle imports
|
||||||
if (global.is(ElementFlags.DECLARED)) {
|
if (global.is(ElementFlags.DECLARED)) {
|
||||||
|
|
||||||
|
// constant global
|
||||||
if (global.is(ElementFlags.CONSTANT)) {
|
if (global.is(ElementFlags.CONSTANT)) {
|
||||||
this.module.addGlobalImport(
|
this.module.addGlobalImport(
|
||||||
global.internalName,
|
global.internalName,
|
||||||
@ -517,6 +527,8 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
);
|
);
|
||||||
global.set(ElementFlags.COMPILED);
|
global.set(ElementFlags.COMPILED);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
// importing mutable globals is not supported in the MVP
|
||||||
} else {
|
} else {
|
||||||
this.error(
|
this.error(
|
||||||
DiagnosticCode.Operation_not_supported,
|
DiagnosticCode.Operation_not_supported,
|
||||||
@ -526,16 +538,26 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
return false;
|
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;
|
var initializeInStart = false;
|
||||||
|
|
||||||
|
// inlined constant can be compiled as-is
|
||||||
if (global.is(ElementFlags.INLINED)) {
|
if (global.is(ElementFlags.INLINED)) {
|
||||||
initExpr = this.compileInlineConstant(global, global.type, true);
|
initExpr = this.compileInlineConstant(global, global.type, true);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
// evaluate initializer if present
|
||||||
if (declaration.initializer) {
|
if (declaration.initializer) {
|
||||||
if (!initExpr) {
|
if (!initExpr) {
|
||||||
initExpr = this.compileExpression(declaration.initializer, global.type);
|
initExpr = this.compileExpression(declaration.initializer, global.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if the initializer is constant
|
||||||
if (_BinaryenExpressionGetId(initExpr) != ExpressionId.Const) {
|
if (_BinaryenExpressionGetId(initExpr) != ExpressionId.Const) {
|
||||||
|
|
||||||
|
// if a constant global, check if the initializer becomes constant after precompute
|
||||||
if (global.is(ElementFlags.CONSTANT)) {
|
if (global.is(ElementFlags.CONSTANT)) {
|
||||||
initExpr = this.precomputeExpressionRef(initExpr);
|
initExpr = this.precomputeExpressionRef(initExpr);
|
||||||
if (_BinaryenExpressionGetId(initExpr) != ExpressionId.Const) {
|
if (_BinaryenExpressionGetId(initExpr) != ExpressionId.Const) {
|
||||||
@ -549,17 +571,22 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
initializeInStart = true;
|
initializeInStart = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// initialize to zero if there's no initializer
|
||||||
} else {
|
} else {
|
||||||
initExpr = global.type.toNativeZero(this.module);
|
initExpr = global.type.toNativeZero(this.module);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var internalName = global.internalName;
|
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));
|
this.module.addGlobal(internalName, nativeType, true, global.type.toNativeZero(this.module));
|
||||||
var setExpr = this.module.createSetGlobal(internalName, initExpr);
|
var setExpr = this.module.createSetGlobal(internalName, initExpr);
|
||||||
this.startFunctionBody.push(setExpr);
|
this.startFunctionBody.push(setExpr);
|
||||||
} else {
|
|
||||||
|
} else { // compile as-is
|
||||||
|
|
||||||
if (global.is(ElementFlags.CONSTANT)) {
|
if (global.is(ElementFlags.CONSTANT)) {
|
||||||
var exprType = _BinaryenExpressionGetType(initExpr);
|
var exprType = _BinaryenExpressionGetType(initExpr);
|
||||||
switch (exprType) {
|
switch (exprType) {
|
||||||
@ -590,18 +617,18 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
default:
|
default:
|
||||||
throw new Error("concrete type expected");
|
throw new Error("concrete type expected");
|
||||||
}
|
}
|
||||||
global.set(ElementFlags.INLINED);
|
global.set(ElementFlags.INLINED); // inline the value from now on
|
||||||
if (declaration.isTopLevel) { // might be re-exported
|
if (declaration.isTopLevel) { // but keep the element if it might be re-exported
|
||||||
this.module.addGlobal(internalName, nativeType, !global.is(ElementFlags.CONSTANT), initExpr);
|
this.module.addGlobal(internalName, nativeType, false, initExpr);
|
||||||
}
|
}
|
||||||
if (declaration.range.source.isEntry && declaration.isTopLevelExport) {
|
if (declaration.range.source.isEntry && declaration.isTopLevelExport) {
|
||||||
this.module.addGlobalExport(global.internalName, declaration.programLevelInternalName);
|
this.module.addGlobalExport(global.internalName, declaration.programLevelInternalName);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
|
} else /* mutable */ {
|
||||||
this.module.addGlobal(internalName, nativeType, !global.is(ElementFlags.CONSTANT), initExpr);
|
this.module.addGlobal(internalName, nativeType, !global.is(ElementFlags.CONSTANT), initExpr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
global.set(ElementFlags.COMPILED);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -615,8 +642,6 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
|
|
||||||
compileEnum(element: Enum): bool {
|
compileEnum(element: Enum): bool {
|
||||||
if (element.is(ElementFlags.COMPILED)) return true;
|
if (element.is(ElementFlags.COMPILED)) return true;
|
||||||
|
|
||||||
// members might reference each other, triggering another compile
|
|
||||||
element.set(ElementFlags.COMPILED);
|
element.set(ElementFlags.COMPILED);
|
||||||
|
|
||||||
this.currentEnum = element;
|
this.currentEnum = element;
|
||||||
@ -711,6 +736,7 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
|
|
||||||
// functions
|
// functions
|
||||||
|
|
||||||
|
/** Compiles a function given its declaration. */
|
||||||
compileFunctionDeclaration(
|
compileFunctionDeclaration(
|
||||||
declaration: FunctionDeclaration,
|
declaration: FunctionDeclaration,
|
||||||
typeArguments: TypeNode[],
|
typeArguments: TypeNode[],
|
||||||
@ -728,13 +754,14 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Resolves the specified type arguments prior to compiling the resulting function instance. */
|
||||||
compileFunctionUsingTypeArguments(
|
compileFunctionUsingTypeArguments(
|
||||||
prototype: FunctionPrototype,
|
prototype: FunctionPrototype,
|
||||||
typeArguments: TypeNode[],
|
typeArguments: TypeNode[],
|
||||||
contextualTypeArguments: Map<string,Type> | null,
|
contextualTypeArguments: Map<string,Type> | null,
|
||||||
reportNode: Node
|
reportNode: Node
|
||||||
): Function | null {
|
): Function | null {
|
||||||
var instance = prototype.resolveInclTypeArguments( // reports
|
var instance = prototype.resolveUsingTypeArguments( // reports
|
||||||
typeArguments,
|
typeArguments,
|
||||||
contextualTypeArguments,
|
contextualTypeArguments,
|
||||||
reportNode
|
reportNode
|
||||||
@ -743,6 +770,7 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
return this.compileFunction(instance) ? instance : null;
|
return this.compileFunction(instance) ? instance : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Compiles a readily resolved function instance. */
|
||||||
compileFunction(instance: Function): bool {
|
compileFunction(instance: Function): bool {
|
||||||
if (instance.is(ElementFlags.COMPILED)) return true;
|
if (instance.is(ElementFlags.COMPILED)) return true;
|
||||||
|
|
||||||
@ -1064,7 +1092,7 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
contextualTypeArguments: Map<string,Type> | null = null,
|
contextualTypeArguments: Map<string,Type> | null = null,
|
||||||
alternativeReportNode: Node | null = null
|
alternativeReportNode: Node | null = null
|
||||||
): void {
|
): void {
|
||||||
var instance = prototype.resolveInclTypeArguments( // reports
|
var instance = prototype.resolveUsingTypeArguments( // reports
|
||||||
typeArguments,
|
typeArguments,
|
||||||
contextualTypeArguments,
|
contextualTypeArguments,
|
||||||
alternativeReportNode
|
alternativeReportNode
|
||||||
@ -3029,6 +3057,14 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
);
|
);
|
||||||
break;
|
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:
|
case TypeKind.VOID:
|
||||||
this.error(
|
this.error(
|
||||||
DiagnosticCode.Operation_not_supported,
|
DiagnosticCode.Operation_not_supported,
|
||||||
@ -3097,6 +3133,14 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
);
|
);
|
||||||
break;
|
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:
|
case TypeKind.VOID:
|
||||||
this.error(
|
this.error(
|
||||||
DiagnosticCode.Operation_not_supported,
|
DiagnosticCode.Operation_not_supported,
|
||||||
@ -3780,76 +3824,67 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
if (!resolved) return this.module.createUnreachable();
|
if (!resolved) return this.module.createUnreachable();
|
||||||
|
|
||||||
var element = resolved.element;
|
var element = resolved.element;
|
||||||
if (element.kind != ElementKind.FUNCTION_PROTOTYPE) {
|
var functionPrototype: FunctionPrototype;
|
||||||
this.error(
|
var functionInstance: Function | null;
|
||||||
DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures,
|
switch (element.kind) {
|
||||||
expression.range, element.internalName
|
|
||||||
);
|
|
||||||
return this.module.createUnreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
var functionPrototype = <FunctionPrototype>element;
|
// direct function call
|
||||||
var functionInstance: Function | null = null;
|
case ElementKind.FUNCTION_PROTOTYPE:
|
||||||
|
functionPrototype = <FunctionPrototype>element;
|
||||||
// TODO: generalize?
|
if (functionPrototype.is(ElementFlags.BUILTIN)) {
|
||||||
if (functionPrototype.is(ElementFlags.BUILTIN)) {
|
let expr = compileBuiltinCall( // reports
|
||||||
var resolvedTypeArguments: Type[] | null = null;
|
this,
|
||||||
if (expression.typeArguments) {
|
functionPrototype,
|
||||||
var k = expression.typeArguments.length;
|
functionPrototype.resolveBuiltinTypeArguments(
|
||||||
resolvedTypeArguments = new Array<Type>(k);
|
expression.typeArguments,
|
||||||
for (var i = 0; i < k; ++i) {
|
this.currentFunction.contextualTypeArguments
|
||||||
var resolvedType = this.program.resolveType( // reports
|
),
|
||||||
expression.typeArguments[i],
|
expression.arguments,
|
||||||
this.currentFunction.contextualTypeArguments,
|
contextualType,
|
||||||
true
|
expression
|
||||||
);
|
);
|
||||||
if (!resolvedType) return this.module.createUnreachable();
|
if (!expr) {
|
||||||
resolvedTypeArguments[i] = resolvedType;
|
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();
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
var expr = compileBuiltinCall(
|
|
||||||
this,
|
// indirect function call
|
||||||
functionPrototype,
|
// TODO: currently these are bound to distinct functions, not types.
|
||||||
resolvedTypeArguments,
|
case ElementKind.LOCAL:
|
||||||
expression.arguments,
|
case ElementKind.GLOBAL:
|
||||||
contextualType,
|
case ElementKind.FIELD:
|
||||||
expression
|
if ((<VariableLikeElement>element).type.isFunction) {
|
||||||
);
|
functionInstance = <Function>(<VariableLikeElement>element).type.functionType;
|
||||||
if (!expr) {
|
} 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(
|
this.error(
|
||||||
DiagnosticCode.Operation_not_supported,
|
DiagnosticCode.Cannot_invoke_an_expression_whose_type_lacks_a_call_signature_Type_0_has_no_compatible_call_signatures,
|
||||||
expression.range
|
expression.range, element.internalName
|
||||||
);
|
);
|
||||||
return this.module.createUnreachable();
|
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 numArguments = expression.arguments.length;
|
||||||
var numArgumentsInclThis = functionInstance.instanceMethodOf
|
var numArgumentsInclThis = functionInstance.instanceMethodOf
|
||||||
? numArguments + 1
|
? numArguments + 1
|
||||||
@ -3861,7 +3896,7 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
assert(resolved.targetExpression != null);
|
assert(resolved.targetExpression != null);
|
||||||
args[argumentIndex++] = <Expression>resolved.targetExpression;
|
args[argumentIndex++] = <Expression>resolved.targetExpression;
|
||||||
}
|
}
|
||||||
for (i = 0; i < numArguments; ++i) {
|
for (var i = 0; i < numArguments; ++i) {
|
||||||
args[argumentIndex++] = expression.arguments[i];
|
args[argumentIndex++] = expression.arguments[i];
|
||||||
}
|
}
|
||||||
return this.compileCall(functionInstance, args, expression);
|
return this.compileCall(functionInstance, args, expression);
|
||||||
@ -4413,7 +4448,7 @@ export class Compiler extends DiagnosticEmitter {
|
|||||||
if (resolved) {
|
if (resolved) {
|
||||||
if (resolved.element.kind == ElementKind.CLASS_PROTOTYPE) {
|
if (resolved.element.kind == ElementKind.CLASS_PROTOTYPE) {
|
||||||
var prototype = <ClassPrototype>resolved.element;
|
var prototype = <ClassPrototype>resolved.element;
|
||||||
var instance = prototype.resolveInclTypeArguments( // reports
|
var instance = prototype.resolveUsingTypeArguments( // reports
|
||||||
expression.typeArguments,
|
expression.typeArguments,
|
||||||
null,
|
null,
|
||||||
expression
|
expression
|
||||||
|
110
src/program.ts
110
src/program.ts
@ -1347,7 +1347,7 @@ export class Program extends DiagnosticEmitter {
|
|||||||
if ((element = this.elements.get(localName)) || (element = this.elements.get(globalName))) {
|
if ((element = this.elements.get(localName)) || (element = this.elements.get(globalName))) {
|
||||||
switch (element.kind) {
|
switch (element.kind) {
|
||||||
case ElementKind.CLASS_PROTOTYPE:
|
case ElementKind.CLASS_PROTOTYPE:
|
||||||
var instance = (<ClassPrototype>element).resolveInclTypeArguments(
|
var instance = (<ClassPrototype>element).resolveUsingTypeArguments(
|
||||||
node.typeArguments,
|
node.typeArguments,
|
||||||
contextualTypeArguments,
|
contextualTypeArguments,
|
||||||
null
|
null
|
||||||
@ -1402,7 +1402,7 @@ export class Program extends DiagnosticEmitter {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Resolves an array of type parameters to concrete types. */
|
/** Resolves an array of type arguments to concrete types. */
|
||||||
resolveTypeArguments(
|
resolveTypeArguments(
|
||||||
typeParameters: TypeParameter[],
|
typeParameters: TypeParameter[],
|
||||||
typeArgumentNodes: TypeNode[] | null,
|
typeArgumentNodes: TypeNode[] | null,
|
||||||
@ -1511,7 +1511,10 @@ export class Program extends DiagnosticEmitter {
|
|||||||
): ResolvedElement | null {
|
): ResolvedElement | null {
|
||||||
// start by resolving the lhs target (expression before the last dot)
|
// start by resolving the lhs target (expression before the last dot)
|
||||||
var targetExpression = propertyAccess.expression;
|
var targetExpression = propertyAccess.expression;
|
||||||
resolvedElement = this.resolveExpression(targetExpression, contextualFunction); // reports
|
resolvedElement = this.resolveExpression( // reports
|
||||||
|
targetExpression,
|
||||||
|
contextualFunction
|
||||||
|
);
|
||||||
if (!resolvedElement) return null;
|
if (!resolvedElement) return null;
|
||||||
var target = resolvedElement.element;
|
var target = resolvedElement.element;
|
||||||
|
|
||||||
@ -1592,10 +1595,16 @@ export class Program extends DiagnosticEmitter {
|
|||||||
return null;
|
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)
|
// start by resolving the lhs target (expression before the last dot)
|
||||||
var targetExpression = elementAccess.expression;
|
var targetExpression = elementAccess.expression;
|
||||||
resolvedElement = this.resolveExpression(targetExpression, contextualFunction);
|
resolvedElement = this.resolveExpression(
|
||||||
|
targetExpression,
|
||||||
|
contextualFunction
|
||||||
|
);
|
||||||
if (!resolvedElement) return null;
|
if (!resolvedElement) return null;
|
||||||
var target = resolvedElement.element;
|
var target = resolvedElement.element;
|
||||||
switch (target.kind) {
|
switch (target.kind) {
|
||||||
@ -1625,7 +1634,10 @@ export class Program extends DiagnosticEmitter {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveExpression(expression: Expression, contextualFunction: Function): ResolvedElement | null {
|
resolveExpression(
|
||||||
|
expression: Expression,
|
||||||
|
contextualFunction: Function
|
||||||
|
): ResolvedElement | null {
|
||||||
var classType: Class | null;
|
var classType: Class | null;
|
||||||
|
|
||||||
while (expression.kind == NodeKind.PARENTHESIZED) {
|
while (expression.kind == NodeKind.PARENTHESIZED) {
|
||||||
@ -1672,17 +1684,26 @@ export class Program extends DiagnosticEmitter {
|
|||||||
return this.resolveIdentifier(<IdentifierExpression>expression, contextualFunction);
|
return this.resolveIdentifier(<IdentifierExpression>expression, contextualFunction);
|
||||||
|
|
||||||
case NodeKind.PROPERTYACCESS:
|
case NodeKind.PROPERTYACCESS:
|
||||||
return this.resolvePropertyAccess(<PropertyAccessExpression>expression, contextualFunction);
|
return this.resolvePropertyAccess(
|
||||||
|
<PropertyAccessExpression>expression,
|
||||||
|
contextualFunction
|
||||||
|
);
|
||||||
|
|
||||||
case NodeKind.ELEMENTACCESS:
|
case NodeKind.ELEMENTACCESS:
|
||||||
return this.resolveElementAccess(<ElementAccessExpression>expression, contextualFunction);
|
return this.resolveElementAccess(
|
||||||
|
<ElementAccessExpression>expression,
|
||||||
|
contextualFunction
|
||||||
|
);
|
||||||
|
|
||||||
case NodeKind.CALL:
|
case NodeKind.CALL:
|
||||||
var resolved = this.resolveExpression((<CallExpression>expression).expression, contextualFunction);
|
var resolved = this.resolveExpression(
|
||||||
|
(<CallExpression>expression).expression,
|
||||||
|
contextualFunction
|
||||||
|
);
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
var element = resolved.element;
|
var element = resolved.element;
|
||||||
if (element && element.kind == ElementKind.FUNCTION_PROTOTYPE) {
|
if (element && element.kind == ElementKind.FUNCTION_PROTOTYPE) {
|
||||||
var instance = (<FunctionPrototype>element).resolveInclTypeArguments(
|
var instance = (<FunctionPrototype>element).resolveUsingTypeArguments(
|
||||||
(<CallExpression>expression).typeArguments,
|
(<CallExpression>expression).typeArguments,
|
||||||
null,
|
null,
|
||||||
expression
|
expression
|
||||||
@ -2130,6 +2151,7 @@ export class FunctionPrototype extends Element {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Resolves this prototype to an instance using the specified concrete type arguments. */
|
||||||
resolve(
|
resolve(
|
||||||
functionTypeArguments: Type[] | null = null,
|
functionTypeArguments: Type[] | null = null,
|
||||||
contextualTypeArguments: Map<string,Type> | null = null
|
contextualTypeArguments: Map<string,Type> | null = null
|
||||||
@ -2226,7 +2248,28 @@ export class FunctionPrototype extends Element {
|
|||||||
return instance;
|
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,
|
typeArgumentNodes: TypeNode[] | null,
|
||||||
contextualTypeArguments: Map<string,Type> | null,
|
contextualTypeArguments: Map<string,Type> | null,
|
||||||
reportNode: Node
|
reportNode: Node
|
||||||
@ -2245,23 +2288,27 @@ export class FunctionPrototype extends Element {
|
|||||||
return this.resolve(resolvedTypeArguments, contextualTypeArguments);
|
return this.resolve(resolvedTypeArguments, contextualTypeArguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
resolvePartial(classTypeArguments: Type[] | null): FunctionPrototype | null {
|
/** Resolves the type arguments to use when compiling a built-in call. Must be a built-in. */
|
||||||
if (!this.classPrototype) {
|
resolveBuiltinTypeArguments(
|
||||||
throw new Error("partially resolved instance method must reference its class prototype");
|
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) {
|
return resolvedTypeArguments;
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toString(): string { return this.simpleName; }
|
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 instanceKey = typeArguments ? typesToString(typeArguments) : "";
|
||||||
var instance = this.instances.get(instanceKey);
|
var instance = this.instances.get(instanceKey);
|
||||||
if (instance) return instance;
|
if (instance) return instance;
|
||||||
@ -2790,7 +2841,8 @@ export class ClassPrototype extends Element {
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
resolveInclTypeArguments(
|
/** Resolves the specified type arguments prior to resolving this prototype to an instance. */
|
||||||
|
resolveUsingTypeArguments(
|
||||||
typeArgumentNodes: TypeNode[] | null,
|
typeArgumentNodes: TypeNode[] | null,
|
||||||
contextualTypeArguments: Map<string,Type> | null,
|
contextualTypeArguments: Map<string,Type> | null,
|
||||||
alternativeReportNode: Node | null
|
alternativeReportNode: Node | null
|
||||||
|
@ -5,11 +5,24 @@
|
|||||||
(elem (i32.const 0) $start~anonymous|0 $start~anonymous|0 $start~someName|2)
|
(elem (i32.const 0) $start~anonymous|0 $start~anonymous|0 $start~someName|2)
|
||||||
(memory $0 1)
|
(memory $0 1)
|
||||||
(export "memory" (memory $0))
|
(export "memory" (memory $0))
|
||||||
(start $start~someName|2)
|
(start $start)
|
||||||
(func $start~anonymous|0 (; 0 ;) (type $ii) (param $0 i32) (result i32)
|
(func $start~anonymous|0 (; 0 ;) (type $ii) (param $0 i32) (result i32)
|
||||||
(get_local $0)
|
(get_local $0)
|
||||||
)
|
)
|
||||||
(func $start~someName|2 (; 1 ;) (type $v)
|
(func $start~someName|2 (; 1 ;) (type $v)
|
||||||
(nop)
|
(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)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
var f1 = function(a: i32): i32 {
|
var f1 = function(a: i32): i32 {
|
||||||
return a;
|
return a;
|
||||||
};
|
};
|
||||||
f1;
|
f1(1);
|
||||||
var f2 = (a: i32): i32 => {
|
var f2 = (a: i32): i32 => {
|
||||||
return a;
|
return a;
|
||||||
};
|
};
|
||||||
f2;
|
f2(2);
|
||||||
var f3 = function someName(): void {
|
var f3 = function someName(): void {
|
||||||
};
|
};
|
||||||
f3;
|
f3();
|
||||||
|
@ -24,13 +24,15 @@
|
|||||||
)
|
)
|
||||||
(func $start (; 3 ;) (type $v)
|
(func $start (; 3 ;) (type $v)
|
||||||
(drop
|
(drop
|
||||||
(get_global $function-expression/f1)
|
(call $start~anonymous|0
|
||||||
|
(i32.const 1)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
(drop
|
(drop
|
||||||
(get_global $function-expression/f2)
|
(call $start~anonymous|1
|
||||||
)
|
(i32.const 2)
|
||||||
(drop
|
)
|
||||||
(get_global $function-expression/f3)
|
|
||||||
)
|
)
|
||||||
|
(call $start~someName|2)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -4,7 +4,6 @@ const webpack = require("webpack");
|
|||||||
|
|
||||||
// Build the C-like library
|
// Build the C-like library
|
||||||
const lib = {
|
const lib = {
|
||||||
mode: "production",
|
|
||||||
entry: [ "./src/glue/js", "./src/index.ts" ],
|
entry: [ "./src/glue/js", "./src/index.ts" ],
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
@ -23,10 +22,8 @@ const lib = {
|
|||||||
filename: "assemblyscript.js",
|
filename: "assemblyscript.js",
|
||||||
path: path.resolve(__dirname, "dist"),
|
path: path.resolve(__dirname, "dist"),
|
||||||
library: "assemblyscript",
|
library: "assemblyscript",
|
||||||
libraryTarget: "umd"
|
libraryTarget: "umd",
|
||||||
},
|
globalObject: "typeof self !== 'undefined' ? self : this"
|
||||||
optimization: {
|
|
||||||
minimize: true
|
|
||||||
},
|
},
|
||||||
devtool: "source-map",
|
devtool: "source-map",
|
||||||
performance: {
|
performance: {
|
||||||
@ -36,16 +33,10 @@ const lib = {
|
|||||||
|
|
||||||
// Build asc for browser usage
|
// Build asc for browser usage
|
||||||
const bin = {
|
const bin = {
|
||||||
mode: "production",
|
|
||||||
context: path.join(__dirname, "bin"),
|
context: path.join(__dirname, "bin"),
|
||||||
entry: [ "./asc.js" ],
|
entry: [ "./asc.js" ],
|
||||||
externals: [{
|
externals: [{
|
||||||
"../dist/assemblyscript.js": {
|
"../dist/assemblyscript.js": "assemblyscript"
|
||||||
commonjs: "assemblyscript",
|
|
||||||
commonjs2: "assemblyscript",
|
|
||||||
amd: "assemblyscript",
|
|
||||||
root: "_"
|
|
||||||
}
|
|
||||||
}],
|
}],
|
||||||
node: {
|
node: {
|
||||||
"fs": "empty",
|
"fs": "empty",
|
||||||
@ -57,10 +48,8 @@ const bin = {
|
|||||||
filename: "asc.js",
|
filename: "asc.js",
|
||||||
path: path.resolve(__dirname, "dist"),
|
path: path.resolve(__dirname, "dist"),
|
||||||
library: "asc",
|
library: "asc",
|
||||||
libraryTarget: "umd"
|
libraryTarget: "umd",
|
||||||
},
|
globalObject: "typeof self !== 'undefined' ? self : this"
|
||||||
optimization: {
|
|
||||||
minimize: true
|
|
||||||
},
|
},
|
||||||
devtool: "source-map",
|
devtool: "source-map",
|
||||||
plugins: [
|
plugins: [
|
||||||
@ -79,7 +68,7 @@ const bin = {
|
|||||||
},
|
},
|
||||||
__dirname: JSON.stringify(".")
|
__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)$/),
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user