mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-09 21:11:27 +00:00
Implement reference counting (#592)
This commit is contained in:
parent
3ed76a97f0
commit
0484a6b740
12
.travis.yml
12
.travis.yml
@ -3,19 +3,15 @@ notifications:
|
||||
email: false
|
||||
before_install: npm config set progress=false && npm i -g npm@latest --no-audit
|
||||
install: npm ci --no-audit
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
jobs:
|
||||
include:
|
||||
- node_js: lts/*
|
||||
script:
|
||||
- if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then ./scripts/check-pr.sh; fi
|
||||
- if [ "$TRAVIS_PULL_REQUEST" != "false" ] && [ "$TRAVIS_PULL_REQUEST_BRANCH" != "dev" ]; then ./scripts/check-pr.sh; fi
|
||||
- npm run all
|
||||
- if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then
|
||||
cd $TRAVIS_BUILD_DIR/tests/allocators/arena && npm run build && cd .. && npm test arena &&
|
||||
cd $TRAVIS_BUILD_DIR/tests/allocators/buddy && npm run build && cd .. && npm test buddy &&
|
||||
cd $TRAVIS_BUILD_DIR/tests/allocators/tlsf && npm run build && cd .. && npm test tlsf;
|
||||
cd $TRAVIS_BUILD_DIR/tests/allocators/rt-full && npm run build && cd .. && npm test rt-full &&
|
||||
cd $TRAVIS_BUILD_DIR/tests/allocators/rt-stub && npm run build && cd .. && npm test rt-stub;
|
||||
fi
|
||||
env: Runs the tests on node.js LTS, also tests allocators
|
||||
- node_js: node
|
||||
@ -24,7 +20,7 @@ jobs:
|
||||
env: Runs the tests on node.js stable
|
||||
- node_js: node
|
||||
script:
|
||||
- npm run clean && npm run test:compiler
|
||||
- npm run clean && npm run test:compiler rt/flags threads
|
||||
env:
|
||||
- Runs experimental tests on node.js v8-canary using
|
||||
- ASC_FEATURES="simd,threads"
|
||||
|
54
README.md
54
README.md
@ -3,9 +3,9 @@
|
||||
|
||||
[](https://travis-ci.org/AssemblyScript/assemblyscript)
|
||||
|
||||
**AssemblyScript** compiles strictly typed [TypeScript](http://www.typescriptlang.org) (basically JavaScript with types) to [WebAssembly](http://webassembly.org) using [Binaryen](https://github.com/WebAssembly/binaryen). It generates lean and mean WebAssembly modules while being just an `npm install` away.
|
||||
**AssemblyScript** compiles a strict subset of [TypeScript](http://www.typescriptlang.org) (basically JavaScript with types) to [WebAssembly](http://webassembly.org) using [Binaryen](https://github.com/WebAssembly/binaryen). It generates lean and mean WebAssembly modules while being just an `npm install` away.
|
||||
|
||||
Try it out in [WebAssembly Studio](https://webassembly.studio)!
|
||||
Check out the [documentation](https://docs.assemblyscript.org) or try it out in [WebAssembly Studio](https://webassembly.studio)!
|
||||
|
||||
---
|
||||
|
||||
@ -43,60 +43,20 @@ Motivation
|
||||
|
||||
> I do think [compiling TypeScript into WASM] is tremendously useful. It allows JavaScript developers to create WASM modules without having to learn C. – Colin Eberhardt, [Exploring different approaches to building WebAssembly modules](http://blog.scottlogic.com/2017/10/17/wasm-mandelbrot.html) (Oct 17, 2017)
|
||||
|
||||
Getting started
|
||||
---------------
|
||||
Instructions
|
||||
------------
|
||||
|
||||
All the details are provided in the [AssemblyScript wiki](https://github.com/AssemblyScript/assemblyscript/wiki) - make sure to pay it a visit. With that being said, the easiest way to get started with AssemblyScript is to point npm at the GitHub repository (for now)
|
||||
|
||||
```
|
||||
$> npm install --save-dev AssemblyScript/assemblyscript
|
||||
```
|
||||
|
||||
followed by [scaffolding](https://github.com/AssemblyScript/assemblyscript/wiki/Using-the-CLI#scaffolding-with-asinit) a new project including the necessary configuration files, for example in the current directory:
|
||||
|
||||
```
|
||||
$> npx asinit .
|
||||
```
|
||||
|
||||
Once the project is set up, it's just a matter of using your existing [TypeScript tooling](https://code.visualstudio.com) while coding, and [using the CLI](https://github.com/AssemblyScript/assemblyscript/wiki/Using-the-CLI) to build to WebAssembly, either manually, or using (and maybe modifying) the generated build task in the generated `package.json`:
|
||||
|
||||
```
|
||||
$> npm run asbuild
|
||||
```
|
||||
|
||||
The CLI API can also [be used programmatically](./cli).
|
||||
|
||||
If you rather prefer an installation suitable for development, pretty much the same can be achieved by cloning the GitHub repository instead:
|
||||
For general usage instructions, please refer to the [documentation](https://docs.assemblyscript.org) instead. The following sets up a *development environment* of the compiler, for example if you plan to make a pull request:
|
||||
|
||||
```
|
||||
$> git clone https://github.com/AssemblyScript/assemblyscript.git
|
||||
$> cd assemblyscript
|
||||
$> npm install
|
||||
$> npm link
|
||||
$> npm clean
|
||||
```
|
||||
|
||||
**Note** that a fresh clone of the compiler will use the distribution files in `dist/`, but it can also run [the sources](./src) directly through ts-node after an `npm run clean`, which is useful in development. This condition can also be checked by running `asc -v` (it is running the sources if it states `-dev`).
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
* **[Conway's Game of Life](./examples/game-of-life)** [ [demo](https://assemblyscript.github.io/assemblyscript/examples/game-of-life) | [fiddle](https://webassembly.studio/?f=gvuw4enb3qk) ]<br />
|
||||
Continuously updates the cellular automaton and visualizes its state on a canvas.
|
||||
|
||||
* **[Mandelbrot Set](./examples/mandelbrot)** [ [demo](https://assemblyscript.github.io/assemblyscript/examples/mandelbrot) | [fiddle](https://webassembly.studio/?f=m6hbiw9wyq) ]<br />
|
||||
Renders the Mandelbrot set to a canvas.
|
||||
|
||||
* **[i64 polyfill](./examples/i64-polyfill)**<br />
|
||||
Exposes WebAssembly's i64 operations to JavaScript using 32-bit integers (low and high bits).
|
||||
|
||||
* **[PSON decoder](./examples/pson)**<br />
|
||||
A simple decoder for the PSON binary format.
|
||||
|
||||
* **[WASM parser](./lib/parse)**<br />
|
||||
A WebAssembly binary parser in WebAssembly.
|
||||
|
||||
* **[N-body system](./examples/n-body)** [ [demo](https://assemblyscript.github.io/assemblyscript/examples/n-body) ]<br />
|
||||
An implementation of the N-body system from the [Computer Language Benchmarks Game](https://benchmarksgame-team.pages.debian.net/benchmarksgame/).
|
||||
Note that a fresh clone of the compiler will use the distribution files in `dist/`, but after an `npm clean` it will run [the sources](./src) directly through ts-node, which is useful in development. This condition can also be checked by running `asc -v` (it is running the sources if it states `-dev`). Also please see our [contribution guidelines](./CONTRIBUTING.md) before making your first pull request.
|
||||
|
||||
Building
|
||||
--------
|
||||
|
99
cli/asc.js
99
cli/asc.js
@ -22,6 +22,8 @@ const optionsUtil = require("./util/options");
|
||||
const mkdirp = require("./util/mkdirp");
|
||||
const EOL = process.platform === "win32" ? "\r\n" : "\n";
|
||||
|
||||
// global.Binaryen = require("../lib/binaryen");
|
||||
|
||||
// Emscripten adds an `uncaughtException` listener to Binaryen that results in an additional
|
||||
// useless code fragment on top of an actual error. suppress this:
|
||||
if (process.removeAllListeners) process.removeAllListeners("uncaughtException");
|
||||
@ -68,7 +70,7 @@ exports.sourceMapRoot = "assemblyscript:///";
|
||||
exports.libraryPrefix = assemblyscript.LIBRARY_PREFIX;
|
||||
|
||||
/** Default Binaryen optimization level. */
|
||||
exports.defaultOptimizeLevel = 2;
|
||||
exports.defaultOptimizeLevel = 3;
|
||||
|
||||
/** Default Binaryen shrink level. */
|
||||
exports.defaultShrinkLevel = 1;
|
||||
@ -228,30 +230,18 @@ exports.main = function main(argv, options, callback) {
|
||||
var parser = null;
|
||||
|
||||
// Include library files
|
||||
if (!args.noLib) {
|
||||
Object.keys(exports.libraryFiles).forEach(libPath => {
|
||||
if (libPath.indexOf("/") >= 0) return; // in sub-directory: imported on demand
|
||||
stats.parseCount++;
|
||||
stats.parseTime += measure(() => {
|
||||
parser = assemblyscript.parseFile(
|
||||
exports.libraryFiles[libPath],
|
||||
exports.libraryPrefix + libPath + ".ts",
|
||||
false,
|
||||
parser
|
||||
);
|
||||
});
|
||||
});
|
||||
} else { // always include builtins
|
||||
Object.keys(exports.libraryFiles).forEach(libPath => {
|
||||
if (libPath.indexOf("/") >= 0) return; // in sub-directory: imported on demand
|
||||
stats.parseCount++;
|
||||
stats.parseTime += measure(() => {
|
||||
parser = assemblyscript.parseFile(
|
||||
exports.libraryFiles["builtins"],
|
||||
exports.libraryPrefix + "builtins.ts",
|
||||
exports.libraryFiles[libPath],
|
||||
exports.libraryPrefix + libPath + ".ts",
|
||||
false,
|
||||
parser
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
const customLibDirs = [];
|
||||
if (args.lib) {
|
||||
let lib = args.lib;
|
||||
@ -287,6 +277,7 @@ exports.main = function main(argv, options, callback) {
|
||||
function parseBacklog() {
|
||||
var sourcePath, sourceText;
|
||||
while ((sourcePath = parser.nextFile()) != null) {
|
||||
sourceText = null;
|
||||
|
||||
// Load library file if explicitly requested
|
||||
if (sourcePath.startsWith(exports.libraryPrefix)) {
|
||||
@ -335,12 +326,12 @@ exports.main = function main(argv, options, callback) {
|
||||
} else {
|
||||
for (let i = 0, k = customLibDirs.length; i < k; ++i) {
|
||||
const dir = customLibDirs[i];
|
||||
sourceText = readFile(plainName + ".ts", customLibDirs[i]);
|
||||
sourceText = readFile(plainName + ".ts", dir);
|
||||
if (sourceText !== null) {
|
||||
sourcePath = exports.libraryPrefix + plainName + ".ts";
|
||||
break;
|
||||
} else {
|
||||
sourceText = readFile(indexName + ".ts", customLibDirs[i]);
|
||||
sourceText = readFile(indexName + ".ts", dir);
|
||||
if (sourceText !== null) {
|
||||
sourcePath = exports.libraryPrefix + indexName + ".ts";
|
||||
break;
|
||||
@ -364,6 +355,26 @@ exports.main = function main(argv, options, callback) {
|
||||
}
|
||||
}
|
||||
|
||||
// Include runtime template before entry files so its setup runs first
|
||||
{
|
||||
let runtimeName = String(args.runtime);
|
||||
let runtimePath = "rt/index-" + runtimeName;
|
||||
let runtimeText = exports.libraryFiles[runtimePath];
|
||||
if (runtimeText == null) {
|
||||
runtimePath = runtimeName;
|
||||
runtimeText = readFile(runtimePath + ".ts", baseDir);
|
||||
if (runtimeText == null) {
|
||||
return callback(Error("Runtime '" + runtimeName + "' not found."));
|
||||
}
|
||||
} else {
|
||||
runtimePath = "~lib/" + runtimePath;
|
||||
}
|
||||
stats.parseCount++;
|
||||
stats.parseTime += measure(() => {
|
||||
parser = assemblyscript.parseFile(runtimeText, runtimePath, true, parser);
|
||||
});
|
||||
}
|
||||
|
||||
// Include entry files
|
||||
for (let i = 0, k = argv.length; i < k; ++i) {
|
||||
const filename = argv[i];
|
||||
@ -387,11 +398,18 @@ exports.main = function main(argv, options, callback) {
|
||||
stats.parseTime += measure(() => {
|
||||
parser = assemblyscript.parseFile(sourceText, sourcePath, true, parser);
|
||||
});
|
||||
}
|
||||
|
||||
// Parse entry files
|
||||
{
|
||||
let code = parseBacklog();
|
||||
if (code) return code;
|
||||
}
|
||||
|
||||
// Call afterParse transform hook
|
||||
applyTransform("afterParse", parser);
|
||||
|
||||
// Parse additional files, if any
|
||||
{
|
||||
let code = parseBacklog();
|
||||
if (code) return code;
|
||||
@ -423,17 +441,16 @@ exports.main = function main(argv, options, callback) {
|
||||
assemblyscript.setImportMemory(compilerOptions, args.importMemory);
|
||||
assemblyscript.setSharedMemory(compilerOptions, args.sharedMemory);
|
||||
assemblyscript.setImportTable(compilerOptions, args.importTable);
|
||||
assemblyscript.setExplicitStart(compilerOptions, args.explicitStart);
|
||||
assemblyscript.setMemoryBase(compilerOptions, args.memoryBase >>> 0);
|
||||
assemblyscript.setSourceMap(compilerOptions, args.sourceMap != null);
|
||||
assemblyscript.setOptimizeLevelHints(compilerOptions, optimizeLevel, shrinkLevel);
|
||||
|
||||
if (!args.noLib) {
|
||||
// Initialize default aliases
|
||||
assemblyscript.setGlobalAlias(compilerOptions, "Math", "NativeMath");
|
||||
assemblyscript.setGlobalAlias(compilerOptions, "Mathf", "NativeMathf");
|
||||
assemblyscript.setGlobalAlias(compilerOptions, "abort", "~lib/env/abort");
|
||||
assemblyscript.setGlobalAlias(compilerOptions, "trace", "~lib/env/trace");
|
||||
}
|
||||
// Initialize default aliases
|
||||
assemblyscript.setGlobalAlias(compilerOptions, "Math", "NativeMath");
|
||||
assemblyscript.setGlobalAlias(compilerOptions, "Mathf", "NativeMathf");
|
||||
assemblyscript.setGlobalAlias(compilerOptions, "abort", "~lib/builtins/abort");
|
||||
assemblyscript.setGlobalAlias(compilerOptions, "trace", "~lib/builtins/trace");
|
||||
|
||||
// Add or override aliases if specified
|
||||
if (args.use) {
|
||||
@ -463,15 +480,13 @@ exports.main = function main(argv, options, callback) {
|
||||
|
||||
var module;
|
||||
stats.compileCount++;
|
||||
(() => {
|
||||
try {
|
||||
stats.compileTime += measure(() => {
|
||||
module = assemblyscript.compileProgram(program, compilerOptions);
|
||||
});
|
||||
} catch (e) {
|
||||
return callback(e);
|
||||
}
|
||||
})();
|
||||
try {
|
||||
stats.compileTime += measure(() => {
|
||||
module = assemblyscript.compileProgram(program, compilerOptions);
|
||||
});
|
||||
} catch (e) {
|
||||
return callback(e);
|
||||
}
|
||||
if (checkDiagnostics(parser, stderr)) {
|
||||
if (module) module.dispose();
|
||||
return callback(Error("Compile error"));
|
||||
@ -697,6 +712,9 @@ exports.main = function main(argv, options, callback) {
|
||||
if (args.measure) {
|
||||
printStats(stats, stderr);
|
||||
}
|
||||
if (args.printrtti) {
|
||||
printRTTI(program, stderr);
|
||||
}
|
||||
return callback(null);
|
||||
|
||||
function readFileNode(filename, baseDir) {
|
||||
@ -833,6 +851,15 @@ function printStats(stats, output) {
|
||||
|
||||
exports.printStats = printStats;
|
||||
|
||||
/** Prints runtime type information. */
|
||||
function printRTTI(program, output) {
|
||||
if (!output) output = process.stderr;
|
||||
output.write("# Runtime type information (RTTI)\n");
|
||||
output.write(assemblyscript.buildRTTI(program));
|
||||
}
|
||||
|
||||
exports.printRTTI = printRTTI;
|
||||
|
||||
var allocBuffer = typeof global !== "undefined" && global.Buffer
|
||||
? global.Buffer.allocUnsafe || function(len) { return new global.Buffer(len); }
|
||||
: function(len) { return new Uint8Array(len) };
|
||||
|
24
cli/asc.json
24
cli/asc.json
@ -13,7 +13,7 @@
|
||||
"description": [
|
||||
"Optimizes the module. Also has the usual shorthands:",
|
||||
"",
|
||||
" -O Uses defaults. Equivalent to -O2s",
|
||||
" -O Uses defaults. Equivalent to -O3s",
|
||||
" -O0 Equivalent to --optimizeLevel 0",
|
||||
" -O1 Equivalent to --optimizeLevel 1",
|
||||
" -O2 Equivalent to --optimizeLevel 2",
|
||||
@ -81,6 +81,19 @@
|
||||
],
|
||||
"type": "s"
|
||||
},
|
||||
"runtime": {
|
||||
"description": [
|
||||
"Specifies the runtime implementation to include in the program.",
|
||||
"",
|
||||
" full Default runtime based on TLSF and reference counting.",
|
||||
" half Same as 'full', but not exported to the host.",
|
||||
" stub Minimal stub implementation without free/GC support.",
|
||||
" none Same as 'stub', but not exported to the host.",
|
||||
""
|
||||
],
|
||||
"type": "s",
|
||||
"default": "full"
|
||||
},
|
||||
"debug": {
|
||||
"description": "Enables debug information in emitted binaries.",
|
||||
"type": "b",
|
||||
@ -116,8 +129,8 @@
|
||||
"type": "b",
|
||||
"default": false
|
||||
},
|
||||
"noLib": {
|
||||
"description": "Does not include the shipped standard library.",
|
||||
"explicitStart": {
|
||||
"description": "Exports an explicit start function to be called manually.",
|
||||
"type": "b",
|
||||
"default": false
|
||||
},
|
||||
@ -177,6 +190,11 @@
|
||||
"type": "b",
|
||||
"default": false
|
||||
},
|
||||
"printrtti": {
|
||||
"description": "Prints the module's runtime type information to stderr.",
|
||||
"type": "b",
|
||||
"default": false
|
||||
},
|
||||
"noColors": {
|
||||
"description": "Disables terminal colors.",
|
||||
"type": "b",
|
||||
|
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
5606
dist/assemblyscript.d.ts
vendored
Normal file
5606
dist/assemblyscript.d.ts
vendored
Normal file
File diff suppressed because it is too large
Load Diff
3
dist/assemblyscript.js
vendored
3
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
15
examples/game-of-life/build/optimized.d.ts
vendored
15
examples/game-of-life/build/optimized.d.ts
vendored
@ -8,17 +8,8 @@ declare module ASModule {
|
||||
type f32 = number;
|
||||
type f64 = number;
|
||||
type bool = any;
|
||||
namespace JSMath {
|
||||
function random(): f64;
|
||||
}
|
||||
var w: i32;
|
||||
var h: i32;
|
||||
var s: i32;
|
||||
function init(width: i32, height: i32): void;
|
||||
function step(): void;
|
||||
function fill(x: u32, y: u32, p: f64): void;
|
||||
var BGR_ALIVE: u32;
|
||||
var BGR_DEAD: u32;
|
||||
var BIT_ROT: u32;
|
||||
export function init(width: i32, height: i32): void;
|
||||
export function step(): void;
|
||||
export function fill(x: u32, y: u32, p: f64): void;
|
||||
}
|
||||
export default ASModule;
|
||||
|
Binary file not shown.
@ -8,13 +8,10 @@
|
||||
(import "config" "BGR_ALIVE" (global $assembly/config/BGR_ALIVE i32))
|
||||
(import "config" "BIT_ROT" (global $assembly/config/BIT_ROT i32))
|
||||
(import "Math" "random" (func $~lib/bindings/Math/random (result f64)))
|
||||
(table $0 1 funcref)
|
||||
(elem (i32.const 0) $null)
|
||||
(global $assembly/index/w (mut i32) (i32.const 0))
|
||||
(global $assembly/index/h (mut i32) (i32.const 0))
|
||||
(global $assembly/index/s (mut i32) (i32.const 0))
|
||||
(export "memory" (memory $0))
|
||||
(export "table" (table $0))
|
||||
(export "init" (func $assembly/index/init))
|
||||
(export "step" (func $assembly/index/step))
|
||||
(export "fill" (func $assembly/index/fill))
|
||||
@ -136,7 +133,7 @@
|
||||
local.get $7
|
||||
local.get $1
|
||||
select
|
||||
local.tee $2
|
||||
local.tee $3
|
||||
global.get $assembly/index/w
|
||||
local.get $4
|
||||
i32.mul
|
||||
@ -165,7 +162,7 @@
|
||||
local.get $7
|
||||
i32.eq
|
||||
select
|
||||
local.tee $3
|
||||
local.tee $2
|
||||
global.get $assembly/index/w
|
||||
local.get $4
|
||||
i32.mul
|
||||
@ -179,17 +176,6 @@
|
||||
global.get $assembly/index/w
|
||||
local.get $0
|
||||
i32.mul
|
||||
local.get $2
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
i32.load
|
||||
i32.const 1
|
||||
i32.and
|
||||
i32.add
|
||||
global.get $assembly/index/w
|
||||
local.get $0
|
||||
i32.mul
|
||||
local.get $3
|
||||
i32.add
|
||||
i32.const 2
|
||||
@ -199,7 +185,7 @@
|
||||
i32.and
|
||||
i32.add
|
||||
global.get $assembly/index/w
|
||||
local.get $5
|
||||
local.get $0
|
||||
i32.mul
|
||||
local.get $2
|
||||
i32.add
|
||||
@ -212,6 +198,17 @@
|
||||
global.get $assembly/index/w
|
||||
local.get $5
|
||||
i32.mul
|
||||
local.get $3
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
i32.load
|
||||
i32.const 1
|
||||
i32.and
|
||||
i32.add
|
||||
global.get $assembly/index/w
|
||||
local.get $5
|
||||
i32.mul
|
||||
local.get $1
|
||||
i32.add
|
||||
i32.const 2
|
||||
@ -223,7 +220,7 @@
|
||||
global.get $assembly/index/w
|
||||
local.get $5
|
||||
i32.mul
|
||||
local.get $3
|
||||
local.get $2
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
|
@ -13,9 +13,7 @@
|
||||
(global $assembly/index/w (mut i32) (i32.const 0))
|
||||
(global $assembly/index/h (mut i32) (i32.const 0))
|
||||
(global $assembly/index/s (mut i32) (i32.const 0))
|
||||
(global $~lib/memory/HEAP_BASE i32 (i32.const 8))
|
||||
(export "memory" (memory $0))
|
||||
(export "table" (table $0))
|
||||
(export "init" (func $assembly/index/init))
|
||||
(export "step" (func $assembly/index/step))
|
||||
(export "fill" (func $assembly/index/fill))
|
||||
@ -53,7 +51,7 @@
|
||||
br_if $break|1
|
||||
block $assembly/index/set|inlined.0
|
||||
local.get $3
|
||||
local.set $4
|
||||
local.set $6
|
||||
local.get $2
|
||||
local.set $5
|
||||
call $~lib/bindings/Math/random
|
||||
@ -68,17 +66,17 @@
|
||||
i32.const -16777216
|
||||
i32.or
|
||||
end
|
||||
local.set $6
|
||||
local.set $4
|
||||
global.get $assembly/index/s
|
||||
local.get $5
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
i32.add
|
||||
local.get $4
|
||||
local.get $6
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
local.get $6
|
||||
local.get $4
|
||||
i32.store
|
||||
end
|
||||
local.get $3
|
||||
@ -135,368 +133,364 @@
|
||||
i32.lt_s
|
||||
i32.eqz
|
||||
br_if $break|0
|
||||
block
|
||||
local.get $2
|
||||
i32.const 0
|
||||
i32.eq
|
||||
if (result i32)
|
||||
local.get $0
|
||||
else
|
||||
local.get $2
|
||||
i32.const 1
|
||||
i32.sub
|
||||
end
|
||||
local.set $3
|
||||
local.get $2
|
||||
local.get $2
|
||||
i32.const 0
|
||||
i32.eq
|
||||
if (result i32)
|
||||
local.get $0
|
||||
i32.eq
|
||||
if (result i32)
|
||||
else
|
||||
local.get $2
|
||||
i32.const 1
|
||||
i32.sub
|
||||
end
|
||||
local.set $3
|
||||
local.get $2
|
||||
local.get $0
|
||||
i32.eq
|
||||
if (result i32)
|
||||
i32.const 0
|
||||
else
|
||||
local.get $2
|
||||
i32.const 1
|
||||
i32.add
|
||||
end
|
||||
local.set $4
|
||||
block $break|1
|
||||
i32.const 0
|
||||
local.set $5
|
||||
loop $repeat|1
|
||||
local.get $5
|
||||
global.get $assembly/index/w
|
||||
i32.lt_s
|
||||
i32.eqz
|
||||
br_if $break|1
|
||||
local.get $5
|
||||
i32.const 0
|
||||
else
|
||||
local.get $2
|
||||
i32.const 1
|
||||
i32.add
|
||||
end
|
||||
local.set $4
|
||||
block $break|1
|
||||
i32.const 0
|
||||
local.set $5
|
||||
loop $repeat|1
|
||||
i32.eq
|
||||
if (result i32)
|
||||
local.get $1
|
||||
else
|
||||
local.get $5
|
||||
global.get $assembly/index/w
|
||||
i32.lt_s
|
||||
i32.eqz
|
||||
br_if $break|1
|
||||
block
|
||||
local.get $5
|
||||
i32.const 0
|
||||
i32.eq
|
||||
if (result i32)
|
||||
local.get $1
|
||||
else
|
||||
local.get $5
|
||||
i32.const 1
|
||||
i32.sub
|
||||
end
|
||||
local.set $6
|
||||
local.get $5
|
||||
local.get $1
|
||||
i32.eq
|
||||
if (result i32)
|
||||
i32.const 0
|
||||
else
|
||||
local.get $5
|
||||
i32.const 1
|
||||
i32.add
|
||||
end
|
||||
local.set $7
|
||||
block $assembly/index/get|inlined.0 (result i32)
|
||||
local.get $6
|
||||
local.set $8
|
||||
local.get $3
|
||||
local.set $9
|
||||
local.get $9
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
local.get $8
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
i32.load
|
||||
end
|
||||
i32.const 1
|
||||
i32.and
|
||||
block $assembly/index/get|inlined.1 (result i32)
|
||||
local.get $5
|
||||
local.set $9
|
||||
local.get $3
|
||||
local.set $8
|
||||
local.get $8
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
local.get $9
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
i32.load
|
||||
end
|
||||
i32.const 1
|
||||
i32.and
|
||||
i32.add
|
||||
block $assembly/index/get|inlined.2 (result i32)
|
||||
local.get $7
|
||||
local.set $8
|
||||
local.get $3
|
||||
local.set $9
|
||||
local.get $9
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
local.get $8
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
i32.load
|
||||
end
|
||||
i32.const 1
|
||||
i32.and
|
||||
i32.add
|
||||
block $assembly/index/get|inlined.3 (result i32)
|
||||
local.get $6
|
||||
local.set $9
|
||||
local.get $2
|
||||
local.set $8
|
||||
local.get $8
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
local.get $9
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
i32.load
|
||||
end
|
||||
i32.const 1
|
||||
i32.and
|
||||
i32.add
|
||||
block $assembly/index/get|inlined.4 (result i32)
|
||||
local.get $7
|
||||
local.set $8
|
||||
local.get $2
|
||||
local.set $9
|
||||
local.get $9
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
local.get $8
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
i32.load
|
||||
end
|
||||
i32.const 1
|
||||
i32.and
|
||||
i32.add
|
||||
block $assembly/index/get|inlined.5 (result i32)
|
||||
local.get $6
|
||||
local.set $9
|
||||
local.get $4
|
||||
local.set $8
|
||||
local.get $8
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
local.get $9
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
i32.load
|
||||
end
|
||||
i32.const 1
|
||||
i32.and
|
||||
i32.add
|
||||
block $assembly/index/get|inlined.6 (result i32)
|
||||
local.get $5
|
||||
local.set $8
|
||||
local.get $4
|
||||
local.set $9
|
||||
local.get $9
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
local.get $8
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
i32.load
|
||||
end
|
||||
i32.const 1
|
||||
i32.and
|
||||
i32.add
|
||||
block $assembly/index/get|inlined.7 (result i32)
|
||||
local.get $7
|
||||
local.set $9
|
||||
local.get $4
|
||||
local.set $8
|
||||
local.get $8
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
local.get $9
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
i32.load
|
||||
end
|
||||
i32.const 1
|
||||
i32.and
|
||||
i32.add
|
||||
local.set $8
|
||||
block $assembly/index/get|inlined.8 (result i32)
|
||||
local.get $5
|
||||
local.set $9
|
||||
local.get $2
|
||||
local.set $10
|
||||
local.get $10
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
local.get $9
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
i32.load
|
||||
end
|
||||
local.set $10
|
||||
local.get $10
|
||||
i32.const 1
|
||||
i32.and
|
||||
if
|
||||
local.get $8
|
||||
i32.const 14
|
||||
i32.and
|
||||
i32.const 2
|
||||
i32.eq
|
||||
if
|
||||
local.get $5
|
||||
local.set $9
|
||||
local.get $2
|
||||
local.set $11
|
||||
local.get $10
|
||||
local.set $12
|
||||
local.get $12
|
||||
i32.const 24
|
||||
i32.shr_u
|
||||
global.get $assembly/config/BIT_ROT
|
||||
i32.sub
|
||||
local.tee $13
|
||||
i32.const 0
|
||||
local.tee $14
|
||||
local.get $13
|
||||
local.get $14
|
||||
i32.gt_s
|
||||
select
|
||||
local.set $13
|
||||
block $assembly/index/set|inlined.1
|
||||
local.get $9
|
||||
local.set $14
|
||||
local.get $11
|
||||
local.set $15
|
||||
local.get $13
|
||||
i32.const 24
|
||||
i32.shl
|
||||
local.get $12
|
||||
i32.const 16777215
|
||||
i32.and
|
||||
i32.or
|
||||
local.set $16
|
||||
global.get $assembly/index/s
|
||||
local.get $15
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
i32.add
|
||||
local.get $14
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
local.get $16
|
||||
i32.store
|
||||
end
|
||||
else
|
||||
local.get $5
|
||||
local.set $13
|
||||
local.get $2
|
||||
local.set $12
|
||||
global.get $assembly/config/BGR_DEAD
|
||||
i32.const -16777216
|
||||
i32.or
|
||||
local.set $11
|
||||
global.get $assembly/index/s
|
||||
local.get $12
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
i32.add
|
||||
local.get $13
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
local.get $11
|
||||
i32.store
|
||||
end
|
||||
else
|
||||
local.get $8
|
||||
i32.const 3
|
||||
i32.eq
|
||||
if
|
||||
local.get $5
|
||||
local.set $11
|
||||
local.get $2
|
||||
local.set $12
|
||||
global.get $assembly/config/BGR_ALIVE
|
||||
i32.const -16777216
|
||||
i32.or
|
||||
local.set $13
|
||||
global.get $assembly/index/s
|
||||
local.get $12
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
i32.add
|
||||
local.get $11
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
local.get $13
|
||||
i32.store
|
||||
else
|
||||
local.get $5
|
||||
local.set $13
|
||||
local.get $2
|
||||
local.set $12
|
||||
local.get $10
|
||||
local.set $11
|
||||
local.get $11
|
||||
i32.const 24
|
||||
i32.shr_u
|
||||
global.get $assembly/config/BIT_ROT
|
||||
i32.sub
|
||||
local.tee $9
|
||||
i32.const 0
|
||||
local.tee $16
|
||||
local.get $9
|
||||
local.get $16
|
||||
i32.gt_s
|
||||
select
|
||||
local.set $9
|
||||
block $assembly/index/set|inlined.4
|
||||
local.get $13
|
||||
local.set $16
|
||||
local.get $12
|
||||
local.set $15
|
||||
local.get $9
|
||||
i32.const 24
|
||||
i32.shl
|
||||
local.get $11
|
||||
i32.const 16777215
|
||||
i32.and
|
||||
i32.or
|
||||
local.set $14
|
||||
global.get $assembly/index/s
|
||||
local.get $15
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
i32.add
|
||||
local.get $16
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
local.get $14
|
||||
i32.store
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
i32.const 1
|
||||
i32.sub
|
||||
end
|
||||
local.set $6
|
||||
local.get $5
|
||||
local.get $1
|
||||
i32.eq
|
||||
if (result i32)
|
||||
i32.const 0
|
||||
else
|
||||
local.get $5
|
||||
i32.const 1
|
||||
i32.add
|
||||
local.set $5
|
||||
br $repeat|1
|
||||
unreachable
|
||||
end
|
||||
local.set $7
|
||||
block $assembly/index/get|inlined.0 (result i32)
|
||||
local.get $6
|
||||
local.set $9
|
||||
local.get $3
|
||||
local.set $8
|
||||
local.get $8
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
local.get $9
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
i32.load
|
||||
end
|
||||
i32.const 1
|
||||
i32.and
|
||||
block $assembly/index/get|inlined.1 (result i32)
|
||||
local.get $5
|
||||
local.set $9
|
||||
local.get $3
|
||||
local.set $8
|
||||
local.get $8
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
local.get $9
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
i32.load
|
||||
end
|
||||
i32.const 1
|
||||
i32.and
|
||||
i32.add
|
||||
block $assembly/index/get|inlined.2 (result i32)
|
||||
local.get $7
|
||||
local.set $9
|
||||
local.get $3
|
||||
local.set $8
|
||||
local.get $8
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
local.get $9
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
i32.load
|
||||
end
|
||||
i32.const 1
|
||||
i32.and
|
||||
i32.add
|
||||
block $assembly/index/get|inlined.3 (result i32)
|
||||
local.get $6
|
||||
local.set $9
|
||||
local.get $2
|
||||
local.set $8
|
||||
local.get $8
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
local.get $9
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
i32.load
|
||||
end
|
||||
i32.const 1
|
||||
i32.and
|
||||
i32.add
|
||||
block $assembly/index/get|inlined.4 (result i32)
|
||||
local.get $7
|
||||
local.set $9
|
||||
local.get $2
|
||||
local.set $8
|
||||
local.get $8
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
local.get $9
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
i32.load
|
||||
end
|
||||
i32.const 1
|
||||
i32.and
|
||||
i32.add
|
||||
block $assembly/index/get|inlined.5 (result i32)
|
||||
local.get $6
|
||||
local.set $9
|
||||
local.get $4
|
||||
local.set $8
|
||||
local.get $8
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
local.get $9
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
i32.load
|
||||
end
|
||||
i32.const 1
|
||||
i32.and
|
||||
i32.add
|
||||
block $assembly/index/get|inlined.6 (result i32)
|
||||
local.get $5
|
||||
local.set $9
|
||||
local.get $4
|
||||
local.set $8
|
||||
local.get $8
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
local.get $9
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
i32.load
|
||||
end
|
||||
i32.const 1
|
||||
i32.and
|
||||
i32.add
|
||||
block $assembly/index/get|inlined.7 (result i32)
|
||||
local.get $7
|
||||
local.set $9
|
||||
local.get $4
|
||||
local.set $8
|
||||
local.get $8
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
local.get $9
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
i32.load
|
||||
end
|
||||
i32.const 1
|
||||
i32.and
|
||||
i32.add
|
||||
local.set $9
|
||||
block $assembly/index/get|inlined.8 (result i32)
|
||||
local.get $5
|
||||
local.set $10
|
||||
local.get $2
|
||||
local.set $8
|
||||
local.get $8
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
local.get $10
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
i32.load
|
||||
end
|
||||
local.set $10
|
||||
local.get $10
|
||||
i32.const 1
|
||||
i32.and
|
||||
if
|
||||
local.get $9
|
||||
i32.const 14
|
||||
i32.and
|
||||
i32.const 2
|
||||
i32.eq
|
||||
if
|
||||
local.get $5
|
||||
local.set $12
|
||||
local.get $2
|
||||
local.set $11
|
||||
local.get $10
|
||||
local.set $8
|
||||
local.get $8
|
||||
i32.const 24
|
||||
i32.shr_u
|
||||
global.get $assembly/config/BIT_ROT
|
||||
i32.sub
|
||||
local.tee $13
|
||||
i32.const 0
|
||||
local.tee $14
|
||||
local.get $13
|
||||
local.get $14
|
||||
i32.gt_s
|
||||
select
|
||||
local.set $13
|
||||
block $assembly/index/set|inlined.1
|
||||
local.get $12
|
||||
local.set $16
|
||||
local.get $11
|
||||
local.set $15
|
||||
local.get $13
|
||||
i32.const 24
|
||||
i32.shl
|
||||
local.get $8
|
||||
i32.const 16777215
|
||||
i32.and
|
||||
i32.or
|
||||
local.set $14
|
||||
global.get $assembly/index/s
|
||||
local.get $15
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
i32.add
|
||||
local.get $16
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
local.get $14
|
||||
i32.store
|
||||
end
|
||||
else
|
||||
local.get $5
|
||||
local.set $16
|
||||
local.get $2
|
||||
local.set $15
|
||||
global.get $assembly/config/BGR_DEAD
|
||||
i32.const -16777216
|
||||
i32.or
|
||||
local.set $14
|
||||
global.get $assembly/index/s
|
||||
local.get $15
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
i32.add
|
||||
local.get $16
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
local.get $14
|
||||
i32.store
|
||||
end
|
||||
else
|
||||
local.get $9
|
||||
i32.const 3
|
||||
i32.eq
|
||||
if
|
||||
local.get $5
|
||||
local.set $12
|
||||
local.get $2
|
||||
local.set $11
|
||||
global.get $assembly/config/BGR_ALIVE
|
||||
i32.const -16777216
|
||||
i32.or
|
||||
local.set $8
|
||||
global.get $assembly/index/s
|
||||
local.get $11
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
i32.add
|
||||
local.get $12
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
local.get $8
|
||||
i32.store
|
||||
else
|
||||
local.get $5
|
||||
local.set $15
|
||||
local.get $2
|
||||
local.set $14
|
||||
local.get $10
|
||||
local.set $13
|
||||
local.get $13
|
||||
i32.const 24
|
||||
i32.shr_u
|
||||
global.get $assembly/config/BIT_ROT
|
||||
i32.sub
|
||||
local.tee $12
|
||||
i32.const 0
|
||||
local.tee $11
|
||||
local.get $12
|
||||
local.get $11
|
||||
i32.gt_s
|
||||
select
|
||||
local.set $12
|
||||
block $assembly/index/set|inlined.4
|
||||
local.get $15
|
||||
local.set $11
|
||||
local.get $14
|
||||
local.set $8
|
||||
local.get $12
|
||||
i32.const 24
|
||||
i32.shl
|
||||
local.get $13
|
||||
i32.const 16777215
|
||||
i32.and
|
||||
i32.or
|
||||
local.set $16
|
||||
global.get $assembly/index/s
|
||||
local.get $8
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
i32.add
|
||||
local.get $11
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
local.get $16
|
||||
i32.store
|
||||
end
|
||||
end
|
||||
end
|
||||
local.get $5
|
||||
i32.const 1
|
||||
i32.add
|
||||
local.set $5
|
||||
br $repeat|1
|
||||
unreachable
|
||||
end
|
||||
unreachable
|
||||
end
|
||||
local.get $2
|
||||
i32.const 1
|
||||
@ -527,23 +521,23 @@
|
||||
f64.lt
|
||||
if
|
||||
local.get $3
|
||||
local.set $4
|
||||
local.set $6
|
||||
local.get $1
|
||||
local.set $5
|
||||
global.get $assembly/config/BGR_ALIVE
|
||||
i32.const -16777216
|
||||
i32.or
|
||||
local.set $6
|
||||
local.set $4
|
||||
global.get $assembly/index/s
|
||||
local.get $5
|
||||
global.get $assembly/index/w
|
||||
i32.mul
|
||||
i32.add
|
||||
local.get $4
|
||||
local.get $6
|
||||
i32.add
|
||||
i32.const 2
|
||||
i32.shl
|
||||
local.get $6
|
||||
local.get $4
|
||||
i32.store
|
||||
end
|
||||
local.get $3
|
||||
|
@ -3,8 +3,8 @@
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"asbuild:untouched": "asc assembly/index.ts -b build/untouched.wasm -t build/untouched.wat --use Math=JSMath --importMemory --sourceMap --debug --validate --measure",
|
||||
"asbuild:optimized": "asc assembly/index.ts -b build/optimized.wasm -t build/optimized.wat -d build/optimized.d.ts --use Math=JSMath -O3 --importMemory --sourceMap --validate --measure",
|
||||
"asbuild:untouched": "asc assembly/index.ts -b build/untouched.wasm -t build/untouched.wat --use Math=JSMath --runtime none --importMemory --sourceMap --debug --validate --measure",
|
||||
"asbuild:optimized": "asc assembly/index.ts -b build/optimized.wasm -t build/optimized.wat -d build/optimized.d.ts --use Math=JSMath -O3 --runtime none --importMemory --sourceMap --validate --measure",
|
||||
"asbuild": "npm run asbuild:untouched && npm run asbuild:optimized",
|
||||
"server": "http-server . -o -c-1"
|
||||
},
|
||||
|
@ -43,7 +43,7 @@ export function computeLine(y: u32, width: u32, height: u32, limit: u32): void {
|
||||
let sqd = ix * ix + iy * iy;
|
||||
if (sqd > 1.0) {
|
||||
let frac = Math.log2(0.5 * Math.log(sqd));
|
||||
col = <u32>((NUM_COLORS - 1) * clamp((iteration + 1 - frac) * invLimit, 0.0, 1.0));
|
||||
col = <u32>((NUM_COLORS - 1) * clamp<f64>((iteration + 1 - frac) * invLimit, 0.0, 1.0));
|
||||
}
|
||||
store<u16>(stride + (x << 1), col);
|
||||
}
|
||||
|
6
examples/mandelbrot/build/optimized.d.ts
vendored
6
examples/mandelbrot/build/optimized.d.ts
vendored
@ -8,12 +8,6 @@ declare module ASModule {
|
||||
type f32 = number;
|
||||
type f64 = number;
|
||||
type bool = any;
|
||||
namespace JSMath {
|
||||
function log(x: f64): f64;
|
||||
function log2(x: f64): f64;
|
||||
}
|
||||
var NUM_COLORS: i32;
|
||||
function computeLine(y: u32, width: u32, height: u32, limit: u32): void;
|
||||
function clamp<f64>(value: f64, minValue: f64, maxValue: f64): f64;
|
||||
}
|
||||
export default ASModule;
|
||||
|
Binary file not shown.
@ -5,52 +5,51 @@
|
||||
(import "env" "memory" (memory $0 0))
|
||||
(import "Math" "log" (func $~lib/bindings/Math/log (param f64) (result f64)))
|
||||
(import "Math" "log2" (func $~lib/bindings/Math/log2 (param f64) (result f64)))
|
||||
(table $0 1 funcref)
|
||||
(elem (i32.const 0) $null)
|
||||
(export "memory" (memory $0))
|
||||
(export "table" (table $0))
|
||||
(export "computeLine" (func $assembly/index/computeLine))
|
||||
(func $assembly/index/computeLine (; 2 ;) (type $FUNCSIG$viiii) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32)
|
||||
(local $4 f64)
|
||||
(local $5 f64)
|
||||
(local $6 f64)
|
||||
(local $7 i32)
|
||||
(local $8 f64)
|
||||
(local $8 i32)
|
||||
(local $9 f64)
|
||||
(local $10 f64)
|
||||
(local $11 f64)
|
||||
(local $12 f64)
|
||||
(local $13 f64)
|
||||
(local $14 f64)
|
||||
f64.const 10
|
||||
f64.const 3
|
||||
(local $15 f64)
|
||||
local.get $1
|
||||
f64.convert_i32_u
|
||||
local.tee $8
|
||||
local.tee $9
|
||||
f64.const 0.625
|
||||
f64.mul
|
||||
f64.const 4
|
||||
local.get $2
|
||||
f64.convert_i32_u
|
||||
local.tee $4
|
||||
f64.mul
|
||||
f64.min
|
||||
f64.div
|
||||
local.set $9
|
||||
local.set $4
|
||||
local.get $0
|
||||
f64.convert_i32_u
|
||||
local.get $4
|
||||
local.get $2
|
||||
f64.convert_i32_u
|
||||
local.tee $6
|
||||
f64.const 0.5
|
||||
f64.mul
|
||||
f64.sub
|
||||
f64.const 10
|
||||
f64.const 3
|
||||
local.get $9
|
||||
f64.mul
|
||||
local.set $10
|
||||
local.get $8
|
||||
f64.const 0.625
|
||||
f64.const 4
|
||||
local.get $6
|
||||
f64.mul
|
||||
local.get $9
|
||||
f64.min
|
||||
f64.div
|
||||
local.tee $10
|
||||
f64.mul
|
||||
local.set $12
|
||||
local.set $11
|
||||
local.get $4
|
||||
local.get $10
|
||||
f64.mul
|
||||
local.set $13
|
||||
local.get $0
|
||||
local.get $1
|
||||
i32.mul
|
||||
@ -62,34 +61,35 @@
|
||||
f64.convert_i32_u
|
||||
local.tee $6
|
||||
f64.div
|
||||
local.set $13
|
||||
local.set $14
|
||||
f64.const 8
|
||||
local.get $6
|
||||
f64.min
|
||||
local.set $8
|
||||
local.set $15
|
||||
loop $repeat|0
|
||||
local.get $7
|
||||
local.get $1
|
||||
i32.lt_u
|
||||
if
|
||||
local.get $7
|
||||
block $break|0
|
||||
local.get $8
|
||||
local.get $1
|
||||
i32.ge_u
|
||||
br_if $break|0
|
||||
local.get $8
|
||||
f64.convert_i32_u
|
||||
local.get $9
|
||||
local.get $10
|
||||
f64.mul
|
||||
local.get $12
|
||||
local.get $13
|
||||
f64.sub
|
||||
local.set $11
|
||||
local.set $12
|
||||
f64.const 0
|
||||
local.set $4
|
||||
f64.const 0
|
||||
local.set $5
|
||||
i32.const 0
|
||||
local.set $2
|
||||
local.set $7
|
||||
loop $continue|1
|
||||
local.get $4
|
||||
local.get $4
|
||||
f64.mul
|
||||
local.tee $14
|
||||
local.tee $9
|
||||
local.get $5
|
||||
local.get $5
|
||||
f64.mul
|
||||
@ -104,31 +104,31 @@
|
||||
f64.mul
|
||||
local.get $5
|
||||
f64.mul
|
||||
local.get $10
|
||||
f64.add
|
||||
local.set $5
|
||||
local.get $14
|
||||
local.get $6
|
||||
f64.sub
|
||||
local.get $11
|
||||
f64.add
|
||||
local.set $5
|
||||
local.get $9
|
||||
local.get $6
|
||||
f64.sub
|
||||
local.get $12
|
||||
f64.add
|
||||
local.set $4
|
||||
local.get $2
|
||||
local.get $7
|
||||
local.get $3
|
||||
i32.ge_u
|
||||
br_if $break|1
|
||||
local.get $2
|
||||
local.get $7
|
||||
i32.const 1
|
||||
i32.add
|
||||
local.set $2
|
||||
local.set $7
|
||||
br $continue|1
|
||||
end
|
||||
end
|
||||
end
|
||||
loop $continue|2
|
||||
local.get $2
|
||||
local.get $7
|
||||
f64.convert_i32_u
|
||||
local.get $8
|
||||
local.get $15
|
||||
f64.lt
|
||||
if
|
||||
local.get $4
|
||||
@ -138,7 +138,7 @@
|
||||
local.get $5
|
||||
f64.mul
|
||||
f64.sub
|
||||
local.get $11
|
||||
local.get $12
|
||||
f64.add
|
||||
local.set $6
|
||||
f64.const 2
|
||||
@ -146,19 +146,21 @@
|
||||
f64.mul
|
||||
local.get $5
|
||||
f64.mul
|
||||
local.get $10
|
||||
local.get $11
|
||||
f64.add
|
||||
local.set $5
|
||||
local.get $6
|
||||
local.set $4
|
||||
local.get $2
|
||||
local.get $7
|
||||
i32.const 1
|
||||
i32.add
|
||||
local.set $2
|
||||
local.set $7
|
||||
br $continue|2
|
||||
end
|
||||
end
|
||||
local.get $7
|
||||
i32.const 2047
|
||||
local.set $2
|
||||
local.get $8
|
||||
i32.const 1
|
||||
i32.shl
|
||||
local.get $0
|
||||
@ -175,7 +177,7 @@
|
||||
f64.gt
|
||||
if (result i32)
|
||||
f64.const 2047
|
||||
local.get $2
|
||||
local.get $7
|
||||
i32.const 1
|
||||
i32.add
|
||||
f64.convert_i32_u
|
||||
@ -185,7 +187,7 @@
|
||||
f64.mul
|
||||
call $~lib/bindings/Math/log2
|
||||
f64.sub
|
||||
local.get $13
|
||||
local.get $14
|
||||
f64.mul
|
||||
f64.const 0
|
||||
f64.max
|
||||
@ -197,10 +199,10 @@
|
||||
i32.const 2047
|
||||
end
|
||||
i32.store16
|
||||
local.get $7
|
||||
local.get $8
|
||||
i32.const 1
|
||||
i32.add
|
||||
local.set $7
|
||||
local.set $8
|
||||
br $repeat|0
|
||||
end
|
||||
end
|
||||
|
@ -1,7 +1,6 @@
|
||||
(module
|
||||
(type $FUNCSIG$viiii (func (param i32 i32 i32 i32)))
|
||||
(type $FUNCSIG$dd (func (param f64) (result f64)))
|
||||
(type $FUNCSIG$dddd (func (param f64 f64 f64) (result f64)))
|
||||
(type $FUNCSIG$v (func))
|
||||
(import "env" "memory" (memory $0 0))
|
||||
(import "Math" "log" (func $~lib/bindings/Math/log (param f64) (result f64)))
|
||||
@ -9,18 +8,9 @@
|
||||
(table $0 1 funcref)
|
||||
(elem (i32.const 0) $null)
|
||||
(global $assembly/index/NUM_COLORS i32 (i32.const 2048))
|
||||
(global $~lib/memory/HEAP_BASE i32 (i32.const 8))
|
||||
(export "memory" (memory $0))
|
||||
(export "table" (table $0))
|
||||
(export "computeLine" (func $assembly/index/computeLine))
|
||||
(func $assembly/index/clamp<f64> (; 2 ;) (type $FUNCSIG$dddd) (param $0 f64) (param $1 f64) (param $2 f64) (result f64)
|
||||
local.get $0
|
||||
local.get $1
|
||||
f64.max
|
||||
local.get $2
|
||||
f64.min
|
||||
)
|
||||
(func $assembly/index/computeLine (; 3 ;) (type $FUNCSIG$viiii) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32)
|
||||
(func $assembly/index/computeLine (; 2 ;) (type $FUNCSIG$viiii) (param $0 i32) (param $1 i32) (param $2 i32) (param $3 i32)
|
||||
(local $4 f64)
|
||||
(local $5 f64)
|
||||
(local $6 f64)
|
||||
@ -39,6 +29,9 @@
|
||||
(local $19 f64)
|
||||
(local $20 i32)
|
||||
(local $21 f64)
|
||||
(local $22 f64)
|
||||
(local $23 f64)
|
||||
(local $24 f64)
|
||||
local.get $1
|
||||
f64.convert_i32_u
|
||||
f64.const 1
|
||||
@ -101,127 +94,123 @@
|
||||
i32.lt_u
|
||||
i32.eqz
|
||||
br_if $break|0
|
||||
block
|
||||
local.get $12
|
||||
f64.convert_i32_u
|
||||
local.get $6
|
||||
f64.mul
|
||||
local.get $8
|
||||
f64.sub
|
||||
local.set $13
|
||||
f64.const 0
|
||||
local.set $14
|
||||
f64.const 0
|
||||
local.set $15
|
||||
i32.const 0
|
||||
local.set $18
|
||||
block $break|1
|
||||
loop $continue|1
|
||||
local.get $14
|
||||
local.get $12
|
||||
f64.convert_i32_u
|
||||
local.get $6
|
||||
f64.mul
|
||||
local.get $8
|
||||
f64.sub
|
||||
local.set $13
|
||||
f64.const 0
|
||||
local.set $14
|
||||
f64.const 0
|
||||
local.set $15
|
||||
i32.const 0
|
||||
local.set $18
|
||||
block $break|1
|
||||
loop $continue|1
|
||||
local.get $14
|
||||
local.get $14
|
||||
f64.mul
|
||||
local.tee $16
|
||||
local.get $15
|
||||
local.get $15
|
||||
f64.mul
|
||||
local.tee $17
|
||||
f64.add
|
||||
f64.const 4
|
||||
f64.le
|
||||
if
|
||||
f64.const 2
|
||||
local.get $14
|
||||
f64.mul
|
||||
local.tee $16
|
||||
local.get $15
|
||||
local.get $15
|
||||
f64.mul
|
||||
local.tee $17
|
||||
local.get $7
|
||||
f64.add
|
||||
f64.const 4
|
||||
f64.le
|
||||
if
|
||||
block
|
||||
f64.const 2
|
||||
local.get $14
|
||||
f64.mul
|
||||
local.get $15
|
||||
f64.mul
|
||||
local.get $7
|
||||
f64.add
|
||||
local.set $15
|
||||
local.get $16
|
||||
local.get $17
|
||||
f64.sub
|
||||
local.get $13
|
||||
f64.add
|
||||
local.set $14
|
||||
local.get $18
|
||||
local.get $3
|
||||
i32.ge_u
|
||||
if
|
||||
br $break|1
|
||||
end
|
||||
local.get $18
|
||||
i32.const 1
|
||||
i32.add
|
||||
local.set $18
|
||||
end
|
||||
br $continue|1
|
||||
end
|
||||
end
|
||||
end
|
||||
block $break|2
|
||||
loop $continue|2
|
||||
local.set $15
|
||||
local.get $16
|
||||
local.get $17
|
||||
f64.sub
|
||||
local.get $13
|
||||
f64.add
|
||||
local.set $14
|
||||
local.get $18
|
||||
f64.convert_i32_u
|
||||
local.get $11
|
||||
f64.lt
|
||||
local.get $3
|
||||
i32.ge_u
|
||||
if
|
||||
block
|
||||
local.get $14
|
||||
local.get $14
|
||||
f64.mul
|
||||
local.get $15
|
||||
local.get $15
|
||||
f64.mul
|
||||
f64.sub
|
||||
local.get $13
|
||||
f64.add
|
||||
local.set $19
|
||||
f64.const 2
|
||||
local.get $14
|
||||
f64.mul
|
||||
local.get $15
|
||||
f64.mul
|
||||
local.get $7
|
||||
f64.add
|
||||
local.set $15
|
||||
local.get $19
|
||||
local.set $14
|
||||
local.get $18
|
||||
i32.const 1
|
||||
i32.add
|
||||
local.set $18
|
||||
end
|
||||
br $continue|2
|
||||
br $break|1
|
||||
end
|
||||
local.get $18
|
||||
i32.const 1
|
||||
i32.add
|
||||
local.set $18
|
||||
br $continue|1
|
||||
end
|
||||
end
|
||||
end
|
||||
block $break|2
|
||||
loop $continue|2
|
||||
local.get $18
|
||||
f64.convert_i32_u
|
||||
local.get $11
|
||||
f64.lt
|
||||
if
|
||||
local.get $14
|
||||
local.get $14
|
||||
f64.mul
|
||||
local.get $15
|
||||
local.get $15
|
||||
f64.mul
|
||||
f64.sub
|
||||
local.get $13
|
||||
f64.add
|
||||
local.set $19
|
||||
f64.const 2
|
||||
local.get $14
|
||||
f64.mul
|
||||
local.get $15
|
||||
f64.mul
|
||||
local.get $7
|
||||
f64.add
|
||||
local.set $15
|
||||
local.get $19
|
||||
local.set $14
|
||||
local.get $18
|
||||
i32.const 1
|
||||
i32.add
|
||||
local.set $18
|
||||
br $continue|2
|
||||
end
|
||||
end
|
||||
end
|
||||
global.get $assembly/index/NUM_COLORS
|
||||
i32.const 1
|
||||
i32.sub
|
||||
local.set $20
|
||||
local.get $14
|
||||
local.get $14
|
||||
f64.mul
|
||||
local.get $15
|
||||
local.get $15
|
||||
f64.mul
|
||||
f64.add
|
||||
local.set $19
|
||||
local.get $19
|
||||
f64.const 1
|
||||
f64.gt
|
||||
if
|
||||
f64.const 0.5
|
||||
local.get $19
|
||||
call $~lib/bindings/Math/log
|
||||
f64.mul
|
||||
call $~lib/bindings/Math/log2
|
||||
local.set $21
|
||||
global.get $assembly/index/NUM_COLORS
|
||||
i32.const 1
|
||||
i32.sub
|
||||
local.set $20
|
||||
local.get $14
|
||||
local.get $14
|
||||
f64.mul
|
||||
local.get $15
|
||||
local.get $15
|
||||
f64.mul
|
||||
f64.add
|
||||
local.set $19
|
||||
local.get $19
|
||||
f64.const 1
|
||||
f64.gt
|
||||
if
|
||||
f64.const 0.5
|
||||
local.get $19
|
||||
call $~lib/bindings/Math/log
|
||||
f64.mul
|
||||
call $~lib/bindings/Math/log2
|
||||
local.set $21
|
||||
global.get $assembly/index/NUM_COLORS
|
||||
i32.const 1
|
||||
i32.sub
|
||||
f64.convert_i32_s
|
||||
f64.convert_i32_s
|
||||
block $assembly/index/clamp<f64>|inlined.0 (result f64)
|
||||
local.get $18
|
||||
i32.const 1
|
||||
i32.add
|
||||
@ -230,21 +219,28 @@
|
||||
f64.sub
|
||||
local.get $10
|
||||
f64.mul
|
||||
local.set $24
|
||||
f64.const 0
|
||||
local.set $23
|
||||
f64.const 1
|
||||
call $assembly/index/clamp<f64>
|
||||
f64.mul
|
||||
i32.trunc_f64_u
|
||||
local.set $20
|
||||
local.set $22
|
||||
local.get $24
|
||||
local.get $23
|
||||
f64.max
|
||||
local.get $22
|
||||
f64.min
|
||||
end
|
||||
local.get $9
|
||||
local.get $12
|
||||
i32.const 1
|
||||
i32.shl
|
||||
i32.add
|
||||
local.get $20
|
||||
i32.store16
|
||||
f64.mul
|
||||
i32.trunc_f64_u
|
||||
local.set $20
|
||||
end
|
||||
local.get $9
|
||||
local.get $12
|
||||
i32.const 1
|
||||
i32.shl
|
||||
i32.add
|
||||
local.get $20
|
||||
i32.store16
|
||||
local.get $12
|
||||
i32.const 1
|
||||
i32.add
|
||||
@ -255,6 +251,6 @@
|
||||
unreachable
|
||||
end
|
||||
)
|
||||
(func $null (; 4 ;) (type $FUNCSIG$v)
|
||||
(func $null (; 3 ;) (type $FUNCSIG$v)
|
||||
)
|
||||
)
|
||||
|
@ -3,8 +3,8 @@
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"asbuild:untouched": "asc assembly/index.ts -b build/untouched.wasm -t build/untouched.wat --use Math=JSMath --importMemory --sourceMap --debug --validate --measure",
|
||||
"asbuild:optimized": "asc assembly/index.ts -b build/optimized.wasm -t build/optimized.wat -d build/optimized.d.ts --use Math=JSMath -O3 --importMemory --sourceMap --validate --measure",
|
||||
"asbuild:untouched": "asc assembly/index.ts -b build/untouched.wasm -t build/untouched.wat --use Math=JSMath --runtime none --importMemory --sourceMap --debug --validate --measure",
|
||||
"asbuild:optimized": "asc assembly/index.ts -b build/optimized.wasm -t build/optimized.wat -d build/optimized.d.ts --use Math=JSMath --runtime none -O3 --importMemory --sourceMap --validate --measure",
|
||||
"asbuild": "npm run asbuild:untouched && npm run asbuild:optimized",
|
||||
"server": "http-server . -o -c-1"
|
||||
},
|
||||
|
@ -9,7 +9,7 @@ const imports = {
|
||||
env: {
|
||||
memory: new WebAssembly.Memory({ initial: 10 }),
|
||||
abort: (filename, line, column) => {
|
||||
throw Error("abort called at " + line + ":" + colum);
|
||||
throw Error("abort called at " + line + ":" + column);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,5 +1,3 @@
|
||||
import "allocator/arena";
|
||||
|
||||
// From The Computer Language Benchmarks Game
|
||||
// http://benchmarksgame.alioth.debian.org
|
||||
|
||||
@ -96,7 +94,7 @@ class NBodySystem {
|
||||
py += b.vy * m;
|
||||
pz += b.vz * m;
|
||||
}
|
||||
bodies[0].offsetMomentum(px, py, pz);
|
||||
unchecked(bodies[0]).offsetMomentum(px, py, pz);
|
||||
}
|
||||
|
||||
advance(dt: float): void {
|
||||
@ -206,5 +204,5 @@ export function bench(steps: u32): void {
|
||||
|
||||
export function getBody(index: i32): Body | null {
|
||||
var bodies = system.bodies;
|
||||
return <u32>index < <u32>bodies.length ? bodies[index] : null;
|
||||
return <u32>index < <u32>bodies.length ? unchecked(bodies[index]) : null;
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
|
||||
function asmFunc(global, env, buffer) {
|
||||
"almost asm";
|
||||
var HEAP8 = new global.Int8Array(buffer);
|
||||
@ -20,427 +21,231 @@ function asmFunc(global, env, buffer) {
|
||||
var abort = env.abort;
|
||||
var nan = global.NaN;
|
||||
var infinity = global.Infinity;
|
||||
var $lib_allocator_arena_startOffset = 0;
|
||||
var $lib_allocator_arena_offset = 0;
|
||||
var assembly_index_system = 0;
|
||||
var i64toi32_i32$HIGH_BITS = 0;
|
||||
function $lib_allocator_arena___memory_allocate($0) {
|
||||
$0 = $0 | 0;
|
||||
var $1 = 0, $2 = 0, $3 = 0, wasm2js_i32$0 = 0, wasm2js_i32$1 = 0, wasm2js_i32$2 = 0;
|
||||
if ($0 >>> 0 > 1073741824 >>> 0) abort();
|
||||
$1 = $lib_allocator_arena_offset;
|
||||
$2 = (($1 + (wasm2js_i32$0 = $0, wasm2js_i32$1 = 1, wasm2js_i32$2 = $0 >>> 0 > 1 >>> 0, wasm2js_i32$2 ? wasm2js_i32$0 : wasm2js_i32$1) | 0) + 7 | 0) & 4294967288 | 0;
|
||||
$3 = __wasm_current_memory();
|
||||
if ($2 >>> 0 > ($3 << 16 | 0) >>> 0) {
|
||||
$0 = ((($2 - $1 | 0) + 65535 | 0) & 4294901760 | 0) >>> 16 | 0;
|
||||
if ((__wasm_grow_memory((wasm2js_i32$0 = $3, wasm2js_i32$1 = $0, wasm2js_i32$2 = ($3 | 0) > ($0 | 0), wasm2js_i32$2 ? wasm2js_i32$0 : wasm2js_i32$1) | 0) | 0) < (0 | 0)) if ((__wasm_grow_memory($0 | 0) | 0) < (0 | 0)) abort();;
|
||||
var $lib_rt_stub_startOffset = 0;
|
||||
var $lib_rt_stub_offset = 0;
|
||||
function $lib_rt_stub___alloc($0, $1) {
|
||||
var $2 = 0, $3 = 0, $4 = 0, $5 = 0;
|
||||
if ($0 >>> 0 > 1073741808 >>> 0) {
|
||||
abort()
|
||||
}
|
||||
$lib_allocator_arena_offset = $2;
|
||||
return $1 | 0;
|
||||
$3 = $lib_rt_stub_offset + 16 | 0;
|
||||
$2 = (($3 + ($0 >>> 0 > 1 >>> 0 ? $0 : 1) | 0) + 15 | 0) & -16 | 0;
|
||||
$4 = __wasm_memory_size();
|
||||
if ($2 >>> 0 > ($4 << 16 | 0) >>> 0) {
|
||||
$5 = ((($2 - $3 | 0) + 65535 | 0) & -65536 | 0) >>> 16 | 0;
|
||||
if ((__wasm_memory_grow((($4 | 0) > ($5 | 0) ? $4 : $5) | 0) | 0) < (0 | 0)) {
|
||||
if ((__wasm_memory_grow($5 | 0) | 0) < (0 | 0)) {
|
||||
abort()
|
||||
}
|
||||
}
|
||||
}
|
||||
$lib_rt_stub_offset = $2;
|
||||
$2 = $3 - 16 | 0;
|
||||
HEAP32[($2 + 8 | 0) >> 2] = $1;
|
||||
HEAP32[($2 + 12 | 0) >> 2] = $0;
|
||||
return $3;
|
||||
}
|
||||
|
||||
function assembly_index_NBodySystem_constructor($0) {
|
||||
$0 = $0 | 0;
|
||||
var $1 = 0, $2 = 0, $3 = 0.0, $4 = 0.0, $5 = 0.0, $6 = 0.0, $7 = 0, $49 = 0, wasm2js_i32$0 = 0, wasm2js_f64$0 = 0.0, wasm2js_i32$1 = 0;
|
||||
$7 = HEAP32[($0 + 4 | 0) >> 2] | 0;
|
||||
repeat_0 : do {
|
||||
var $1 = 0, $2 = 0, $3 = 0.0, $4 = 0.0, $5 = 0.0, $6 = 0.0, $7 = 0;
|
||||
$7 = HEAP32[($0 + 12 | 0) >> 2];
|
||||
repeat_0 : while (1) {
|
||||
if (($1 | 0) < ($7 | 0)) {
|
||||
$2 = HEAPU32[(((HEAPU32[$0 >> 2] | 0) + ($1 << 2 | 0) | 0) + 8 | 0) >> 2] | 0;
|
||||
$3 = +HEAPF64[($2 + 48 | 0) >> 3];
|
||||
$4 = $4 + +HEAPF64[($2 + 24 | 0) >> 3] * $3;
|
||||
$5 = $5 + +HEAPF64[($2 + 32 | 0) >> 3] * $3;
|
||||
$6 = $6 + +HEAPF64[($2 + 40 | 0) >> 3] * $3;
|
||||
$2 = HEAP32[(HEAP32[($0 + 4 | 0) >> 2] + ($1 << 2 | 0) | 0) >> 2];
|
||||
$3 = HEAPF64[($2 + 48 | 0) >> 3];
|
||||
$4 = $4 + HEAPF64[($2 + 24 | 0) >> 3] * $3;
|
||||
$5 = $5 + HEAPF64[($2 + 32 | 0) >> 3] * $3;
|
||||
$6 = $6 + HEAPF64[($2 + 40 | 0) >> 3] * $3;
|
||||
$1 = $1 + 1 | 0;
|
||||
continue repeat_0;
|
||||
}
|
||||
break repeat_0;
|
||||
} while (1);
|
||||
$1 = HEAPU32[$0 >> 2] | 0;
|
||||
if (0 >>> 0 < ((HEAP32[$1 >> 2] | 0) >>> 2 | 0) >>> 0) $49 = HEAPU32[($1 + 8 | 0) >> 2] | 0; else abort();
|
||||
$1 = $49;
|
||||
wasm2js_i32$0 = $1;
|
||||
wasm2js_f64$0 = -$4 / 39.47841760435743;
|
||||
HEAPF64[(wasm2js_i32$0 + 24 | 0) >> 3] = wasm2js_f64$0;
|
||||
wasm2js_i32$0 = $1;
|
||||
wasm2js_f64$0 = -$5 / 39.47841760435743;
|
||||
HEAPF64[(wasm2js_i32$0 + 32 | 0) >> 3] = wasm2js_f64$0;
|
||||
wasm2js_i32$0 = $1;
|
||||
wasm2js_f64$0 = -$6 / 39.47841760435743;
|
||||
HEAPF64[(wasm2js_i32$0 + 40 | 0) >> 3] = wasm2js_f64$0;
|
||||
$1 = $lib_allocator_arena___memory_allocate(4 | 0) | 0;
|
||||
wasm2js_i32$0 = $1;
|
||||
wasm2js_i32$1 = $0;
|
||||
HEAP32[wasm2js_i32$0 >> 2] = wasm2js_i32$1;
|
||||
return $1 | 0;
|
||||
};
|
||||
$1 = HEAP32[HEAP32[($0 + 4 | 0) >> 2] >> 2];
|
||||
HEAPF64[($1 + 24 | 0) >> 3] = -$4 / 39.47841760435743;
|
||||
HEAPF64[($1 + 32 | 0) >> 3] = -$5 / 39.47841760435743;
|
||||
HEAPF64[($1 + 40 | 0) >> 3] = -$6 / 39.47841760435743;
|
||||
$1 = $lib_rt_stub___alloc(4, 3);
|
||||
HEAP32[$1 >> 2] = $0;
|
||||
return $1;
|
||||
}
|
||||
|
||||
function assembly_index_Body_constructor($0, $1, $2, $3, $4, $5, $6) {
|
||||
$0 = +$0;
|
||||
$1 = +$1;
|
||||
$2 = +$2;
|
||||
$3 = +$3;
|
||||
$4 = +$4;
|
||||
$5 = +$5;
|
||||
$6 = +$6;
|
||||
var $7 = 0, wasm2js_i32$0 = 0, wasm2js_f64$0 = 0.0;
|
||||
$7 = $lib_allocator_arena___memory_allocate(56 | 0) | 0;
|
||||
wasm2js_i32$0 = $7;
|
||||
wasm2js_f64$0 = $0;
|
||||
HEAPF64[wasm2js_i32$0 >> 3] = wasm2js_f64$0;
|
||||
wasm2js_i32$0 = $7;
|
||||
wasm2js_f64$0 = $1;
|
||||
HEAPF64[(wasm2js_i32$0 + 8 | 0) >> 3] = wasm2js_f64$0;
|
||||
wasm2js_i32$0 = $7;
|
||||
wasm2js_f64$0 = $2;
|
||||
HEAPF64[(wasm2js_i32$0 + 16 | 0) >> 3] = wasm2js_f64$0;
|
||||
wasm2js_i32$0 = $7;
|
||||
wasm2js_f64$0 = $3;
|
||||
HEAPF64[(wasm2js_i32$0 + 24 | 0) >> 3] = wasm2js_f64$0;
|
||||
wasm2js_i32$0 = $7;
|
||||
wasm2js_f64$0 = $4;
|
||||
HEAPF64[(wasm2js_i32$0 + 32 | 0) >> 3] = wasm2js_f64$0;
|
||||
wasm2js_i32$0 = $7;
|
||||
wasm2js_f64$0 = $5;
|
||||
HEAPF64[(wasm2js_i32$0 + 40 | 0) >> 3] = wasm2js_f64$0;
|
||||
wasm2js_i32$0 = $7;
|
||||
wasm2js_f64$0 = $6;
|
||||
HEAPF64[(wasm2js_i32$0 + 48 | 0) >> 3] = wasm2js_f64$0;
|
||||
return $7 | 0;
|
||||
var $7 = 0;
|
||||
$7 = $lib_rt_stub___alloc(56, 4);
|
||||
HEAPF64[$7 >> 3] = $0;
|
||||
HEAPF64[($7 + 8 | 0) >> 3] = $1;
|
||||
HEAPF64[($7 + 16 | 0) >> 3] = $2;
|
||||
HEAPF64[($7 + 24 | 0) >> 3] = $3;
|
||||
HEAPF64[($7 + 32 | 0) >> 3] = $4;
|
||||
HEAPF64[($7 + 40 | 0) >> 3] = $5;
|
||||
HEAPF64[($7 + 48 | 0) >> 3] = $6;
|
||||
return $7;
|
||||
}
|
||||
|
||||
function $lib_internal_memory_memset($0) {
|
||||
$0 = $0 | 0;
|
||||
var $1 = 0, $2 = 0, i64toi32_i32$1 = 0, i64toi32_i32$0 = 0, wasm2js_i32$0 = 0, wasm2js_i32$1 = 0, wasm2js_i32$2 = 0, wasm2js_i32$3 = 0;
|
||||
wasm2js_i32$0 = $0;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP8[wasm2js_i32$0 >> 0] = wasm2js_i32$1;
|
||||
$1 = $0 + 20 | 0;
|
||||
wasm2js_i32$0 = $1 - 1 | 0;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP8[wasm2js_i32$0 >> 0] = wasm2js_i32$1;
|
||||
wasm2js_i32$0 = $0 + 1 | 0;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP8[wasm2js_i32$0 >> 0] = wasm2js_i32$1;
|
||||
wasm2js_i32$0 = $0 + 2 | 0;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP8[wasm2js_i32$0 >> 0] = wasm2js_i32$1;
|
||||
wasm2js_i32$0 = $1 - 2 | 0;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP8[wasm2js_i32$0 >> 0] = wasm2js_i32$1;
|
||||
wasm2js_i32$0 = $1 - 3 | 0;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP8[wasm2js_i32$0 >> 0] = wasm2js_i32$1;
|
||||
wasm2js_i32$0 = $0 + 3 | 0;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP8[wasm2js_i32$0 >> 0] = wasm2js_i32$1;
|
||||
wasm2js_i32$0 = $1 - 4 | 0;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP8[wasm2js_i32$0 >> 0] = wasm2js_i32$1;
|
||||
$1 = (0 - $0 | 0) & 3 | 0;
|
||||
$0 = $1 + $0 | 0;
|
||||
wasm2js_i32$0 = $0;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP32[wasm2js_i32$0 >> 2] = wasm2js_i32$1;
|
||||
$2 = (20 - $1 | 0) & 4294967292 | 0;
|
||||
wasm2js_i32$0 = ($2 + $0 | 0) - 4 | 0;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP32[wasm2js_i32$0 >> 2] = wasm2js_i32$1;
|
||||
if ($2 >>> 0 <= 8 >>> 0) return;
|
||||
wasm2js_i32$0 = $0 + 4 | 0;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP32[wasm2js_i32$0 >> 2] = wasm2js_i32$1;
|
||||
wasm2js_i32$0 = $0 + 8 | 0;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP32[wasm2js_i32$0 >> 2] = wasm2js_i32$1;
|
||||
$1 = $0 + $2 | 0;
|
||||
wasm2js_i32$0 = $1 - 12 | 0;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP32[wasm2js_i32$0 >> 2] = wasm2js_i32$1;
|
||||
wasm2js_i32$0 = $1 - 8 | 0;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP32[wasm2js_i32$0 >> 2] = wasm2js_i32$1;
|
||||
if ($2 >>> 0 <= 24 >>> 0) return;
|
||||
wasm2js_i32$0 = $0 + 12 | 0;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP32[wasm2js_i32$0 >> 2] = wasm2js_i32$1;
|
||||
wasm2js_i32$0 = $0 + 16 | 0;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP32[wasm2js_i32$0 >> 2] = wasm2js_i32$1;
|
||||
wasm2js_i32$0 = $0 + 20 | 0;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP32[wasm2js_i32$0 >> 2] = wasm2js_i32$1;
|
||||
wasm2js_i32$0 = $0 + 24 | 0;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP32[wasm2js_i32$0 >> 2] = wasm2js_i32$1;
|
||||
$1 = $0 + $2 | 0;
|
||||
wasm2js_i32$0 = $1 - 28 | 0;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP32[wasm2js_i32$0 >> 2] = wasm2js_i32$1;
|
||||
wasm2js_i32$0 = $1 - 24 | 0;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP32[wasm2js_i32$0 >> 2] = wasm2js_i32$1;
|
||||
wasm2js_i32$0 = $1 - 20 | 0;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP32[wasm2js_i32$0 >> 2] = wasm2js_i32$1;
|
||||
wasm2js_i32$0 = $1 - 16 | 0;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP32[wasm2js_i32$0 >> 2] = wasm2js_i32$1;
|
||||
$1 = ($0 & 4 | 0) + 24 | 0;
|
||||
$0 = $1 + $0 | 0;
|
||||
$2 = $2 - $1 | 0;
|
||||
continue_0 : do {
|
||||
if ($2 >>> 0 >= 32 >>> 0) {
|
||||
i64toi32_i32$1 = $0;
|
||||
i64toi32_i32$0 = 0;
|
||||
wasm2js_i32$0 = $0;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP32[wasm2js_i32$0 >> 2] = wasm2js_i32$1;
|
||||
wasm2js_i32$0 = $0;
|
||||
wasm2js_i32$1 = i64toi32_i32$0;
|
||||
(wasm2js_i32$2 = wasm2js_i32$0, wasm2js_i32$3 = wasm2js_i32$1), ((HEAP8[(wasm2js_i32$2 + 4 | 0) >> 0] = wasm2js_i32$3 & 255 | 0, HEAP8[(wasm2js_i32$2 + 5 | 0) >> 0] = (wasm2js_i32$3 >>> 8 | 0) & 255 | 0), HEAP8[(wasm2js_i32$2 + 6 | 0) >> 0] = (wasm2js_i32$3 >>> 16 | 0) & 255 | 0), HEAP8[(wasm2js_i32$2 + 7 | 0) >> 0] = (wasm2js_i32$3 >>> 24 | 0) & 255 | 0;
|
||||
i64toi32_i32$1 = $0 + 8 | 0;
|
||||
i64toi32_i32$0 = 0;
|
||||
wasm2js_i32$0 = i64toi32_i32$1;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP32[wasm2js_i32$0 >> 2] = wasm2js_i32$1;
|
||||
wasm2js_i32$0 = i64toi32_i32$1;
|
||||
wasm2js_i32$1 = i64toi32_i32$0;
|
||||
(wasm2js_i32$2 = wasm2js_i32$0, wasm2js_i32$3 = wasm2js_i32$1), ((HEAP8[(wasm2js_i32$2 + 4 | 0) >> 0] = wasm2js_i32$3 & 255 | 0, HEAP8[(wasm2js_i32$2 + 5 | 0) >> 0] = (wasm2js_i32$3 >>> 8 | 0) & 255 | 0), HEAP8[(wasm2js_i32$2 + 6 | 0) >> 0] = (wasm2js_i32$3 >>> 16 | 0) & 255 | 0), HEAP8[(wasm2js_i32$2 + 7 | 0) >> 0] = (wasm2js_i32$3 >>> 24 | 0) & 255 | 0;
|
||||
i64toi32_i32$1 = $0 + 16 | 0;
|
||||
i64toi32_i32$0 = 0;
|
||||
wasm2js_i32$0 = i64toi32_i32$1;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP32[wasm2js_i32$0 >> 2] = wasm2js_i32$1;
|
||||
wasm2js_i32$0 = i64toi32_i32$1;
|
||||
wasm2js_i32$1 = i64toi32_i32$0;
|
||||
(wasm2js_i32$2 = wasm2js_i32$0, wasm2js_i32$3 = wasm2js_i32$1), ((HEAP8[(wasm2js_i32$2 + 4 | 0) >> 0] = wasm2js_i32$3 & 255 | 0, HEAP8[(wasm2js_i32$2 + 5 | 0) >> 0] = (wasm2js_i32$3 >>> 8 | 0) & 255 | 0), HEAP8[(wasm2js_i32$2 + 6 | 0) >> 0] = (wasm2js_i32$3 >>> 16 | 0) & 255 | 0), HEAP8[(wasm2js_i32$2 + 7 | 0) >> 0] = (wasm2js_i32$3 >>> 24 | 0) & 255 | 0;
|
||||
i64toi32_i32$1 = $0 + 24 | 0;
|
||||
i64toi32_i32$0 = 0;
|
||||
wasm2js_i32$0 = i64toi32_i32$1;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP32[wasm2js_i32$0 >> 2] = wasm2js_i32$1;
|
||||
wasm2js_i32$0 = i64toi32_i32$1;
|
||||
wasm2js_i32$1 = i64toi32_i32$0;
|
||||
(wasm2js_i32$2 = wasm2js_i32$0, wasm2js_i32$3 = wasm2js_i32$1), ((HEAP8[(wasm2js_i32$2 + 4 | 0) >> 0] = wasm2js_i32$3 & 255 | 0, HEAP8[(wasm2js_i32$2 + 5 | 0) >> 0] = (wasm2js_i32$3 >>> 8 | 0) & 255 | 0), HEAP8[(wasm2js_i32$2 + 6 | 0) >> 0] = (wasm2js_i32$3 >>> 16 | 0) & 255 | 0), HEAP8[(wasm2js_i32$2 + 7 | 0) >> 0] = (wasm2js_i32$3 >>> 24 | 0) & 255 | 0;
|
||||
$2 = $2 - 32 | 0;
|
||||
$0 = $0 + 32 | 0;
|
||||
continue continue_0;
|
||||
}
|
||||
break continue_0;
|
||||
} while (1);
|
||||
}
|
||||
|
||||
function $lib_array_Array_Body__constructor() {
|
||||
var $0 = 0, $1 = 0, wasm2js_i32$0 = 0, wasm2js_i32$1 = 0;
|
||||
$1 = $lib_allocator_arena___memory_allocate(32 | 0) | 0;
|
||||
wasm2js_i32$0 = $1;
|
||||
wasm2js_i32$1 = 20;
|
||||
HEAP32[wasm2js_i32$0 >> 2] = wasm2js_i32$1;
|
||||
$0 = $lib_allocator_arena___memory_allocate(8 | 0) | 0;
|
||||
wasm2js_i32$0 = $0;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP32[wasm2js_i32$0 >> 2] = wasm2js_i32$1;
|
||||
wasm2js_i32$0 = $0;
|
||||
wasm2js_i32$1 = 0;
|
||||
HEAP32[(wasm2js_i32$0 + 4 | 0) >> 2] = wasm2js_i32$1;
|
||||
wasm2js_i32$0 = $0;
|
||||
wasm2js_i32$1 = $1;
|
||||
HEAP32[wasm2js_i32$0 >> 2] = wasm2js_i32$1;
|
||||
wasm2js_i32$0 = $0;
|
||||
wasm2js_i32$1 = 5;
|
||||
HEAP32[(wasm2js_i32$0 + 4 | 0) >> 2] = wasm2js_i32$1;
|
||||
$lib_internal_memory_memset($1 + 8 | 0 | 0);
|
||||
return $0 | 0;
|
||||
function $lib_rt___allocArray() {
|
||||
var $0 = 0, $1 = 0;
|
||||
$0 = $lib_rt_stub___alloc(16, 5);
|
||||
$1 = $lib_rt_stub___alloc(20, 0);
|
||||
HEAP32[$0 >> 2] = $1;
|
||||
HEAP32[($0 + 4 | 0) >> 2] = $1;
|
||||
HEAP32[($0 + 8 | 0) >> 2] = 20;
|
||||
HEAP32[($0 + 12 | 0) >> 2] = 5;
|
||||
return $0;
|
||||
}
|
||||
|
||||
function assembly_index_init() {
|
||||
var $0 = 0, $1 = 0, wasm2js_i32$0 = 0, wasm2js_i32$1 = 0;
|
||||
$1 = $lib_array_Array_Body__constructor() | 0;
|
||||
$0 = assembly_index_Body_constructor(+(0.0), +(0.0), +(0.0), +(0.0), +(0.0), +(0.0), +(39.47841760435743)) | 0;
|
||||
wasm2js_i32$0 = HEAPU32[$1 >> 2] | 0;
|
||||
wasm2js_i32$1 = $0;
|
||||
HEAP32[(wasm2js_i32$0 + 8 | 0) >> 2] = wasm2js_i32$1;
|
||||
$0 = assembly_index_Body_constructor(+(4.841431442464721), +(-1.1603200440274284), +(-.10362204447112311), +(.606326392995832), +(2.81198684491626), +(-.02521836165988763), +(.03769367487038949)) | 0;
|
||||
wasm2js_i32$0 = (HEAPU32[$1 >> 2] | 0) + 4 | 0;
|
||||
wasm2js_i32$1 = $0;
|
||||
HEAP32[(wasm2js_i32$0 + 8 | 0) >> 2] = wasm2js_i32$1;
|
||||
$0 = assembly_index_Body_constructor(+(8.34336671824458), +(4.124798564124305), +(-.4035234171143214), +(-1.0107743461787924), +(1.8256623712304119), +(.008415761376584154), +(.011286326131968767)) | 0;
|
||||
wasm2js_i32$0 = (HEAPU32[$1 >> 2] | 0) + 8 | 0;
|
||||
wasm2js_i32$1 = $0;
|
||||
HEAP32[(wasm2js_i32$0 + 8 | 0) >> 2] = wasm2js_i32$1;
|
||||
$0 = assembly_index_Body_constructor(+(12.894369562139131), +(-15.111151401698631), +(-.22330757889265573), +(1.0827910064415354), +(.8687130181696082), +(-.010832637401363636), +(1.7237240570597112e-03)) | 0;
|
||||
wasm2js_i32$0 = (HEAPU32[$1 >> 2] | 0) + 12 | 0;
|
||||
wasm2js_i32$1 = $0;
|
||||
HEAP32[(wasm2js_i32$0 + 8 | 0) >> 2] = wasm2js_i32$1;
|
||||
$0 = assembly_index_Body_constructor(+(15.379697114850917), +(-25.919314609987964), +(.17925877295037118), +(.979090732243898), +(.5946989986476762), +(-.034755955504078104), +(2.0336868699246304e-03)) | 0;
|
||||
wasm2js_i32$0 = (HEAPU32[$1 >> 2] | 0) + 16 | 0;
|
||||
wasm2js_i32$1 = $0;
|
||||
HEAP32[(wasm2js_i32$0 + 8 | 0) >> 2] = wasm2js_i32$1;
|
||||
assembly_index_system = assembly_index_NBodySystem_constructor($1 | 0) | 0;
|
||||
$1 = $lib_rt___allocArray();
|
||||
$0 = HEAP32[($1 + 4 | 0) >> 2];
|
||||
(wasm2js_i32$0 = $0, wasm2js_i32$1 = assembly_index_Body_constructor(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 39.47841760435743)), HEAP32[wasm2js_i32$0 >> 2] = wasm2js_i32$1;
|
||||
(wasm2js_i32$0 = $0, wasm2js_i32$1 = assembly_index_Body_constructor(4.841431442464721, -1.1603200440274284, -.10362204447112311, .606326392995832, 2.81198684491626, -.02521836165988763, .03769367487038949)), HEAP32[(wasm2js_i32$0 + 4 | 0) >> 2] = wasm2js_i32$1;
|
||||
(wasm2js_i32$0 = $0, wasm2js_i32$1 = assembly_index_Body_constructor(8.34336671824458, 4.124798564124305, -.4035234171143214, -1.0107743461787924, 1.8256623712304119, .008415761376584154, .011286326131968767)), HEAP32[(wasm2js_i32$0 + 8 | 0) >> 2] = wasm2js_i32$1;
|
||||
(wasm2js_i32$0 = $0, wasm2js_i32$1 = assembly_index_Body_constructor(12.894369562139131, -15.111151401698631, -.22330757889265573, 1.0827910064415354, .8687130181696082, -.010832637401363636, 1.7237240570597112e-03)), HEAP32[(wasm2js_i32$0 + 12 | 0) >> 2] = wasm2js_i32$1;
|
||||
(wasm2js_i32$0 = $0, wasm2js_i32$1 = assembly_index_Body_constructor(15.379697114850917, -25.919314609987964, .17925877295037118, .979090732243898, .5946989986476762, -.034755955504078104, 2.0336868699246304e-03)), HEAP32[(wasm2js_i32$0 + 16 | 0) >> 2] = wasm2js_i32$1;
|
||||
assembly_index_system = assembly_index_NBodySystem_constructor($1);
|
||||
}
|
||||
|
||||
function assembly_index_NBodySystem_advance($0) {
|
||||
$0 = $0 | 0;
|
||||
var $1 = 0, $2 = 0.0, $8 = 0.0, $3 = 0, $4 = 0.0, $5 = 0.0, $6 = 0.0, $7 = 0, $9 = 0.0, $10 = 0.0, $11 = 0.0, $12 = 0, $13 = 0, $18 = 0.0, $14 = 0.0, $15 = 0.0, $16 = 0.0, $17 = 0.0, wasm2js_i32$0 = 0, wasm2js_f64$0 = 0.0;
|
||||
$12 = HEAPU32[$0 >> 2] | 0;
|
||||
$13 = HEAP32[($12 + 4 | 0) >> 2] | 0;
|
||||
repeat_0 : do {
|
||||
if ($3 >>> 0 < $13 >>> 0) {
|
||||
$0 = HEAPU32[(((HEAPU32[$12 >> 2] | 0) + ($3 << 2 | 0) | 0) + 8 | 0) >> 2] | 0;
|
||||
$14 = +HEAPF64[$0 >> 3];
|
||||
$15 = +HEAPF64[($0 + 8 | 0) >> 3];
|
||||
$16 = +HEAPF64[($0 + 16 | 0) >> 3];
|
||||
$4 = +HEAPF64[($0 + 24 | 0) >> 3];
|
||||
$5 = +HEAPF64[($0 + 32 | 0) >> 3];
|
||||
$6 = +HEAPF64[($0 + 40 | 0) >> 3];
|
||||
$17 = +HEAPF64[($0 + 48 | 0) >> 3];
|
||||
$7 = $3 + 1 | 0;
|
||||
repeat_1 : do {
|
||||
if ($7 >>> 0 < $13 >>> 0) {
|
||||
$1 = HEAPU32[(((HEAPU32[$12 >> 2] | 0) + ($7 << 2 | 0) | 0) + 8 | 0) >> 2] | 0;
|
||||
$18 = $14 - +HEAPF64[$1 >> 3];
|
||||
$2 = $18;
|
||||
$9 = $15 - +HEAPF64[($1 + 8 | 0) >> 3];
|
||||
$10 = $16 - +HEAPF64[($1 + 16 | 0) >> 3];
|
||||
$8 = $2 * $2 + $9 * $9 + $10 * $10;
|
||||
$11 = Math_sqrt($8);
|
||||
$11 = .01 / ($8 * $11);
|
||||
$8 = +HEAPF64[($1 + 48 | 0) >> 3] * $11;
|
||||
$4 = $4 - $2 * $8;
|
||||
$5 = $5 - $9 * $8;
|
||||
$6 = $6 - $10 * $8;
|
||||
var $1 = 0, $2 = 0.0, $3 = 0.0, $4 = 0, $5 = 0.0, $6 = 0.0, $7 = 0.0, $8 = 0, $9 = 0.0, $10 = 0.0, $11 = 0.0, $12 = 0, $13 = 0, $14 = 0.0, $15 = 0.0, $16 = 0.0, $17 = 0.0;
|
||||
$12 = HEAP32[$0 >> 2];
|
||||
$13 = HEAP32[($12 + 12 | 0) >> 2];
|
||||
repeat_0 : while (1) {
|
||||
if ($4 >>> 0 < $13 >>> 0) {
|
||||
$0 = HEAP32[(HEAP32[($12 + 4 | 0) >> 2] + ($4 << 2 | 0) | 0) >> 2];
|
||||
$14 = HEAPF64[$0 >> 3];
|
||||
$15 = HEAPF64[($0 + 8 | 0) >> 3];
|
||||
$16 = HEAPF64[($0 + 16 | 0) >> 3];
|
||||
$5 = HEAPF64[($0 + 24 | 0) >> 3];
|
||||
$6 = HEAPF64[($0 + 32 | 0) >> 3];
|
||||
$7 = HEAPF64[($0 + 40 | 0) >> 3];
|
||||
$17 = HEAPF64[($0 + 48 | 0) >> 3];
|
||||
$8 = $4 + 1 | 0;
|
||||
repeat_1 : while (1) {
|
||||
if ($8 >>> 0 < $13 >>> 0) {
|
||||
$1 = HEAP32[(HEAP32[($12 + 4 | 0) >> 2] + ($8 << 2 | 0) | 0) >> 2];
|
||||
$2 = $14 - HEAPF64[$1 >> 3];
|
||||
$9 = $15 - HEAPF64[($1 + 8 | 0) >> 3];
|
||||
$10 = $16 - HEAPF64[($1 + 16 | 0) >> 3];
|
||||
$3 = $2 * $2 + $9 * $9 + $10 * $10;
|
||||
$11 = Math_sqrt($3);
|
||||
$11 = .01 / ($3 * $11);
|
||||
$3 = HEAPF64[($1 + 48 | 0) >> 3] * $11;
|
||||
$5 = $5 - $2 * $3;
|
||||
$6 = $6 - $9 * $3;
|
||||
$7 = $7 - $10 * $3;
|
||||
$3 = $2;
|
||||
$2 = $17 * $11;
|
||||
wasm2js_i32$0 = $1;
|
||||
wasm2js_f64$0 = +HEAPF64[($1 + 24 | 0) >> 3] + $18 * $2;
|
||||
HEAPF64[(wasm2js_i32$0 + 24 | 0) >> 3] = wasm2js_f64$0;
|
||||
wasm2js_i32$0 = $1;
|
||||
wasm2js_f64$0 = +HEAPF64[($1 + 32 | 0) >> 3] + $9 * $2;
|
||||
HEAPF64[(wasm2js_i32$0 + 32 | 0) >> 3] = wasm2js_f64$0;
|
||||
wasm2js_i32$0 = $1;
|
||||
wasm2js_f64$0 = +HEAPF64[($1 + 40 | 0) >> 3] + $10 * $2;
|
||||
HEAPF64[(wasm2js_i32$0 + 40 | 0) >> 3] = wasm2js_f64$0;
|
||||
$7 = $7 + 1 | 0;
|
||||
HEAPF64[($1 + 24 | 0) >> 3] = HEAPF64[($1 + 24 | 0) >> 3] + $3 * $2;
|
||||
HEAPF64[($1 + 32 | 0) >> 3] = HEAPF64[($1 + 32 | 0) >> 3] + $9 * $2;
|
||||
HEAPF64[($1 + 40 | 0) >> 3] = HEAPF64[($1 + 40 | 0) >> 3] + $10 * $2;
|
||||
$8 = $8 + 1 | 0;
|
||||
continue repeat_1;
|
||||
}
|
||||
break repeat_1;
|
||||
} while (1);
|
||||
wasm2js_i32$0 = $0;
|
||||
wasm2js_f64$0 = $4;
|
||||
HEAPF64[(wasm2js_i32$0 + 24 | 0) >> 3] = wasm2js_f64$0;
|
||||
wasm2js_i32$0 = $0;
|
||||
wasm2js_f64$0 = $5;
|
||||
HEAPF64[(wasm2js_i32$0 + 32 | 0) >> 3] = wasm2js_f64$0;
|
||||
wasm2js_i32$0 = $0;
|
||||
wasm2js_f64$0 = $6;
|
||||
HEAPF64[(wasm2js_i32$0 + 40 | 0) >> 3] = wasm2js_f64$0;
|
||||
wasm2js_i32$0 = $0;
|
||||
wasm2js_f64$0 = +HEAPF64[$0 >> 3] + .01 * $4;
|
||||
HEAPF64[wasm2js_i32$0 >> 3] = wasm2js_f64$0;
|
||||
wasm2js_i32$0 = $0;
|
||||
wasm2js_f64$0 = +HEAPF64[($0 + 8 | 0) >> 3] + .01 * $5;
|
||||
HEAPF64[(wasm2js_i32$0 + 8 | 0) >> 3] = wasm2js_f64$0;
|
||||
wasm2js_i32$0 = $0;
|
||||
wasm2js_f64$0 = +HEAPF64[($0 + 16 | 0) >> 3] + .01 * $6;
|
||||
HEAPF64[(wasm2js_i32$0 + 16 | 0) >> 3] = wasm2js_f64$0;
|
||||
$3 = $3 + 1 | 0;
|
||||
};
|
||||
HEAPF64[($0 + 24 | 0) >> 3] = $5;
|
||||
HEAPF64[($0 + 32 | 0) >> 3] = $6;
|
||||
HEAPF64[($0 + 40 | 0) >> 3] = $7;
|
||||
HEAPF64[$0 >> 3] = HEAPF64[$0 >> 3] + .01 * $5;
|
||||
HEAPF64[($0 + 8 | 0) >> 3] = HEAPF64[($0 + 8 | 0) >> 3] + .01 * $6;
|
||||
HEAPF64[($0 + 16 | 0) >> 3] = HEAPF64[($0 + 16 | 0) >> 3] + .01 * $7;
|
||||
$4 = $4 + 1 | 0;
|
||||
continue repeat_0;
|
||||
}
|
||||
break repeat_0;
|
||||
} while (1);
|
||||
};
|
||||
}
|
||||
|
||||
function assembly_index_NBodySystem_energy($0) {
|
||||
$0 = $0 | 0;
|
||||
var $1 = 0.0, $2 = 0, $3 = 0, $4 = 0, $5 = 0, $10 = 0.0, $6 = 0.0, $7 = 0.0, $8 = 0.0, $9 = 0.0, $30 = 0.0, $39 = 0.0, $45 = 0.0, $69 = 0.0, $84 = 0.0;
|
||||
$4 = HEAPU32[$0 >> 2] | 0;
|
||||
$5 = HEAP32[($4 + 4 | 0) >> 2] | 0;
|
||||
repeat_0 : do {
|
||||
if ($2 >>> 0 < $5 >>> 0) {
|
||||
$0 = HEAPU32[(((HEAPU32[$4 >> 2] | 0) + ($2 << 2 | 0) | 0) + 8 | 0) >> 2] | 0;
|
||||
$7 = +HEAPF64[$0 >> 3];
|
||||
$8 = +HEAPF64[($0 + 8 | 0) >> 3];
|
||||
$9 = +HEAPF64[($0 + 16 | 0) >> 3];
|
||||
$30 = $1;
|
||||
$10 = +HEAPF64[($0 + 48 | 0) >> 3];
|
||||
$1 = +HEAPF64[($0 + 24 | 0) >> 3];
|
||||
$39 = $1 * $1;
|
||||
$1 = +HEAPF64[($0 + 32 | 0) >> 3];
|
||||
$45 = $39 + $1 * $1;
|
||||
$1 = +HEAPF64[($0 + 40 | 0) >> 3];
|
||||
$1 = $30 + .5 * $10 * ($45 + $1 * $1);
|
||||
$0 = $2 + 1 | 0;
|
||||
repeat_1 : do {
|
||||
if ($0 >>> 0 < $5 >>> 0) {
|
||||
$3 = HEAPU32[(((HEAPU32[$4 >> 2] | 0) + ($0 << 2 | 0) | 0) + 8 | 0) >> 2] | 0;
|
||||
$6 = $7 - +HEAPF64[$3 >> 3];
|
||||
$69 = $1;
|
||||
$1 = $8 - +HEAPF64[($3 + 8 | 0) >> 3];
|
||||
$84 = $6 * $6 + $1 * $1;
|
||||
$1 = $9 - +HEAPF64[($3 + 16 | 0) >> 3];
|
||||
$1 = $69 - $10 * +HEAPF64[($3 + 48 | 0) >> 3] / Math_sqrt($84 + $1 * $1);
|
||||
var $1 = 0.0, $2 = 0.0, $3 = 0, $4 = 0, $5 = 0, $6 = 0.0, $7 = 0, $8 = 0.0, $9 = 0.0, $10 = 0.0, $11 = 0.0;
|
||||
$5 = HEAP32[$0 >> 2];
|
||||
$7 = HEAP32[($5 + 12 | 0) >> 2];
|
||||
repeat_0 : while (1) {
|
||||
if ($3 >>> 0 < $7 >>> 0) {
|
||||
$0 = HEAP32[(HEAP32[($5 + 4 | 0) >> 2] + ($3 << 2 | 0) | 0) >> 2];
|
||||
$9 = HEAPF64[$0 >> 3];
|
||||
$10 = HEAPF64[($0 + 8 | 0) >> 3];
|
||||
$11 = HEAPF64[($0 + 16 | 0) >> 3];
|
||||
$6 = $1;
|
||||
$8 = HEAPF64[($0 + 48 | 0) >> 3];
|
||||
$1 = HEAPF64[($0 + 24 | 0) >> 3];
|
||||
$2 = $1 * $1;
|
||||
$1 = HEAPF64[($0 + 32 | 0) >> 3];
|
||||
$2 = $2 + $1 * $1;
|
||||
$1 = HEAPF64[($0 + 40 | 0) >> 3];
|
||||
$1 = $6 + .5 * $8 * ($2 + $1 * $1);
|
||||
$0 = $3 + 1 | 0;
|
||||
repeat_1 : while (1) {
|
||||
if ($0 >>> 0 < $7 >>> 0) {
|
||||
$4 = HEAP32[(HEAP32[($5 + 4 | 0) >> 2] + ($0 << 2 | 0) | 0) >> 2];
|
||||
$6 = $1;
|
||||
$1 = $9 - HEAPF64[$4 >> 3];
|
||||
$2 = $1 * $1;
|
||||
$1 = $10 - HEAPF64[($4 + 8 | 0) >> 3];
|
||||
$2 = $2 + $1 * $1;
|
||||
$1 = $11 - HEAPF64[($4 + 16 | 0) >> 3];
|
||||
$1 = $6 - $8 * HEAPF64[($4 + 48 | 0) >> 3] / Math_sqrt($2 + $1 * $1);
|
||||
$0 = $0 + 1 | 0;
|
||||
continue repeat_1;
|
||||
}
|
||||
break repeat_1;
|
||||
} while (1);
|
||||
$2 = $2 + 1 | 0;
|
||||
};
|
||||
$3 = $3 + 1 | 0;
|
||||
continue repeat_0;
|
||||
}
|
||||
break repeat_0;
|
||||
} while (1);
|
||||
return +$1;
|
||||
};
|
||||
return $1;
|
||||
}
|
||||
|
||||
function assembly_index_step() {
|
||||
assembly_index_NBodySystem_advance(assembly_index_system | 0);
|
||||
return +(+assembly_index_NBodySystem_energy(assembly_index_system | 0));
|
||||
assembly_index_NBodySystem_advance(assembly_index_system);
|
||||
return +assembly_index_NBodySystem_energy(assembly_index_system);
|
||||
}
|
||||
|
||||
function assembly_index_bench($0) {
|
||||
$0 = $0 | 0;
|
||||
var $1 = 0;
|
||||
break_0 : {
|
||||
repeat_0 : do {
|
||||
if ($1 >>> 0 >= $0 >>> 0) break break_0;
|
||||
assembly_index_NBodySystem_advance(assembly_index_system | 0);
|
||||
repeat_0 : while (1) {
|
||||
if (!($1 >>> 0 >= $0 >>> 0)) {
|
||||
assembly_index_NBodySystem_advance(assembly_index_system);
|
||||
$1 = $1 + 1 | 0;
|
||||
continue repeat_0;
|
||||
break repeat_0;
|
||||
} while (1);
|
||||
}
|
||||
break repeat_0;
|
||||
};
|
||||
}
|
||||
|
||||
function assembly_index_getBody($0) {
|
||||
$0 = $0 | 0;
|
||||
var $1 = 0, $22 = 0, $20 = 0;
|
||||
$1 = HEAPU32[assembly_index_system >> 2] | 0;
|
||||
if ($0 >>> 0 < (HEAP32[($1 + 4 | 0) >> 2] | 0) >>> 0) {
|
||||
$1 = HEAPU32[$1 >> 2] | 0;
|
||||
if ($0 >>> 0 < ((HEAP32[$1 >> 2] | 0) >>> 2 | 0) >>> 0) $20 = HEAPU32[((($0 << 2 | 0) + $1 | 0) + 8 | 0) >> 2] | 0; else abort();
|
||||
$22 = $20;
|
||||
} else $22 = 0;
|
||||
return $22 | 0;
|
||||
var $1 = 0;
|
||||
$1 = HEAP32[assembly_index_system >> 2];
|
||||
if ($0 >>> 0 < HEAP32[($1 + 12 | 0) >> 2] >>> 0) {
|
||||
$0 = HEAP32[(HEAP32[($1 + 4 | 0) >> 2] + ($0 << 2 | 0) | 0) >> 2]
|
||||
} else {
|
||||
$0 = 0
|
||||
}
|
||||
return $0 | 0;
|
||||
}
|
||||
|
||||
function start() {
|
||||
$lib_allocator_arena_startOffset = 40;
|
||||
$lib_allocator_arena_offset = $lib_allocator_arena_startOffset;
|
||||
$lib_rt_stub_startOffset = 16;
|
||||
$lib_rt_stub_offset = $lib_rt_stub_startOffset;
|
||||
}
|
||||
|
||||
function null_() {
|
||||
|
||||
}
|
||||
|
||||
var FUNCTION_TABLE_v = [null_];
|
||||
function __wasm_grow_memory(pagesToAdd) {
|
||||
var FUNCTION_TABLE = [];
|
||||
function __wasm_memory_grow(pagesToAdd) {
|
||||
pagesToAdd = pagesToAdd | 0;
|
||||
var oldPages = __wasm_current_memory() | 0;
|
||||
var oldPages = __wasm_memory_size() | 0;
|
||||
var newPages = oldPages + pagesToAdd | 0;
|
||||
if ((oldPages < newPages) && (newPages < 65536)) {
|
||||
var newBuffer = new ArrayBuffer(Math_imul(newPages, 65536));
|
||||
var newHEAP8 = new global.Int8Array(newBuffer);
|
||||
newHEAP8.set(HEAP8);
|
||||
HEAP8 = newHEAP8;
|
||||
HEAP8 = new global.Int8Array(newBuffer);
|
||||
HEAP16 = new global.Int16Array(newBuffer);
|
||||
HEAP32 = new global.Int32Array(newBuffer);
|
||||
HEAPU8 = new global.Uint8Array(newBuffer);
|
||||
@ -453,49 +258,33 @@ function asmFunc(global, env, buffer) {
|
||||
return oldPages;
|
||||
}
|
||||
|
||||
function __wasm_current_memory() {
|
||||
function __wasm_memory_size() {
|
||||
return buffer.byteLength / 65536 | 0;
|
||||
}
|
||||
|
||||
return {
|
||||
memory: Object.create(Object.prototype, {
|
||||
grow: {
|
||||
value: __wasm_grow_memory
|
||||
"memory": Object.create(Object.prototype, {
|
||||
"grow": {
|
||||
"value": __wasm_memory_grow
|
||||
},
|
||||
buffer: {
|
||||
get: function () {
|
||||
"buffer": {
|
||||
"get": function () {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
}
|
||||
}),
|
||||
init: assembly_index_init,
|
||||
step: assembly_index_step,
|
||||
bench: assembly_index_bench,
|
||||
getBody: assembly_index_getBody
|
||||
"init": assembly_index_init,
|
||||
"step": assembly_index_step,
|
||||
"bench": assembly_index_bench,
|
||||
"getBody": assembly_index_getBody
|
||||
};
|
||||
}
|
||||
|
||||
const memasmFunc = new ArrayBuffer(65536);
|
||||
const assignasmFunc = (
|
||||
function(mem) {
|
||||
const _mem = new Uint8Array(mem);
|
||||
return function(offset, s) {
|
||||
if (typeof Buffer === 'undefined') {
|
||||
const bytes = atob(s);
|
||||
for (let i = 0; i < bytes.length; i++)
|
||||
_mem[offset + i] = bytes.charCodeAt(i);
|
||||
} else {
|
||||
const bytes = Buffer.from(s, 'base64');
|
||||
for (let i = 0; i < bytes.length; i++)
|
||||
_mem[offset + i] = bytes[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
)(memasmFunc);
|
||||
assignasmFunc(8, "DQAAAH4AbABpAGIALwBhAHIAcgBhAHkALgB0AHM=");
|
||||
const retasmFunc = asmFunc({Math,Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,NaN,Infinity}, {abort:function() { throw new Error('abort'); }},memasmFunc);
|
||||
export const memory = retasmFunc.memory;
|
||||
export const init = retasmFunc.init;
|
||||
export const step = retasmFunc.step;
|
||||
export const bench = retasmFunc.bench;
|
||||
var memasmFunc = new ArrayBuffer(65536);
|
||||
var retasmFunc = asmFunc({Math,Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,NaN,Infinity}, {abort:function() { throw new Error('abort'); }},memasmFunc);
|
||||
exports.memory = retasmFunc.memory;
|
||||
exports.init = retasmFunc.init;
|
||||
exports.step = retasmFunc.step;
|
||||
exports.bench = retasmFunc.bench;
|
||||
exports.getBody = retasmFunc.getBody;
|
||||
|
@ -49,7 +49,7 @@ class NBodySystem {
|
||||
py += b.vy * m;
|
||||
pz += b.vz * m;
|
||||
}
|
||||
bodies[0].offsetMomentum(px, py, pz);
|
||||
unchecked(bodies[0]).offsetMomentum(px, py, pz);
|
||||
}
|
||||
advance(dt) {
|
||||
var bodies = this.bodies;
|
||||
@ -139,6 +139,6 @@ function bench(steps) {
|
||||
exports.bench = bench;
|
||||
function getBody(index) {
|
||||
var bodies = system.bodies;
|
||||
return index < bodies.length ? bodies[index] : null;
|
||||
return index < bodies.length ? unchecked(bodies[index]) : null;
|
||||
}
|
||||
exports.getBody = getBody;
|
||||
|
Binary file not shown.
@ -1,37 +1,37 @@
|
||||
(module
|
||||
(type $FUNCSIG$v (func))
|
||||
(type $FUNCSIG$iii (func (param i32 i32) (result i32)))
|
||||
(type $FUNCSIG$ii (func (param i32) (result i32)))
|
||||
(type $FUNCSIG$vi (func (param i32)))
|
||||
(type $FUNCSIG$d (func (result f64)))
|
||||
(type $FUNCSIG$di (func (param i32) (result f64)))
|
||||
(type $FUNCSIG$vi (func (param i32)))
|
||||
(type $FUNCSIG$iddddddd (func (param f64 f64 f64 f64 f64 f64 f64) (result i32)))
|
||||
(type $FUNCSIG$i (func (result i32)))
|
||||
(import "env" "memory" (memory $0 1))
|
||||
(data (i32.const 8) "\0d\00\00\00~\00l\00i\00b\00/\00a\00r\00r\00a\00y\00.\00t\00s")
|
||||
(table $0 1 funcref)
|
||||
(elem (i32.const 0) $null)
|
||||
(global $~lib/allocator/arena/startOffset (mut i32) (i32.const 0))
|
||||
(global $~lib/allocator/arena/offset (mut i32) (i32.const 0))
|
||||
(import "env" "memory" (memory $0 0))
|
||||
(global $assembly/index/system (mut i32) (i32.const 0))
|
||||
(global $~lib/rt/stub/startOffset (mut i32) (i32.const 0))
|
||||
(global $~lib/rt/stub/offset (mut i32) (i32.const 0))
|
||||
(export "memory" (memory $0))
|
||||
(export "table" (table $0))
|
||||
(export "init" (func $assembly/index/init))
|
||||
(export "step" (func $assembly/index/step))
|
||||
(export "bench" (func $assembly/index/bench))
|
||||
(export "getBody" (func $assembly/index/getBody))
|
||||
(start $start)
|
||||
(func $~lib/allocator/arena/__memory_allocate (; 0 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
|
||||
(local $1 i32)
|
||||
(func $~lib/rt/stub/__alloc (; 0 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32)
|
||||
(local $2 i32)
|
||||
(local $3 i32)
|
||||
(local $4 i32)
|
||||
(local $5 i32)
|
||||
local.get $0
|
||||
i32.const 1073741824
|
||||
i32.const 1073741808
|
||||
i32.gt_u
|
||||
if
|
||||
unreachable
|
||||
end
|
||||
global.get $~lib/allocator/arena/offset
|
||||
local.tee $1
|
||||
global.get $~lib/rt/stub/offset
|
||||
i32.const 16
|
||||
i32.add
|
||||
local.tee $3
|
||||
local.get $0
|
||||
i32.const 1
|
||||
local.get $0
|
||||
@ -39,20 +39,20 @@
|
||||
i32.gt_u
|
||||
select
|
||||
i32.add
|
||||
i32.const 7
|
||||
i32.const 15
|
||||
i32.add
|
||||
i32.const -8
|
||||
i32.const -16
|
||||
i32.and
|
||||
local.tee $2
|
||||
current_memory
|
||||
local.tee $3
|
||||
memory.size
|
||||
local.tee $4
|
||||
i32.const 16
|
||||
i32.shl
|
||||
i32.gt_u
|
||||
if
|
||||
local.get $3
|
||||
local.get $4
|
||||
local.get $2
|
||||
local.get $1
|
||||
local.get $3
|
||||
i32.sub
|
||||
i32.const 65535
|
||||
i32.add
|
||||
@ -60,17 +60,17 @@
|
||||
i32.and
|
||||
i32.const 16
|
||||
i32.shr_u
|
||||
local.tee $0
|
||||
local.get $3
|
||||
local.get $0
|
||||
local.tee $5
|
||||
local.get $4
|
||||
local.get $5
|
||||
i32.gt_s
|
||||
select
|
||||
grow_memory
|
||||
memory.grow
|
||||
i32.const 0
|
||||
i32.lt_s
|
||||
if
|
||||
local.get $0
|
||||
grow_memory
|
||||
local.get $5
|
||||
memory.grow
|
||||
i32.const 0
|
||||
i32.lt_s
|
||||
if
|
||||
@ -79,8 +79,17 @@
|
||||
end
|
||||
end
|
||||
local.get $2
|
||||
global.set $~lib/allocator/arena/offset
|
||||
global.set $~lib/rt/stub/offset
|
||||
local.get $3
|
||||
i32.const 16
|
||||
i32.sub
|
||||
local.tee $2
|
||||
local.get $1
|
||||
i32.store offset=8
|
||||
local.get $2
|
||||
local.get $0
|
||||
i32.store offset=12
|
||||
local.get $3
|
||||
)
|
||||
(func $assembly/index/NBodySystem#constructor (; 1 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
|
||||
(local $1 i32)
|
||||
@ -91,7 +100,7 @@
|
||||
(local $6 f64)
|
||||
(local $7 i32)
|
||||
local.get $0
|
||||
i32.load offset=4
|
||||
i32.load offset=12
|
||||
local.set $7
|
||||
loop $repeat|0
|
||||
local.get $1
|
||||
@ -99,12 +108,12 @@
|
||||
i32.lt_s
|
||||
if
|
||||
local.get $0
|
||||
i32.load
|
||||
i32.load offset=4
|
||||
local.get $1
|
||||
i32.const 2
|
||||
i32.shl
|
||||
i32.add
|
||||
i32.load offset=8
|
||||
i32.load
|
||||
local.tee $2
|
||||
f64.load offset=48
|
||||
local.set $3
|
||||
@ -136,21 +145,10 @@
|
||||
br $repeat|0
|
||||
end
|
||||
end
|
||||
i32.const 0
|
||||
local.get $0
|
||||
i32.load offset=4
|
||||
i32.load
|
||||
local.tee $1
|
||||
i32.load
|
||||
i32.const 2
|
||||
i32.shr_u
|
||||
i32.lt_u
|
||||
if (result i32)
|
||||
local.get $1
|
||||
i32.load offset=8
|
||||
else
|
||||
unreachable
|
||||
end
|
||||
local.tee $1
|
||||
local.get $4
|
||||
f64.neg
|
||||
f64.const 39.47841760435743
|
||||
@ -169,7 +167,8 @@
|
||||
f64.div
|
||||
f64.store offset=40
|
||||
i32.const 4
|
||||
call $~lib/allocator/arena/__memory_allocate
|
||||
i32.const 3
|
||||
call $~lib/rt/stub/__alloc
|
||||
local.tee $1
|
||||
local.get $0
|
||||
i32.store
|
||||
@ -178,7 +177,8 @@
|
||||
(func $assembly/index/Body#constructor (; 2 ;) (type $FUNCSIG$iddddddd) (param $0 f64) (param $1 f64) (param $2 f64) (param $3 f64) (param $4 f64) (param $5 f64) (param $6 f64) (result i32)
|
||||
(local $7 i32)
|
||||
i32.const 56
|
||||
call $~lib/allocator/arena/__memory_allocate
|
||||
i32.const 4
|
||||
call $~lib/rt/stub/__alloc
|
||||
local.tee $7
|
||||
local.get $0
|
||||
f64.store
|
||||
@ -202,232 +202,36 @@
|
||||
f64.store offset=48
|
||||
local.get $7
|
||||
)
|
||||
(func $~lib/internal/memory/memset (; 3 ;) (type $FUNCSIG$vi) (param $0 i32)
|
||||
(local $1 i32)
|
||||
(local $2 i32)
|
||||
local.get $0
|
||||
i32.const 0
|
||||
i32.store8
|
||||
local.get $0
|
||||
i32.const 20
|
||||
i32.add
|
||||
local.tee $1
|
||||
i32.const 1
|
||||
i32.sub
|
||||
i32.const 0
|
||||
i32.store8
|
||||
local.get $0
|
||||
i32.const 1
|
||||
i32.add
|
||||
i32.const 0
|
||||
i32.store8
|
||||
local.get $0
|
||||
i32.const 2
|
||||
i32.add
|
||||
i32.const 0
|
||||
i32.store8
|
||||
local.get $1
|
||||
i32.const 2
|
||||
i32.sub
|
||||
i32.const 0
|
||||
i32.store8
|
||||
local.get $1
|
||||
i32.const 3
|
||||
i32.sub
|
||||
i32.const 0
|
||||
i32.store8
|
||||
local.get $0
|
||||
i32.const 3
|
||||
i32.add
|
||||
i32.const 0
|
||||
i32.store8
|
||||
local.get $1
|
||||
i32.const 4
|
||||
i32.sub
|
||||
i32.const 0
|
||||
i32.store8
|
||||
i32.const 0
|
||||
local.get $0
|
||||
i32.sub
|
||||
i32.const 3
|
||||
i32.and
|
||||
local.tee $1
|
||||
local.get $0
|
||||
i32.add
|
||||
local.tee $0
|
||||
i32.const 0
|
||||
i32.store
|
||||
i32.const 20
|
||||
local.get $1
|
||||
i32.sub
|
||||
i32.const -4
|
||||
i32.and
|
||||
local.tee $2
|
||||
local.get $0
|
||||
i32.add
|
||||
i32.const 4
|
||||
i32.sub
|
||||
i32.const 0
|
||||
i32.store
|
||||
local.get $2
|
||||
i32.const 8
|
||||
i32.le_u
|
||||
if
|
||||
return
|
||||
end
|
||||
local.get $0
|
||||
i32.const 4
|
||||
i32.add
|
||||
i32.const 0
|
||||
i32.store
|
||||
local.get $0
|
||||
i32.const 8
|
||||
i32.add
|
||||
i32.const 0
|
||||
i32.store
|
||||
local.get $0
|
||||
local.get $2
|
||||
i32.add
|
||||
local.tee $1
|
||||
i32.const 12
|
||||
i32.sub
|
||||
i32.const 0
|
||||
i32.store
|
||||
local.get $1
|
||||
i32.const 8
|
||||
i32.sub
|
||||
i32.const 0
|
||||
i32.store
|
||||
local.get $2
|
||||
i32.const 24
|
||||
i32.le_u
|
||||
if
|
||||
return
|
||||
end
|
||||
local.get $0
|
||||
i32.const 12
|
||||
i32.add
|
||||
i32.const 0
|
||||
i32.store
|
||||
local.get $0
|
||||
i32.const 16
|
||||
i32.add
|
||||
i32.const 0
|
||||
i32.store
|
||||
local.get $0
|
||||
i32.const 20
|
||||
i32.add
|
||||
i32.const 0
|
||||
i32.store
|
||||
local.get $0
|
||||
i32.const 24
|
||||
i32.add
|
||||
i32.const 0
|
||||
i32.store
|
||||
local.get $0
|
||||
local.get $2
|
||||
i32.add
|
||||
local.tee $1
|
||||
i32.const 28
|
||||
i32.sub
|
||||
i32.const 0
|
||||
i32.store
|
||||
local.get $1
|
||||
i32.const 24
|
||||
i32.sub
|
||||
i32.const 0
|
||||
i32.store
|
||||
local.get $1
|
||||
i32.const 20
|
||||
i32.sub
|
||||
i32.const 0
|
||||
i32.store
|
||||
local.get $1
|
||||
i32.const 16
|
||||
i32.sub
|
||||
i32.const 0
|
||||
i32.store
|
||||
local.get $0
|
||||
i32.const 4
|
||||
i32.and
|
||||
i32.const 24
|
||||
i32.add
|
||||
local.tee $1
|
||||
local.get $0
|
||||
i32.add
|
||||
local.set $0
|
||||
local.get $2
|
||||
local.get $1
|
||||
i32.sub
|
||||
local.set $2
|
||||
loop $continue|0
|
||||
local.get $2
|
||||
i32.const 32
|
||||
i32.ge_u
|
||||
if
|
||||
local.get $0
|
||||
i64.const 0
|
||||
i64.store
|
||||
local.get $0
|
||||
i32.const 8
|
||||
i32.add
|
||||
i64.const 0
|
||||
i64.store
|
||||
local.get $0
|
||||
i32.const 16
|
||||
i32.add
|
||||
i64.const 0
|
||||
i64.store
|
||||
local.get $0
|
||||
i32.const 24
|
||||
i32.add
|
||||
i64.const 0
|
||||
i64.store
|
||||
local.get $2
|
||||
i32.const 32
|
||||
i32.sub
|
||||
local.set $2
|
||||
local.get $0
|
||||
i32.const 32
|
||||
i32.add
|
||||
local.set $0
|
||||
br $continue|0
|
||||
end
|
||||
end
|
||||
)
|
||||
(func $~lib/array/Array<Body>#constructor (; 4 ;) (type $FUNCSIG$i) (result i32)
|
||||
(func $~lib/rt/__allocArray (; 3 ;) (type $FUNCSIG$i) (result i32)
|
||||
(local $0 i32)
|
||||
(local $1 i32)
|
||||
i32.const 32
|
||||
call $~lib/allocator/arena/__memory_allocate
|
||||
local.tee $1
|
||||
i32.const 20
|
||||
i32.store
|
||||
i32.const 8
|
||||
call $~lib/allocator/arena/__memory_allocate
|
||||
i32.const 16
|
||||
i32.const 5
|
||||
call $~lib/rt/stub/__alloc
|
||||
local.tee $0
|
||||
i32.const 20
|
||||
i32.const 0
|
||||
call $~lib/rt/stub/__alloc
|
||||
local.tee $1
|
||||
i32.store
|
||||
local.get $0
|
||||
i32.const 0
|
||||
i32.store offset=4
|
||||
local.get $0
|
||||
local.get $1
|
||||
i32.store
|
||||
i32.store offset=4
|
||||
local.get $0
|
||||
i32.const 20
|
||||
i32.store offset=8
|
||||
local.get $0
|
||||
i32.const 5
|
||||
i32.store offset=4
|
||||
local.get $1
|
||||
i32.const 8
|
||||
i32.add
|
||||
call $~lib/internal/memory/memset
|
||||
i32.store offset=12
|
||||
local.get $0
|
||||
)
|
||||
(func $assembly/index/init (; 5 ;) (type $FUNCSIG$v)
|
||||
(func $assembly/index/init (; 4 ;) (type $FUNCSIG$v)
|
||||
(local $0 i32)
|
||||
(local $1 i32)
|
||||
call $~lib/array/Array<Body>#constructor
|
||||
local.set $1
|
||||
call $~lib/rt/__allocArray
|
||||
local.tee $1
|
||||
i32.load offset=4
|
||||
local.tee $0
|
||||
f64.const 0
|
||||
f64.const 0
|
||||
f64.const 0
|
||||
@ -436,11 +240,8 @@
|
||||
f64.const 0
|
||||
f64.const 39.47841760435743
|
||||
call $assembly/index/Body#constructor
|
||||
local.set $0
|
||||
local.get $1
|
||||
i32.load
|
||||
i32.store
|
||||
local.get $0
|
||||
i32.store offset=8
|
||||
f64.const 4.841431442464721
|
||||
f64.const -1.1603200440274284
|
||||
f64.const -0.10362204447112311
|
||||
@ -449,13 +250,8 @@
|
||||
f64.const -0.02521836165988763
|
||||
f64.const 0.03769367487038949
|
||||
call $assembly/index/Body#constructor
|
||||
local.set $0
|
||||
local.get $1
|
||||
i32.load
|
||||
i32.const 4
|
||||
i32.add
|
||||
i32.store offset=4
|
||||
local.get $0
|
||||
i32.store offset=8
|
||||
f64.const 8.34336671824458
|
||||
f64.const 4.124798564124305
|
||||
f64.const -0.4035234171143214
|
||||
@ -464,13 +260,8 @@
|
||||
f64.const 0.008415761376584154
|
||||
f64.const 0.011286326131968767
|
||||
call $assembly/index/Body#constructor
|
||||
local.set $0
|
||||
local.get $1
|
||||
i32.load
|
||||
i32.const 8
|
||||
i32.add
|
||||
local.get $0
|
||||
i32.store offset=8
|
||||
local.get $0
|
||||
f64.const 12.894369562139131
|
||||
f64.const -15.111151401698631
|
||||
f64.const -0.22330757889265573
|
||||
@ -479,13 +270,8 @@
|
||||
f64.const -0.010832637401363636
|
||||
f64.const 1.7237240570597112e-03
|
||||
call $assembly/index/Body#constructor
|
||||
local.set $0
|
||||
local.get $1
|
||||
i32.load
|
||||
i32.const 12
|
||||
i32.add
|
||||
i32.store offset=12
|
||||
local.get $0
|
||||
i32.store offset=8
|
||||
f64.const 15.379697114850917
|
||||
f64.const -25.919314609987964
|
||||
f64.const 0.17925877295037118
|
||||
@ -494,18 +280,12 @@
|
||||
f64.const -0.034755955504078104
|
||||
f64.const 2.0336868699246304e-03
|
||||
call $assembly/index/Body#constructor
|
||||
local.set $0
|
||||
local.get $1
|
||||
i32.load
|
||||
i32.const 16
|
||||
i32.add
|
||||
local.get $0
|
||||
i32.store offset=8
|
||||
i32.store offset=16
|
||||
local.get $1
|
||||
call $assembly/index/NBodySystem#constructor
|
||||
global.set $assembly/index/system
|
||||
)
|
||||
(func $assembly/index/NBodySystem#advance (; 6 ;) (type $FUNCSIG$vi) (param $0 i32)
|
||||
(func $assembly/index/NBodySystem#advance (; 5 ;) (type $FUNCSIG$vi) (param $0 i32)
|
||||
(local $1 i32)
|
||||
(local $2 f64)
|
||||
(local $3 i32)
|
||||
@ -523,11 +303,10 @@
|
||||
(local $15 f64)
|
||||
(local $16 f64)
|
||||
(local $17 f64)
|
||||
(local $18 f64)
|
||||
local.get $0
|
||||
i32.load
|
||||
local.tee $12
|
||||
i32.load offset=4
|
||||
i32.load offset=12
|
||||
local.set $13
|
||||
loop $repeat|0
|
||||
local.get $3
|
||||
@ -535,12 +314,12 @@
|
||||
i32.lt_u
|
||||
if
|
||||
local.get $12
|
||||
i32.load
|
||||
i32.load offset=4
|
||||
local.get $3
|
||||
i32.const 2
|
||||
i32.shl
|
||||
i32.add
|
||||
i32.load offset=8
|
||||
i32.load
|
||||
local.tee $0
|
||||
f64.load
|
||||
local.set $14
|
||||
@ -573,16 +352,15 @@
|
||||
if
|
||||
local.get $14
|
||||
local.get $12
|
||||
i32.load
|
||||
i32.load offset=4
|
||||
local.get $7
|
||||
i32.const 2
|
||||
i32.shl
|
||||
i32.add
|
||||
i32.load offset=8
|
||||
i32.load
|
||||
local.tee $1
|
||||
f64.load
|
||||
f64.sub
|
||||
local.tee $18
|
||||
local.tee $2
|
||||
local.get $2
|
||||
f64.mul
|
||||
@ -635,7 +413,7 @@
|
||||
local.get $1
|
||||
local.get $1
|
||||
f64.load offset=24
|
||||
local.get $18
|
||||
local.get $2
|
||||
local.get $17
|
||||
local.get $11
|
||||
f64.mul
|
||||
@ -707,7 +485,7 @@
|
||||
end
|
||||
end
|
||||
)
|
||||
(func $assembly/index/NBodySystem#energy (; 7 ;) (type $FUNCSIG$di) (param $0 i32) (result f64)
|
||||
(func $assembly/index/NBodySystem#energy (; 6 ;) (type $FUNCSIG$di) (param $0 i32) (result f64)
|
||||
(local $1 f64)
|
||||
(local $2 i32)
|
||||
(local $3 i32)
|
||||
@ -721,7 +499,7 @@
|
||||
local.get $0
|
||||
i32.load
|
||||
local.tee $4
|
||||
i32.load offset=4
|
||||
i32.load offset=12
|
||||
local.set $5
|
||||
loop $repeat|0
|
||||
local.get $2
|
||||
@ -729,12 +507,12 @@
|
||||
i32.lt_u
|
||||
if
|
||||
local.get $4
|
||||
i32.load
|
||||
i32.load offset=4
|
||||
local.get $2
|
||||
i32.const 2
|
||||
i32.shl
|
||||
i32.add
|
||||
i32.load offset=8
|
||||
i32.load
|
||||
local.tee $0
|
||||
f64.load
|
||||
local.set $7
|
||||
@ -781,12 +559,12 @@
|
||||
if
|
||||
local.get $7
|
||||
local.get $4
|
||||
i32.load
|
||||
i32.load offset=4
|
||||
local.get $0
|
||||
i32.const 2
|
||||
i32.shl
|
||||
i32.add
|
||||
i32.load offset=8
|
||||
i32.load
|
||||
local.tee $3
|
||||
f64.load
|
||||
f64.sub
|
||||
@ -835,13 +613,13 @@
|
||||
end
|
||||
local.get $1
|
||||
)
|
||||
(func $assembly/index/step (; 8 ;) (type $FUNCSIG$d) (result f64)
|
||||
(func $assembly/index/step (; 7 ;) (type $FUNCSIG$d) (result f64)
|
||||
global.get $assembly/index/system
|
||||
call $assembly/index/NBodySystem#advance
|
||||
global.get $assembly/index/system
|
||||
call $assembly/index/NBodySystem#energy
|
||||
)
|
||||
(func $assembly/index/bench (; 9 ;) (type $FUNCSIG$vi) (param $0 i32)
|
||||
(func $assembly/index/bench (; 8 ;) (type $FUNCSIG$vi) (param $0 i32)
|
||||
(local $1 i32)
|
||||
block $break|0
|
||||
loop $repeat|0
|
||||
@ -861,44 +639,33 @@
|
||||
unreachable
|
||||
end
|
||||
)
|
||||
(func $assembly/index/getBody (; 10 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
|
||||
(func $assembly/index/getBody (; 9 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
|
||||
(local $1 i32)
|
||||
local.get $0
|
||||
global.get $assembly/index/system
|
||||
i32.load
|
||||
local.tee $1
|
||||
i32.load offset=4
|
||||
i32.load offset=12
|
||||
i32.lt_u
|
||||
if (result i32)
|
||||
local.get $0
|
||||
local.get $1
|
||||
i32.load
|
||||
local.tee $1
|
||||
i32.load
|
||||
i32.load offset=4
|
||||
local.get $0
|
||||
i32.const 2
|
||||
i32.shr_u
|
||||
i32.lt_u
|
||||
if (result i32)
|
||||
local.get $0
|
||||
i32.const 2
|
||||
i32.shl
|
||||
local.get $1
|
||||
i32.add
|
||||
i32.load offset=8
|
||||
else
|
||||
unreachable
|
||||
end
|
||||
i32.shl
|
||||
i32.add
|
||||
i32.load
|
||||
else
|
||||
i32.const 0
|
||||
end
|
||||
)
|
||||
(func $start (; 11 ;) (type $FUNCSIG$v)
|
||||
i32.const 40
|
||||
global.set $~lib/allocator/arena/startOffset
|
||||
global.get $~lib/allocator/arena/startOffset
|
||||
global.set $~lib/allocator/arena/offset
|
||||
(func $start (; 10 ;) (type $FUNCSIG$v)
|
||||
i32.const 16
|
||||
global.set $~lib/rt/stub/startOffset
|
||||
global.get $~lib/rt/stub/startOffset
|
||||
global.set $~lib/rt/stub/offset
|
||||
)
|
||||
(func $null (; 12 ;) (type $FUNCSIG$v)
|
||||
(func $null (; 11 ;) (type $FUNCSIG$v)
|
||||
nop
|
||||
)
|
||||
)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -3,9 +3,9 @@
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"asbuild:untouched": "asc assembly/index.ts -b build/untouched.wasm -t build/untouched.wat --sourceMap --debug --validate --importMemory",
|
||||
"asbuild:optimized": "asc assembly/index.ts -b build/optimized.wasm -t build/optimized.wat -O3 --validate --noAssert --importMemory",
|
||||
"asbuild:asmjs": "asc assembly/index.ts -a build/index.asm.js -O3 --validate --noAssert",
|
||||
"asbuild:untouched": "asc assembly/index.ts -b build/untouched.wasm -t build/untouched.wat --sourceMap --debug --validate --runtime none --importMemory",
|
||||
"asbuild:optimized": "asc assembly/index.ts -b build/optimized.wasm -t build/optimized.wat -O3 --validate --runtime none --noAssert --importMemory",
|
||||
"asbuild:asmjs": "asc assembly/index.ts -a build/index.asm.js -O3 --validate --runtime none --noAssert && node scripts/postprocess-asmjs",
|
||||
"asbuild": "npm run asbuild:untouched && npm run asbuild:optimized && npm run asbuild:asmjs",
|
||||
"tsbuild": "tsc -p assembly -t ES2017 -m commonjs --outDir build",
|
||||
"build": "npm run asbuild && npm run tsbuild",
|
||||
|
7
examples/n-body/scripts/postprocess-asmjs.js
Normal file
7
examples/n-body/scripts/postprocess-asmjs.js
Normal file
@ -0,0 +1,7 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const filename = path.join(__dirname, "..", "build" , "index.asm.js");
|
||||
var source = fs.readFileSync(filename, { encoding: "utf8" });
|
||||
source = source.replace(/^export var ([^ ]+) =/mg, ($0, $1) => "exports." + $1 + " = ");
|
||||
fs.writeFileSync(filename, source);
|
@ -21,11 +21,8 @@
|
||||
(import "pson" "onString" (func $assembly/pson/onString (param i32 i32)))
|
||||
(import "pson" "onBinary" (func $assembly/pson/onBinary (param i32 i32)))
|
||||
(memory $0 0)
|
||||
(table $0 1 funcref)
|
||||
(elem (i32.const 0) $null)
|
||||
(global $assembly/pson/offset (mut i32) (i32.const 0))
|
||||
(export "memory" (memory $0))
|
||||
(export "table" (table $0))
|
||||
(export "onNull" (func $assembly/pson/onNull))
|
||||
(export "onTrue" (func $assembly/pson/onTrue))
|
||||
(export "onFalse" (func $assembly/pson/onFalse))
|
||||
@ -120,7 +117,7 @@
|
||||
(local $2 i64)
|
||||
block $break|0
|
||||
global.get $assembly/pson/offset
|
||||
local.tee $1
|
||||
local.tee $0
|
||||
i32.const 1
|
||||
i32.add
|
||||
global.set $assembly/pson/offset
|
||||
@ -141,7 +138,7 @@
|
||||
block $case1|0
|
||||
block $case0|0
|
||||
block $tablify|0
|
||||
local.get $1
|
||||
local.get $0
|
||||
i32.load8_u
|
||||
local.tee $0
|
||||
local.tee $1
|
||||
|
@ -24,9 +24,7 @@
|
||||
(table $0 1 funcref)
|
||||
(elem (i32.const 0) $null)
|
||||
(global $assembly/pson/offset (mut i32) (i32.const 0))
|
||||
(global $~lib/memory/HEAP_BASE i32 (i32.const 8))
|
||||
(export "memory" (memory $0))
|
||||
(export "table" (table $0))
|
||||
(export "onNull" (func $assembly/pson/onNull))
|
||||
(export "onTrue" (func $assembly/pson/onTrue))
|
||||
(export "onFalse" (func $assembly/pson/onFalse))
|
||||
|
3
index.d.ts
vendored
3
index.d.ts
vendored
@ -1 +1,2 @@
|
||||
export * from "./src";
|
||||
/// <reference path="./dist/assemblyscript.d.ts" />
|
||||
export * from "assemblyscript";
|
||||
|
@ -8,10 +8,6 @@ declare module ASModule {
|
||||
type f32 = number;
|
||||
type f64 = number;
|
||||
type bool = any;
|
||||
const NaN: f64;
|
||||
const Infinity: f64;
|
||||
var lo: u32;
|
||||
var hi: u32;
|
||||
function getLo(): u32;
|
||||
function getHi(): u32;
|
||||
function clz(loLeft: u32, hiLeft: u32): void;
|
@ -4,12 +4,9 @@
|
||||
(type $FUNCSIG$viiii (func (param i32 i32 i32 i32)))
|
||||
(type $FUNCSIG$v (func))
|
||||
(memory $0 0)
|
||||
(table $0 1 funcref)
|
||||
(elem (i32.const 0) $null)
|
||||
(global $assembly/i64/lo (mut i32) (i32.const 0))
|
||||
(global $assembly/i64/hi (mut i32) (i32.const 0))
|
||||
(export "memory" (memory $0))
|
||||
(export "table" (table $0))
|
||||
(export "getLo" (func $assembly/i64/getLo))
|
||||
(export "getHi" (func $assembly/i64/getHi))
|
||||
(export "clz" (func $assembly/i64/clz))
|
@ -8,9 +8,7 @@
|
||||
(elem (i32.const 0) $null)
|
||||
(global $assembly/i64/lo (mut i32) (i32.const 0))
|
||||
(global $assembly/i64/hi (mut i32) (i32.const 0))
|
||||
(global $~lib/memory/HEAP_BASE i32 (i32.const 8))
|
||||
(export "memory" (memory $0))
|
||||
(export "table" (table $0))
|
||||
(export "getLo" (func $assembly/i64/getLo))
|
||||
(export "getHi" (func $assembly/i64/getHi))
|
||||
(export "clz" (func $assembly/i64/clz))
|
@ -17,8 +17,8 @@
|
||||
},
|
||||
"scripts": {
|
||||
"asbuild": "npm run asbuild:untouched && npm run asbuild:optimized",
|
||||
"asbuild:untouched": "asc assembly/i64.ts -t build/untouched.wat -b build/untouched.wasm --validate --sourceMap --debug --measure",
|
||||
"asbuild:optimized": "asc -O assembly/i64.ts -b build/optimized.wasm -t build/optimized.wat -d build/optimized.d.ts --validate --sourceMap --measure",
|
||||
"asbuild:untouched": "asc assembly/i64.ts -t build/untouched.wat -b build/untouched.wasm --runtime none --validate --sourceMap --debug --measure",
|
||||
"asbuild:optimized": "asc assembly/i64.ts -b build/optimized.wasm -t build/optimized.wat -d build/optimized.d.ts -O3 --runtime none --validate --sourceMap --measure",
|
||||
"test": "node tests"
|
||||
},
|
||||
"files": [
|
11
lib/libm/README.md
Normal file
11
lib/libm/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
#  libm
|
||||
|
||||
AssemblyScript's math routines for double and single precision as a library.
|
||||
|
||||
```ts
|
||||
const libm = require("@assemblyscript/libm");
|
||||
const libmf = libm.libmf;
|
||||
...
|
||||
```
|
||||
|
||||
Both `libm` and `libmf` have the same general interface as JavaScript's `Math`, with `libmf` doing single precision math.
|
144
lib/libm/assembly/libm.ts
Normal file
144
lib/libm/assembly/libm.ts
Normal file
@ -0,0 +1,144 @@
|
||||
export const E = Math.E;
|
||||
export const LN10 = Math.LN10;
|
||||
export const LN2 = Math.LN2;
|
||||
export const LOG10E = Math.LOG10E;
|
||||
export const LOG2E = Math.LOG2E;
|
||||
export const PI = Math.PI;
|
||||
export const SQRT1_2 = Math.SQRT1_2;
|
||||
export const SQRT2 = Math.SQRT2;
|
||||
|
||||
export function abs(x: f64): f64 {
|
||||
return Math.abs(x);
|
||||
}
|
||||
|
||||
export function acos(x: f64): f64 {
|
||||
return Math.acos(x);
|
||||
}
|
||||
|
||||
export function acosh(x: f64): f64 {
|
||||
return Math.acosh(x);
|
||||
}
|
||||
|
||||
export function asin(x: f64): f64 {
|
||||
return Math.asin(x);
|
||||
}
|
||||
|
||||
export function asinh(x: f64): f64 {
|
||||
return Math.asinh(x);
|
||||
}
|
||||
|
||||
export function atan(x: f64): f64 {
|
||||
return Math.atan(x);
|
||||
}
|
||||
|
||||
export function atanh(x: f64): f64 {
|
||||
return Math.atanh(x);
|
||||
}
|
||||
|
||||
export function atan2(y: f64, x: f64): f64 {
|
||||
return Math.atan2(y, x);
|
||||
}
|
||||
|
||||
export function cbrt(x: f64): f64 {
|
||||
return Math.cbrt(x);
|
||||
}
|
||||
|
||||
export function ceil(x: f64): f64 {
|
||||
return Math.ceil(x);
|
||||
}
|
||||
|
||||
export function clz32(x: f64): f64 {
|
||||
return Math.clz32(x);
|
||||
}
|
||||
|
||||
export function cos(x: f64): f64 {
|
||||
return Math.cos(x);
|
||||
}
|
||||
|
||||
export function cosh(x: f64): f64 {
|
||||
return Math.cosh(x);
|
||||
}
|
||||
|
||||
export function exp(x: f64): f64 {
|
||||
return Math.exp(x);
|
||||
}
|
||||
|
||||
export function expm1(x: f64): f64 {
|
||||
return Math.expm1(x);
|
||||
}
|
||||
|
||||
export function floor(x: f64): f64 {
|
||||
return Math.floor(x);
|
||||
}
|
||||
|
||||
export function fround(x: f64): f64 {
|
||||
return Math.fround(x);
|
||||
}
|
||||
|
||||
export function hypot(a: f64, b: f64): f64 {
|
||||
return Math.hypot(a, b);
|
||||
}
|
||||
|
||||
export function imul(a: f64, b: f64): f64 {
|
||||
return Math.imul(a, b);
|
||||
}
|
||||
|
||||
export function log(x: f64): f64 {
|
||||
return Math.log(x);
|
||||
}
|
||||
|
||||
export function log10(x: f64): f64 {
|
||||
return Math.log10(x);
|
||||
}
|
||||
|
||||
export function log1p(x: f64): f64 {
|
||||
return Math.log1p(x);
|
||||
}
|
||||
|
||||
export function log2(x: f64): f64 {
|
||||
return Math.log2(x);
|
||||
}
|
||||
|
||||
export function max(a: f64, b: f64): f64 {
|
||||
return Math.max(a, b);
|
||||
}
|
||||
|
||||
export function min(a: f64, b: f64): f64 {
|
||||
return Math.min(a, b);
|
||||
}
|
||||
|
||||
export function pow(x: f64, y: f64): f64 {
|
||||
return Math.pow(x, y);
|
||||
}
|
||||
|
||||
export function round(x: f64): f64 {
|
||||
return Math.round(x);
|
||||
}
|
||||
|
||||
export function sign(x: f64): f64 {
|
||||
return Math.sign(x);
|
||||
}
|
||||
|
||||
export function sin(x: f64): f64 {
|
||||
return Math.sin(x);
|
||||
}
|
||||
|
||||
export function sinh(x: f64): f64 {
|
||||
return Math.sinh(x);
|
||||
}
|
||||
|
||||
export function sqrt(x: f64): f64 {
|
||||
return Math.sqrt(x);
|
||||
}
|
||||
|
||||
export function tan(x: f64): f64 {
|
||||
return Math.tan(x);
|
||||
}
|
||||
|
||||
export function tanh(x: f64): f64 {
|
||||
return Math.tanh(x);
|
||||
}
|
||||
|
||||
export function trunc(x: f64): f64 {
|
||||
return Math.trunc(x);
|
||||
}
|
144
lib/libm/assembly/libmf.ts
Normal file
144
lib/libm/assembly/libmf.ts
Normal file
@ -0,0 +1,144 @@
|
||||
export const E = Mathf.E;
|
||||
export const LN10 = Mathf.LN10;
|
||||
export const LN2 = Mathf.LN2;
|
||||
export const LOG10E = Mathf.LOG10E;
|
||||
export const LOG2E = Mathf.LOG2E;
|
||||
export const PI = Mathf.PI;
|
||||
export const SQRT1_2 = Mathf.SQRT1_2;
|
||||
export const SQRT2 = Mathf.SQRT2;
|
||||
|
||||
export function abs(x: f32): f32 {
|
||||
return Mathf.abs(x);
|
||||
}
|
||||
|
||||
export function acos(x: f32): f32 {
|
||||
return Mathf.acos(x);
|
||||
}
|
||||
|
||||
export function acosh(x: f32): f32 {
|
||||
return Mathf.acosh(x);
|
||||
}
|
||||
|
||||
export function asin(x: f32): f32 {
|
||||
return Mathf.asin(x);
|
||||
}
|
||||
|
||||
export function asinh(x: f32): f32 {
|
||||
return Mathf.asinh(x);
|
||||
}
|
||||
|
||||
export function atan(x: f32): f32 {
|
||||
return Mathf.atan(x);
|
||||
}
|
||||
|
||||
export function atanh(x: f32): f32 {
|
||||
return Mathf.atanh(x);
|
||||
}
|
||||
|
||||
export function atan2(y: f32, x: f32): f32 {
|
||||
return Mathf.atan2(y, x);
|
||||
}
|
||||
|
||||
export function cbrt(x: f32): f32 {
|
||||
return Mathf.cbrt(x);
|
||||
}
|
||||
|
||||
export function ceil(x: f32): f32 {
|
||||
return Mathf.ceil(x);
|
||||
}
|
||||
|
||||
export function clz32(x: f32): f32 {
|
||||
return Mathf.clz32(x);
|
||||
}
|
||||
|
||||
export function cos(x: f32): f32 {
|
||||
return Mathf.cos(x);
|
||||
}
|
||||
|
||||
export function cosh(x: f32): f32 {
|
||||
return Mathf.cosh(x);
|
||||
}
|
||||
|
||||
export function exp(x: f32): f32 {
|
||||
return Mathf.exp(x);
|
||||
}
|
||||
|
||||
export function expm1(x: f32): f32 {
|
||||
return Mathf.expm1(x);
|
||||
}
|
||||
|
||||
export function floor(x: f32): f32 {
|
||||
return Mathf.floor(x);
|
||||
}
|
||||
|
||||
export function fround(x: f32): f32 {
|
||||
return Mathf.fround(x);
|
||||
}
|
||||
|
||||
export function hypot(a: f32, b: f32): f32 {
|
||||
return Mathf.hypot(a, b);
|
||||
}
|
||||
|
||||
export function imul(a: f32, b: f32): f32 {
|
||||
return Mathf.imul(a, b);
|
||||
}
|
||||
|
||||
export function log(x: f32): f32 {
|
||||
return Mathf.log(x);
|
||||
}
|
||||
|
||||
export function log10(x: f32): f32 {
|
||||
return Mathf.log10(x);
|
||||
}
|
||||
|
||||
export function log1p(x: f32): f32 {
|
||||
return Mathf.log1p(x);
|
||||
}
|
||||
|
||||
export function log2(x: f32): f32 {
|
||||
return Mathf.log2(x);
|
||||
}
|
||||
|
||||
export function max(a: f32, b: f32): f32 {
|
||||
return Mathf.max(a, b);
|
||||
}
|
||||
|
||||
export function min(a: f32, b: f32): f32 {
|
||||
return Mathf.min(a, b);
|
||||
}
|
||||
|
||||
export function pow(x: f32, y: f32): f32 {
|
||||
return Mathf.pow(x, y);
|
||||
}
|
||||
|
||||
export function round(x: f32): f32 {
|
||||
return Mathf.round(x);
|
||||
}
|
||||
|
||||
export function sign(x: f32): f32 {
|
||||
return Mathf.sign(x);
|
||||
}
|
||||
|
||||
export function sin(x: f32): f32 {
|
||||
return Mathf.sin(x);
|
||||
}
|
||||
|
||||
export function sinh(x: f32): f32 {
|
||||
return Mathf.sinh(x);
|
||||
}
|
||||
|
||||
export function sqrt(x: f32): f32 {
|
||||
return Mathf.sqrt(x);
|
||||
}
|
||||
|
||||
export function tan(x: f32): f32 {
|
||||
return Mathf.tan(x);
|
||||
}
|
||||
|
||||
export function tanh(x: f32): f32 {
|
||||
return Mathf.tanh(x);
|
||||
}
|
||||
|
||||
export function trunc(x: f32): f32 {
|
||||
return Mathf.trunc(x);
|
||||
}
|
6
lib/libm/assembly/tsconfig.json
Normal file
6
lib/libm/assembly/tsconfig.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "../../../std/assembly.json",
|
||||
"include": [
|
||||
"./**/*.ts"
|
||||
]
|
||||
}
|
2
lib/libm/build/.gitignore
vendored
Normal file
2
lib/libm/build/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.wasm
|
||||
*.wasm.map
|
3934
lib/libm/build/libm.wat
Normal file
3934
lib/libm/build/libm.wat
Normal file
File diff suppressed because it is too large
Load Diff
4763
lib/libm/build/libmf.wat
Normal file
4763
lib/libm/build/libmf.wat
Normal file
File diff suppressed because it is too large
Load Diff
2
lib/libm/index.d.ts
vendored
Normal file
2
lib/libm/index.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
declare const libm: typeof Math & { libm: typeof Math, libmf: typeof Math };
|
||||
export = libm;
|
7
lib/libm/index.js
Normal file
7
lib/libm/index.js
Normal file
@ -0,0 +1,7 @@
|
||||
const fs = require("fs");
|
||||
const libm = new WebAssembly.Instance(new WebAssembly.Module(fs.readFileSync("./build/libm.wasm")), { Math }).exports;
|
||||
const libmf = new WebAssembly.Instance(new WebAssembly.Module(fs.readFileSync("./build/libmf.wasm")), {}).exports;
|
||||
module.exports = Object.create(libm, {
|
||||
libm: { value: libm, enumerable: true },
|
||||
libmf: { value: libmf, enumerable: true }
|
||||
});
|
23
lib/libm/package.json
Normal file
23
lib/libm/package.json
Normal file
@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "@assemblyscript/libm",
|
||||
"version": "1.0.0",
|
||||
"license": "Apache-2.0",
|
||||
"main": "index.js",
|
||||
"types": "index.d.ts",
|
||||
"scripts": {
|
||||
"asbuild": "npm run asbuild:libm && npm run asbuild:libmf",
|
||||
"asbuild:libm": "asc assembly/libm.ts -O3 -b build/libm.wasm -t build/libm.wat --runtime none --validate",
|
||||
"asbuild:libmf": "asc assembly/libmf.ts -O3 -b build/libmf.wasm -t build/libmf.wat --runtime none --validate"
|
||||
},
|
||||
"files": [
|
||||
"package.json",
|
||||
"index.d.ts",
|
||||
"index.js",
|
||||
"build/*.wasm",
|
||||
"README.md"
|
||||
],
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"assemblyscript": "AssemblyScript/assemblyscript"
|
||||
}
|
||||
}
|
@ -39,12 +39,6 @@
|
||||
"no-debugger": {
|
||||
"severity": "error"
|
||||
},
|
||||
"no-default-export": {
|
||||
"severity": "error"
|
||||
},
|
||||
"no-duplicate-imports": {
|
||||
"severity": "error"
|
||||
},
|
||||
"no-duplicate-super": {
|
||||
"severity": "error"
|
||||
},
|
||||
|
@ -7,7 +7,7 @@ Usage
|
||||
-----
|
||||
|
||||
```js
|
||||
const loader = require("@assemblyscript/loader");
|
||||
const loader = require("assemblyscript/lib/loader");
|
||||
...
|
||||
```
|
||||
|
||||
@ -26,67 +26,164 @@ API
|
||||
* **demangle**<`T`>(exports: `WasmExports`, baseModule?: `Object`): `T`<br />
|
||||
Demangles an AssemblyScript module's exports to a friendly object structure. You usually don't have to call this manually as instantiation does this implicitly.
|
||||
|
||||
**Note:** `T` above can either be omitted if the structure of the module is unknown, or can reference a `.d.ts` (i.e. `typeof MyModule`) as produced by the compiler with the `-d` option.
|
||||
**Note** that `T` above can either be omitted if the structure of the module is unknown, or can reference a `.d.ts` (i.e. `typeof MyModule`) as produced by the compiler with the `-d` option.
|
||||
|
||||
Instances are automatically populated with useful utility:
|
||||
Besides demangling classes exported from your entry file to a handy object structure one can use like JS objects, instances are automatically populated with useful utility:
|
||||
|
||||
* **I8**: `Int8Array`<br />
|
||||
An 8-bit signed integer view on the memory.
|
||||
|
||||
```ts
|
||||
var value = module.I8[ptr];
|
||||
```
|
||||
|
||||
* **U8**: `Uint8Array`<br />
|
||||
An 8-bit unsigned integer view on the memory.
|
||||
|
||||
```ts
|
||||
var value = module.U8[ptr];
|
||||
```
|
||||
|
||||
* **I16**: `Int16Array`<br />
|
||||
A 16-bit signed integer view on the memory.
|
||||
|
||||
```ts
|
||||
var value = module.I16[ptr >>> 1];
|
||||
```
|
||||
|
||||
* **U16**: `Uint16Array`<br />
|
||||
A 16-bit unsigned integer view on the memory.
|
||||
|
||||
```ts
|
||||
var value = module.U16[ptr >>> 1];
|
||||
```
|
||||
|
||||
* **I32**: `Int32Array`<br />
|
||||
A 32-bit signed integer view on the memory.
|
||||
|
||||
```ts
|
||||
var value = module.I32[ptr >>> 2];
|
||||
```
|
||||
|
||||
* **U32**: `Uint32Array`<br />
|
||||
A 32-bit unsigned integer view on the memory.
|
||||
|
||||
```ts
|
||||
var value = module.U32[ptr >>> 2];
|
||||
```
|
||||
|
||||
* **I64**: `BigInt64Array`<br />
|
||||
A 64-bit signed integer view on the memory<sup>1</sup>.
|
||||
A 64-bit signed integer view on the memory, if supported by the VM.
|
||||
|
||||
```ts
|
||||
var value = module.I64[ptr >>> 3];
|
||||
```
|
||||
|
||||
* **U64**: `BigUint64Array`<br />
|
||||
A 64-bit unsigned integer view on the memory<sup>1</sup>.
|
||||
A 64-bit unsigned integer view on the memory, if supported by the VM.
|
||||
|
||||
```ts
|
||||
var value = module.U64[ptr >>> 3];
|
||||
```
|
||||
|
||||
* **F32**: `Float32Array`<br />
|
||||
A 32-bit float view on the memory.
|
||||
|
||||
```ts
|
||||
var value = module.I32[ptr >>> 2];
|
||||
```
|
||||
|
||||
* **F64**: `Float64Array`<br />
|
||||
A 64-bit float view on the memory.
|
||||
|
||||
* **newString**(str: `string`): `number`<br />
|
||||
Allocates a new string in the module's memory and returns its pointer.<sup>2</sup>
|
||||
```ts
|
||||
var value = module.F64[ptr >>> 3];
|
||||
```
|
||||
|
||||
* **getString**(ptr: `number`): `string`<br />
|
||||
Gets a string from the module's memory by its pointer.
|
||||
* **__start**(): `void`<br />
|
||||
Explicit start function if the `--explicitStart` option is used. Must be called before any other exports if present.
|
||||
|
||||
* **newArray**(view: `TypedArray`, length?: `number`, unsafe?: `boolean`): `number`<br />
|
||||
Copies a typed array into the module's memory and returns its pointer.<sup>2</sup>
|
||||
* **__allocString**(str: `string`): `number`<br />
|
||||
Allocates a new string in the module's memory and returns a reference (pointer) to it.
|
||||
|
||||
* **newArray**(ctor: `TypedArrayConstructor`, length: `number`, unsafe?: `boolean`): `number`<br />
|
||||
Creates a typed array in the module's memory and returns its pointer.<sup>2</sup>
|
||||
```ts
|
||||
var ref = module.__retain(module.__allocString("hello world"));
|
||||
...
|
||||
module.__release(ref);
|
||||
```
|
||||
|
||||
* **getArray**(ctor: `TypedArrayConstructor`, ptr: `number`): `TypedArray`<br />
|
||||
Gets a view on a typed array in the module's memory by its pointer.
|
||||
* **__getString**(ref: `number`): `string`<br />
|
||||
Reads (copies) the value of a string from the module's memory.
|
||||
|
||||
* **freeArray**(ptr: `number`): `void`<br />
|
||||
Frees a typed array in the module's memory. Must not be accessed anymore afterwards.
|
||||
```ts
|
||||
var str = module.__getString(ref);
|
||||
...
|
||||
```
|
||||
|
||||
* **getFunction**(ptr: `number`): `function`<br />
|
||||
Gets a function by its pointer.
|
||||
* **__allocArray**(id: `number`, values: `number[]`): `number`<br />
|
||||
Allocates a new array in the module's memory and returns a reference (pointer) to it.
|
||||
Automatically retains interior pointers. The `id` is the unique runtime id of the respective array class. If you are using `Int32Array` for example, the best way to know the id is an `export const INT32ARRAY_ID = idof<Int32Array>()`. When done with the array, make sure to release it.
|
||||
|
||||
* **newFunction**(fn: `function`): `number`<br />
|
||||
Creates a new function in the module's table and returns its pointer. Note that only actual
|
||||
WebAssembly functions, i.e. as exported by the module, are supported.
|
||||
```ts
|
||||
var ref = module.__retain(module.__allocArray(module.INT32ARRAY, [1, 2, 3]));
|
||||
...
|
||||
module.__release(ref);
|
||||
```
|
||||
|
||||
<sup>1</sup> This feature has not yet landed in any VM as of this writing.<br />
|
||||
<sup>2</sup> Requires that memory utilities have been exported through `export { memory }` within the entry file.
|
||||
* **__getArray**(ref: `number`): `number[]`<br />
|
||||
Reads (copies) the values of an array from the module's memory.
|
||||
|
||||
```ts
|
||||
var arr = module.__getArray(ref);
|
||||
...
|
||||
```
|
||||
|
||||
* **__getArrayView**(ref: `number`): `TypedArray`<br />
|
||||
Gets a view on the values of an array in the module's memory. This differs from `__getArray` in that the data isn't copied but remains *live* in both directions. That's faster but also unsafe because if the array grows or becomes released, the view will no longer represent the correct memory region and modifying its values in this state will most likely corrupt memory. Use, but use with care.
|
||||
|
||||
* **__retain**(ref: `number`): `number`<br />
|
||||
Retains a reference externally, making sure that it doesn't become collected prematurely. Returns the reference.
|
||||
|
||||
* **__release**(ref: `number`): `void`<br />
|
||||
Releases a previously retained reference to an object, allowing the runtime to collect it once its reference count reaches zero.
|
||||
|
||||
* **__alloc**(size: `number`, id: `number`): `number`<br />
|
||||
Allocates an instance of the class represented by the specified id. If you are using `MyClass` for example, the best way to know the id and the necessary size is an `export const MYCLASS_ID = idof<MyClass>()` and an `export const MYCLASS_SIZE = offsetof<MyClass>()`. Afterwards, use the respective views to assign values to the class's memory while making sure to retain interior references to other managed objects once. When done with the class, make sure to release it, which will automatically release any interior references once the class becomes collected.
|
||||
|
||||
```ts
|
||||
var ref = module.__retain(module.__alloc(module.MYCLASS_SIZE, module.MYCLASS_ID));
|
||||
F32[ref + MYCLASS_BASICFIELD1_OFFSET >>> 2] = field1_value_f32;
|
||||
U32[ref + MYCLASS_MANAGEDFIELD2_OFFSET >>> 2] = module.__retain(field2_value_ref);
|
||||
...
|
||||
module.__release(ref);
|
||||
```
|
||||
|
||||
* **__instanceof**(ref: `number`, baseId: `number`): `boolean`<br />
|
||||
Tests whether an object is an instance of the class represented by the specified base id.
|
||||
|
||||
```ts
|
||||
if (module.__instanceof(ref, module.MYCLASS_ID)) {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
* **__collect**(): `void`<br />
|
||||
Forces a cycle collection. Only relevant if objects potentially forming reference cycles are used.
|
||||
|
||||
**Note** that the views like `I32` above will automatically be updated when the module's memory grows. Don't cache these if this can happen.
|
||||
|
||||
**Note** that the allocation and ownership features above require the `full` (this is the default) or the `stub` runtime to be present in your module. Other runtime variations do not export this functionality without further ado (so the compiler can eliminate what's dead code).
|
||||
|
||||
**Note** that references returned from exported functions have already been retained for you and the runtime expects that you release them once not needed anymore. This is also true for constructors and getters.
|
||||
|
||||
**Beware that your module is likely going to explode with seemingly random errors when using the allocation and ownership features incorrectly!**
|
||||
|
||||
* Use the correct ids, sizes and layouts (C-style non-packed, export `offsetof<MyClass>("myField")` in case of doubt)
|
||||
* Clear unused memory to zero
|
||||
* Retain what you allocate
|
||||
* Retain interior pointers (except in `__allocArray`)
|
||||
* Don't retain what's returned (is already retained for you)
|
||||
* Release when you're done with something and don't ever use it again
|
||||
|
||||
Examples
|
||||
--------
|
||||
@ -101,58 +198,6 @@ const myModule = loader.instantiateBuffer(fs.readFileSync("myModule.wasm"), myIm
|
||||
const myModule = await loader.instantiateStreaming(fetch("myModule.wasm"), myImports);
|
||||
```
|
||||
|
||||
### Reading/writing basic values to/from memory
|
||||
|
||||
```js
|
||||
var ptrToInt8 = ...;
|
||||
var value = myModule.I8[ptrToInt8]; // alignment of log2(1)=0
|
||||
|
||||
var ptrToInt16 = ...;
|
||||
var value = myModule.I16[ptrToInt16 >>> 1]; // alignment of log2(2)=1
|
||||
|
||||
var ptrToInt32 = ...;
|
||||
var value = myModule.I32[ptrToInt32 >>> 2]; // alignment of log2(4)=2
|
||||
|
||||
var ptrToInt64 = ...;
|
||||
var value = myModule.I64[ptrToInt64 >>> 3]; // alignment of log2(8)=3
|
||||
|
||||
var ptrToFloat32 = ...;
|
||||
var value = myModule.F32[ptrToFloat32 >>> 2]; // alignment of log2(4)=2
|
||||
|
||||
var ptrToFloat64 = ...;
|
||||
var value = myModule.F64[ptrToFloat64 >>> 3]; // alignment of log2(8)=3
|
||||
|
||||
// Likewise, for writing
|
||||
myModule.I8[ptrToInt8] = newValue;
|
||||
myModule.I16[ptrToInt16 >>> 1] = newValue;
|
||||
myModule.I32[ptrToInt32 >>> 2] = newValue;
|
||||
myModule.I64[ptrToInt64 >>> 3] = newValue;
|
||||
myModule.F32[ptrToFloat32 >>> 2] = newValue;
|
||||
myModule.F64[ptrToFloat64 >>> 3] = newValue;
|
||||
```
|
||||
|
||||
**Note:** Make sure to reference the views as shown above as these will automatically be updated when the module's memory grows.
|
||||
|
||||
### Working with strings and arrays
|
||||
|
||||
Strings and arrays cannot yet flow in and out of WebAssembly naturally, hence it is necessary to create them in the module's memory using the `newString` and `newArray` helpers. Afterwards, instead of passing the string or array directly, the resulting reference (pointer) is provided instead:
|
||||
|
||||
```js
|
||||
var str = "Hello world!";
|
||||
var ptr = module.newString(str);
|
||||
|
||||
// ... do something with ptr, i.e. call a WebAssembly export ...
|
||||
```
|
||||
|
||||
Similarly, when a string or array is returned from a WebAssembly function, a reference (pointer) is received on the JS side and the `getString` and `getArray` helpers can be used to obtain their values:
|
||||
|
||||
```js
|
||||
var ptrToString = ...;
|
||||
var str = module.getString(ptrToString);
|
||||
|
||||
// ... do something with str ...
|
||||
```
|
||||
|
||||
### Usage with TypeScript definitions produced by the compiler
|
||||
|
||||
```ts
|
||||
|
60
lib/loader/index.d.ts
vendored
60
lib/loader/index.d.ts
vendored
@ -1,18 +1,19 @@
|
||||
import "@types/webassembly-js-api";
|
||||
|
||||
/** WebAssembly imports with two levels of nesting. */
|
||||
interface ImportsObject {
|
||||
[key: string]: {},
|
||||
env: {
|
||||
interface ImportsObject extends Record<string, any> {
|
||||
env?: {
|
||||
memory?: WebAssembly.Memory,
|
||||
table?: WebAssembly.Table,
|
||||
abort?: (msg: number, file: number, line: number, column: number) => void
|
||||
abort?: (msg: number, file: number, line: number, column: number) => void,
|
||||
trace?: (msg: number, numArgs?: number, ...args: any[]) => void
|
||||
}
|
||||
}
|
||||
|
||||
type TypedArray
|
||||
= Int8Array
|
||||
| Uint8Array
|
||||
| Uint8ClampedArray
|
||||
| Int16Array
|
||||
| Uint16Array
|
||||
| Int32Array
|
||||
@ -20,16 +21,6 @@ type TypedArray
|
||||
| Float32Array
|
||||
| Float64Array;
|
||||
|
||||
type TypedArrayConstructor
|
||||
= Int8ArrayConstructor
|
||||
| Uint8ArrayConstructor
|
||||
| Int16ArrayConstructor
|
||||
| Uint16ArrayConstructor
|
||||
| Int32ArrayConstructor
|
||||
| Uint32ArrayConstructor
|
||||
| Float32ArrayConstructor
|
||||
| Float32ArrayConstructor;
|
||||
|
||||
/** Utility mixed in by the loader. */
|
||||
interface ASUtil {
|
||||
/** An 8-bit signed integer view on the memory. */
|
||||
@ -52,25 +43,28 @@ interface ASUtil {
|
||||
readonly F32: Float32Array;
|
||||
/** A 64-bit float view on the memory. */
|
||||
readonly F64: Float64Array;
|
||||
/** Allocates a new string in the module's memory and returns its pointer. */
|
||||
newString(str: string): number;
|
||||
/** Gets a string from the module's memory by its pointer. */
|
||||
getString(ptr: number): string;
|
||||
/** Copies a typed array into the module's memory and returns its pointer. */
|
||||
newArray(view: TypedArray, length?: number): number;
|
||||
/** Creates a typed array in the module's memory and returns its pointer. */
|
||||
newArray(ctor: TypedArrayConstructor, length: number, unsafe?: boolean): number;
|
||||
/** Gets a view on a typed array in the module's memory by its pointer. */
|
||||
getArray<T extends TypedArray = TypedArray>(ctor: TypedArrayConstructor, ptr: number): T;
|
||||
/** Frees a typed array in the module's memory. Must not be accessed anymore afterwards. */
|
||||
freeArray(ptr: number): void;
|
||||
/** Gets a function by its pointer. */
|
||||
getFunction<R = any>(ptr: number): (...args: any[]) => R;
|
||||
/**
|
||||
* Creates a new function in the module's table and returns its pointer. Note that only actual
|
||||
* WebAssembly functions, i.e. as exported by the module, are supported.
|
||||
*/
|
||||
newFunction(fn: (...args: any[]) => any): number;
|
||||
/** Explicit start function, if requested. */
|
||||
__start(): void;
|
||||
/** Allocates a new string in the module's memory and returns a reference (pointer) to it. */
|
||||
__allocString(str: string): number;
|
||||
/** Reads (copies) the value of a string from the module's memory. */
|
||||
__getString(ref: number): string;
|
||||
/** Allocates a new array in the module's memory and returns a reference (pointer) to it. */
|
||||
__allocArray(id: number, values: number[]): number;
|
||||
/** Reads (copies) the values of an array from the module's memory. */
|
||||
__getArray(ref: number): number[];
|
||||
/** Gets a view on the values of an array in the module's memory. */
|
||||
__getArrayView(ref: number): TypedArray;
|
||||
/** Retains a reference externally, making sure that it doesn't become collected prematurely. Returns the reference. */
|
||||
__retain(ref: number): number;
|
||||
/** Releases a previously retained reference to an object, allowing the runtime to collect it once its reference count reaches zero. */
|
||||
__release(ref: number): void;
|
||||
/** Allocates an instance of the class represented by the specified id. */
|
||||
__alloc(size: number, id: number): number;
|
||||
/** Tests whether an object is an instance of the class represented by the specified base id. */
|
||||
__instanceof(ref: number, baseId: number): boolean;
|
||||
/** Forces a cycle collection. Only relevant if objects potentially forming reference cycles are used. */
|
||||
__collect(): void;
|
||||
}
|
||||
|
||||
/** Instantiates an AssemblyScript module using the specified imports. */
|
||||
|
@ -1,43 +1,75 @@
|
||||
"use strict";
|
||||
|
||||
const hasBigInt64 = typeof BigUint64Array !== "undefined";
|
||||
const thisPtr = Symbol();
|
||||
// Runtime header offsets
|
||||
const ID_OFFSET = -8;
|
||||
const SIZE_OFFSET = -4;
|
||||
|
||||
// Runtime ids
|
||||
const ARRAYBUFFER_ID = 0;
|
||||
const STRING_ID = 1;
|
||||
const ARRAYBUFFERVIEW_ID = 2;
|
||||
|
||||
// Runtime type information
|
||||
const ARRAYBUFFERVIEW = 1 << 0;
|
||||
const ARRAY = 1 << 1;
|
||||
const SET = 1 << 2;
|
||||
const MAP = 1 << 3;
|
||||
const VAL_ALIGN = 1 << 5;
|
||||
const VAL_SIGNED = 1 << 10;
|
||||
const VAL_FLOAT = 1 << 11;
|
||||
const VAL_NULLABLE = 1 << 12;
|
||||
const VAL_MANAGED = 1 << 13;
|
||||
const KEY_ALIGN = 1 << 14;
|
||||
const KEY_SIGNED = 1 << 19;
|
||||
const KEY_FLOAT = 1 << 20;
|
||||
const KEY_NULLABLE = 1 << 21;
|
||||
const KEY_MANAGED = 1 << 22;
|
||||
|
||||
// Array(BufferView) layout
|
||||
const ARRAYBUFFERVIEW_BUFFER_OFFSET = 0;
|
||||
const ARRAYBUFFERVIEW_DATASTART_OFFSET = 4;
|
||||
const ARRAYBUFFERVIEW_DATALENGTH_OFFSET = 8;
|
||||
const ARRAYBUFFERVIEW_SIZE = 12;
|
||||
const ARRAY_LENGTH_OFFSET = 12;
|
||||
const ARRAY_SIZE = 16;
|
||||
|
||||
const BIGINT = typeof BigUint64Array !== "undefined";
|
||||
const THIS = Symbol();
|
||||
const CHUNKSIZE = 1024;
|
||||
|
||||
/** Gets a string from an U32 and an U16 view on a memory. */
|
||||
function getStringImpl(U32, U16, ptr) {
|
||||
var dataLength = U32[ptr >>> 2];
|
||||
var dataOffset = (ptr + 4) >>> 1;
|
||||
var dataRemain = dataLength;
|
||||
var parts = [];
|
||||
const chunkSize = 1024;
|
||||
while (dataRemain > chunkSize) {
|
||||
let last = U16[dataOffset + chunkSize - 1];
|
||||
let size = last >= 0xD800 && last < 0xDC00 ? chunkSize - 1 : chunkSize;
|
||||
let part = U16.subarray(dataOffset, dataOffset += size);
|
||||
parts.push(String.fromCharCode.apply(String, part));
|
||||
dataRemain -= size;
|
||||
}
|
||||
return parts.join("") + String.fromCharCode.apply(String, U16.subarray(dataOffset, dataOffset + dataRemain));
|
||||
function getStringImpl(U32, U16, ref) {
|
||||
var length = U32[(ref + SIZE_OFFSET) >>> 2] >>> 1;
|
||||
var offset = ref >>> 1;
|
||||
if (length <= CHUNKSIZE) return String.fromCharCode.apply(String, U16.subarray(offset, offset + length));
|
||||
const parts = [];
|
||||
do {
|
||||
const last = U16[offset + CHUNKSIZE - 1];
|
||||
const size = last >= 0xD800 && last < 0xDC00 ? CHUNKSIZE - 1 : CHUNKSIZE;
|
||||
parts.push(String.fromCharCode.apply(String, U16.subarray(offset, offset += size)));
|
||||
length -= size;
|
||||
} while (length > CHUNKSIZE);
|
||||
return parts.join("") + String.fromCharCode.apply(String, U16.subarray(offset, offset + length));
|
||||
}
|
||||
|
||||
/** Prepares the base module prior to instantiation. */
|
||||
function preInstantiate(imports) {
|
||||
var baseModule = {};
|
||||
const baseModule = {};
|
||||
|
||||
function getString(memory, ptr) {
|
||||
function getString(memory, ref) {
|
||||
if (!memory) return "<yet unknown>";
|
||||
var buffer = memory.buffer;
|
||||
return getStringImpl(new Uint32Array(buffer), new Uint16Array(buffer), ptr);
|
||||
const buffer = memory.buffer;
|
||||
return getStringImpl(new Uint32Array(buffer), new Uint16Array(buffer), ref);
|
||||
}
|
||||
|
||||
// add common imports used by stdlib for convenience
|
||||
var env = (imports.env = imports.env || {});
|
||||
const env = (imports.env = imports.env || {});
|
||||
env.abort = env.abort || function abort(mesg, file, line, colm) {
|
||||
var memory = baseModule.memory || env.memory; // prefer exported, otherwise try imported
|
||||
const memory = baseModule.memory || env.memory; // prefer exported, otherwise try imported
|
||||
throw Error("abort: " + getString(memory, mesg) + " at " + getString(memory, file) + ":" + line + ":" + colm);
|
||||
}
|
||||
env.trace = env.trace || function trace(mesg, n) {
|
||||
var memory = baseModule.memory || env.memory;
|
||||
const memory = baseModule.memory || env.memory;
|
||||
console.log("trace: " + getString(memory, mesg) + (n ? " " : "") + Array.prototype.slice.call(arguments, 2, 2 + n).join(", "));
|
||||
}
|
||||
imports.Math = imports.Math || Math;
|
||||
@ -48,13 +80,12 @@ function preInstantiate(imports) {
|
||||
|
||||
/** Prepares the final module once instantiation is complete. */
|
||||
function postInstantiate(baseModule, instance) {
|
||||
var rawExports = instance.exports;
|
||||
var memory = rawExports.memory;
|
||||
var memory_allocate = rawExports["memory.allocate"];
|
||||
var memory_fill = rawExports["memory.fill"];
|
||||
var memory_free = rawExports["memory.free"];
|
||||
var table = rawExports.table;
|
||||
var setargc = rawExports._setargc || function() {};
|
||||
const rawExports = instance.exports;
|
||||
const memory = rawExports.memory;
|
||||
const table = rawExports.table;
|
||||
const alloc = rawExports["__alloc"];
|
||||
const retain = rawExports["__retain"];
|
||||
const rttiBase = rawExports["__rtti_base"] || ~0; // oob if not present
|
||||
|
||||
// Provide views for all sorts of basic values
|
||||
var buffer, I8, U8, I16, U16, I32, U32, F32, F64, I64, U64;
|
||||
@ -70,7 +101,7 @@ function postInstantiate(baseModule, instance) {
|
||||
U16 = new Uint16Array(buffer);
|
||||
I32 = new Int32Array(buffer);
|
||||
U32 = new Uint32Array(buffer);
|
||||
if (hasBigInt64) {
|
||||
if (BIGINT) {
|
||||
I64 = new BigInt64Array(buffer);
|
||||
U64 = new BigUint64Array(buffer);
|
||||
}
|
||||
@ -80,109 +111,120 @@ function postInstantiate(baseModule, instance) {
|
||||
}
|
||||
checkMem();
|
||||
|
||||
/** Allocates a new string in the module's memory and returns its pointer. */
|
||||
function newString(str) {
|
||||
var dataLength = str.length;
|
||||
var ptr = memory_allocate(4 + (dataLength << 1));
|
||||
var dataOffset = (4 + ptr) >>> 1;
|
||||
checkMem();
|
||||
U32[ptr >>> 2] = dataLength;
|
||||
for (let i = 0; i < dataLength; ++i) U16[dataOffset + i] = str.charCodeAt(i);
|
||||
return ptr;
|
||||
/** Gets the runtime type info for the given id. */
|
||||
function getInfo(id) {
|
||||
const count = U32[rttiBase >>> 2];
|
||||
if ((id >>>= 0) >= count) throw Error("invalid id: " + id);
|
||||
return U32[(rttiBase + 4 >>> 2) + id * 2];
|
||||
}
|
||||
|
||||
baseModule.newString = newString;
|
||||
|
||||
/** Gets a string from the module's memory by its pointer. */
|
||||
function getString(ptr) {
|
||||
checkMem();
|
||||
return getStringImpl(U32, U16, ptr);
|
||||
/** Gets the runtime base id for the given id. */
|
||||
function getBase(id) {
|
||||
const count = U32[rttiBase >>> 2];
|
||||
if ((id >>>= 0) >= count) throw Error("invalid id: " + id);
|
||||
return U32[(rttiBase + 4 >>> 2) + id * 2 + 1];
|
||||
}
|
||||
|
||||
baseModule.getString = getString;
|
||||
|
||||
function computeBufferSize(byteLength) {
|
||||
const HEADER_SIZE = 8;
|
||||
return 1 << (32 - Math.clz32(byteLength + HEADER_SIZE - 1));
|
||||
/** Gets the runtime alignment of a collection's values or keys. */
|
||||
function getAlign(which, info) {
|
||||
return 31 - Math.clz32((info / which) & 31); // -1 if none
|
||||
}
|
||||
|
||||
/** Creates a new typed array in the module's memory and returns its pointer. */
|
||||
function newArray(view, length, unsafe) {
|
||||
var ctor = view.constructor;
|
||||
if (ctor === Function) { // TypedArray constructor created in memory
|
||||
ctor = view;
|
||||
view = null;
|
||||
} else { // TypedArray instance copied into memory
|
||||
if (length === undefined) length = view.length;
|
||||
}
|
||||
var elementSize = ctor.BYTES_PER_ELEMENT;
|
||||
if (!elementSize) throw Error("not a typed array");
|
||||
var byteLength = elementSize * length;
|
||||
var ptr = memory_allocate(12); // TypedArray header
|
||||
var buf = memory_allocate(computeBufferSize(byteLength)); // ArrayBuffer
|
||||
/** Allocates a new string in the module's memory and returns its retained pointer. */
|
||||
function __allocString(str) {
|
||||
const length = str.length;
|
||||
const ref = alloc(length << 1, STRING_ID);
|
||||
checkMem();
|
||||
U32[ ptr >>> 2] = buf; // .buffer
|
||||
U32[(ptr + 4) >>> 2] = 0; // .byteOffset
|
||||
U32[(ptr + 8) >>> 2] = byteLength; // .byteLength
|
||||
U32[ buf >>> 2] = byteLength; // .byteLength
|
||||
U32[(buf + 4) >>> 2] = 0; // 0
|
||||
if (view) {
|
||||
new ctor(buffer, buf + 8, length).set(view);
|
||||
if (view.length < length && !unsafe) {
|
||||
let setLength = elementSize * view.length;
|
||||
memory_fill(buf + 8 + setLength, 0, byteLength - setLength);
|
||||
for (let i = 0, j = ref >>> 1; i < length; ++i) U16[j + i] = str.charCodeAt(i);
|
||||
return ref;
|
||||
}
|
||||
|
||||
baseModule.__allocString = __allocString;
|
||||
|
||||
/** Reads a string from the module's memory by its pointer. */
|
||||
function __getString(ref) {
|
||||
checkMem();
|
||||
const id = U32[ref + ID_OFFSET >>> 2];
|
||||
if (id !== STRING_ID) throw Error("not a string: " + ref);
|
||||
return getStringImpl(U32, U16, ref);
|
||||
}
|
||||
|
||||
baseModule.__getString = __getString;
|
||||
|
||||
/** Gets the view matching the specified alignment, signedness and floatness. */
|
||||
function getView(align, signed, float) {
|
||||
if (float) {
|
||||
switch (align) {
|
||||
case 2: return F32;
|
||||
case 3: return F64;
|
||||
}
|
||||
} else {
|
||||
switch (align) {
|
||||
case 0: return signed ? I8 : U8;
|
||||
case 1: return signed ? I16 : U16;
|
||||
case 2: return signed ? I32 : U32;
|
||||
case 3: return signed ? I64 : U64;
|
||||
}
|
||||
} else if (!unsafe) {
|
||||
memory_fill(buf + 8, 0, byteLength);
|
||||
}
|
||||
return ptr;
|
||||
throw Error("unsupported align: " + align);
|
||||
}
|
||||
|
||||
baseModule.newArray = newArray;
|
||||
|
||||
/** Gets a view on a typed array in the module's memory by its pointer. */
|
||||
function getArray(ctor, ptr) {
|
||||
var elementSize = ctor.BYTES_PER_ELEMENT;
|
||||
if (!elementSize) throw Error("not a typed array");
|
||||
/** Allocates a new array in the module's memory and returns its retained pointer. */
|
||||
function __allocArray(id, values) {
|
||||
const info = getInfo(id);
|
||||
if (!(info & (ARRAYBUFFERVIEW | ARRAY))) throw Error("not an array: " + id + " @ " + info);
|
||||
const align = getAlign(VAL_ALIGN, info);
|
||||
const length = values.length;
|
||||
const buf = alloc(length << align, ARRAYBUFFER_ID);
|
||||
const arr = alloc(info & ARRAY ? ARRAY_SIZE : ARRAYBUFFERVIEW_SIZE, id);
|
||||
checkMem();
|
||||
var buf = U32[ ptr >>> 2];
|
||||
var byteOffset = U32[(ptr + 4) >>> 2];
|
||||
var byteLength = U32[(ptr + 8) >>> 2];
|
||||
return new ctor(buffer, buf + 8 + byteOffset, (byteLength - byteOffset) / elementSize);
|
||||
U32[arr + ARRAYBUFFERVIEW_BUFFER_OFFSET >>> 2] = retain(buf);
|
||||
U32[arr + ARRAYBUFFERVIEW_DATASTART_OFFSET >>> 2] = buf;
|
||||
U32[arr + ARRAYBUFFERVIEW_DATALENGTH_OFFSET >>> 2] = length << align;
|
||||
if (info & ARRAY) U32[arr + ARRAY_LENGTH_OFFSET >>> 2] = length;
|
||||
const view = getView(align, info & VAL_SIGNED, info & VAL_FLOAT);
|
||||
for (let i = 0; i < length; ++i) view[(buf >> align) + i] = values[i];
|
||||
if (info & VAL_MANAGED) for (let i = 0; i < length; ++i) retain(values[i]);
|
||||
return arr;
|
||||
}
|
||||
|
||||
baseModule.getArray = getArray;
|
||||
baseModule.__allocArray = __allocArray;
|
||||
|
||||
/** Frees a typed array in the module's memory. Must not be accessed anymore afterwards. */
|
||||
function freeArray(ptr) {
|
||||
/** Gets a view on the values of an array in the module's memory. */
|
||||
function __getArrayView(arr) {
|
||||
checkMem();
|
||||
var buf = U32[ptr >>> 2];
|
||||
memory_free(buf);
|
||||
memory_free(ptr);
|
||||
const id = U32[arr + ID_OFFSET >>> 2];
|
||||
const info = getInfo(id);
|
||||
if (!(info & ARRAYBUFFERVIEW)) throw Error("not an array: " + id);
|
||||
const align = getAlign(VAL_ALIGN, info);
|
||||
var buf = U32[arr + ARRAYBUFFERVIEW_DATASTART_OFFSET >>> 2];
|
||||
const length = info & ARRAY
|
||||
? U32[arr + ARRAY_LENGTH_OFFSET >>> 2]
|
||||
: U32[buf + SIZE_OFFSET >>> 2] >>> align;
|
||||
return getView(align, info & VAL_SIGNED, info & VAL_FLOAT)
|
||||
.slice(buf >>>= align, buf + length);
|
||||
}
|
||||
|
||||
baseModule.freeArray = freeArray;
|
||||
baseModule.__getArrayView = __getArrayView;
|
||||
|
||||
/**
|
||||
* Creates a new function in the module's table and returns its pointer. Note that only actual
|
||||
* WebAssembly functions, i.e. as exported by the module, are supported.
|
||||
*/
|
||||
function newFunction(fn) {
|
||||
if (typeof fn.original === "function") fn = fn.original;
|
||||
var index = table.length;
|
||||
table.grow(1);
|
||||
table.set(index, fn);
|
||||
return index;
|
||||
/** Reads (copies) the values of an array from the module's memory. */
|
||||
function __getArray(arr) {
|
||||
return Array.from(__getArrayView(arr));
|
||||
}
|
||||
|
||||
baseModule.newFunction = newFunction;
|
||||
baseModule.__getArray = __getArray;
|
||||
|
||||
/** Gets a function by its pointer. */
|
||||
function getFunction(ptr) {
|
||||
return wrapFunction(table.get(ptr), setargc);
|
||||
/** Tests whether an object is an instance of the class represented by the specified base id. */
|
||||
function __instanceof(ref, baseId) {
|
||||
var id = U32[(ref + ID_OFFSET) >>> 2];
|
||||
if (id <= U32[rttiBase >>> 2]) {
|
||||
do if (id == baseId) return true;
|
||||
while (id = getBase(id));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
baseModule.getFunction = getFunction;
|
||||
baseModule.__instanceof = __instanceof;
|
||||
|
||||
// Pull basic exports to baseModule so code in preInstantiate can use them
|
||||
baseModule.memory = baseModule.memory || memory;
|
||||
@ -209,8 +251,6 @@ function wrapFunction(fn, setargc) {
|
||||
setargc(args.length);
|
||||
return fn(...args);
|
||||
}
|
||||
// adding a function to the table with `newFunction` is limited to actual WebAssembly functions,
|
||||
// hence we can't use the wrapper and instead need to provide a reference to the original
|
||||
wrap.original = fn;
|
||||
return wrap;
|
||||
}
|
||||
@ -245,7 +285,7 @@ exports.instantiateStreaming = instantiateStreaming;
|
||||
/** Demangles an AssemblyScript module's exports to a friendly object structure. */
|
||||
function demangle(exports, baseModule) {
|
||||
var module = baseModule ? Object.create(baseModule) : {};
|
||||
var setargc = exports._setargc || function() {};
|
||||
var setargc = exports["__setargc"] || function() {};
|
||||
function hasOwnProperty(elem, prop) {
|
||||
return Object.prototype.hasOwnProperty.call(elem, prop);
|
||||
}
|
||||
@ -268,9 +308,13 @@ function demangle(exports, baseModule) {
|
||||
let ctor = function(...args) {
|
||||
return ctor.wrap(ctor.prototype.constructor(0, ...args));
|
||||
};
|
||||
ctor.prototype = {};
|
||||
ctor.prototype = {
|
||||
valueOf: function valueOf() {
|
||||
return this[THIS];
|
||||
}
|
||||
};
|
||||
ctor.wrap = function(thisValue) {
|
||||
return Object.create(ctor.prototype, { [thisPtr]: { value: thisValue, writable: false } });
|
||||
return Object.create(ctor.prototype, { [THIS]: { value: thisValue, writable: false } });
|
||||
};
|
||||
if (classElem) Object.getOwnPropertyNames(classElem).forEach(name =>
|
||||
Object.defineProperty(ctor, name, Object.getOwnPropertyDescriptor(classElem, name))
|
||||
@ -284,8 +328,8 @@ function demangle(exports, baseModule) {
|
||||
let getter = exports[internalName.replace("set:", "get:")];
|
||||
let setter = exports[internalName.replace("get:", "set:")];
|
||||
Object.defineProperty(curr, name, {
|
||||
get: function() { return getter(this[thisPtr]); },
|
||||
set: function(value) { setter(this[thisPtr], value); },
|
||||
get: function() { return getter(this[THIS]); },
|
||||
set: function(value) { setter(this[THIS], value); },
|
||||
enumerable: true
|
||||
});
|
||||
}
|
||||
@ -296,7 +340,7 @@ function demangle(exports, baseModule) {
|
||||
Object.defineProperty(curr, name, {
|
||||
value: function (...args) {
|
||||
setargc(args.length);
|
||||
return elem(this[thisPtr], ...args);
|
||||
return elem(this[THIS], ...args);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1,5 +1,3 @@
|
||||
import "allocator/tlsf";
|
||||
|
||||
export { memory };
|
||||
|
||||
export const COLOR: string = "red";
|
||||
@ -39,10 +37,6 @@ export class Car {
|
||||
this.doorsOpen = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
memory.free(changetype<usize>(this));
|
||||
}
|
||||
}
|
||||
|
||||
export function sum(arr: Int32Array): i32 {
|
||||
@ -51,11 +45,15 @@ export function sum(arr: Int32Array): i32 {
|
||||
return v;
|
||||
}
|
||||
|
||||
export function changeLength(arr: Array<i32>, length: i32): void {
|
||||
arr.length = length;
|
||||
}
|
||||
|
||||
export function varadd(a: i32 = 1, b: i32 = 2): i32 {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
export const varadd_ptr = varadd;
|
||||
export const varadd_ref = varadd;
|
||||
|
||||
export function calladd(fn: (a: i32, b: i32) => i32, a: i32, b: i32): i32 {
|
||||
return fn(a, b);
|
||||
@ -64,3 +62,8 @@ export function calladd(fn: (a: i32, b: i32) => i32, a: i32, b: i32): i32 {
|
||||
export function dotrace(num: f64): void {
|
||||
trace("The answer is", 1, num);
|
||||
}
|
||||
|
||||
export const INT32ARRAY_ID = idof<Int32Array>();
|
||||
export const UINT32ARRAY_ID = idof<Uint32Array>();
|
||||
export const FLOAT32ARRAY_ID = idof<Float32Array>();
|
||||
export const ARRAYI32_ID = idof<Array<i32>>();
|
||||
|
Binary file not shown.
@ -21,44 +21,87 @@ assert(proto.F64 instanceof Float64Array);
|
||||
|
||||
// should export memory
|
||||
assert(module.memory instanceof WebAssembly.Memory);
|
||||
assert(typeof module.memory.free === "function");
|
||||
assert(typeof module.memory.copy === "function");
|
||||
|
||||
// should be able to get an exported string
|
||||
assert.strictEqual(module.getString(module.COLOR), "red");
|
||||
assert.strictEqual(module.__getString(module.COLOR), "red");
|
||||
|
||||
// should be able to allocate and work with a new string
|
||||
var str = "Hello world!𤭢";
|
||||
var ptr = module.newString(str);
|
||||
assert.strictEqual(module.getString(ptr), str);
|
||||
assert.strictEqual(module.strlen(ptr), str.length);
|
||||
{
|
||||
let str = "Hello world!𤭢";
|
||||
let ref = module.__retain(module.__allocString(str));
|
||||
assert.strictEqual(module.__getString(ref), str);
|
||||
assert.strictEqual(module.strlen(ref), str.length);
|
||||
module.__release(ref);
|
||||
}
|
||||
|
||||
// should be able to allocate a typed array and sum up its values
|
||||
var arr = [1, 2, 3, 4, 5, 0x7fffffff];
|
||||
ptr = module.newArray(new Int32Array(arr));
|
||||
assert.strictEqual(module.sum(ptr), arr.reduce((a, b) => a + b, 0) | 0);
|
||||
// should be able to allocate a typed array
|
||||
{
|
||||
var arr = [1, 2, 3, 4, 5, 0x80000000 | 0];
|
||||
let ref = module.__retain(module.__allocArray(module.INT32ARRAY_ID, arr));
|
||||
assert(module.__instanceof(ref, module.INT32ARRAY_ID));
|
||||
|
||||
// should be able to get a view on an internal typed array
|
||||
assert.deepEqual(module.getArray(Int32Array, ptr), arr);
|
||||
// should be able to get the values of an array
|
||||
assert.deepEqual(module.__getArray(ref), arr);
|
||||
|
||||
// should be able to free and reuse the space of an internal typed array
|
||||
module.freeArray(ptr);
|
||||
var ptr2 = module.newArray(new Int32Array(arr));
|
||||
assert.strictEqual(ptr, ptr2);
|
||||
// should be able to get a view on an array
|
||||
assert.deepEqual(module.__getArrayView(ref), new Int32Array(arr));
|
||||
|
||||
// should be able to just call a function with variable arguments
|
||||
// should be able to sum up its values
|
||||
assert.strictEqual(module.sum(ref), arr.reduce((a, b) => (a + b) | 0, 0) | 0);
|
||||
|
||||
// should be able to release no longer needed references
|
||||
module.__release(ref);
|
||||
try { module.__release(ref); assert(false); } catch (e) {};
|
||||
}
|
||||
|
||||
// should be able to distinguish between signed and unsigned
|
||||
{
|
||||
let arr = [1, -1 >>> 0, 0x80000000];
|
||||
let ref = module.__retain(module.__allocArray(module.UINT32ARRAY_ID, arr));
|
||||
assert(module.__instanceof(ref, module.UINT32ARRAY_ID));
|
||||
assert.deepEqual(module.__getArray(ref), arr);
|
||||
module.__release(ref);
|
||||
try { module.__release(ref); assert(false); } catch (e) {};
|
||||
}
|
||||
|
||||
// should be able to distinguish between integer and float
|
||||
{
|
||||
let arr = [0.0, 1.5, 2.5];
|
||||
let ref = module.__retain(module.__allocArray(module.FLOAT32ARRAY_ID, arr));
|
||||
assert(module.__instanceof(ref, module.FLOAT32ARRAY_ID));
|
||||
assert.deepEqual(module.__getArray(ref), arr);
|
||||
module.__release(ref);
|
||||
try { module.__release(ref); assert(false); } catch (e) {};
|
||||
}
|
||||
|
||||
// should be able to work with normal arrays
|
||||
{
|
||||
let arr = [1, 2, 3, 4, 5];
|
||||
let ref = module.__retain(module.__allocArray(module.ARRAYI32_ID, arr));
|
||||
assert(module.__instanceof(ref, module.ARRAYI32_ID));
|
||||
module.changeLength(ref, 3);
|
||||
assert.deepEqual(module.__getArray(ref), [1, 2, 3]);
|
||||
module.__release(ref);
|
||||
try { module.__release(ref); assert(false); } catch (e) {};
|
||||
}
|
||||
|
||||
// should be able to correctly call a function with variable arguments
|
||||
assert.strictEqual(module.varadd(), 3);
|
||||
assert.strictEqual(module.varadd(2, 3), 5);
|
||||
assert.strictEqual(module.varadd(2), 4);
|
||||
|
||||
// TBD: table is no more exported by default to allow more optimizations
|
||||
|
||||
// should be able to get a function from the table and just call it with variable arguments
|
||||
var fn = module.getFunction(module.varadd_ptr);
|
||||
assert.strictEqual(fn(), 3);
|
||||
assert.strictEqual(fn(2, 3), 5);
|
||||
assert.strictEqual(fn(2), 4);
|
||||
// var fn = module.getFunction(module.varadd_ref);
|
||||
// assert.strictEqual(fn(), 3);
|
||||
// assert.strictEqual(fn(2, 3), 5);
|
||||
// assert.strictEqual(fn(2), 4);
|
||||
|
||||
// should be able to create a new function and call it from WASM
|
||||
ptr = module.newFunction(module.varadd);
|
||||
assert.strictEqual(module.calladd(ptr, 2, 3), 5);
|
||||
// ref = module.newFunction(module.varadd);
|
||||
// assert.strictEqual(module.calladd(ref, 2, 3), 5);
|
||||
|
||||
// should be able to use a class
|
||||
var car = new module.Car(5);
|
||||
@ -68,6 +111,7 @@ car.openDoors();
|
||||
assert.strictEqual(car.isDoorsOpen, 1);
|
||||
car.closeDoors();
|
||||
assert.strictEqual(car.isDoorsOpen, 0);
|
||||
module.__release(car); // uses Car.prototype.valueOf to obtain `thisPtr`
|
||||
|
||||
// should be able to use trace
|
||||
module.dotrace(42);
|
||||
module.dotrace(42);
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -5,7 +5,7 @@
|
||||
"main": "index.js",
|
||||
"types": "index.d.ts",
|
||||
"scripts": {
|
||||
"asbuild": "asc assembly/index.ts -O3 -b build/index.wasm -t build/index.wat --importMemory --sourceMap --validate",
|
||||
"asbuild": "asc assembly/index.ts -O3 -b build/index.wasm -t build/index.wat --importMemory --runtime none --sourceMap --validate",
|
||||
"build": "npm run asbuild && webpack --mode production --display-modules",
|
||||
"test": "ts-node tests/"
|
||||
},
|
||||
|
26
lib/rtrace/README.md
Normal file
26
lib/rtrace/README.md
Normal file
@ -0,0 +1,26 @@
|
||||
#  rtrace
|
||||
|
||||
A tiny utility that records allocations, retains, releases and frees performed by the runtime and emits an error if something is off. Also checks for leaks.
|
||||
|
||||
Instructions
|
||||
------------
|
||||
|
||||
Compile your module that uses the full or half runtime with `-use ASC_RTRACE=1` and include an instance of this module as the import named `rtrace`.
|
||||
|
||||
```js
|
||||
var rtr = rtrace(e => {
|
||||
// handle error
|
||||
});
|
||||
|
||||
WebAssembly.instantiate(..., { rtrace: rtr, ... });
|
||||
...
|
||||
|
||||
if (rtr.active) {
|
||||
let leakCount = rtr.check();
|
||||
if (leakCount) {
|
||||
// handle error
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that references retained in globals which are not cleared before execution concludes appear as leaks, including their inner members. A TypedArray would leak itself and its backing ArrayBuffer in this case for example. This is perfectly normal and clearing all globals avoids this.
|
73
lib/rtrace/index.js
Normal file
73
lib/rtrace/index.js
Normal file
@ -0,0 +1,73 @@
|
||||
function rtrace(onerror, oninfo) {
|
||||
if (!onerror) onerror = function() {};
|
||||
if (!oninfo) oninfo = function() {};
|
||||
var blocks = new Map();
|
||||
var rtrace = {
|
||||
|
||||
allocCount: 0,
|
||||
freeCount: 0,
|
||||
incrementCount: 0,
|
||||
decrementCount: 0,
|
||||
|
||||
onalloc: function(block) {
|
||||
++rtrace.allocCount;
|
||||
if (blocks.has(block)) {
|
||||
onerror(Error("duplicate alloc: " + block));
|
||||
} else {
|
||||
oninfo("ALLOC " + block);
|
||||
blocks.set(block, 0);
|
||||
}
|
||||
},
|
||||
|
||||
onfree: function(block) {
|
||||
++rtrace.freeCount;
|
||||
if (!blocks.has(block)) {
|
||||
onerror(Error("orphaned free: " + block));
|
||||
} else {
|
||||
oninfo("FREE " + block + " @ " + blocks.get(block));
|
||||
blocks.delete(block);
|
||||
}
|
||||
},
|
||||
|
||||
onincrement: function(block) {
|
||||
++rtrace.incrementCount;
|
||||
if (!blocks.has(block)) {
|
||||
onerror(Error("orphaned increment: " + block));
|
||||
} else {
|
||||
let rc = blocks.get(block);
|
||||
oninfo("++ " + block + " @ " + rc + "->" + (rc + 1));
|
||||
blocks.set(block, rc + 1);
|
||||
}
|
||||
},
|
||||
|
||||
ondecrement: function(block) {
|
||||
++rtrace.decrementCount;
|
||||
if (!blocks.has(block)) {
|
||||
onerror(Error("orphaned decrement: " + block));
|
||||
} else {
|
||||
let rc = blocks.get(block);
|
||||
if (rc < 1) {
|
||||
onerror(Error("invalid decrement: " + block));
|
||||
} else {
|
||||
oninfo("-- " + block + " @ " + rc + "->" + (rc - 1));
|
||||
blocks.set(block, rc - 1);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
get active() {
|
||||
return Boolean(rtrace.allocCount || rtrace.freeCount || rtrace.incrementCount || rtrace.decrementCount);
|
||||
},
|
||||
|
||||
check() {
|
||||
if (oninfo) {
|
||||
for (let [block, rc ] of blocks) {
|
||||
oninfo("LEAKING " + block + " @ " + rc);
|
||||
}
|
||||
}
|
||||
return blocks.size;
|
||||
}
|
||||
};
|
||||
return rtrace;
|
||||
}
|
||||
module.exports = rtrace;
|
6
lib/rtrace/package.json
Normal file
6
lib/rtrace/package.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "@assemblyscript/rtrace",
|
||||
"version": "0.1.0",
|
||||
"license": "Apache-2.0",
|
||||
"main": "index.js"
|
||||
}
|
263
package-lock.json
generated
263
package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "assemblyscript",
|
||||
"version": "0.6.0",
|
||||
"version": "0.7.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@ -9,78 +9,12 @@
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
|
||||
"integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA="
|
||||
},
|
||||
"@types/events": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
|
||||
"integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/fs-extra": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-5.0.5.tgz",
|
||||
"integrity": "sha512-w7iqhDH9mN8eLClQOYTkhdYUOSpp25eXxfc6VbFOGtzxW34JcvctH2bKjj4jD4++z4R5iO5D+pg48W2e03I65A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/glob": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz",
|
||||
"integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/events": "*",
|
||||
"@types/minimatch": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/handlebars": {
|
||||
"version": "4.0.40",
|
||||
"resolved": "https://registry.npmjs.org/@types/handlebars/-/handlebars-4.0.40.tgz",
|
||||
"integrity": "sha512-sGWNtsjNrLOdKha2RV1UeF8+UbQnPSG7qbe5wwbni0mw4h2gHXyPFUMOC+xwGirIiiydM/HSqjDO4rk6NFB18w==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/highlight.js": {
|
||||
"version": "9.12.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/highlight.js/-/highlight.js-9.12.3.tgz",
|
||||
"integrity": "sha512-pGF/zvYOACZ/gLGWdQH8zSwteQS1epp68yRcVLJMgUck/MjEn/FBYmPub9pXT8C1e4a8YZfHo1CKyV8q1vKUnQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/lodash": {
|
||||
"version": "4.14.121",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.121.tgz",
|
||||
"integrity": "sha512-ORj7IBWj13iYufXt/VXrCNMbUuCTJfhzme5kx9U/UtcIPdJYuvPDUAlHlbNhz/8lKCLy9XGIZnGrqXOtQbPGoQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/marked": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/marked/-/marked-0.4.2.tgz",
|
||||
"integrity": "sha512-cDB930/7MbzaGF6U3IwSQp6XBru8xWajF5PV2YZZeV8DyiliTuld11afVztGI9+yJZ29il5E+NpGA6ooV/Cjkg==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/minimatch": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
|
||||
"integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "11.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.0.tgz",
|
||||
"integrity": "sha512-rx29MMkRdVmzunmiA4lzBYJNnXsW/PhG4kMBy2ATsYaDjGGR75dCFEVVROKpNwlVdcUX3xxlghKQOeDPBJobng==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/shelljs": {
|
||||
"version": "0.8.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/shelljs/-/shelljs-0.8.3.tgz",
|
||||
"integrity": "sha512-miY41hqc5SkRlsZDod3heDa4OS9xv8G77EMBQuSpqq86HBn66l7F+f8y9YKm+1PIuwC8QEZVwN8YxOOG7Y67fA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/glob": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@webassemblyjs/ast": {
|
||||
"version": "1.8.5",
|
||||
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz",
|
||||
@ -429,15 +363,6 @@
|
||||
"integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
|
||||
"dev": true
|
||||
},
|
||||
"async": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz",
|
||||
"integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash": "^4.17.11"
|
||||
}
|
||||
},
|
||||
"async-each": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz",
|
||||
@ -567,9 +492,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"binaryen": {
|
||||
"version": "77.0.0-nightly.20190407",
|
||||
"resolved": "https://registry.npmjs.org/binaryen/-/binaryen-77.0.0-nightly.20190407.tgz",
|
||||
"integrity": "sha512-1mxYNvQ0xywMe582K7V6Vo2zzhZZxMTeGHH8aE/+/AND8f64D8Q1GThVY3RVRwGY/4p+p95ccw9Xbw2ovFXRIg=="
|
||||
"version": "84.0.0-nightly.20190522",
|
||||
"resolved": "https://registry.npmjs.org/binaryen/-/binaryen-84.0.0-nightly.20190522.tgz",
|
||||
"integrity": "sha512-bxSPi3MOkFmK5W6VIlqxnOc1nYzpUCzT/tHz3C7sgbz7jTR2lOBlZnKStTJlBt018xeZK9/JpK/jXdduH7eQFg=="
|
||||
},
|
||||
"bluebird": {
|
||||
"version": "3.5.3",
|
||||
@ -1564,17 +1489,6 @@
|
||||
"readable-stream": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
|
||||
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.2",
|
||||
"jsonfile": "^4.0.0",
|
||||
"universalify": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"fs-write-stream-atomic": {
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
|
||||
@ -2225,26 +2139,6 @@
|
||||
"integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
|
||||
"dev": true
|
||||
},
|
||||
"handlebars": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.0.tgz",
|
||||
"integrity": "sha512-l2jRuU1NAWK6AW5qqcTATWQJvNPEwkM7NEKSiv/gqOsoSQbVoWyqVEY5GS+XPQ88zLNmqASRpzfdm8d79hJS+w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"async": "^2.5.0",
|
||||
"optimist": "^0.6.1",
|
||||
"source-map": "^0.6.1",
|
||||
"uglify-js": "^3.1.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"has-ansi": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
|
||||
@ -2312,12 +2206,6 @@
|
||||
"minimalistic-assert": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"highlight.js": {
|
||||
"version": "9.14.2",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.14.2.tgz",
|
||||
"integrity": "sha512-Nc6YNECYpxyJABGYJAyw7dBAYbXEuIzwzkqoJnwbc1nIpCiN+3ioYf0XrBnLiyyG0JLuJhpPtt2iTSbXiKLoyA==",
|
||||
"dev": true
|
||||
},
|
||||
"hmac-drbg": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
||||
@ -2607,15 +2495,6 @@
|
||||
"minimist": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
||||
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"kind-of": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
|
||||
@ -2658,12 +2537,6 @@
|
||||
"path-exists": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.11",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
|
||||
"integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
|
||||
"dev": true
|
||||
},
|
||||
"long": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
|
||||
@ -2723,12 +2596,6 @@
|
||||
"object-visit": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"marked": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-0.4.0.tgz",
|
||||
"integrity": "sha512-tMsdNBgOsrUophCAFQl0XPe6Zqk/uy9gnue+jIIKhykO51hxyu6uNx7zBPy0+y/WKYVZZMspV9YeXLNdKk+iYw==",
|
||||
"dev": true
|
||||
},
|
||||
"md5.js": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
||||
@ -3060,24 +2927,6 @@
|
||||
"resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz",
|
||||
"integrity": "sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw=="
|
||||
},
|
||||
"optimist": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
|
||||
"integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "~0.0.1",
|
||||
"wordwrap": "~0.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",
|
||||
"integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"os-browserify": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
|
||||
@ -3261,12 +3110,6 @@
|
||||
"integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
|
||||
"dev": true
|
||||
},
|
||||
"progress": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
|
||||
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
|
||||
"dev": true
|
||||
},
|
||||
"promise-inflight": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
|
||||
@ -3389,15 +3232,6 @@
|
||||
"readable-stream": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"rechoir": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
|
||||
"integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"resolve": "^1.1.6"
|
||||
}
|
||||
},
|
||||
"regex-not": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
|
||||
@ -3610,17 +3444,6 @@
|
||||
"integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
|
||||
"dev": true
|
||||
},
|
||||
"shelljs": {
|
||||
"version": "0.8.3",
|
||||
"resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.3.tgz",
|
||||
"integrity": "sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.0.0",
|
||||
"interpret": "^1.0.0",
|
||||
"rechoir": "^0.6.2"
|
||||
}
|
||||
},
|
||||
"signal-exit": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
|
||||
@ -4140,45 +3963,6 @@
|
||||
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
|
||||
"dev": true
|
||||
},
|
||||
"typedoc": {
|
||||
"version": "0.14.2",
|
||||
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.14.2.tgz",
|
||||
"integrity": "sha512-aEbgJXV8/KqaVhcedT7xG6d2r+mOvB5ep3eIz1KuB5sc4fDYXcepEEMdU7XSqLFO5hVPu0nllHi1QxX2h/QlpQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/fs-extra": "^5.0.3",
|
||||
"@types/handlebars": "^4.0.38",
|
||||
"@types/highlight.js": "^9.12.3",
|
||||
"@types/lodash": "^4.14.110",
|
||||
"@types/marked": "^0.4.0",
|
||||
"@types/minimatch": "3.0.3",
|
||||
"@types/shelljs": "^0.8.0",
|
||||
"fs-extra": "^7.0.0",
|
||||
"handlebars": "^4.0.6",
|
||||
"highlight.js": "^9.13.1",
|
||||
"lodash": "^4.17.10",
|
||||
"marked": "^0.4.0",
|
||||
"minimatch": "^3.0.0",
|
||||
"progress": "^2.0.0",
|
||||
"shelljs": "^0.8.2",
|
||||
"typedoc-default-themes": "^0.5.0",
|
||||
"typescript": "3.2.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"typescript": {
|
||||
"version": "3.2.4",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.4.tgz",
|
||||
"integrity": "sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"typedoc-default-themes": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.5.0.tgz",
|
||||
"integrity": "sha1-bcJDPnjti+qOiHo6zeLzF4W9Yic=",
|
||||
"dev": true
|
||||
},
|
||||
"typedoc-plugin-external-module-name": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/typedoc-plugin-external-module-name/-/typedoc-plugin-external-module-name-2.0.0.tgz",
|
||||
@ -4191,33 +3975,6 @@
|
||||
"integrity": "sha512-Og2Vn6Mk7JAuWA1hQdDQN/Ekm/SchX80VzLhjKN9ETYrIepBFAd8PkOdOTK2nKt0FCkmMZKBJvQ1dV1gIxPu/A==",
|
||||
"dev": true
|
||||
},
|
||||
"uglify-js": {
|
||||
"version": "3.4.9",
|
||||
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz",
|
||||
"integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"commander": "~2.17.1",
|
||||
"source-map": "~0.6.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": {
|
||||
"version": "2.17.1",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
|
||||
"integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"union-value": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",
|
||||
@ -4271,12 +4028,6 @@
|
||||
"imurmurhash": "^0.1.4"
|
||||
}
|
||||
},
|
||||
"universalify": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
|
||||
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
|
||||
"dev": true
|
||||
},
|
||||
"unset-value": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
|
||||
@ -4487,12 +4238,6 @@
|
||||
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
|
||||
"dev": true
|
||||
},
|
||||
"wordwrap": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
|
||||
"integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=",
|
||||
"dev": true
|
||||
},
|
||||
"worker-farm": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz",
|
||||
|
13
package.json
13
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "assemblyscript",
|
||||
"version": "0.6.0",
|
||||
"version": "0.7.0",
|
||||
"author": "Daniel Wirtz <dcode+assemblyscript@dcode.io>",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
@ -12,7 +12,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@protobufjs/utf8": "^1.1.0",
|
||||
"binaryen": "77.0.0-nightly.20190407",
|
||||
"binaryen": "84.0.0-nightly.20190522",
|
||||
"glob": "^7.1.3",
|
||||
"long": "^4.0.0",
|
||||
"opencollective-postinstall": "^2.0.0",
|
||||
@ -25,7 +25,6 @@
|
||||
"ts-loader": "^5.3.3",
|
||||
"ts-node": "^6.2.0",
|
||||
"tslint": "^5.15.0",
|
||||
"typedoc": "^0.14.0",
|
||||
"typedoc-plugin-external-module-name": "^2.0.0",
|
||||
"typescript": "^3.4.2",
|
||||
"webpack": "^4.29.6",
|
||||
@ -41,12 +40,13 @@
|
||||
"node": ">=8"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "webpack --mode production --display-modules",
|
||||
"build": "npm run build:bundle && npm run build:dts",
|
||||
"build:bundle": "webpack --mode production --display-modules",
|
||||
"build:dts": "node scripts/build-dts",
|
||||
"clean": "node scripts/clean",
|
||||
"check": "npm run check:config && npm run check:compiler && npm run check:library",
|
||||
"check": "npm run check:config && npm run check:compiler",
|
||||
"check:config": "tsc --noEmit -p src --diagnostics --listFiles",
|
||||
"check:compiler": "tslint -c tslint.json --project src --formatters-dir lib/lint/formatters --format as",
|
||||
"check:library": "tslint -c tslint.json --project std/assembly --formatters-dir lib/lint/formatters --format as",
|
||||
"test": "npm run test:parser && npm run test:compiler",
|
||||
"test:parser": "node tests/parser",
|
||||
"test:compiler": "node tests/compiler",
|
||||
@ -69,7 +69,6 @@
|
||||
"package.json",
|
||||
"package-lock.json",
|
||||
"README.md",
|
||||
"src/",
|
||||
"std/",
|
||||
"tsconfig-base.json"
|
||||
],
|
||||
|
450
scripts/build-dts.js
Normal file
450
scripts/build-dts.js
Normal file
@ -0,0 +1,450 @@
|
||||
// © 2015-2019 SitePen, Inc. New BSD License.
|
||||
// see: https://github.com/SitePen/dts-generator
|
||||
(function (factory) {
|
||||
var v = factory(require, exports);
|
||||
if (v !== undefined) module.exports = v;
|
||||
})(function (require, exports) {
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const fs = require("fs");
|
||||
const glob = require("glob");
|
||||
const mkdirp = require("mkdirp");
|
||||
const os = require("os");
|
||||
const pathUtil = require("path");
|
||||
const ts = require("typescript");
|
||||
// declare some constants so we don't have magic integers without explanation
|
||||
const DTSLEN = '.d.ts'.length;
|
||||
const filenameToMid = (function () {
|
||||
if (pathUtil.sep === '/') {
|
||||
return function (filename) {
|
||||
return filename;
|
||||
};
|
||||
}
|
||||
else {
|
||||
const separatorExpression = new RegExp(pathUtil.sep.replace('\\', '\\\\'), 'g');
|
||||
return function (filename) {
|
||||
return filename.replace(separatorExpression, '/');
|
||||
};
|
||||
}
|
||||
})();
|
||||
/**
|
||||
* A helper function that takes TypeScript diagnostic errors and returns an error
|
||||
* object.
|
||||
* @param diagnostics The array of TypeScript Diagnostic objects
|
||||
*/
|
||||
function getError(diagnostics) {
|
||||
let message = 'Declaration generation failed';
|
||||
diagnostics.forEach(function (diagnostic) {
|
||||
// not all errors have an associated file: in particular, problems with a
|
||||
// the tsconfig.json don't; the messageText is enough to diagnose in those
|
||||
// cases.
|
||||
if (diagnostic.file) {
|
||||
const position = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
|
||||
message +=
|
||||
`\n${diagnostic.file.fileName}(${position.line + 1},${position.character + 1}): ` +
|
||||
`error TS${diagnostic.code}: ${diagnostic.messageText}`;
|
||||
}
|
||||
else {
|
||||
message += `\nerror TS${diagnostic.code}: ${diagnostic.messageText}`;
|
||||
}
|
||||
});
|
||||
const error = new Error(message);
|
||||
error.name = 'EmitterError';
|
||||
return error;
|
||||
}
|
||||
function getFilenames(baseDir, files) {
|
||||
return files.map(function (filename) {
|
||||
const resolvedFilename = pathUtil.resolve(filename);
|
||||
if (resolvedFilename.indexOf(baseDir) === 0) {
|
||||
return resolvedFilename;
|
||||
}
|
||||
return pathUtil.resolve(baseDir, filename);
|
||||
});
|
||||
}
|
||||
function processTree(sourceFile, replacer) {
|
||||
let code = '';
|
||||
let cursorPosition = 0;
|
||||
function skip(node) {
|
||||
cursorPosition = node.end;
|
||||
}
|
||||
function readThrough(node) {
|
||||
code += sourceFile.text.slice(cursorPosition, node.pos);
|
||||
cursorPosition = node.pos;
|
||||
}
|
||||
function visit(node) {
|
||||
readThrough(node);
|
||||
const replacement = replacer(node);
|
||||
if (replacement != null) {
|
||||
code += replacement;
|
||||
skip(node);
|
||||
}
|
||||
else {
|
||||
ts.forEachChild(node, visit);
|
||||
}
|
||||
}
|
||||
visit(sourceFile);
|
||||
code += sourceFile.text.slice(cursorPosition);
|
||||
return code;
|
||||
}
|
||||
/**
|
||||
* Load and parse a TSConfig File
|
||||
* @param options The dts-generator options to load config into
|
||||
* @param fileName The path to the file
|
||||
*/
|
||||
function getTSConfig(fileName) {
|
||||
// TODO this needs a better design than merging stuff into options.
|
||||
// the trouble is what to do when no tsconfig is specified...
|
||||
const configText = fs.readFileSync(fileName, { encoding: 'utf8' });
|
||||
const result = ts.parseConfigFileTextToJson(fileName, configText);
|
||||
if (result.error) {
|
||||
throw getError([result.error]);
|
||||
}
|
||||
const configObject = result.config;
|
||||
const configParseResult = ts.parseJsonConfigFileContent(configObject, ts.sys, pathUtil.dirname(fileName));
|
||||
if (configParseResult.errors && configParseResult.errors.length) {
|
||||
throw getError(configParseResult.errors);
|
||||
}
|
||||
return [
|
||||
configParseResult.fileNames,
|
||||
configParseResult.options
|
||||
];
|
||||
}
|
||||
function isNodeKindImportDeclaration(value) {
|
||||
return value && value.kind === ts.SyntaxKind.ImportDeclaration;
|
||||
}
|
||||
function isNodeKindExternalModuleReference(value) {
|
||||
return value && value.kind === ts.SyntaxKind.ExternalModuleReference;
|
||||
}
|
||||
function isNodeKindStringLiteral(value) {
|
||||
return value && value.kind === ts.SyntaxKind.StringLiteral;
|
||||
}
|
||||
function isNodeKindExportDeclaration(value) {
|
||||
return value && value.kind === ts.SyntaxKind.ExportDeclaration;
|
||||
}
|
||||
function isNodeKindExportAssignment(value) {
|
||||
return value && value.kind === ts.SyntaxKind.ExportAssignment;
|
||||
}
|
||||
function isNodeKindModuleDeclaration(value) {
|
||||
return value && value.kind === ts.SyntaxKind.ModuleDeclaration;
|
||||
}
|
||||
function generate(options) {
|
||||
if (Boolean(options.main) !== Boolean(options.name)) {
|
||||
if (Boolean(options.name)) {
|
||||
// since options.name used to do double duty as the prefix, let's be
|
||||
// considerate and point out that name should be replaced with prefix.
|
||||
// TODO update this error message when we finalize which version this change
|
||||
// will be released in.
|
||||
throw new Error(`name and main must be used together. Perhaps you want prefix instead of
|
||||
name? In dts-generator version 2.1, name did double duty as the option to
|
||||
use to prefix module names with, but in >=2.2 the name option was split
|
||||
into two; prefix is what is now used to prefix imports and module names
|
||||
in the output.`);
|
||||
}
|
||||
else {
|
||||
throw new Error('name and main must be used together.');
|
||||
}
|
||||
}
|
||||
const noop = function () { };
|
||||
const sendMessage = options.sendMessage || noop;
|
||||
const verboseMessage = options.verbose ? sendMessage : noop;
|
||||
let compilerOptions = {};
|
||||
let files = options.files;
|
||||
/* following tsc behaviour, if a project is specified, or if no files are specified then
|
||||
* attempt to load tsconfig.json */
|
||||
if (options.project || !options.files || options.files.length === 0) {
|
||||
verboseMessage(`project = "${options.project || options.baseDir}"`);
|
||||
// if project isn't specified, use baseDir. If it is and it's a directory,
|
||||
// assume we want tsconfig.json in that directory. If it is a file, though
|
||||
// use that as our tsconfig.json. This allows for projects that have more
|
||||
// than one tsconfig.json file.
|
||||
let tsconfigFilename;
|
||||
if (Boolean(options.project)) {
|
||||
if (fs.lstatSync(options.project).isDirectory()) {
|
||||
tsconfigFilename = pathUtil.join(options.project, 'tsconfig.json');
|
||||
}
|
||||
else {
|
||||
// project isn't a diretory, it's a file
|
||||
tsconfigFilename = options.project;
|
||||
}
|
||||
}
|
||||
else {
|
||||
tsconfigFilename = pathUtil.join(options.baseDir, 'tsconfig.json');
|
||||
}
|
||||
if (fs.existsSync(tsconfigFilename)) {
|
||||
verboseMessage(` parsing "${tsconfigFilename}"`);
|
||||
[files, compilerOptions] = getTSConfig(tsconfigFilename);
|
||||
}
|
||||
else {
|
||||
sendMessage(`No "tsconfig.json" found at "${tsconfigFilename}"!`);
|
||||
return new Promise(function ({}, reject) {
|
||||
reject(new SyntaxError('Unable to resolve configuration.'));
|
||||
});
|
||||
}
|
||||
}
|
||||
const eol = options.eol || os.EOL;
|
||||
const nonEmptyLineStart = new RegExp(eol + '(?!' + eol + '|$)', 'g');
|
||||
const indent = options.indent === undefined ? '\t' : options.indent;
|
||||
// use input values if tsconfig leaves any of these undefined.
|
||||
// this is for backwards compatibility
|
||||
compilerOptions.declaration = true;
|
||||
compilerOptions.target = compilerOptions.target || ts.ScriptTarget.Latest; // is this necessary?
|
||||
compilerOptions.moduleResolution = compilerOptions.moduleResolution || options.moduleResolution;
|
||||
compilerOptions.outDir = compilerOptions.outDir || options.outDir;
|
||||
// TODO should compilerOptions.baseDir come into play?
|
||||
const baseDir = pathUtil.resolve(compilerOptions.rootDir || options.project || options.baseDir);
|
||||
const outDir = compilerOptions.outDir;
|
||||
verboseMessage(`baseDir = "${baseDir}"`);
|
||||
verboseMessage(`target = ${compilerOptions.target}`);
|
||||
verboseMessage(`outDir = ${compilerOptions.outDir}`);
|
||||
verboseMessage(`rootDir = ${compilerOptions.rootDir}`);
|
||||
verboseMessage(`moduleResolution = ${compilerOptions.moduleResolution}`);
|
||||
const filenames = getFilenames(baseDir, files);
|
||||
verboseMessage('filenames:');
|
||||
filenames.forEach(name => { verboseMessage(' ' + name); });
|
||||
const excludesMap = {};
|
||||
options.exclude = options.exclude || ['node_modules/**/*.d.ts'];
|
||||
options.exclude && options.exclude.forEach(function (filename) {
|
||||
glob.sync(filename, { cwd: baseDir }).forEach(function (globFileName) {
|
||||
excludesMap[filenameToMid(pathUtil.resolve(baseDir, globFileName))] = true;
|
||||
});
|
||||
});
|
||||
if (options.exclude) {
|
||||
verboseMessage('exclude:');
|
||||
options.exclude.forEach(name => { verboseMessage(' ' + name); });
|
||||
}
|
||||
if (!options.stdout) mkdirp.sync(pathUtil.dirname(options.out));
|
||||
/* node.js typings are missing the optional mode in createWriteStream options and therefore
|
||||
* in TS 1.6 the strict object literal checking is throwing, therefore a hammer to the nut */
|
||||
const output = options.stdout || fs.createWriteStream(options.out, { mode: parseInt('644', 8) });
|
||||
const host = ts.createCompilerHost(compilerOptions);
|
||||
const program = ts.createProgram(filenames, compilerOptions, host);
|
||||
function writeFile(filename, data) {
|
||||
// Compiler is emitting the non-declaration file, which we do not care about
|
||||
if (filename.slice(-DTSLEN) !== '.d.ts') {
|
||||
return;
|
||||
}
|
||||
writeDeclaration(ts.createSourceFile(filename, data, compilerOptions.target, true), true);
|
||||
}
|
||||
let declaredExternalModules = [];
|
||||
return new Promise(function (resolve, reject) {
|
||||
output.on('close', () => { resolve(undefined); });
|
||||
output.on('error', reject);
|
||||
if (options.externs) {
|
||||
options.externs.forEach(function (path) {
|
||||
sendMessage(`Writing external dependency ${path}`);
|
||||
output.write(`/// <reference path="${path}" />` + eol);
|
||||
});
|
||||
}
|
||||
if (options.types) {
|
||||
options.types.forEach(function (type) {
|
||||
sendMessage(`Writing external @types package dependency ${type}`);
|
||||
output.write(`/// <reference types="${type}" />` + eol);
|
||||
});
|
||||
}
|
||||
sendMessage('processing:');
|
||||
let mainExportDeclaration = false;
|
||||
let mainExportAssignment = false;
|
||||
let foundMain = false;
|
||||
program.getSourceFiles().forEach(function (sourceFile) {
|
||||
processTree(sourceFile, function (node) {
|
||||
if (isNodeKindModuleDeclaration(node)) {
|
||||
const name = node.name;
|
||||
if (isNodeKindStringLiteral(name)) {
|
||||
declaredExternalModules.push(name.text);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
});
|
||||
program.getSourceFiles().some(function (sourceFile) {
|
||||
// Source file is a default library, or other dependency from another project, that should not be included in
|
||||
// our bundled output
|
||||
if (pathUtil.normalize(sourceFile.fileName).indexOf(baseDir + pathUtil.sep) !== 0) {
|
||||
return;
|
||||
}
|
||||
if (excludesMap[filenameToMid(pathUtil.normalize(sourceFile.fileName))]) {
|
||||
return;
|
||||
}
|
||||
sendMessage(` ${sourceFile.fileName}`);
|
||||
// Source file is already a declaration file so should does not need to be pre-processed by the emitter
|
||||
if (sourceFile.fileName.slice(-DTSLEN) === '.d.ts') {
|
||||
writeDeclaration(sourceFile, false);
|
||||
return;
|
||||
}
|
||||
// We can optionally output the main module if there's something to export.
|
||||
if (options.main && options.main === (options.prefix + filenameToMid(sourceFile.fileName.slice(baseDir.length, -3)))) {
|
||||
foundMain = true;
|
||||
ts.forEachChild(sourceFile, function (node) {
|
||||
mainExportDeclaration = mainExportDeclaration || isNodeKindExportDeclaration(node);
|
||||
mainExportAssignment = mainExportAssignment || isNodeKindExportAssignment(node);
|
||||
});
|
||||
}
|
||||
const emitOutput = program.emit(sourceFile, writeFile);
|
||||
if (emitOutput.emitSkipped || emitOutput.diagnostics.length > 0) {
|
||||
reject(getError(emitOutput.diagnostics
|
||||
.concat(program.getSemanticDiagnostics(sourceFile))
|
||||
.concat(program.getSyntacticDiagnostics(sourceFile))
|
||||
.concat(program.getDeclarationDiagnostics(sourceFile))));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (options.main && !foundMain) {
|
||||
throw new Error(`main module ${options.main} was not found`);
|
||||
}
|
||||
if (options.main) {
|
||||
output.write(`declare module '${options.name}' {` + eol + indent);
|
||||
if (compilerOptions.target >= ts.ScriptTarget.ES2015) {
|
||||
if (mainExportAssignment) {
|
||||
output.write(`export {default} from '${options.main}';` + eol + indent);
|
||||
}
|
||||
if (mainExportDeclaration) {
|
||||
output.write(`export * from '${options.main}';` + eol);
|
||||
}
|
||||
}
|
||||
else {
|
||||
output.write(`import main = require('${options.main}');` + eol + indent);
|
||||
output.write('export = main;' + eol);
|
||||
}
|
||||
output.write('}' + eol);
|
||||
sendMessage(`Aliased main module ${options.name} to ${options.main}`);
|
||||
}
|
||||
if (!options.stdout) {
|
||||
sendMessage(`output to "${options.out}"`);
|
||||
output.end();
|
||||
}
|
||||
});
|
||||
function writeDeclaration(declarationFile, isOutput) {
|
||||
// resolving is important for dealting with relative outDirs
|
||||
const filename = pathUtil.resolve(declarationFile.fileName);
|
||||
// use the outDir here, not the baseDir, because the declarationFiles are
|
||||
// outputs of the build process; baseDir points instead to the inputs.
|
||||
// However we have to account for .d.ts files in our inputs that this code
|
||||
// is also used for. Also if no outDir is used, the compiled code ends up
|
||||
// alongside the source, so use baseDir in that case too.
|
||||
const outputDir = (isOutput && Boolean(outDir)) ? pathUtil.resolve(outDir) : baseDir;
|
||||
const sourceModuleId = filenameToMid(filename.slice(outputDir.length + 1, -DTSLEN));
|
||||
const currentModuleId = filenameToMid(filename.slice(outputDir.length + 1, -DTSLEN));
|
||||
function resolveModuleImport(moduleId) {
|
||||
const isDeclaredExternalModule = declaredExternalModules.indexOf(moduleId) !== -1;
|
||||
let resolved;
|
||||
if (options.resolveModuleImport) {
|
||||
resolved = options.resolveModuleImport({
|
||||
importedModuleId: moduleId,
|
||||
currentModuleId: currentModuleId,
|
||||
isDeclaredExternalModule: isDeclaredExternalModule
|
||||
});
|
||||
}
|
||||
if (!resolved) {
|
||||
// resolve relative imports relative to the current module id.
|
||||
if (moduleId.charAt(0) === '.') {
|
||||
resolved = filenameToMid(pathUtil.join(pathUtil.dirname(sourceModuleId), moduleId));
|
||||
}
|
||||
else {
|
||||
resolved = moduleId;
|
||||
}
|
||||
// prefix the import with options.prefix, so that both non-relative imports
|
||||
// and relative imports end up prefixed with options.prefix. We only
|
||||
// do this when no resolveModuleImport function is given so that that
|
||||
// function has complete control of the imports that get outputed.
|
||||
// NOTE: we may want to revisit the isDeclaredExternalModule behavior.
|
||||
// discussion is on https://github.com/SitePen/dts-generator/pull/94
|
||||
// but currently there's no strong argument against this behavior.
|
||||
if (Boolean(options.prefix) && !isDeclaredExternalModule) {
|
||||
resolved = `${options.prefix}/${resolved}`;
|
||||
}
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
/* For some reason, SourceFile.externalModuleIndicator is missing from 1.6+, so having
|
||||
* to use a sledgehammer on the nut */
|
||||
if (declarationFile.externalModuleIndicator) {
|
||||
let resolvedModuleId = sourceModuleId;
|
||||
if (options.resolveModuleId) {
|
||||
const resolveModuleIdResult = options.resolveModuleId({
|
||||
currentModuleId: currentModuleId
|
||||
});
|
||||
if (resolveModuleIdResult) {
|
||||
resolvedModuleId = resolveModuleIdResult;
|
||||
}
|
||||
else if (options.prefix) {
|
||||
resolvedModuleId = `${options.prefix}/${resolvedModuleId}`;
|
||||
}
|
||||
}
|
||||
else if (options.prefix) {
|
||||
resolvedModuleId = `${options.prefix}/${resolvedModuleId}`;
|
||||
}
|
||||
output.write('declare module \'' + resolvedModuleId + '\' {' + eol + indent);
|
||||
const content = processTree(declarationFile, function (node) {
|
||||
if (isNodeKindExternalModuleReference(node)) {
|
||||
// TODO figure out if this branch is possible, and if so, write a test
|
||||
// that covers it.
|
||||
const expression = node.expression;
|
||||
// convert both relative and non-relative module names in import = require(...)
|
||||
// statements.
|
||||
const resolved = resolveModuleImport(expression.text);
|
||||
return ` require('${resolved}')`;
|
||||
}
|
||||
else if (node.kind === ts.SyntaxKind.DeclareKeyword) {
|
||||
return '';
|
||||
}
|
||||
else if (isNodeKindStringLiteral(node) && node.parent &&
|
||||
(isNodeKindExportDeclaration(node.parent) || isNodeKindImportDeclaration(node.parent))) {
|
||||
// This block of code is modifying the names of imported modules
|
||||
const text = node.text;
|
||||
const resolved = resolveModuleImport(text);
|
||||
if (resolved) {
|
||||
return ` '${resolved}'`;
|
||||
}
|
||||
}
|
||||
});
|
||||
output.write(content.replace(nonEmptyLineStart, '$&' + indent));
|
||||
output.write(eol + '}' + eol);
|
||||
}
|
||||
else {
|
||||
output.write(declarationFile.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.default = generate;
|
||||
});
|
||||
|
||||
const prelude = `declare type bool = boolean;
|
||||
declare type i8 = number;
|
||||
declare type i16 = number;
|
||||
declare type i32 = number;
|
||||
declare type isize = number;
|
||||
declare type u8 = number;
|
||||
declare type u16 = number;
|
||||
declare type u32 = number;
|
||||
declare type usize = number;
|
||||
declare type f32 = number;
|
||||
declare type f64 = number;
|
||||
declare module 'assemblyscript' {
|
||||
export * from 'assemblyscript/src/index';
|
||||
}
|
||||
`;
|
||||
|
||||
var path = require("path");
|
||||
var fs = require("fs");
|
||||
var stdout = fs.createWriteStream(path.resolve(__dirname, "..", "dist", "assemblyscript.d.ts"));
|
||||
stdout.write(prelude);
|
||||
stdout.write = (function(_write) {
|
||||
return function(...args) {
|
||||
if (typeof args[0] === "string") {
|
||||
args[0] = args[0].replace(/\/\/\/ <reference[^>]*>\r?\n/g, "");
|
||||
}
|
||||
return _write.apply(stdout, args);
|
||||
};
|
||||
})(stdout.write);
|
||||
|
||||
module.exports.default({
|
||||
project: path.resolve(__dirname, "..", "src"),
|
||||
prefix: "assemblyscript",
|
||||
exclude: [
|
||||
"glue/js/index.ts",
|
||||
"glue/js/node.d.ts"
|
||||
],
|
||||
verbose: true,
|
||||
sendMessage: console.log,
|
||||
stdout: stdout
|
||||
});
|
42
src/ast.ts
42
src/ast.ts
@ -67,6 +67,7 @@ export enum NodeKind {
|
||||
DO,
|
||||
EMPTY,
|
||||
EXPORT,
|
||||
EXPORTDEFAULT,
|
||||
EXPORTIMPORT,
|
||||
EXPRESSION,
|
||||
FOR,
|
||||
@ -687,6 +688,9 @@ export abstract class Node {
|
||||
range.source.normalizedPath
|
||||
);
|
||||
} else { // absolute
|
||||
if (!normalizedPath.startsWith(LIBRARY_PREFIX)) {
|
||||
normalizedPath = LIBRARY_PREFIX + normalizedPath;
|
||||
}
|
||||
stmt.normalizedPath = normalizedPath;
|
||||
}
|
||||
stmt.internalPath = mangleInternalPath(stmt.normalizedPath);
|
||||
@ -698,6 +702,16 @@ export abstract class Node {
|
||||
return stmt;
|
||||
}
|
||||
|
||||
static createExportDefaultStatement(
|
||||
declaration: DeclarationStatement,
|
||||
range: Range
|
||||
): ExportDefaultStatement {
|
||||
var stmt = new ExportDefaultStatement();
|
||||
stmt.declaration = declaration;
|
||||
stmt.range = range;
|
||||
return stmt;
|
||||
}
|
||||
|
||||
static createExportImportStatement(
|
||||
name: IdentifierExpression,
|
||||
externalName: IdentifierExpression,
|
||||
@ -782,10 +796,18 @@ export abstract class Node {
|
||||
stmt.declarations = null;
|
||||
stmt.namespaceName = identifier;
|
||||
stmt.path = path;
|
||||
stmt.normalizedPath = resolvePath(
|
||||
normalizePath(path.value),
|
||||
range.source.normalizedPath
|
||||
);
|
||||
var normalizedPath = normalizePath(path.value);
|
||||
if (path.value.startsWith(".")) {
|
||||
stmt.normalizedPath = resolvePath(
|
||||
normalizedPath,
|
||||
range.source.normalizedPath
|
||||
);
|
||||
} else {
|
||||
if (!normalizedPath.startsWith(LIBRARY_PREFIX)) {
|
||||
normalizedPath = LIBRARY_PREFIX + normalizedPath;
|
||||
}
|
||||
stmt.normalizedPath = normalizedPath;
|
||||
}
|
||||
stmt.internalPath = mangleInternalPath(stmt.normalizedPath);
|
||||
return stmt;
|
||||
}
|
||||
@ -1162,7 +1184,7 @@ export enum DecoratorKind {
|
||||
EXTERNAL,
|
||||
BUILTIN,
|
||||
LAZY,
|
||||
START
|
||||
UNSAFE
|
||||
}
|
||||
|
||||
/** Returns the kind of the specified decorator. Defaults to {@link DecoratorKind.CUSTOM}. */
|
||||
@ -1198,11 +1220,11 @@ export function decoratorNameToKind(name: Expression): DecoratorKind {
|
||||
}
|
||||
case CharCode.s: {
|
||||
if (nameStr == "sealed") return DecoratorKind.SEALED;
|
||||
if (nameStr == "start") return DecoratorKind.START;
|
||||
break;
|
||||
}
|
||||
case CharCode.u: {
|
||||
if (nameStr == "unmanaged") return DecoratorKind.UNMANAGED;
|
||||
if (nameStr == "unsafe") return DecoratorKind.UNSAFE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1762,6 +1784,14 @@ export class ExportStatement extends Statement {
|
||||
isDeclare: bool;
|
||||
}
|
||||
|
||||
/** Represents an `export default` statement. */
|
||||
export class ExportDefaultStatement extends Statement {
|
||||
kind = NodeKind.EXPORTDEFAULT;
|
||||
|
||||
/** Declaration being exported as default. */
|
||||
declaration: DeclarationStatement;
|
||||
}
|
||||
|
||||
/** Represents an expression that is used as a statement. */
|
||||
export class ExpressionStatement extends Statement {
|
||||
kind = NodeKind.EXPRESSION;
|
||||
|
1588
src/builtins.ts
1588
src/builtins.ts
File diff suppressed because it is too large
Load Diff
@ -75,6 +75,7 @@ export enum CommonFlags {
|
||||
|
||||
// Other
|
||||
|
||||
/** Is quoted. */
|
||||
QUOTED = 1 << 28
|
||||
}
|
||||
|
||||
@ -141,10 +142,6 @@ export namespace CommonSymbols {
|
||||
export const this_ = "this";
|
||||
export const super_ = "super";
|
||||
export const constructor = "constructor";
|
||||
}
|
||||
|
||||
/** Common standard library symbols. */
|
||||
export namespace LibrarySymbols {
|
||||
// constants
|
||||
export const ASC_TARGET = "ASC_TARGET";
|
||||
export const ASC_NO_TREESHAKING = "ASC_NO_TREESHAKING";
|
||||
@ -174,17 +171,41 @@ export namespace LibrarySymbols {
|
||||
export const V128 = "V128";
|
||||
export const String = "String";
|
||||
export const Array = "Array";
|
||||
export const FixedArray = "FixedArray";
|
||||
export const Set = "Set";
|
||||
export const Map = "Map";
|
||||
export const ArrayBufferView = "ArrayBufferView";
|
||||
export const ArrayBuffer = "ArrayBuffer";
|
||||
export const Math = "Math";
|
||||
export const Mathf = "Mathf";
|
||||
export const Int8Array = "Int8Array";
|
||||
export const Int16Array = "Int16Array";
|
||||
export const Int32Array = "Int32Array";
|
||||
export const Int64Array = "Int64Array";
|
||||
export const Uint8Array = "Uint8Array";
|
||||
export const Uint8ClampedArray = "Uint8ClampedArray";
|
||||
export const Uint16Array = "Uint16Array";
|
||||
export const Uint32Array = "Uint32Array";
|
||||
export const Uint64Array = "Uint64Array";
|
||||
export const Float32Array = "Float32Array";
|
||||
export const Float64Array = "Float64Array";
|
||||
// runtime
|
||||
export const memory = "memory";
|
||||
export const allocate = "allocate";
|
||||
export const abort = "abort";
|
||||
export const main = "main";
|
||||
// other
|
||||
export const length = "length";
|
||||
export const byteLength = "byteLength";
|
||||
export const pow = "pow";
|
||||
export const mod = "mod";
|
||||
export const alloc = "__alloc";
|
||||
export const realloc = "__realloc";
|
||||
export const free = "__free";
|
||||
export const retain = "__retain";
|
||||
export const release = "__release";
|
||||
export const collect = "__collect";
|
||||
export const typeinfo = "__typeinfo";
|
||||
export const instanceof_ = "__instanceof";
|
||||
export const visit = "__visit";
|
||||
export const allocArray = "__allocArray";
|
||||
}
|
||||
|
||||
// shared
|
||||
export { Feature } from "../std/assembly/shared/feature";
|
||||
export { Target } from "../std/assembly/shared/target";
|
||||
export { Typeinfo, TypeinfoFlags } from "../std/assembly/shared/typeinfo";
|
||||
|
12438
src/compiler.ts
12438
src/compiler.ts
File diff suppressed because it is too large
Load Diff
@ -30,9 +30,9 @@ import {
|
||||
getLoopBody,
|
||||
getBreakName,
|
||||
getBreakCondition,
|
||||
getGetLocalIndex,
|
||||
getSetLocalIndex,
|
||||
getSetLocalValue,
|
||||
getLocalGetIndex,
|
||||
getLocalSetIndex,
|
||||
getLocalSetValue,
|
||||
getLoadOffset,
|
||||
getLoadPtr,
|
||||
getStoreOffset,
|
||||
@ -173,20 +173,20 @@ export class Decompiler {
|
||||
case ExpressionId.CallIndirect: {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
case ExpressionId.GetLocal: {
|
||||
case ExpressionId.LocalGet: {
|
||||
this.push("$");
|
||||
this.push(getGetLocalIndex(expr).toString(10));
|
||||
this.push(getLocalGetIndex(expr).toString(10));
|
||||
return;
|
||||
}
|
||||
case ExpressionId.SetLocal: {
|
||||
case ExpressionId.LocalSet: {
|
||||
this.push("$");
|
||||
this.push(getSetLocalIndex(expr).toString(10));
|
||||
this.push(getLocalSetIndex(expr).toString(10));
|
||||
this.push(" = ");
|
||||
this.decompileExpression(getSetLocalValue(expr));
|
||||
this.decompileExpression(getLocalSetValue(expr));
|
||||
return;
|
||||
}
|
||||
case ExpressionId.GetGlobal:
|
||||
case ExpressionId.SetGlobal: {
|
||||
case ExpressionId.GlobalGet:
|
||||
case ExpressionId.GlobalSet: {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
case ExpressionId.Load: {
|
||||
@ -832,11 +832,11 @@ export class Decompiler {
|
||||
}
|
||||
case ExpressionId.Host: {
|
||||
switch (getHostOp(expr)) {
|
||||
case HostOp.CurrentMemory: {
|
||||
case HostOp.MemorySize: {
|
||||
this.push("memory.size()");
|
||||
return;
|
||||
}
|
||||
case HostOp.GrowMemory: {
|
||||
case HostOp.MemoryGrow: {
|
||||
this.push("memory.grow(");
|
||||
this.decompileExpression(getHostOperand(expr, 0));
|
||||
this.push(")");
|
||||
|
@ -3,7 +3,7 @@
|
||||
* @module definitions
|
||||
*//***/
|
||||
|
||||
import {
|
||||
import {
|
||||
CommonFlags
|
||||
} from "./common";
|
||||
|
||||
@ -23,7 +23,8 @@ import {
|
||||
ConstantValueKind,
|
||||
Interface,
|
||||
Property,
|
||||
PropertyPrototype
|
||||
PropertyPrototype,
|
||||
File
|
||||
} from "./program";
|
||||
|
||||
import {
|
||||
@ -42,10 +43,8 @@ abstract class ExportsWalker {
|
||||
program: Program;
|
||||
/** Whether to include private members */
|
||||
includePrivate: bool;
|
||||
/** Elements still to do. */
|
||||
todo: Element[] = [];
|
||||
/** Already seen elements. */
|
||||
seen: Set<Element> = new Set();
|
||||
seen: Map<Element,string> = new Map();
|
||||
|
||||
/** Constructs a new Element walker. */
|
||||
constructor(program: Program, includePrivate: bool = false) {
|
||||
@ -56,57 +55,66 @@ abstract class ExportsWalker {
|
||||
/** Walks all elements and calls the respective handlers. */
|
||||
walk(): void {
|
||||
for (let file of this.program.filesByName.values()) {
|
||||
let members = file.members;
|
||||
if (!members) continue;
|
||||
for (let member of members.values()) {
|
||||
// FIXME: doesn't honor the actual externally visible name
|
||||
this.visitElement(member);
|
||||
}
|
||||
if (file.source.isEntry) this.visitFile(file);
|
||||
}
|
||||
}
|
||||
|
||||
/** Visits all exported elements of a file. */
|
||||
visitFile(file: File): void {
|
||||
var members = file.exports;
|
||||
if (members) {
|
||||
for (let [name, member] of members) this.visitElement(name, member);
|
||||
}
|
||||
var exportsStar = file.exportsStar;
|
||||
if (exportsStar) {
|
||||
for (let exportStar of exportsStar) this.visitFile(exportStar);
|
||||
}
|
||||
var todo = this.todo;
|
||||
for (let i = 0; i < todo.length; ) this.visitElement(todo[i]);
|
||||
}
|
||||
|
||||
/** Visits an element.*/
|
||||
visitElement(element: Element): void {
|
||||
visitElement(name: string, element: Element): void {
|
||||
if (element.is(CommonFlags.PRIVATE) && !this.includePrivate) return;
|
||||
if (this.seen.has(element)) return;
|
||||
this.seen.add(element);
|
||||
var seen = this.seen;
|
||||
if (seen.has(element)) {
|
||||
this.visitAlias(name, element, <string>seen.get(element));
|
||||
return;
|
||||
}
|
||||
seen.set(element, name);
|
||||
switch (element.kind) {
|
||||
case ElementKind.GLOBAL: {
|
||||
if (element.is(CommonFlags.COMPILED)) this.visitGlobal(<Global>element);
|
||||
if (element.is(CommonFlags.COMPILED)) this.visitGlobal(name, <Global>element);
|
||||
break;
|
||||
}
|
||||
case ElementKind.ENUM: {
|
||||
if (element.is(CommonFlags.COMPILED)) this.visitEnum(<Enum>element);
|
||||
if (element.is(CommonFlags.COMPILED)) this.visitEnum(name, <Enum>element);
|
||||
break;
|
||||
}
|
||||
case ElementKind.FUNCTION_PROTOTYPE: {
|
||||
this.visitFunctionInstances(<FunctionPrototype>element);
|
||||
this.visitFunctionInstances(name, <FunctionPrototype>element);
|
||||
break;
|
||||
}
|
||||
case ElementKind.CLASS_PROTOTYPE: {
|
||||
this.visitClassInstances(<ClassPrototype>element);
|
||||
this.visitClassInstances(name, <ClassPrototype>element);
|
||||
break;
|
||||
}
|
||||
case ElementKind.FIELD: {
|
||||
if ((<Field>element).is(CommonFlags.COMPILED)) this.visitField(<Field>element);
|
||||
if ((<Field>element).is(CommonFlags.COMPILED)) this.visitField(name, <Field>element);
|
||||
break;
|
||||
}
|
||||
case ElementKind.PROPERTY_PROTOTYPE: {
|
||||
this.visitPropertyInstances(<PropertyPrototype>element);
|
||||
this.visitPropertyInstances(name, <PropertyPrototype>element);
|
||||
break;
|
||||
}
|
||||
case ElementKind.PROPERTY: {
|
||||
let prop = <Property>element;
|
||||
let getter = prop.getterInstance;
|
||||
if (getter) this.visitFunction(getter);
|
||||
if (getter) this.visitFunction(name, getter);
|
||||
let setter = prop.setterInstance;
|
||||
if (setter) this.visitFunction(setter);
|
||||
if (setter) this.visitFunction(name, setter);
|
||||
break;
|
||||
}
|
||||
case ElementKind.NAMESPACE: {
|
||||
if (hasCompiledMember(element)) this.visitNamespace(element);
|
||||
if (hasCompiledMember(element)) this.visitNamespace(name, element);
|
||||
break;
|
||||
}
|
||||
case ElementKind.TYPEDEFINITION: break;
|
||||
@ -114,25 +122,25 @@ abstract class ExportsWalker {
|
||||
}
|
||||
}
|
||||
|
||||
private visitFunctionInstances(element: FunctionPrototype): void {
|
||||
private visitFunctionInstances(name: string, element: FunctionPrototype): void {
|
||||
var instances = element.instances;
|
||||
if (instances) {
|
||||
for (let instance of instances.values()) {
|
||||
if (instance.is(CommonFlags.COMPILED)) this.visitFunction(<Function>instance);
|
||||
if (instance.is(CommonFlags.COMPILED)) this.visitFunction(name, <Function>instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private visitClassInstances(element: ClassPrototype): void {
|
||||
private visitClassInstances(name: string, element: ClassPrototype): void {
|
||||
var instances = element.instances;
|
||||
if (instances) {
|
||||
for (let instance of instances.values()) {
|
||||
if (instance.is(CommonFlags.COMPILED)) this.visitClass(<Class>instance);
|
||||
if (instance.is(CommonFlags.COMPILED)) this.visitClass(name, <Class>instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private visitPropertyInstances(element: PropertyPrototype): void {
|
||||
private visitPropertyInstances(name: string, element: PropertyPrototype): void {
|
||||
// var instances = element.instances;
|
||||
// if (instances) {
|
||||
// for (let instance of instances.values()) {
|
||||
@ -142,13 +150,14 @@ abstract class ExportsWalker {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
abstract visitGlobal(element: Global): void;
|
||||
abstract visitEnum(element: Enum): void;
|
||||
abstract visitFunction(element: Function): void;
|
||||
abstract visitClass(element: Class): void;
|
||||
abstract visitInterface(element: Interface): void;
|
||||
abstract visitField(element: Field): void;
|
||||
abstract visitNamespace(element: Element): void;
|
||||
abstract visitGlobal(name: string, element: Global): void;
|
||||
abstract visitEnum(name: string, element: Enum): void;
|
||||
abstract visitFunction(name: string, element: Function): void;
|
||||
abstract visitClass(name: string, element: Class): void;
|
||||
abstract visitInterface(name: string, element: Interface): void;
|
||||
abstract visitField(name: string, element: Field): void;
|
||||
abstract visitNamespace(name: string, element: Element): void;
|
||||
abstract visitAlias(name: string, element: Element, originalName: string): void;
|
||||
}
|
||||
|
||||
/** A WebIDL definitions builder. */
|
||||
@ -167,14 +176,14 @@ export class IDLBuilder extends ExportsWalker {
|
||||
super(program, includePrivate);
|
||||
}
|
||||
|
||||
visitGlobal(element: Global): void {
|
||||
visitGlobal(name: string, element: Global): void {
|
||||
var sb = this.sb;
|
||||
var isConst = element.is(CommonFlags.INLINED);
|
||||
indent(sb, this.indentLevel);
|
||||
if (isConst) sb.push("const ");
|
||||
sb.push(this.typeToString(element.type));
|
||||
sb.push(" ");
|
||||
sb.push(element.name);
|
||||
sb.push(name);
|
||||
if (isConst) {
|
||||
switch (element.constantValueKind) {
|
||||
case ConstantValueKind.INTEGER: {
|
||||
@ -193,11 +202,11 @@ export class IDLBuilder extends ExportsWalker {
|
||||
sb.push(";\n");
|
||||
}
|
||||
|
||||
visitEnum(element: Enum): void {
|
||||
visitEnum(name: string, element: Enum): void {
|
||||
var sb = this.sb;
|
||||
indent(sb, this.indentLevel++);
|
||||
sb.push("interface ");
|
||||
sb.push(element.name);
|
||||
sb.push(name);
|
||||
sb.push(" {\n");
|
||||
var members = element.members;
|
||||
if (members) {
|
||||
@ -218,20 +227,20 @@ export class IDLBuilder extends ExportsWalker {
|
||||
}
|
||||
}
|
||||
for (let member of members.values()) {
|
||||
if (member.kind != ElementKind.ENUMVALUE) this.visitElement(member);
|
||||
if (member.kind != ElementKind.ENUMVALUE) this.visitElement(member.name, member);
|
||||
}
|
||||
}
|
||||
indent(sb, --this.indentLevel);
|
||||
sb.push("}\n");
|
||||
}
|
||||
|
||||
visitFunction(element: Function): void {
|
||||
visitFunction(name: string, element: Function): void {
|
||||
var sb = this.sb;
|
||||
var signature = element.signature;
|
||||
indent(sb, this.indentLevel);
|
||||
sb.push(this.typeToString(signature.returnType));
|
||||
sb.push(" ");
|
||||
sb.push(element.name);
|
||||
sb.push(name);
|
||||
sb.push("(");
|
||||
var parameters = signature.parameterTypes;
|
||||
var numParameters = parameters.length;
|
||||
@ -250,45 +259,49 @@ export class IDLBuilder extends ExportsWalker {
|
||||
sb.push("interface ");
|
||||
sb.push(element.name);
|
||||
sb.push(" {\n");
|
||||
for (let member of members.values()) this.visitElement(member);
|
||||
for (let member of members.values()) this.visitElement(member.name, member);
|
||||
indent(sb, --this.indentLevel);
|
||||
sb.push("}\n");
|
||||
}
|
||||
}
|
||||
|
||||
visitClass(element: Class): void {
|
||||
visitClass(name: string, element: Class): void {
|
||||
var sb = this.sb;
|
||||
indent(sb, this.indentLevel++);
|
||||
sb.push("interface ");
|
||||
sb.push(element.name);
|
||||
sb.push(name);
|
||||
sb.push(" {\n");
|
||||
// TODO
|
||||
indent(sb, --this.indentLevel);
|
||||
sb.push("}\n");
|
||||
}
|
||||
|
||||
visitInterface(element: Interface): void {
|
||||
this.visitClass(element);
|
||||
visitInterface(name: string, element: Interface): void {
|
||||
this.visitClass(name, element);
|
||||
}
|
||||
|
||||
visitField(element: Field): void {
|
||||
visitField(name: string, element: Field): void {
|
||||
// TODO
|
||||
}
|
||||
|
||||
visitNamespace(element: Namespace): void {
|
||||
visitNamespace(name: string, element: Namespace): void {
|
||||
var sb = this.sb;
|
||||
indent(sb, this.indentLevel++);
|
||||
sb.push("interface ");
|
||||
sb.push(element.name);
|
||||
sb.push(name);
|
||||
sb.push(" {\n");
|
||||
var members = element.members;
|
||||
if (members) {
|
||||
for (let member of members.values()) this.visitElement(member);
|
||||
for (let member of members.values()) this.visitElement(member.name, member);
|
||||
}
|
||||
indent(sb, --this.indentLevel);
|
||||
sb.push("}\n");
|
||||
}
|
||||
|
||||
visitAlias(name: string, element: Element, originalName: string): void {
|
||||
// TODO
|
||||
}
|
||||
|
||||
typeToString(type: Type): string {
|
||||
switch (type.kind) {
|
||||
case TypeKind.I8: return "byte";
|
||||
@ -335,13 +348,14 @@ export class TSDBuilder extends ExportsWalker {
|
||||
|
||||
private sb: string[] = [];
|
||||
private indentLevel: i32 = 0;
|
||||
private unknown: Set<string> = new Set();
|
||||
|
||||
/** Constructs a new WebIDL builder. */
|
||||
constructor(program: Program, includePrivate: bool = false) {
|
||||
super(program, includePrivate);
|
||||
}
|
||||
|
||||
visitGlobal(element: Global): void {
|
||||
visitGlobal(name: string, element: Global): void {
|
||||
var sb = this.sb;
|
||||
var isConst = element.is(CommonFlags.INLINED);
|
||||
indent(sb, this.indentLevel);
|
||||
@ -349,21 +363,21 @@ export class TSDBuilder extends ExportsWalker {
|
||||
if (isConst) sb.push("static readonly ");
|
||||
else sb.push("static ");
|
||||
} else {
|
||||
if (isConst) sb.push("const ");
|
||||
else sb.push("var ");
|
||||
if (isConst) sb.push("export const ");
|
||||
else sb.push("export var ");
|
||||
}
|
||||
sb.push(element.name);
|
||||
sb.push(name);
|
||||
sb.push(": ");
|
||||
sb.push(this.typeToString(element.type));
|
||||
sb.push(";\n");
|
||||
this.visitNamespace(element);
|
||||
this.visitNamespace(name, element);
|
||||
}
|
||||
|
||||
visitEnum(element: Enum): void {
|
||||
visitEnum(name: string, element: Enum): void {
|
||||
var sb = this.sb;
|
||||
indent(sb, this.indentLevel++);
|
||||
sb.push("enum ");
|
||||
sb.push(element.name);
|
||||
sb.push("export enum ");
|
||||
sb.push(name);
|
||||
sb.push(" {\n");
|
||||
var members = element.members;
|
||||
if (members) {
|
||||
@ -381,13 +395,13 @@ export class TSDBuilder extends ExportsWalker {
|
||||
--numMembers;
|
||||
}
|
||||
}
|
||||
if (numMembers) this.visitNamespace(element);
|
||||
if (numMembers) this.visitNamespace(name, element);
|
||||
}
|
||||
indent(sb, --this.indentLevel);
|
||||
sb.push("}\n");
|
||||
}
|
||||
|
||||
visitFunction(element: Function): void {
|
||||
visitFunction(name: string, element: Function): void {
|
||||
if (element.isAny(CommonFlags.PRIVATE | CommonFlags.SET)) return;
|
||||
var sb = this.sb;
|
||||
var signature = element.signature;
|
||||
@ -395,14 +409,15 @@ export class TSDBuilder extends ExportsWalker {
|
||||
if (element.is(CommonFlags.PROTECTED)) sb.push("protected ");
|
||||
if (element.is(CommonFlags.STATIC)) sb.push("static ");
|
||||
if (element.is(CommonFlags.GET)) {
|
||||
sb.push(element.identifierNode.text); // 'get:funcName' internally
|
||||
sb.push(": ");
|
||||
sb.push("get ");
|
||||
sb.push(name); // 'get:funcName' internally
|
||||
sb.push("(): ");
|
||||
sb.push(this.typeToString(signature.returnType));
|
||||
sb.push(";\n");
|
||||
return;
|
||||
} else {
|
||||
if (!element.isAny(CommonFlags.STATIC | CommonFlags.INSTANCE)) sb.push("function ");
|
||||
sb.push(element.name);
|
||||
if (!element.isAny(CommonFlags.STATIC | CommonFlags.INSTANCE)) sb.push("export function ");
|
||||
sb.push(name);
|
||||
}
|
||||
sb.push("(");
|
||||
var parameters = signature.parameterTypes;
|
||||
@ -422,73 +437,79 @@ export class TSDBuilder extends ExportsWalker {
|
||||
sb.push(this.typeToString(signature.returnType));
|
||||
}
|
||||
sb.push(";\n");
|
||||
this.visitNamespace(element);
|
||||
this.visitNamespace(name, element);
|
||||
}
|
||||
|
||||
visitClass(element: Class): void {
|
||||
visitClass(name: string, element: Class): void {
|
||||
var sb = this.sb;
|
||||
var isInterface = element.kind == ElementKind.INTERFACE;
|
||||
indent(sb, this.indentLevel++);
|
||||
if (isInterface) {
|
||||
sb.push("interface ");
|
||||
sb.push("export interface ");
|
||||
} else {
|
||||
if (element.is(CommonFlags.ABSTRACT)) sb.push("abstract ");
|
||||
sb.push("class ");
|
||||
}
|
||||
sb.push(element.name);
|
||||
var base = element.base;
|
||||
if (base && base.is(CommonFlags.COMPILED | CommonFlags.MODULE_EXPORT)) {
|
||||
sb.push(" extends ");
|
||||
sb.push(base.name); // TODO: fqn
|
||||
sb.push("export class ");
|
||||
}
|
||||
sb.push(name);
|
||||
// var base = element.base;
|
||||
// if (base && base.is(CommonFlags.COMPILED | CommonFlags.MODULE_EXPORT)) {
|
||||
// sb.push(" extends ");
|
||||
// sb.push(base.name); // TODO: fqn
|
||||
// }
|
||||
sb.push(" {\n");
|
||||
var members = element.parent.members; // static
|
||||
if (members) {
|
||||
for (let member of members.values()) {
|
||||
this.visitElement(member);
|
||||
}
|
||||
var staticMembers = element.prototype.members;
|
||||
if (staticMembers) {
|
||||
for (let member of staticMembers.values()) this.visitElement(member.name, member);
|
||||
}
|
||||
var ctor = element.constructorInstance;
|
||||
if (ctor) this.visitFunction(ctor);
|
||||
members = element.members; // instance
|
||||
if (members) {
|
||||
for (let member of members.values()) this.visitElement(member);
|
||||
var instanceMembers = element.members;
|
||||
if (instanceMembers) {
|
||||
for (let member of instanceMembers.values()) this.visitElement(member.name, member);
|
||||
}
|
||||
indent(sb, --this.indentLevel);
|
||||
sb.push("}\n");
|
||||
}
|
||||
|
||||
visitInterface(element: Interface): void {
|
||||
this.visitClass(element);
|
||||
visitInterface(name: string, element: Interface): void {
|
||||
this.visitClass(name, element);
|
||||
}
|
||||
|
||||
visitField(element: Field): void {
|
||||
visitField(name: string, element: Field): void {
|
||||
if (element.is(CommonFlags.PRIVATE)) return;
|
||||
var sb = this.sb;
|
||||
indent(sb, this.indentLevel);
|
||||
if (element.is(CommonFlags.PROTECTED)) sb.push("protected ");
|
||||
if (element.is(CommonFlags.STATIC)) sb.push("static ");
|
||||
if (element.is(CommonFlags.READONLY)) sb.push("readonly ");
|
||||
sb.push(element.name);
|
||||
sb.push(name);
|
||||
sb.push(": ");
|
||||
sb.push(this.typeToString(element.type));
|
||||
sb.push(";\n");
|
||||
}
|
||||
|
||||
visitNamespace(element: Element): void {
|
||||
visitNamespace(name: string, element: Element): void {
|
||||
var members = element.members;
|
||||
if (members && members.size) {
|
||||
let sb = this.sb;
|
||||
indent(sb, this.indentLevel++);
|
||||
sb.push("namespace ");
|
||||
sb.push(element.name);
|
||||
sb.push("export namespace ");
|
||||
sb.push(name);
|
||||
sb.push(" {\n");
|
||||
for (let member of members.values()) this.visitElement(member);
|
||||
for (let member of members.values()) this.visitElement(member.name, member);
|
||||
indent(sb, --this.indentLevel);
|
||||
sb.push("}\n");
|
||||
}
|
||||
}
|
||||
|
||||
visitAlias(name: string, element: Element, originalName: string): void {
|
||||
var sb = this.sb;
|
||||
indent(sb, this.indentLevel);
|
||||
sb.push("export const ");
|
||||
sb.push(name);
|
||||
sb.push(" = typeof ");
|
||||
sb.push(originalName);
|
||||
sb.push(";\n");
|
||||
}
|
||||
|
||||
typeToString(type: Type): string {
|
||||
switch (type.kind) {
|
||||
case TypeKind.I8: return "i8";
|
||||
@ -510,7 +531,7 @@ export class TSDBuilder extends ExportsWalker {
|
||||
case TypeKind.VOID: return "void";
|
||||
default: {
|
||||
assert(false);
|
||||
return "";
|
||||
return "any";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ export enum DiagnosticCode {
|
||||
Type_0_cannot_be_reinterpreted_as_type_1 = 203,
|
||||
Basic_type_0_cannot_be_nullable = 204,
|
||||
Cannot_export_a_mutable_global = 205,
|
||||
Compiling_constant_with_non_constant_initializer_as_mutable = 206,
|
||||
Mutable_value_cannot_be_inlined = 206,
|
||||
Unmanaged_classes_cannot_extend_managed_classes_and_vice_versa = 207,
|
||||
Unmanaged_classes_cannot_implement_interfaces = 208,
|
||||
Invalid_regular_expression_flags = 209,
|
||||
@ -24,7 +24,7 @@ export enum DiagnosticCode {
|
||||
Class_0_is_sealed_and_cannot_be_extended = 211,
|
||||
Decorator_0_is_not_valid_here = 212,
|
||||
Duplicate_decorator = 213,
|
||||
An_allocator_must_be_declared_to_allocate_memory_Try_importing_allocator_arena_or_allocator_tlsf = 214,
|
||||
An_allocator_must_be_present_to_use_0 = 214,
|
||||
Optional_parameter_must_have_an_initializer = 215,
|
||||
Constructor_of_class_0_must_not_require_any_arguments = 216,
|
||||
Function_0_cannot_be_inlined_into_itself = 217,
|
||||
@ -34,6 +34,8 @@ export enum DiagnosticCode {
|
||||
Module_cannot_have_multiple_start_functions = 221,
|
||||
_0_must_be_a_value_between_1_and_2_inclusive = 222,
|
||||
_0_must_be_a_power_of_two = 223,
|
||||
TODO_Cannot_inline_inferred_calls_and_specific_internals_yet = 224,
|
||||
Expression_is_never_null = 225,
|
||||
Unterminated_string_literal = 1002,
|
||||
Identifier_expected = 1003,
|
||||
_0_expected = 1005,
|
||||
@ -64,6 +66,7 @@ export enum DiagnosticCode {
|
||||
Type_expected = 1110,
|
||||
A_default_clause_cannot_appear_more_than_once_in_a_switch_statement = 1113,
|
||||
Duplicate_label_0 = 1114,
|
||||
An_export_assignment_cannot_have_modifiers = 1120,
|
||||
Octal_literals_are_not_allowed_in_strict_mode = 1121,
|
||||
Digit_expected = 1124,
|
||||
Hexadecimal_digit_expected = 1125,
|
||||
@ -120,6 +123,7 @@ export enum DiagnosticCode {
|
||||
The_0_operator_cannot_be_applied_to_type_1 = 2469,
|
||||
In_const_enum_declarations_member_initializer_must_be_constant_expression = 2474,
|
||||
Export_declaration_conflicts_with_exported_declaration_of_0 = 2484,
|
||||
Object_is_possibly_null = 2531,
|
||||
Cannot_assign_to_0_because_it_is_a_constant_or_a_read_only_property = 2540,
|
||||
The_target_of_an_assignment_must_be_a_variable_or_a_property_access = 2541,
|
||||
Index_signature_in_type_0_only_permits_reading = 2542,
|
||||
@ -151,7 +155,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||
case 203: return "Type '{0}' cannot be reinterpreted as type '{1}'.";
|
||||
case 204: return "Basic type '{0}' cannot be nullable.";
|
||||
case 205: return "Cannot export a mutable global.";
|
||||
case 206: return "Compiling constant with non-constant initializer as mutable.";
|
||||
case 206: return "Mutable value cannot be inlined.";
|
||||
case 207: return "Unmanaged classes cannot extend managed classes and vice-versa.";
|
||||
case 208: return "Unmanaged classes cannot implement interfaces.";
|
||||
case 209: return "Invalid regular expression flags.";
|
||||
@ -159,7 +163,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||
case 211: return "Class '{0}' is sealed and cannot be extended.";
|
||||
case 212: return "Decorator '{0}' is not valid here.";
|
||||
case 213: return "Duplicate decorator.";
|
||||
case 214: return "An allocator must be declared to allocate memory. Try importing allocator/arena or allocator/tlsf.";
|
||||
case 214: return "An allocator must be present to use '{0}'.";
|
||||
case 215: return "Optional parameter must have an initializer.";
|
||||
case 216: return "Constructor of class '{0}' must not require any arguments.";
|
||||
case 217: return "Function '{0}' cannot be inlined into itself.";
|
||||
@ -169,6 +173,8 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||
case 221: return "Module cannot have multiple start functions.";
|
||||
case 222: return "'{0}' must be a value between '{1}' and '{2}' inclusive.";
|
||||
case 223: return "'{0}' must be a power of two.";
|
||||
case 224: return "TODO: Cannot inline inferred calls and specific internals yet.";
|
||||
case 225: return "Expression is never 'null'.";
|
||||
case 1002: return "Unterminated string literal.";
|
||||
case 1003: return "Identifier expected.";
|
||||
case 1005: return "'{0}' expected.";
|
||||
@ -199,6 +205,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||
case 1110: return "Type expected.";
|
||||
case 1113: return "A 'default' clause cannot appear more than once in a 'switch' statement.";
|
||||
case 1114: return "Duplicate label '{0}'.";
|
||||
case 1120: return "An export assignment cannot have modifiers.";
|
||||
case 1121: return "Octal literals are not allowed in strict mode.";
|
||||
case 1124: return "Digit expected.";
|
||||
case 1125: return "Hexadecimal digit expected.";
|
||||
@ -255,6 +262,7 @@ export function diagnosticCodeToString(code: DiagnosticCode): string {
|
||||
case 2469: return "The '{0}' operator cannot be applied to type '{1}'.";
|
||||
case 2474: return "In 'const' enum declarations member initializer must be constant expression.";
|
||||
case 2484: return "Export declaration conflicts with exported declaration of '{0}'.";
|
||||
case 2531: return "Object is possibly 'null'.";
|
||||
case 2540: return "Cannot assign to '{0}' because it is a constant or a read-only property.";
|
||||
case 2541: return "The target of an assignment must be a variable or a property access.";
|
||||
case 2542: return "Index signature in type '{0}' only permits reading.";
|
||||
|
@ -8,7 +8,7 @@
|
||||
"Type '{0}' cannot be reinterpreted as type '{1}'.": 203,
|
||||
"Basic type '{0}' cannot be nullable.": 204,
|
||||
"Cannot export a mutable global.": 205,
|
||||
"Compiling constant with non-constant initializer as mutable.": 206,
|
||||
"Mutable value cannot be inlined.": 206,
|
||||
"Unmanaged classes cannot extend managed classes and vice-versa.": 207,
|
||||
"Unmanaged classes cannot implement interfaces.": 208,
|
||||
"Invalid regular expression flags.": 209,
|
||||
@ -16,7 +16,7 @@
|
||||
"Class '{0}' is sealed and cannot be extended.": 211,
|
||||
"Decorator '{0}' is not valid here.": 212,
|
||||
"Duplicate decorator.": 213,
|
||||
"An allocator must be declared to allocate memory. Try importing allocator/arena or allocator/tlsf.": 214,
|
||||
"An allocator must be present to use '{0}'.": 214,
|
||||
"Optional parameter must have an initializer.": 215,
|
||||
"Constructor of class '{0}' must not require any arguments.": 216,
|
||||
"Function '{0}' cannot be inlined into itself.": 217,
|
||||
@ -26,6 +26,8 @@
|
||||
"Module cannot have multiple start functions.": 221,
|
||||
"'{0}' must be a value between '{1}' and '{2}' inclusive.": 222,
|
||||
"'{0}' must be a power of two.": 223,
|
||||
"TODO: Cannot inline inferred calls and specific internals yet.": 224,
|
||||
"Expression is never 'null'.": 225,
|
||||
|
||||
"Unterminated string literal.": 1002,
|
||||
"Identifier expected.": 1003,
|
||||
@ -57,6 +59,7 @@
|
||||
"Type expected.": 1110,
|
||||
"A 'default' clause cannot appear more than once in a 'switch' statement.": 1113,
|
||||
"Duplicate label '{0}'.": 1114,
|
||||
"An export assignment cannot have modifiers.": 1120,
|
||||
"Octal literals are not allowed in strict mode.": 1121,
|
||||
"Digit expected.": 1124,
|
||||
"Hexadecimal digit expected.": 1125,
|
||||
@ -114,6 +117,7 @@
|
||||
"The '{0}' operator cannot be applied to type '{1}'.": 2469,
|
||||
"In 'const' enum declarations member initializer must be constant expression.": 2474,
|
||||
"Export declaration conflicts with exported declaration of '{0}'.": 2484,
|
||||
"Object is possibly 'null'.": 2531,
|
||||
"Cannot assign to '{0}' because it is a constant or a read-only property.": 2540,
|
||||
"The target of an assignment must be a variable or a property access.": 2541,
|
||||
"Index signature in type '{0}' only permits reading.": 2542,
|
||||
|
@ -14,7 +14,7 @@ import {
|
||||
} from "./diagnosticMessages.generated";
|
||||
|
||||
import {
|
||||
isLineBreak
|
||||
isLineBreak, CharCode
|
||||
} from "./util";
|
||||
|
||||
export {
|
||||
@ -244,7 +244,13 @@ export function formatDiagnosticContext(range: Range, useColors: bool = false):
|
||||
if (range.start == range.end) {
|
||||
sb.push("^");
|
||||
} else {
|
||||
while (start++ < range.end) sb.push("~");
|
||||
while (start++ < range.end) {
|
||||
if (isLineBreak(text.charCodeAt(start))) {
|
||||
sb.push(start == range.start + 1 ? "^" : "~");
|
||||
break;
|
||||
}
|
||||
sb.push("~");
|
||||
}
|
||||
}
|
||||
if (useColors) sb.push(COLOR_RESET);
|
||||
return sb.join("");
|
||||
|
@ -52,6 +52,7 @@ import {
|
||||
EmptyStatement,
|
||||
ExportImportStatement,
|
||||
ExportStatement,
|
||||
ExportDefaultStatement,
|
||||
ExpressionStatement,
|
||||
ForStatement,
|
||||
IfStatement,
|
||||
@ -230,6 +231,10 @@ export class ASTBuilder {
|
||||
this.visitExportStatement(<ExportStatement>node);
|
||||
break;
|
||||
}
|
||||
case NodeKind.EXPORTDEFAULT: {
|
||||
this.visitExportDefaultStatement(<ExportDefaultStatement>node);
|
||||
break;
|
||||
}
|
||||
case NodeKind.EXPORTIMPORT: {
|
||||
this.visitExportImportStatement(<ExportImportStatement>node);
|
||||
break;
|
||||
@ -851,15 +856,19 @@ export class ASTBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
visitClassDeclaration(node: ClassDeclaration): void {
|
||||
visitClassDeclaration(node: ClassDeclaration, isDefault: bool = false): void {
|
||||
var decorators = node.decorators;
|
||||
if (decorators) {
|
||||
for (let i = 0, k = decorators.length; i < k; ++i) {
|
||||
this.serializeDecorator(decorators[i]);
|
||||
}
|
||||
}
|
||||
this.serializeExternalModifiers(node);
|
||||
var sb = this.sb;
|
||||
if (isDefault) {
|
||||
sb.push("export default ");
|
||||
} else {
|
||||
this.serializeExternalModifiers(node);
|
||||
}
|
||||
if (node.is(CommonFlags.ABSTRACT)) sb.push("abstract ");
|
||||
if (node.name.text.length) {
|
||||
sb.push("class ");
|
||||
@ -931,9 +940,13 @@ export class ASTBuilder {
|
||||
visitEmptyStatement(node: EmptyStatement): void {
|
||||
}
|
||||
|
||||
visitEnumDeclaration(node: EnumDeclaration): void {
|
||||
visitEnumDeclaration(node: EnumDeclaration, isDefault: bool = false): void {
|
||||
var sb = this.sb;
|
||||
this.serializeExternalModifiers(node);
|
||||
if (isDefault) {
|
||||
sb.push("export default ");
|
||||
} else {
|
||||
this.serializeExternalModifiers(node);
|
||||
}
|
||||
if (node.is(CommonFlags.CONST)) sb.push("const ");
|
||||
sb.push("enum ");
|
||||
this.visitIdentifierExpression(node.name);
|
||||
@ -1011,6 +1024,33 @@ export class ASTBuilder {
|
||||
sb.push(";");
|
||||
}
|
||||
|
||||
visitExportDefaultStatement(node: ExportDefaultStatement): void {
|
||||
var declaration = node.declaration;
|
||||
switch (declaration.kind) {
|
||||
case NodeKind.ENUMDECLARATION: {
|
||||
this.visitEnumDeclaration(<EnumDeclaration>declaration, true);
|
||||
break;
|
||||
}
|
||||
case NodeKind.FUNCTIONDECLARATION: {
|
||||
this.visitFunctionDeclaration(<FunctionDeclaration>declaration, true);
|
||||
break;
|
||||
}
|
||||
case NodeKind.CLASSDECLARATION: {
|
||||
this.visitClassDeclaration(<ClassDeclaration>declaration, true);
|
||||
break;
|
||||
}
|
||||
case NodeKind.INTERFACEDECLARATION: {
|
||||
this.visitInterfaceDeclaration(<InterfaceDeclaration>declaration, true);
|
||||
break;
|
||||
}
|
||||
case NodeKind.NAMESPACEDECLARATION: {
|
||||
this.visitNamespaceDeclaration(<NamespaceDeclaration>declaration, true);
|
||||
break;
|
||||
}
|
||||
default: assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
visitExpressionStatement(node: ExpressionStatement): void {
|
||||
this.visitNode(node.expression);
|
||||
}
|
||||
@ -1065,7 +1105,7 @@ export class ASTBuilder {
|
||||
this.visitNode(node.statement);
|
||||
}
|
||||
|
||||
visitFunctionDeclaration(node: FunctionDeclaration): void {
|
||||
visitFunctionDeclaration(node: FunctionDeclaration, isDefault: bool = false): void {
|
||||
var sb = this.sb;
|
||||
var decorators = node.decorators;
|
||||
if (decorators) {
|
||||
@ -1073,8 +1113,12 @@ export class ASTBuilder {
|
||||
this.serializeDecorator(decorators[i]);
|
||||
}
|
||||
}
|
||||
this.serializeExternalModifiers(node);
|
||||
this.serializeAccessModifiers(node);
|
||||
if (isDefault) {
|
||||
sb.push("export default ");
|
||||
} else {
|
||||
this.serializeExternalModifiers(node);
|
||||
this.serializeAccessModifiers(node);
|
||||
}
|
||||
if (node.name.text.length) {
|
||||
sb.push("function ");
|
||||
} else {
|
||||
@ -1230,15 +1274,19 @@ export class ASTBuilder {
|
||||
this.visitTypeNode(node.valueType);
|
||||
}
|
||||
|
||||
visitInterfaceDeclaration(node: InterfaceDeclaration): void {
|
||||
visitInterfaceDeclaration(node: InterfaceDeclaration, isDefault: bool = false): void {
|
||||
var decorators = node.decorators;
|
||||
if (decorators) {
|
||||
for (let i = 0, k = decorators.length; i < k; ++i) {
|
||||
this.serializeDecorator(decorators[i]);
|
||||
}
|
||||
}
|
||||
this.serializeExternalModifiers(node);
|
||||
var sb = this.sb;
|
||||
if (isDefault) {
|
||||
sb.push("export default ");
|
||||
} else {
|
||||
this.serializeExternalModifiers(node);
|
||||
}
|
||||
sb.push("interface ");
|
||||
this.visitIdentifierExpression(node.name);
|
||||
var typeParameters = node.typeParameters;
|
||||
@ -1284,15 +1332,19 @@ export class ASTBuilder {
|
||||
this.visitFunctionCommon(node);
|
||||
}
|
||||
|
||||
visitNamespaceDeclaration(node: NamespaceDeclaration): void {
|
||||
visitNamespaceDeclaration(node: NamespaceDeclaration, isDefault: bool = false): void {
|
||||
var decorators = node.decorators;
|
||||
if (decorators) {
|
||||
for (let i = 0, k = decorators.length; i < k; ++i) {
|
||||
this.serializeDecorator(decorators[i]);
|
||||
}
|
||||
}
|
||||
this.serializeExternalModifiers(node);
|
||||
var sb = this.sb;
|
||||
if (isDefault) {
|
||||
sb.push("export default ");
|
||||
} else {
|
||||
this.serializeExternalModifiers(node);
|
||||
}
|
||||
sb.push("namespace ");
|
||||
this.visitIdentifierExpression(node.name);
|
||||
var members = node.members;
|
||||
|
627
src/flow.ts
627
src/flow.ts
@ -23,10 +23,10 @@ import {
|
||||
ExpressionRef,
|
||||
|
||||
getExpressionId,
|
||||
getGetLocalIndex,
|
||||
isTeeLocal,
|
||||
getSetLocalValue,
|
||||
getGetGlobalName,
|
||||
getLocalGetIndex,
|
||||
isLocalTee,
|
||||
getLocalSetValue,
|
||||
getGlobalGetName,
|
||||
getBinaryOp,
|
||||
BinaryOp,
|
||||
getBinaryLeft,
|
||||
@ -47,7 +47,13 @@ import {
|
||||
getIfFalse,
|
||||
getSelectThen,
|
||||
getSelectElse,
|
||||
getCallTarget
|
||||
getCallTarget,
|
||||
getLocalSetIndex,
|
||||
getIfCondition,
|
||||
getConstValueI64High,
|
||||
getUnaryValue,
|
||||
getCallOperand,
|
||||
traverse
|
||||
} from "./module";
|
||||
|
||||
import {
|
||||
@ -62,11 +68,6 @@ import {
|
||||
Node
|
||||
} from "./ast";
|
||||
|
||||
import {
|
||||
bitsetIs,
|
||||
bitsetSet
|
||||
} from "./util";
|
||||
|
||||
/** Control flow flags indicating specific conditions. */
|
||||
export const enum FlowFlags {
|
||||
/** No specific conditions. */
|
||||
@ -78,36 +79,38 @@ export const enum FlowFlags {
|
||||
RETURNS = 1 << 0,
|
||||
/** This flow returns a wrapped value. */
|
||||
RETURNS_WRAPPED = 1 << 1,
|
||||
/** This flow returns a non-null value. */
|
||||
RETURNS_NONNULL = 1 << 2,
|
||||
/** This flow throws. */
|
||||
THROWS = 1 << 2,
|
||||
THROWS = 1 << 3,
|
||||
/** This flow breaks. */
|
||||
BREAKS = 1 << 3,
|
||||
BREAKS = 1 << 4,
|
||||
/** This flow continues. */
|
||||
CONTINUES = 1 << 4,
|
||||
CONTINUES = 1 << 5,
|
||||
/** This flow allocates. Constructors only. */
|
||||
ALLOCATES = 1 << 5,
|
||||
ALLOCATES = 1 << 6,
|
||||
/** This flow calls super. Constructors only. */
|
||||
CALLS_SUPER = 1 << 6,
|
||||
CALLS_SUPER = 1 << 7,
|
||||
|
||||
// conditional
|
||||
|
||||
/** This flow conditionally returns in a child flow. */
|
||||
CONDITIONALLY_RETURNS = 1 << 7,
|
||||
CONDITIONALLY_RETURNS = 1 << 8,
|
||||
/** This flow conditionally throws in a child flow. */
|
||||
CONDITIONALLY_THROWS = 1 << 8,
|
||||
CONDITIONALLY_THROWS = 1 << 9,
|
||||
/** This flow conditionally breaks in a child flow. */
|
||||
CONDITIONALLY_BREAKS = 1 << 9,
|
||||
CONDITIONALLY_BREAKS = 1 << 10,
|
||||
/** This flow conditionally continues in a child flow. */
|
||||
CONDITIONALLY_CONTINUES = 1 << 10,
|
||||
CONDITIONALLY_CONTINUES = 1 << 11,
|
||||
/** This flow conditionally allocates in a child flow. Constructors only. */
|
||||
CONDITIONALLY_ALLOCATES = 1 << 11,
|
||||
CONDITIONALLY_ALLOCATES = 1 << 12,
|
||||
|
||||
// special
|
||||
|
||||
/** This is an inlining flow. */
|
||||
INLINE_CONTEXT = 1 << 12,
|
||||
INLINE_CONTEXT = 1 << 13,
|
||||
/** This is a flow with explicitly disabled bounds checking. */
|
||||
UNCHECKED_CONTEXT = 1 << 13,
|
||||
UNCHECKED_CONTEXT = 1 << 14,
|
||||
|
||||
// masks
|
||||
|
||||
@ -120,6 +123,7 @@ export const enum FlowFlags {
|
||||
/** Any categorical flag. */
|
||||
ANY_CATEGORICAL = FlowFlags.RETURNS
|
||||
| FlowFlags.RETURNS_WRAPPED
|
||||
| FlowFlags.RETURNS_NONNULL
|
||||
| FlowFlags.THROWS
|
||||
| FlowFlags.BREAKS
|
||||
| FlowFlags.CONTINUES
|
||||
@ -134,6 +138,79 @@ export const enum FlowFlags {
|
||||
| FlowFlags.CONDITIONALLY_ALLOCATES
|
||||
}
|
||||
|
||||
/** Flags indicating the current state of a local. */
|
||||
export enum LocalFlags {
|
||||
/** No specific conditions. */
|
||||
NONE = 0,
|
||||
|
||||
/** Local is constant. */
|
||||
CONSTANT = 1 << 0,
|
||||
/** Local is properly wrapped. Relevant for small integers. */
|
||||
WRAPPED = 1 << 1,
|
||||
/** Local is non-null. */
|
||||
NONNULL = 1 << 2,
|
||||
/** Local is read from. */
|
||||
READFROM = 1 << 3,
|
||||
/** Local is written to. */
|
||||
WRITTENTO = 1 << 4,
|
||||
/** Local is retained. */
|
||||
RETAINED = 1 << 5,
|
||||
|
||||
/** Local is conditionally read from. */
|
||||
CONDITIONALLY_READFROM = 1 << 6,
|
||||
/** Local is conditionally written to. */
|
||||
CONDITIONALLY_WRITTENTO = 1 << 7,
|
||||
/** Local must be conditionally retained. */
|
||||
CONDITIONALLY_RETAINED = 1 << 8,
|
||||
|
||||
/** Any categorical flag. */
|
||||
ANY_CATEGORICAL = CONSTANT
|
||||
| WRAPPED
|
||||
| NONNULL
|
||||
| READFROM
|
||||
| WRITTENTO
|
||||
| RETAINED,
|
||||
|
||||
/** Any conditional flag. */
|
||||
ANY_CONDITIONAL = RETAINED
|
||||
| CONDITIONALLY_READFROM
|
||||
| CONDITIONALLY_WRITTENTO
|
||||
| CONDITIONALLY_RETAINED,
|
||||
|
||||
/** Any retained flag. */
|
||||
ANY_RETAINED = RETAINED
|
||||
| CONDITIONALLY_RETAINED
|
||||
}
|
||||
export namespace LocalFlags {
|
||||
export function join(left: LocalFlags, right: LocalFlags): LocalFlags {
|
||||
return ((left & LocalFlags.ANY_CATEGORICAL) & (right & LocalFlags.ANY_CATEGORICAL))
|
||||
| (left & LocalFlags.ANY_CONDITIONAL) | (right & LocalFlags.ANY_CONDITIONAL);
|
||||
}
|
||||
}
|
||||
|
||||
/** Flags indicating the current state of a field. */
|
||||
export enum FieldFlags {
|
||||
/** No specific conditions. */
|
||||
NONE = 0,
|
||||
|
||||
/** Field is initialized. Relevant in constructors. */
|
||||
INITIALIZED = 1 << 0,
|
||||
/** Field is conditionally initialized. Relevant in constructors. */
|
||||
CONDITIONALLY_INITIALIZED = 1 << 1,
|
||||
|
||||
/** Any categorical flag. */
|
||||
ANY_CATEGORICAL = INITIALIZED,
|
||||
|
||||
/** Any conditional flag. */
|
||||
ANY_CONDITIONAL = CONDITIONALLY_INITIALIZED
|
||||
}
|
||||
export namespace FieldFlags {
|
||||
export function join(left: FieldFlags, right: FieldFlags): FieldFlags {
|
||||
return ((left & FieldFlags.ANY_CATEGORICAL) & (right & FieldFlags.ANY_CATEGORICAL))
|
||||
| (left & FieldFlags.ANY_CONDITIONAL) | (right & FieldFlags.ANY_CONDITIONAL);
|
||||
}
|
||||
}
|
||||
|
||||
/** A control flow evaluator. */
|
||||
export class Flow {
|
||||
|
||||
@ -153,10 +230,10 @@ export class Flow {
|
||||
contextualTypeArguments: Map<string,Type> | null;
|
||||
/** Scoped local variables. */
|
||||
scopedLocals: Map<string,Local> | null = null;
|
||||
/** Local variable wrap states for the first 64 locals. */
|
||||
wrappedLocals: I64;
|
||||
/** Local variable wrap states for locals with index >= 64. */
|
||||
wrappedLocalsExt: I64[] | null;
|
||||
/** Local flags. */
|
||||
localFlags: LocalFlags[];
|
||||
/** Field flags. Relevant in constructors. */
|
||||
fieldFlags: Map<string,FieldFlags> | null = null;
|
||||
/** Function being inlined, when inlining. */
|
||||
inlineFunction: Function | null;
|
||||
/** The label we break to when encountering a return statement, when inlining. */
|
||||
@ -172,14 +249,13 @@ export class Flow {
|
||||
flow.breakLabel = null;
|
||||
flow.returnType = parentFunction.signature.returnType;
|
||||
flow.contextualTypeArguments = parentFunction.contextualTypeArguments;
|
||||
flow.wrappedLocals = i64_new(0);
|
||||
flow.wrappedLocalsExt = null;
|
||||
flow.localFlags = [];
|
||||
flow.inlineFunction = null;
|
||||
flow.inlineReturnLabel = null;
|
||||
return flow;
|
||||
}
|
||||
|
||||
/** Creates an inline flow within `currentFunction`. */
|
||||
/** Creates an inline flow within `parentFunction`. */
|
||||
static createInline(parentFunction: Function, inlineFunction: Function): Flow {
|
||||
var flow = Flow.create(parentFunction);
|
||||
flow.set(FlowFlags.INLINE_CONTEXT);
|
||||
@ -216,15 +292,14 @@ export class Flow {
|
||||
branch.breakLabel = this.breakLabel;
|
||||
branch.returnType = this.returnType;
|
||||
branch.contextualTypeArguments = this.contextualTypeArguments;
|
||||
branch.wrappedLocals = this.wrappedLocals;
|
||||
branch.wrappedLocalsExt = this.wrappedLocalsExt ? this.wrappedLocalsExt.slice() : null;
|
||||
branch.localFlags = this.localFlags.slice();
|
||||
branch.inlineFunction = this.inlineFunction;
|
||||
branch.inlineReturnLabel = this.inlineReturnLabel;
|
||||
return branch;
|
||||
}
|
||||
|
||||
/** Gets a free temporary local of the specified type. */
|
||||
getTempLocal(type: Type, wrapped: bool = false): Local {
|
||||
getTempLocal(type: Type, except: Set<i32> | null = null): Local {
|
||||
var parentFunction = this.parentFunction;
|
||||
var temps: Local[] | null;
|
||||
switch (type.toNativeType()) {
|
||||
@ -236,14 +311,43 @@ export class Flow {
|
||||
default: throw new Error("concrete type expected");
|
||||
}
|
||||
var local: Local;
|
||||
if (temps && temps.length) {
|
||||
local = temps.pop();
|
||||
local.type = type;
|
||||
local.flags = CommonFlags.NONE;
|
||||
} else {
|
||||
if (except) {
|
||||
if (temps && temps.length) {
|
||||
for (let i = 0, k = temps.length; i < k; ++i) {
|
||||
if (!except.has(temps[i].index)) {
|
||||
local = temps[i];
|
||||
let k = temps.length - 1;
|
||||
while (i < k) unchecked(temps[i] = temps[i++ + 1]);
|
||||
temps.length = k;
|
||||
local.type = type;
|
||||
local.flags = CommonFlags.NONE;
|
||||
this.unsetLocalFlag(local.index, ~0);
|
||||
return local;
|
||||
}
|
||||
}
|
||||
}
|
||||
local = parentFunction.addLocal(type);
|
||||
} else {
|
||||
if (temps && temps.length) {
|
||||
local = temps.pop();
|
||||
local.type = type;
|
||||
local.flags = CommonFlags.NONE;
|
||||
} else {
|
||||
local = parentFunction.addLocal(type);
|
||||
}
|
||||
}
|
||||
if (type.is(TypeFlags.SHORT | TypeFlags.INTEGER)) this.setLocalWrapped(local.index, wrapped);
|
||||
this.unsetLocalFlag(local.index, ~0);
|
||||
return local;
|
||||
}
|
||||
|
||||
/** Gets a local that sticks around until this flow is exited, and then released. */
|
||||
getAutoreleaseLocal(type: Type, except: Set<i32> | null = null): Local {
|
||||
var local = this.getTempLocal(type, except);
|
||||
local.set(CommonFlags.SCOPED);
|
||||
var scopedLocals = this.scopedLocals;
|
||||
if (!scopedLocals) this.scopedLocals = scopedLocals = new Map();
|
||||
scopedLocals.set("~auto" + (this.parentFunction.nextAutoreleaseId++), local);
|
||||
this.setLocalFlag(local.index, LocalFlags.RETAINED);
|
||||
return local;
|
||||
}
|
||||
|
||||
@ -282,65 +386,27 @@ export class Flow {
|
||||
}
|
||||
|
||||
/** Gets and immediately frees a temporary local of the specified type. */
|
||||
getAndFreeTempLocal(type: Type, wrapped: bool): Local {
|
||||
var parentFunction = this.parentFunction;
|
||||
var temps: Local[];
|
||||
switch (type.toNativeType()) {
|
||||
case NativeType.I32: {
|
||||
temps = parentFunction.tempI32s || (parentFunction.tempI32s = []);
|
||||
break;
|
||||
}
|
||||
case NativeType.I64: {
|
||||
temps = parentFunction.tempI64s || (parentFunction.tempI64s = []);
|
||||
break;
|
||||
}
|
||||
case NativeType.F32: {
|
||||
temps = parentFunction.tempF32s || (parentFunction.tempF32s = []);
|
||||
break;
|
||||
}
|
||||
case NativeType.F64: {
|
||||
temps = parentFunction.tempF64s || (parentFunction.tempF64s = []);
|
||||
break;
|
||||
}
|
||||
case NativeType.V128: {
|
||||
temps = parentFunction.tempV128s || (parentFunction.tempV128s = []);
|
||||
break;
|
||||
}
|
||||
default: throw new Error("concrete type expected");
|
||||
}
|
||||
var local: Local;
|
||||
if (temps.length) {
|
||||
local = temps[temps.length - 1];
|
||||
local.type = type;
|
||||
} else {
|
||||
local = parentFunction.addLocal(type);
|
||||
temps.push(local);
|
||||
}
|
||||
if (type.is(TypeFlags.SHORT | TypeFlags.INTEGER)) this.setLocalWrapped(local.index, wrapped);
|
||||
getAndFreeTempLocal(type: Type, except: Set<i32> | null = null): Local {
|
||||
var local = this.getTempLocal(type, except);
|
||||
this.freeTempLocal(local);
|
||||
return local;
|
||||
}
|
||||
|
||||
/** Gets the scoped local of the specified name. */
|
||||
getScopedLocal(name: string): Local | null {
|
||||
var scopedLocals = this.scopedLocals;
|
||||
if (scopedLocals && scopedLocals.has(name)) return scopedLocals.get(name);
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Adds a new scoped local of the specified name. */
|
||||
addScopedLocal(name: string, type: Type, wrapped: bool, reportNode: Node | null = null): Local {
|
||||
var scopedLocal = this.getTempLocal(type, false);
|
||||
if (!this.scopedLocals) this.scopedLocals = new Map();
|
||||
else {
|
||||
let existingLocal = this.scopedLocals.get(name);
|
||||
if (existingLocal) {
|
||||
if (reportNode) {
|
||||
this.parentFunction.program.error(
|
||||
DiagnosticCode.Duplicate_identifier_0,
|
||||
reportNode.range
|
||||
);
|
||||
}
|
||||
return existingLocal;
|
||||
}
|
||||
}
|
||||
addScopedLocal(name: string, type: Type, except: Set<i32> | null = null): Local {
|
||||
var scopedLocal = this.getTempLocal(type, except);
|
||||
var scopedLocals = this.scopedLocals;
|
||||
if (!scopedLocals) this.scopedLocals = scopedLocals = new Map();
|
||||
else assert(!scopedLocals.has(name));
|
||||
scopedLocal.set(CommonFlags.SCOPED);
|
||||
this.scopedLocals.set(name, scopedLocal);
|
||||
if (type.is(TypeFlags.SHORT | TypeFlags.INTEGER)) {
|
||||
this.setLocalWrapped(scopedLocal.index, wrapped);
|
||||
}
|
||||
scopedLocals.set(name, scopedLocal);
|
||||
return scopedLocal;
|
||||
}
|
||||
|
||||
@ -360,12 +426,7 @@ export class Flow {
|
||||
}
|
||||
}
|
||||
assert(index < this.parentFunction.localsByIndex.length);
|
||||
var scopedAlias = new Local(
|
||||
name,
|
||||
index,
|
||||
type,
|
||||
this.parentFunction
|
||||
);
|
||||
var scopedAlias = new Local(name, index, type, this.parentFunction);
|
||||
// not flagged as SCOPED as it must not be free'd when the flow is finalized
|
||||
this.scopedLocals.set(name, scopedAlias);
|
||||
return scopedAlias;
|
||||
@ -399,32 +460,34 @@ export class Flow {
|
||||
return this.actualFunction.lookup(name);
|
||||
}
|
||||
|
||||
/** Tests if the value of the local at the specified index is considered wrapped. */
|
||||
isLocalWrapped(index: i32): bool {
|
||||
if (index < 0) return true; // inlined constant
|
||||
if (index < 64) return bitsetIs(this.wrappedLocals, index);
|
||||
var ext = this.wrappedLocalsExt;
|
||||
var i = ((index - 64) / 64) | 0;
|
||||
if (!(ext && i < ext.length)) return false;
|
||||
return bitsetIs(ext[i], index - (i + 1) * 64);
|
||||
/** Tests if the local at the specified index has the specified flag or flags. */
|
||||
isLocalFlag(index: i32, flag: LocalFlags, defaultIfInlined: bool = true): bool {
|
||||
if (index < 0) return defaultIfInlined;
|
||||
var localFlags = this.localFlags;
|
||||
return index < localFlags.length && (unchecked(this.localFlags[index]) & flag) == flag;
|
||||
}
|
||||
|
||||
/** Sets if the value of the local at the specified index is considered wrapped. */
|
||||
setLocalWrapped(index: i32, wrapped: bool): void {
|
||||
if (index < 0) return; // inlined constant
|
||||
if (index < 64) {
|
||||
this.wrappedLocals = bitsetSet(this.wrappedLocals, index, wrapped);
|
||||
return;
|
||||
}
|
||||
var ext = this.wrappedLocalsExt;
|
||||
var i = ((index - 64) / 64) | 0;
|
||||
if (!ext) {
|
||||
this.wrappedLocalsExt = ext = new Array(i + 1);
|
||||
for (let j = 0; j <= i; ++j) ext[j] = i64_new(0);
|
||||
} else {
|
||||
while (ext.length <= i) ext.push(i64_new(0));
|
||||
}
|
||||
ext[i] = bitsetSet(ext[i], index - (i + 1) * 64, wrapped);
|
||||
/** Tests if the local at the specified index has any of the specified flags. */
|
||||
isAnyLocalFlag(index: i32, flag: LocalFlags, defaultIfInlined: bool = true): bool {
|
||||
if (index < 0) return defaultIfInlined;
|
||||
var localFlags = this.localFlags;
|
||||
return index < localFlags.length && (unchecked(this.localFlags[index]) & flag) != 0;
|
||||
}
|
||||
|
||||
/** Sets the specified flag or flags on the local at the specified index. */
|
||||
setLocalFlag(index: i32, flag: LocalFlags): void {
|
||||
if (index < 0) return;
|
||||
var localFlags = this.localFlags;
|
||||
var flags = index < localFlags.length ? unchecked(localFlags[index]) : 0;
|
||||
this.localFlags[index] = flags | flag;
|
||||
}
|
||||
|
||||
/** Unsets the specified flag or flags on the local at the specified index. */
|
||||
unsetLocalFlag(index: i32, flag: LocalFlags): void {
|
||||
if (index < 0) return;
|
||||
var localFlags = this.localFlags;
|
||||
var flags = index < localFlags.length ? unchecked(localFlags[index]) : 0;
|
||||
this.localFlags[index] = flags & ~flag;
|
||||
}
|
||||
|
||||
/** Pushes a new break label to the stack, for example when entering a loop that one can `break` from. */
|
||||
@ -454,8 +517,7 @@ export class Flow {
|
||||
/** Inherits flags and local wrap states from the specified flow (e.g. blocks). */
|
||||
inherit(other: Flow): void {
|
||||
this.flags |= other.flags & (FlowFlags.ANY_CATEGORICAL | FlowFlags.ANY_CONDITIONAL);
|
||||
this.wrappedLocals = other.wrappedLocals;
|
||||
this.wrappedLocalsExt = other.wrappedLocalsExt; // no need to slice because other flow is finished
|
||||
this.localFlags = other.localFlags; // no need to slice because other flow is finished
|
||||
}
|
||||
|
||||
/** Inherits categorical flags as conditional flags from the specified flow (e.g. then without else). */
|
||||
@ -475,33 +537,245 @@ export class Flow {
|
||||
if (other.is(FlowFlags.ALLOCATES)) {
|
||||
this.set(FlowFlags.CONDITIONALLY_ALLOCATES);
|
||||
}
|
||||
var localFlags = other.localFlags;
|
||||
for (let i = 0, k = localFlags.length; i < k; ++i) {
|
||||
let flags = localFlags[i];
|
||||
if (flags & LocalFlags.RETAINED) this.setLocalFlag(i, LocalFlags.CONDITIONALLY_RETAINED);
|
||||
if (flags & LocalFlags.READFROM) this.setLocalFlag(i, LocalFlags.CONDITIONALLY_READFROM);
|
||||
if (flags & LocalFlags.WRITTENTO) this.setLocalFlag(i, LocalFlags.CONDITIONALLY_WRITTENTO);
|
||||
}
|
||||
}
|
||||
|
||||
/** Inherits mutual flags and local wrap states from the specified flows (e.g. then with else). */
|
||||
inheritMutual(left: Flow, right: Flow): void {
|
||||
// categorical flags set in both arms
|
||||
this.flags |= left.flags & right.flags & FlowFlags.ANY_CATEGORICAL;
|
||||
this.set(left.flags & right.flags & FlowFlags.ANY_CATEGORICAL);
|
||||
|
||||
// conditional flags set in at least one arm
|
||||
this.flags |= left.flags & FlowFlags.ANY_CONDITIONAL;
|
||||
this.flags |= right.flags & FlowFlags.ANY_CONDITIONAL;
|
||||
this.set(left.flags & FlowFlags.ANY_CONDITIONAL);
|
||||
this.set(right.flags & FlowFlags.ANY_CONDITIONAL);
|
||||
|
||||
// locals wrapped in both arms
|
||||
this.wrappedLocals = i64_and(left.wrappedLocals, right.wrappedLocals);
|
||||
var leftExt = left.wrappedLocalsExt;
|
||||
var rightExt = right.wrappedLocalsExt;
|
||||
if (leftExt != null && rightExt != null) {
|
||||
let thisExt = this.wrappedLocalsExt;
|
||||
let minLength = min(leftExt.length, rightExt.length);
|
||||
if (minLength) {
|
||||
if (!thisExt) thisExt = new Array(minLength);
|
||||
else while (thisExt.length < minLength) thisExt.push(i64_new(0));
|
||||
for (let i = 0; i < minLength; ++i) {
|
||||
thisExt[i] = i64_and(
|
||||
leftExt[i],
|
||||
rightExt[i]
|
||||
);
|
||||
// categorical local flags set in both arms / conditional local flags set in at least one arm
|
||||
var leftLocalFlags = left.localFlags;
|
||||
var numLeftLocalFlags = leftLocalFlags.length;
|
||||
var rightLocalFlags = right.localFlags;
|
||||
var numRightLocalFlags = rightLocalFlags.length;
|
||||
var combinedFlags = new Array<LocalFlags>(max<i32>(numLeftLocalFlags, numRightLocalFlags));
|
||||
for (let i = 0; i < numLeftLocalFlags; ++i) {
|
||||
combinedFlags[i] = LocalFlags.join(
|
||||
unchecked(leftLocalFlags[i]),
|
||||
i < numRightLocalFlags
|
||||
? unchecked(rightLocalFlags[i])
|
||||
: 0
|
||||
);
|
||||
}
|
||||
for (let i = numLeftLocalFlags; i < numRightLocalFlags; ++i) {
|
||||
combinedFlags[i] = LocalFlags.join(
|
||||
0,
|
||||
unchecked(rightLocalFlags[i])
|
||||
);
|
||||
}
|
||||
this.localFlags = combinedFlags;
|
||||
}
|
||||
|
||||
/** Checks if an expression of the specified type is known to be non-null, even if the type might be nullable. */
|
||||
isNonnull(expr: ExpressionRef, type: Type): bool {
|
||||
if (!type.is(TypeFlags.NULLABLE)) return true;
|
||||
// below, only teeLocal/getLocal are relevant because these are the only expressions that
|
||||
// depend on a dynamic nullable state (flag = LocalFlags.NONNULL), while everything else
|
||||
// has already been handled by the nullable type check above.
|
||||
switch (getExpressionId(expr)) {
|
||||
case ExpressionId.LocalSet: {
|
||||
if (!isLocalTee(expr)) break;
|
||||
let local = this.parentFunction.localsByIndex[getLocalSetIndex(expr)];
|
||||
return !local.type.is(TypeFlags.NULLABLE) || this.isLocalFlag(local.index, LocalFlags.NONNULL, false);
|
||||
}
|
||||
case ExpressionId.LocalGet: {
|
||||
let local = this.parentFunction.localsByIndex[getLocalGetIndex(expr)];
|
||||
return !local.type.is(TypeFlags.NULLABLE) || this.isLocalFlag(local.index, LocalFlags.NONNULL, false);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Updates local states to reflect that this branch is only taken when `expr` is true-ish. */
|
||||
inheritNonnullIfTrue(expr: ExpressionRef): void {
|
||||
// A: `expr` is true-ish -> Q: how did that happen?
|
||||
switch (getExpressionId(expr)) {
|
||||
case ExpressionId.LocalSet: {
|
||||
if (!isLocalTee(expr)) break;
|
||||
let local = this.parentFunction.localsByIndex[getLocalSetIndex(expr)];
|
||||
this.setLocalFlag(local.index, LocalFlags.NONNULL);
|
||||
this.inheritNonnullIfTrue(getLocalSetValue(expr)); // must have been true-ish as well
|
||||
break;
|
||||
}
|
||||
case ExpressionId.LocalGet: {
|
||||
let local = this.parentFunction.localsByIndex[getLocalGetIndex(expr)];
|
||||
this.setLocalFlag(local.index, LocalFlags.NONNULL);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.If: {
|
||||
let ifFalse = getIfFalse(expr);
|
||||
if (!ifFalse) break;
|
||||
if (getExpressionId(ifFalse) == ExpressionId.Const) {
|
||||
// Logical AND: (if (condition ifTrue 0))
|
||||
// the only way this had become true is if condition and ifTrue are true
|
||||
if (
|
||||
(getExpressionType(ifFalse) == NativeType.I32 && getConstValueI32(ifFalse) == 0) ||
|
||||
(getExpressionType(ifFalse) == NativeType.I64 && getConstValueI64Low(ifFalse) == 0 && getConstValueI64High(ifFalse) == 0)
|
||||
) {
|
||||
this.inheritNonnullIfTrue(getIfCondition(expr));
|
||||
this.inheritNonnullIfTrue(getIfTrue(expr));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ExpressionId.Unary: {
|
||||
switch (getUnaryOp(expr)) {
|
||||
case UnaryOp.EqzI32:
|
||||
case UnaryOp.EqzI64: {
|
||||
this.inheritNonnullIfFalse(getUnaryValue(expr)); // !value -> value must have been false
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ExpressionId.Binary: {
|
||||
switch (getBinaryOp(expr)) {
|
||||
case BinaryOp.EqI32: {
|
||||
let left = getBinaryLeft(expr);
|
||||
let right = getBinaryRight(expr);
|
||||
if (getExpressionId(left) == ExpressionId.Const && getConstValueI32(left) != 0) {
|
||||
this.inheritNonnullIfTrue(right); // TRUE == right -> right must have been true
|
||||
} else if (getExpressionId(right) == ExpressionId.Const && getConstValueI32(right) != 0) {
|
||||
this.inheritNonnullIfTrue(left); // left == TRUE -> left must have been true
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BinaryOp.EqI64: {
|
||||
let left = getBinaryLeft(expr);
|
||||
let right = getBinaryRight(expr);
|
||||
if (getExpressionId(left) == ExpressionId.Const && (getConstValueI64Low(left) != 0 || getConstValueI64High(left) != 0)) {
|
||||
this.inheritNonnullIfTrue(right); // TRUE == right -> right must have been true
|
||||
} else if (getExpressionId(right) == ExpressionId.Const && (getConstValueI64Low(right) != 0 && getConstValueI64High(right) != 0)) {
|
||||
this.inheritNonnullIfTrue(left); // left == TRUE -> left must have been true
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BinaryOp.NeI32: {
|
||||
let left = getBinaryLeft(expr);
|
||||
let right = getBinaryRight(expr);
|
||||
if (getExpressionId(left) == ExpressionId.Const && getConstValueI32(left) == 0) {
|
||||
this.inheritNonnullIfTrue(right); // FALSE != right -> right must have been true
|
||||
} else if (getExpressionId(right) == ExpressionId.Const && getConstValueI32(right) == 0) {
|
||||
this.inheritNonnullIfTrue(left); // left != FALSE -> left must have been true
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BinaryOp.NeI64: {
|
||||
let left = getBinaryLeft(expr);
|
||||
let right = getBinaryRight(expr);
|
||||
if (getExpressionId(left) == ExpressionId.Const && getConstValueI64Low(left) == 0 && getConstValueI64High(left) == 0) {
|
||||
this.inheritNonnullIfTrue(right); // FALSE != right -> right must have been true
|
||||
} else if (getExpressionId(right) == ExpressionId.Const && getConstValueI64Low(right) == 0 && getConstValueI64High(right) == 0) {
|
||||
this.inheritNonnullIfTrue(left); // left != FALSE -> left must have been true
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ExpressionId.Call: {
|
||||
let name = getCallTarget(expr);
|
||||
let program = this.parentFunction.program;
|
||||
switch (name) {
|
||||
case program.retainInstance.internalName: {
|
||||
this.inheritNonnullIfTrue(getCallOperand(expr, 0));
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Updates local states to reflect that this branch is only taken when `expr` is false-ish. */
|
||||
inheritNonnullIfFalse(expr: ExpressionRef): void {
|
||||
// A: `expr` is false-ish -> Q: how did that happen?
|
||||
switch (getExpressionId(expr)) {
|
||||
case ExpressionId.Unary: {
|
||||
switch (getUnaryOp(expr)) {
|
||||
case UnaryOp.EqzI32:
|
||||
case UnaryOp.EqzI64: {
|
||||
this.inheritNonnullIfTrue(getUnaryValue(expr)); // !value -> value must have been true
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ExpressionId.If: {
|
||||
let ifTrue = getIfTrue(expr);
|
||||
if (getExpressionId(ifTrue) == ExpressionId.Const) {
|
||||
let ifFalse = getIfFalse(expr);
|
||||
if (!ifFalse) break;
|
||||
// Logical OR: (if (condition 1 ifFalse))
|
||||
// the only way this had become false is if condition and ifFalse are false
|
||||
if (
|
||||
(getExpressionType(ifTrue) == NativeType.I32 && getConstValueI32(ifTrue) != 0) ||
|
||||
(getExpressionType(ifTrue) == NativeType.I64 && (getConstValueI64Low(ifTrue) != 0 || getConstValueI64High(ifTrue) != 0))
|
||||
) {
|
||||
this.inheritNonnullIfFalse(getIfCondition(expr));
|
||||
this.inheritNonnullIfFalse(getIfFalse(expr));
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ExpressionId.Binary: {
|
||||
switch (getBinaryOp(expr)) {
|
||||
// remember: we want to know how the _entire_ expression became FALSE (!)
|
||||
case BinaryOp.EqI32: {
|
||||
let left = getBinaryLeft(expr);
|
||||
let right = getBinaryRight(expr);
|
||||
if (getExpressionId(left) == ExpressionId.Const && getConstValueI32(left) == 0) {
|
||||
this.inheritNonnullIfTrue(right); // FALSE == right -> right must have been true
|
||||
} else if (getExpressionId(right) == ExpressionId.Const && getConstValueI32(right) == 0) {
|
||||
this.inheritNonnullIfTrue(left); // left == FALSE -> left must have been true
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BinaryOp.EqI64: {
|
||||
let left = getBinaryLeft(expr);
|
||||
let right = getBinaryRight(expr);
|
||||
if (getExpressionId(left) == ExpressionId.Const && getConstValueI64Low(left) == 0 && getConstValueI64High(left) == 0) {
|
||||
this.inheritNonnullIfTrue(right); // FALSE == right -> right must have been true
|
||||
} else if (getExpressionId(right) == ExpressionId.Const && getConstValueI64Low(right) == 0 && getConstValueI64High(right) == 0) {
|
||||
this.inheritNonnullIfTrue(left); // left == FALSE -> left must have been true
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BinaryOp.NeI32: {
|
||||
let left = getBinaryLeft(expr);
|
||||
let right = getBinaryRight(expr);
|
||||
if (getExpressionId(left) == ExpressionId.Const && getConstValueI32(left) != 0) {
|
||||
this.inheritNonnullIfTrue(right); // TRUE != right -> right must have been true
|
||||
} else if (getExpressionId(right) == ExpressionId.Const && getConstValueI32(right) != 0) {
|
||||
this.inheritNonnullIfTrue(left); // left != TRUE -> left must have been true
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BinaryOp.NeI64: {
|
||||
let left = getBinaryLeft(expr);
|
||||
let right = getBinaryRight(expr);
|
||||
if (getExpressionId(left) == ExpressionId.Const && (getConstValueI64Low(left) != 0 || getConstValueI64High(left) != 0)) {
|
||||
this.inheritNonnullIfTrue(right); // TRUE != right -> right must have been true for this to become false
|
||||
} else if (getExpressionId(right) == ExpressionId.Const && (getConstValueI64Low(right) != 0 || getConstValueI64High(right) != 0)) {
|
||||
this.inheritNonnullIfTrue(left); // left != TRUE -> left must have been true for this to become false
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -523,22 +797,22 @@ export class Flow {
|
||||
switch (getExpressionId(expr)) {
|
||||
|
||||
// overflows if the local isn't wrapped or the conversion does
|
||||
case ExpressionId.GetLocal: {
|
||||
let local = this.parentFunction.localsByIndex[getGetLocalIndex(expr)];
|
||||
return !this.isLocalWrapped(local.index)
|
||||
case ExpressionId.LocalGet: {
|
||||
let local = this.parentFunction.localsByIndex[getLocalGetIndex(expr)];
|
||||
return !this.isLocalFlag(local.index, LocalFlags.WRAPPED, true)
|
||||
|| canConversionOverflow(local.type, type);
|
||||
}
|
||||
|
||||
// overflows if the value does
|
||||
case ExpressionId.SetLocal: { // tee
|
||||
assert(isTeeLocal(expr));
|
||||
return this.canOverflow(getSetLocalValue(expr), type);
|
||||
case ExpressionId.LocalSet: { // tee
|
||||
assert(isLocalTee(expr));
|
||||
return this.canOverflow(getLocalSetValue(expr), type);
|
||||
}
|
||||
|
||||
// overflows if the conversion does (globals are wrapped on set)
|
||||
case ExpressionId.GetGlobal: {
|
||||
case ExpressionId.GlobalGet: {
|
||||
// TODO: this is inefficient because it has to read a string
|
||||
let global = assert(this.parentFunction.program.elementsByName.get(assert(getGetGlobalName(expr))));
|
||||
let global = assert(this.parentFunction.program.elementsByName.get(assert(getGlobalGetName(expr))));
|
||||
assert(global.kind == ElementKind.GLOBAL);
|
||||
return canConversionOverflow(assert((<Global>global).type), type);
|
||||
}
|
||||
@ -705,10 +979,11 @@ export class Flow {
|
||||
// overflows if the conversion does
|
||||
case ExpressionId.Load: {
|
||||
let fromType: Type;
|
||||
let signed = isLoadSigned(expr);
|
||||
switch (getLoadBytes(expr)) {
|
||||
case 1: { fromType = isLoadSigned(expr) ? Type.i8 : Type.u8; break; }
|
||||
case 2: { fromType = isLoadSigned(expr) ? Type.i16 : Type.u16; break; }
|
||||
default: { fromType = isLoadSigned(expr) ? Type.i32 : Type.u32; break; }
|
||||
case 1: { fromType = signed ? Type.i8 : Type.u8; break; }
|
||||
case 2: { fromType = signed ? Type.i16 : Type.u16; break; }
|
||||
default: { fromType = signed ? Type.i32 : Type.u32; break; }
|
||||
}
|
||||
return canConversionOverflow(fromType, type);
|
||||
}
|
||||
@ -740,11 +1015,16 @@ export class Flow {
|
||||
// overflows if the call does not return a wrapped value or the conversion does
|
||||
case ExpressionId.Call: {
|
||||
let program = this.parentFunction.program;
|
||||
let instance = assert(program.instancesByName.get(assert(getCallTarget(expr))));
|
||||
assert(instance.kind == ElementKind.FUNCTION);
|
||||
let returnType = (<Function>instance).signature.returnType;
|
||||
return !(<Function>instance).flow.is(FlowFlags.RETURNS_WRAPPED)
|
||||
|| canConversionOverflow(returnType, type);
|
||||
let instancesByName = program.instancesByName;
|
||||
let instanceName = assert(getCallTarget(expr));
|
||||
if (instancesByName.has(instanceName)) {
|
||||
let instance = instancesByName.get(instanceName)!;
|
||||
assert(instance.kind == ElementKind.FUNCTION);
|
||||
let returnType = (<Function>instance).signature.returnType;
|
||||
return !(<Function>instance).flow.is(FlowFlags.RETURNS_WRAPPED)
|
||||
|| canConversionOverflow(returnType, type);
|
||||
}
|
||||
return false; // assume no overflow for builtins
|
||||
}
|
||||
|
||||
// doesn't technically overflow
|
||||
@ -752,6 +1032,16 @@ export class Flow {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
var levels = 0;
|
||||
var parent = this.parent;
|
||||
while (parent) {
|
||||
parent = parent.parent;
|
||||
++levels;
|
||||
}
|
||||
return "Flow(" + this.actualFunction + ")[" + levels.toString() + "]";
|
||||
}
|
||||
}
|
||||
|
||||
/** Tests if a conversion from one type to another can technically overflow. */
|
||||
@ -760,3 +1050,24 @@ function canConversionOverflow(fromType: Type, toType: Type): bool {
|
||||
|| fromType.size > toType.size
|
||||
|| fromType.is(TypeFlags.SIGNED) != toType.is(TypeFlags.SIGNED);
|
||||
}
|
||||
|
||||
/** Finds all indexes of locals used in the specified expression. */
|
||||
export function findUsedLocals(expr: ExpressionRef, used: Set<i32> = new Set()): Set<i32> {
|
||||
traverse(expr, used, findUsedLocalsVisit);
|
||||
return used;
|
||||
}
|
||||
|
||||
/** A visitor function for use with `traverse` that finds all indexes of used locals. */
|
||||
function findUsedLocalsVisit(expr: ExpressionRef, used: Set<i32>): void {
|
||||
switch (getExpressionId(expr)) {
|
||||
case ExpressionId.LocalGet: {
|
||||
used.add(getLocalGetIndex(expr));
|
||||
break;
|
||||
}
|
||||
case ExpressionId.LocalSet: {
|
||||
used.add(getLocalSetIndex(expr));
|
||||
// fall-through for value
|
||||
}
|
||||
default: traverse(expr, used, findUsedLocalsVisit);
|
||||
}
|
||||
}
|
||||
|
48
src/glue/binaryen.d.ts
vendored
48
src/glue/binaryen.d.ts
vendored
@ -22,6 +22,16 @@ declare function _BinaryenTypeVec128(): BinaryenType;
|
||||
declare function _BinaryenTypeUnreachable(): BinaryenType;
|
||||
declare function _BinaryenTypeAuto(): BinaryenType;
|
||||
|
||||
declare type BinaryenFeatureFlags = u32;
|
||||
|
||||
declare function _BinaryenFeatureAtomics(): BinaryenFeatureFlags;
|
||||
declare function _BinaryenFeatureMutableGlobals(): BinaryenFeatureFlags;
|
||||
declare function _BinaryenFeatureNontrappingFPToInt(): BinaryenFeatureFlags;
|
||||
declare function _BinaryenFeatureSIMD128(): BinaryenFeatureFlags;
|
||||
declare function _BinaryenFeatureBulkMemory(): BinaryenFeatureFlags;
|
||||
declare function _BinaryenFeatureSignExt(): BinaryenFeatureFlags;
|
||||
declare function _BinaryenFeatureExceptionHandling(): BinaryenFeatureFlags;
|
||||
|
||||
declare type BinaryenExpressionId = i32;
|
||||
|
||||
declare function _BinaryenInvalidId(): BinaryenExpressionId;
|
||||
@ -32,10 +42,10 @@ declare function _BinaryenBreakId(): BinaryenExpressionId;
|
||||
declare function _BinaryenSwitchId(): BinaryenExpressionId;
|
||||
declare function _BinaryenCallId(): BinaryenExpressionId;
|
||||
declare function _BinaryenCallIndirectId(): BinaryenExpressionId;
|
||||
declare function _BinaryenGetLocalId(): BinaryenExpressionId;
|
||||
declare function _BinaryenSetLocalId(): BinaryenExpressionId;
|
||||
declare function _BinaryenGetGlobalId(): BinaryenExpressionId;
|
||||
declare function _BinaryenSetGlobalId(): BinaryenExpressionId;
|
||||
declare function _BinaryenLocalGetId(): BinaryenExpressionId;
|
||||
declare function _BinaryenLocalSetId(): BinaryenExpressionId;
|
||||
declare function _BinaryenGlobalGetId(): BinaryenExpressionId;
|
||||
declare function _BinaryenGlobalSetId(): BinaryenExpressionId;
|
||||
declare function _BinaryenLoadId(): BinaryenExpressionId;
|
||||
declare function _BinaryenStoreId(): BinaryenExpressionId;
|
||||
declare function _BinaryenConstId(): BinaryenExpressionId;
|
||||
@ -211,8 +221,8 @@ declare function _BinaryenGeFloat64(): BinaryenOp;
|
||||
|
||||
declare type BinaryenHostOp = BinaryenOp;
|
||||
|
||||
declare function _BinaryenCurrentMemory(): BinaryenHostOp;
|
||||
declare function _BinaryenGrowMemory(): BinaryenHostOp;
|
||||
declare function _BinaryenMemorySize(): BinaryenHostOp;
|
||||
declare function _BinaryenMemoryGrow(): BinaryenHostOp;
|
||||
|
||||
declare type BinaryenAtomicRMWOp = BinaryenOp;
|
||||
|
||||
@ -370,11 +380,11 @@ declare function _BinaryenBreak(module: BinaryenModuleRef, name: usize, conditio
|
||||
declare function _BinaryenSwitch(module: BinaryenModuleRef, names: usize, numNames: BinaryenIndex, defaultName: usize, condition: BinaryenExpressionRef, value: BinaryenExpressionRef): BinaryenExpressionRef;
|
||||
declare function _BinaryenCall(module: BinaryenModuleRef, target: usize, operands: usize, numOperands: BinaryenIndex, returnType: BinaryenType): BinaryenExpressionRef;
|
||||
declare function _BinaryenCallIndirect(module: BinaryenModuleRef, target: BinaryenExpressionRef, operands: usize, numOperands: BinaryenIndex, type: usize): BinaryenExpressionRef;
|
||||
declare function _BinaryenGetLocal(module: BinaryenModuleRef, index: BinaryenIndex, type: BinaryenType): BinaryenExpressionRef;
|
||||
declare function _BinaryenSetLocal(module: BinaryenModuleRef, index: BinaryenIndex, value: BinaryenExpressionRef): BinaryenExpressionRef;
|
||||
declare function _BinaryenTeeLocal(module: BinaryenModuleRef, index: BinaryenIndex, value: BinaryenExpressionRef): BinaryenExpressionRef;
|
||||
declare function _BinaryenGetGlobal(module: BinaryenModuleRef, name: usize, type: BinaryenType): BinaryenExpressionRef;
|
||||
declare function _BinaryenSetGlobal(module: BinaryenModuleRef, name: usize, value: BinaryenExpressionRef): BinaryenExpressionRef;
|
||||
declare function _BinaryenLocalGet(module: BinaryenModuleRef, index: BinaryenIndex, type: BinaryenType): BinaryenExpressionRef;
|
||||
declare function _BinaryenLocalSet(module: BinaryenModuleRef, index: BinaryenIndex, value: BinaryenExpressionRef): BinaryenExpressionRef;
|
||||
declare function _BinaryenLocalTee(module: BinaryenModuleRef, index: BinaryenIndex, value: BinaryenExpressionRef): BinaryenExpressionRef;
|
||||
declare function _BinaryenGlobalGet(module: BinaryenModuleRef, name: usize, type: BinaryenType): BinaryenExpressionRef;
|
||||
declare function _BinaryenGlobalSet(module: BinaryenModuleRef, name: usize, value: BinaryenExpressionRef): BinaryenExpressionRef;
|
||||
declare function _BinaryenLoad(module: BinaryenModuleRef, bytes: u32, signed: i8, offset: u32, align: u32, type: BinaryenType, ptr: BinaryenExpressionRef): BinaryenExpressionRef;
|
||||
declare function _BinaryenStore(module: BinaryenModuleRef, bytes: u32, offset: u32, align: u32, ptr: BinaryenExpressionRef, value: BinaryenExpressionRef, type: BinaryenType): BinaryenExpressionRef;
|
||||
declare function _BinaryenConst(module: BinaryenModuleRef, value: usize): BinaryenExpressionRef;
|
||||
@ -438,16 +448,16 @@ declare function _BinaryenCallIndirectGetTarget(expr: BinaryenExpressionRef): Bi
|
||||
declare function _BinaryenCallIndirectGetNumOperands(expr: BinaryenExpressionRef): BinaryenIndex;
|
||||
declare function _BinaryenCallIndirectGetOperand(expr: BinaryenExpressionRef, index: BinaryenIndex): BinaryenExpressionRef;
|
||||
|
||||
declare function _BinaryenGetLocalGetIndex(expr: BinaryenExpressionRef): BinaryenIndex;
|
||||
declare function _BinaryenLocalGetGetIndex(expr: BinaryenExpressionRef): BinaryenIndex;
|
||||
|
||||
declare function _BinaryenSetLocalIsTee(expr: BinaryenExpressionRef): bool;
|
||||
declare function _BinaryenSetLocalGetIndex(expr: BinaryenExpressionRef): BinaryenIndex;
|
||||
declare function _BinaryenSetLocalGetValue(expr: BinaryenExpressionRef): BinaryenExpressionRef;
|
||||
declare function _BinaryenLocalSetIsTee(expr: BinaryenExpressionRef): bool;
|
||||
declare function _BinaryenLocalSetGetIndex(expr: BinaryenExpressionRef): BinaryenIndex;
|
||||
declare function _BinaryenLocalSetGetValue(expr: BinaryenExpressionRef): BinaryenExpressionRef;
|
||||
|
||||
declare function _BinaryenGetGlobalGetName(expr: BinaryenExpressionRef): usize;
|
||||
declare function _BinaryenGlobalGetGetName(expr: BinaryenExpressionRef): usize;
|
||||
|
||||
declare function _BinaryenSetGlobalGetName(expr: BinaryenExpressionRef): usize;
|
||||
declare function _BinaryenSetGlobalGetValue(expr: BinaryenExpressionRef): BinaryenExpressionRef;
|
||||
declare function _BinaryenGlobalSetGetName(expr: BinaryenExpressionRef): usize;
|
||||
declare function _BinaryenGlobalSetGetValue(expr: BinaryenExpressionRef): BinaryenExpressionRef;
|
||||
|
||||
declare function _BinaryenHostGetOp(expr: BinaryenExpressionRef): BinaryenOp;
|
||||
declare function _BinaryenHostGetNameOperand(expr: BinaryenExpressionRef): usize;
|
||||
@ -612,6 +622,8 @@ declare function _BinaryenModuleRead(input: usize, inputSize: usize): BinaryenMo
|
||||
declare function _BinaryenModuleInterpret(module: BinaryenModuleRef): void;
|
||||
declare function _BinaryenModuleAddDebugInfoFileName(module: BinaryenModuleRef, filename: usize): BinaryenIndex;
|
||||
declare function _BinaryenModuleGetDebugInfoFileName(module: BinaryenModuleRef, index: BinaryenIndex): usize;
|
||||
declare function _BinaryenModuleGetFeatures(module: BinaryenModuleRef): BinaryenFeatureFlags;
|
||||
declare function _BinaryenModuleSetFeatures(module: BinaryenModuleRef, featureFlags: BinaryenFeatureFlags): void;
|
||||
|
||||
declare type BinaryenRelooperRef = usize;
|
||||
declare type BinaryenRelooperBlockRef = usize;
|
||||
|
72
src/index.ts
72
src/index.ts
@ -3,39 +3,14 @@
|
||||
* @module index
|
||||
*//***/
|
||||
|
||||
import {
|
||||
Compiler,
|
||||
Options,
|
||||
Target,
|
||||
Feature
|
||||
} from "./compiler";
|
||||
|
||||
import {
|
||||
Decompiler
|
||||
} from "./decompiler";
|
||||
|
||||
import {
|
||||
IDLBuilder,
|
||||
TSDBuilder
|
||||
} from "./definitions";
|
||||
|
||||
import {
|
||||
DiagnosticMessage,
|
||||
DiagnosticCategory,
|
||||
formatDiagnosticMessage
|
||||
} from "./diagnostics";
|
||||
|
||||
import {
|
||||
Module
|
||||
} from "./module";
|
||||
|
||||
import {
|
||||
Parser
|
||||
} from "./parser";
|
||||
|
||||
import {
|
||||
Program
|
||||
} from "./program";
|
||||
import { Target, Feature } from "./common";
|
||||
import { Compiler, Options } from "./compiler";
|
||||
import { Decompiler } from "./decompiler";
|
||||
import { IDLBuilder, TSDBuilder } from "./definitions";
|
||||
import { DiagnosticMessage, DiagnosticCategory, formatDiagnosticMessage } from "./diagnostics";
|
||||
import { Module } from "./module";
|
||||
import { Parser } from "./parser";
|
||||
import { Program } from "./program";
|
||||
|
||||
/** Parses a source file. If `parser` has been omitted a new one is created. */
|
||||
export function parseFile(text: string, path: string, isEntry: bool = false,
|
||||
@ -124,6 +99,11 @@ export function setGlobalAlias(options: Options, name: string, alias: string): v
|
||||
globalAliases.set(name, alias);
|
||||
}
|
||||
|
||||
/** Sets the `explicitStart` option. */
|
||||
export function setExplicitStart(options: Options, explicitStart: bool): void {
|
||||
options.explicitStart = explicitStart;
|
||||
}
|
||||
|
||||
/** Sign extension operations. */
|
||||
export const FEATURE_SIGN_EXTENSION = Feature.SIGN_EXTENSION;
|
||||
/** Mutable global imports and exports. */
|
||||
@ -173,6 +153,32 @@ export function buildTSD(program: Program): string {
|
||||
return TSDBuilder.build(program);
|
||||
}
|
||||
|
||||
/** Builds a JSON file of a program's runtime type information. */
|
||||
export function buildRTTI(program: Program): string {
|
||||
var sb = new Array<string>();
|
||||
sb.push("{\n \"names\": [\n");
|
||||
for (let cls of program.managedClasses.values()) {
|
||||
sb.push(" \"");
|
||||
sb.push(cls.internalName);
|
||||
sb.push("\",\n");
|
||||
}
|
||||
sb.push(" ],\n \"base\": [\n");
|
||||
for (let cls of program.managedClasses.values()) {
|
||||
let base = cls.base;
|
||||
sb.push(" ");
|
||||
sb.push(base ? base.id.toString() : "0");
|
||||
sb.push(",\n");
|
||||
}
|
||||
sb.push(" ],\n \"flags\": [\n");
|
||||
for (let cls of program.managedClasses.values()) {
|
||||
sb.push(" ");
|
||||
sb.push(cls.rttiFlags.toString());
|
||||
sb.push(",\n");
|
||||
}
|
||||
sb.push(" ]\n}\n");
|
||||
return sb.join("");
|
||||
}
|
||||
|
||||
/** Prefix indicating a library file. */
|
||||
export { LIBRARY_PREFIX } from "./common";
|
||||
|
||||
|
351
src/module.ts
351
src/module.ts
@ -3,9 +3,7 @@
|
||||
* @module module
|
||||
*//***/
|
||||
|
||||
import {
|
||||
Target
|
||||
} from "./compiler";
|
||||
import { Target } from "./common";
|
||||
|
||||
export type ModuleRef = usize;
|
||||
export type FunctionTypeRef = usize;
|
||||
@ -29,6 +27,16 @@ export enum NativeType {
|
||||
Auto = _BinaryenTypeAuto()
|
||||
}
|
||||
|
||||
export enum FeatureFlags {
|
||||
Atomics = _BinaryenFeatureAtomics(),
|
||||
MutableGloabls = _BinaryenFeatureMutableGlobals(),
|
||||
NontrappingFPToInt = _BinaryenFeatureNontrappingFPToInt(),
|
||||
SIMD128 = _BinaryenFeatureSIMD128(),
|
||||
BulkMemory = _BinaryenFeatureBulkMemory(),
|
||||
SignExt = _BinaryenFeatureSignExt(),
|
||||
ExceptionHandling = _BinaryenFeatureExceptionHandling()
|
||||
}
|
||||
|
||||
export enum ExpressionId {
|
||||
Invalid = _BinaryenInvalidId(),
|
||||
Block = _BinaryenBlockId(),
|
||||
@ -38,10 +46,10 @@ export enum ExpressionId {
|
||||
Switch = _BinaryenSwitchId(),
|
||||
Call = _BinaryenCallId(),
|
||||
CallIndirect = _BinaryenCallIndirectId(),
|
||||
GetLocal = _BinaryenGetLocalId(),
|
||||
SetLocal = _BinaryenSetLocalId(),
|
||||
GetGlobal = _BinaryenGetGlobalId(),
|
||||
SetGlobal = _BinaryenSetGlobalId(),
|
||||
LocalGet = _BinaryenLocalGetId(),
|
||||
LocalSet = _BinaryenLocalSetId(),
|
||||
GlobalGet = _BinaryenGlobalGetId(),
|
||||
GlobalSet = _BinaryenGlobalSetId(),
|
||||
Load = _BinaryenLoadId(),
|
||||
Store = _BinaryenStoreId(),
|
||||
Const = _BinaryenConstId(),
|
||||
@ -328,8 +336,8 @@ export enum BinaryOp {
|
||||
}
|
||||
|
||||
export enum HostOp {
|
||||
CurrentMemory = _BinaryenCurrentMemory(),
|
||||
GrowMemory = _BinaryenGrowMemory(),
|
||||
MemorySize = _BinaryenMemorySize(),
|
||||
MemoryGrow = _BinaryenMemoryGrow(),
|
||||
}
|
||||
|
||||
export enum AtomicRMWOp {
|
||||
@ -451,31 +459,31 @@ export class Module {
|
||||
|
||||
// constants
|
||||
|
||||
createI32(value: i32): ExpressionRef {
|
||||
i32(value: i32): ExpressionRef {
|
||||
var out = this.lit;
|
||||
_BinaryenLiteralInt32(out, value);
|
||||
return _BinaryenConst(this.ref, out);
|
||||
}
|
||||
|
||||
createI64(valueLow: i32, valueHigh: i32 = 0): ExpressionRef {
|
||||
i64(valueLow: i32, valueHigh: i32 = 0): ExpressionRef {
|
||||
var out = this.lit;
|
||||
_BinaryenLiteralInt64(out, valueLow, valueHigh);
|
||||
return _BinaryenConst(this.ref, out);
|
||||
}
|
||||
|
||||
createF32(value: f32): ExpressionRef {
|
||||
f32(value: f32): ExpressionRef {
|
||||
var out = this.lit;
|
||||
_BinaryenLiteralFloat32(out, value);
|
||||
return _BinaryenConst(this.ref, out);
|
||||
}
|
||||
|
||||
createF64(value: f64): ExpressionRef {
|
||||
f64(value: f64): ExpressionRef {
|
||||
var out = this.lit;
|
||||
_BinaryenLiteralFloat64(out, value);
|
||||
return _BinaryenConst(this.ref, out);
|
||||
}
|
||||
|
||||
createV128(bytes: Uint8Array): ExpressionRef {
|
||||
v128(bytes: Uint8Array): ExpressionRef {
|
||||
assert(bytes.length == 16);
|
||||
var out = this.lit;
|
||||
for (let i = 0; i < 16; ++i) store<u8>(out + i, bytes[i]);
|
||||
@ -485,14 +493,14 @@ export class Module {
|
||||
|
||||
// expressions
|
||||
|
||||
createUnary(
|
||||
unary(
|
||||
op: UnaryOp,
|
||||
expr: ExpressionRef
|
||||
): ExpressionRef {
|
||||
return _BinaryenUnary(this.ref, op, expr);
|
||||
}
|
||||
|
||||
createBinary(
|
||||
binary(
|
||||
op: BinaryOp,
|
||||
left: ExpressionRef,
|
||||
right: ExpressionRef
|
||||
@ -500,7 +508,7 @@ export class Module {
|
||||
return _BinaryenBinary(this.ref, op, left, right);
|
||||
}
|
||||
|
||||
createHost(
|
||||
host(
|
||||
op: HostOp,
|
||||
name: string | null = null,
|
||||
operands: ExpressionRef[] | null = null
|
||||
@ -514,29 +522,29 @@ export class Module {
|
||||
}
|
||||
}
|
||||
|
||||
createGetLocal(
|
||||
local_get(
|
||||
index: i32,
|
||||
type: NativeType
|
||||
): ExpressionRef {
|
||||
return _BinaryenGetLocal(this.ref, index, type);
|
||||
return _BinaryenLocalGet(this.ref, index, type);
|
||||
}
|
||||
|
||||
createTeeLocal(
|
||||
local_tee(
|
||||
index: i32,
|
||||
value: ExpressionRef
|
||||
): ExpressionRef {
|
||||
return _BinaryenTeeLocal(this.ref, index, value);
|
||||
return _BinaryenLocalTee(this.ref, index, value);
|
||||
}
|
||||
|
||||
createGetGlobal(
|
||||
global_get(
|
||||
name: string,
|
||||
type: NativeType
|
||||
): ExpressionRef {
|
||||
var cStr = this.allocStringCached(name);
|
||||
return _BinaryenGetGlobal(this.ref, cStr, type);
|
||||
return _BinaryenGlobalGet(this.ref, cStr, type);
|
||||
}
|
||||
|
||||
createLoad(
|
||||
load(
|
||||
bytes: Index,
|
||||
signed: bool,
|
||||
ptr: ExpressionRef,
|
||||
@ -547,7 +555,7 @@ export class Module {
|
||||
return _BinaryenLoad(this.ref, bytes, signed ? 1 : 0, offset, align, type, ptr);
|
||||
}
|
||||
|
||||
createStore(
|
||||
store(
|
||||
bytes: Index,
|
||||
ptr: ExpressionRef,
|
||||
value: ExpressionRef,
|
||||
@ -555,10 +563,11 @@ export class Module {
|
||||
offset: Index = 0,
|
||||
align: Index = bytes // naturally aligned by default
|
||||
): ExpressionRef {
|
||||
if (type < NativeType.None || type > NativeType.V128) throw new Error("here: " + type);
|
||||
return _BinaryenStore(this.ref, bytes, offset, align, ptr, value, type);
|
||||
}
|
||||
|
||||
createAtomicLoad(
|
||||
atomic_load(
|
||||
bytes: Index,
|
||||
ptr: ExpressionRef,
|
||||
type: NativeType,
|
||||
@ -567,7 +576,7 @@ export class Module {
|
||||
return _BinaryenAtomicLoad(this.ref, bytes, offset, type, ptr);
|
||||
}
|
||||
|
||||
createAtomicStore(
|
||||
atomic_store(
|
||||
bytes: Index,
|
||||
ptr: ExpressionRef,
|
||||
value: ExpressionRef,
|
||||
@ -577,7 +586,7 @@ export class Module {
|
||||
return _BinaryenAtomicStore(this.ref, bytes, offset, ptr, value, type);
|
||||
}
|
||||
|
||||
createAtomicRMW(
|
||||
atomic_rmw(
|
||||
op: AtomicRMWOp,
|
||||
bytes: Index,
|
||||
offset: Index,
|
||||
@ -588,7 +597,7 @@ export class Module {
|
||||
return _BinaryenAtomicRMW(this.ref, op, bytes, offset, ptr, value, type);
|
||||
}
|
||||
|
||||
createAtomicCmpxchg(
|
||||
atomic_cmpxchg(
|
||||
bytes: Index,
|
||||
offset: Index,
|
||||
ptr: ExpressionRef,
|
||||
@ -599,7 +608,7 @@ export class Module {
|
||||
return _BinaryenAtomicCmpxchg(this.ref, bytes, offset, ptr, expected, replacement, type);
|
||||
}
|
||||
|
||||
createAtomicWait(
|
||||
atomic_wait(
|
||||
ptr: ExpressionRef,
|
||||
expected: ExpressionRef,
|
||||
timeout: ExpressionRef,
|
||||
@ -608,7 +617,7 @@ export class Module {
|
||||
return _BinaryenAtomicWait(this.ref, ptr, expected, timeout, expectedType);
|
||||
}
|
||||
|
||||
createAtomicNotify(
|
||||
atomic_notify(
|
||||
ptr: ExpressionRef,
|
||||
notifyCount: ExpressionRef
|
||||
): ExpressionRef {
|
||||
@ -617,22 +626,22 @@ export class Module {
|
||||
|
||||
// statements
|
||||
|
||||
createSetLocal(
|
||||
local_set(
|
||||
index: Index,
|
||||
value: ExpressionRef
|
||||
): ExpressionRef {
|
||||
return _BinaryenSetLocal(this.ref, index, value);
|
||||
return _BinaryenLocalSet(this.ref, index, value);
|
||||
}
|
||||
|
||||
createSetGlobal(
|
||||
global_set(
|
||||
name: string,
|
||||
value: ExpressionRef
|
||||
): ExpressionRef {
|
||||
var cStr = this.allocStringCached(name);
|
||||
return _BinaryenSetGlobal(this.ref, cStr, value);
|
||||
return _BinaryenGlobalSet(this.ref, cStr, value);
|
||||
}
|
||||
|
||||
createBlock(
|
||||
block(
|
||||
label: string | null,
|
||||
children: ExpressionRef[],
|
||||
type: NativeType = NativeType.None
|
||||
@ -646,7 +655,7 @@ export class Module {
|
||||
}
|
||||
}
|
||||
|
||||
createBreak(
|
||||
br(
|
||||
label: string | null,
|
||||
condition: ExpressionRef = 0,
|
||||
value: ExpressionRef = 0
|
||||
@ -655,13 +664,13 @@ export class Module {
|
||||
return _BinaryenBreak(this.ref, cStr, condition, value);
|
||||
}
|
||||
|
||||
createDrop(
|
||||
drop(
|
||||
expression: ExpressionRef
|
||||
): ExpressionRef {
|
||||
return _BinaryenDrop(this.ref, expression);
|
||||
}
|
||||
|
||||
createLoop(
|
||||
loop(
|
||||
label: string | null,
|
||||
body: ExpressionRef
|
||||
): ExpressionRef {
|
||||
@ -669,7 +678,7 @@ export class Module {
|
||||
return _BinaryenLoop(this.ref, cStr, body);
|
||||
}
|
||||
|
||||
createIf(
|
||||
if(
|
||||
condition: ExpressionRef,
|
||||
ifTrue: ExpressionRef,
|
||||
ifFalse: ExpressionRef = 0
|
||||
@ -677,17 +686,17 @@ export class Module {
|
||||
return _BinaryenIf(this.ref, condition, ifTrue, ifFalse);
|
||||
}
|
||||
|
||||
createNop(): ExpressionRef {
|
||||
nop(): ExpressionRef {
|
||||
return _BinaryenNop(this.ref);
|
||||
}
|
||||
|
||||
createReturn(
|
||||
return(
|
||||
expression: ExpressionRef = 0
|
||||
): ExpressionRef {
|
||||
return _BinaryenReturn(this.ref, expression);
|
||||
}
|
||||
|
||||
createSelect(
|
||||
select(
|
||||
ifTrue: ExpressionRef,
|
||||
ifFalse: ExpressionRef,
|
||||
condition: ExpressionRef
|
||||
@ -695,7 +704,7 @@ export class Module {
|
||||
return _BinaryenSelect(this.ref, condition, ifTrue, ifFalse);
|
||||
}
|
||||
|
||||
createSwitch(
|
||||
switch(
|
||||
names: string[],
|
||||
defaultName: string | null,
|
||||
condition: ExpressionRef,
|
||||
@ -715,7 +724,7 @@ export class Module {
|
||||
}
|
||||
}
|
||||
|
||||
createCall(
|
||||
call(
|
||||
target: string,
|
||||
operands: ExpressionRef[] | null,
|
||||
returnType: NativeType
|
||||
@ -729,7 +738,7 @@ export class Module {
|
||||
}
|
||||
}
|
||||
|
||||
createCallIndirect(
|
||||
call_indirect(
|
||||
index: ExpressionRef,
|
||||
operands: ExpressionRef[] | null,
|
||||
typeName: string
|
||||
@ -743,13 +752,13 @@ export class Module {
|
||||
}
|
||||
}
|
||||
|
||||
createUnreachable(): ExpressionRef {
|
||||
unreachable(): ExpressionRef {
|
||||
return _BinaryenUnreachable(this.ref);
|
||||
}
|
||||
|
||||
// bulk memory
|
||||
|
||||
createMemoryCopy(
|
||||
memory_copy(
|
||||
dest: ExpressionRef,
|
||||
source: ExpressionRef,
|
||||
size: ExpressionRef
|
||||
@ -757,7 +766,7 @@ export class Module {
|
||||
return _BinaryenMemoryCopy(this.ref, dest, source, size);
|
||||
}
|
||||
|
||||
createMemoryFill(
|
||||
memory_fill(
|
||||
dest: ExpressionRef,
|
||||
value: ExpressionRef,
|
||||
size: ExpressionRef
|
||||
@ -767,7 +776,7 @@ export class Module {
|
||||
|
||||
// simd
|
||||
|
||||
createSIMDExtract(
|
||||
simd_extract(
|
||||
op: SIMDExtractOp,
|
||||
vec: ExpressionRef,
|
||||
idx: u8
|
||||
@ -775,7 +784,7 @@ export class Module {
|
||||
return _BinaryenSIMDExtract(this.ref, op, vec, idx);
|
||||
}
|
||||
|
||||
createSIMDReplace(
|
||||
simd_replace(
|
||||
op: SIMDReplaceOp,
|
||||
vec: ExpressionRef,
|
||||
idx: u8,
|
||||
@ -784,7 +793,7 @@ export class Module {
|
||||
return _BinaryenSIMDReplace(this.ref, op, vec, idx, value);
|
||||
}
|
||||
|
||||
createSIMDShuffle(
|
||||
simd_shuffle(
|
||||
vec1: ExpressionRef,
|
||||
vec2: ExpressionRef,
|
||||
mask: Uint8Array
|
||||
@ -798,7 +807,7 @@ export class Module {
|
||||
}
|
||||
}
|
||||
|
||||
createSIMDBitselect(
|
||||
simd_bitselect(
|
||||
vec1: ExpressionRef,
|
||||
vec2: ExpressionRef,
|
||||
cond: ExpressionRef
|
||||
@ -806,7 +815,7 @@ export class Module {
|
||||
return _BinaryenSIMDBitselect(this.ref, vec1, vec2, cond);
|
||||
}
|
||||
|
||||
createSIMDShift(
|
||||
simd_shift(
|
||||
op: SIMDShiftOp,
|
||||
vec: ExpressionRef,
|
||||
shift: ExpressionRef
|
||||
@ -985,8 +994,8 @@ export class Module {
|
||||
segs[i] = allocU8Array(buffer);
|
||||
psvs[i] = 0; // no passive segments currently
|
||||
offs[i] = target == Target.WASM64
|
||||
? this.createI64(i64_low(offset), i64_high(offset))
|
||||
: this.createI32(i64_low(offset));
|
||||
? this.i64(i64_low(offset), i64_high(offset))
|
||||
: this.i32(i64_low(offset));
|
||||
sizs[i] = buffer.length;
|
||||
}
|
||||
var cArr1 = allocI32Array(segs);
|
||||
@ -1046,6 +1055,14 @@ export class Module {
|
||||
_BinaryenSetDebugInfo(on);
|
||||
}
|
||||
|
||||
getFeatures(): BinaryenFeatureFlags {
|
||||
return _BinaryenModuleGetFeatures(this.ref);
|
||||
}
|
||||
|
||||
setFeatures(featureFlags: BinaryenFeatureFlags): void {
|
||||
_BinaryenModuleSetFeatures(this.ref, featureFlags);
|
||||
}
|
||||
|
||||
optimize(func: FunctionRef = 0): void {
|
||||
if (func) {
|
||||
_BinaryenFunctionOptimize(func, this.ref);
|
||||
@ -1181,19 +1198,19 @@ export class Module {
|
||||
case ExpressionId.Const: {
|
||||
switch (_BinaryenExpressionGetType(expr)) {
|
||||
case NativeType.I32: {
|
||||
return this.createI32(_BinaryenConstGetValueI32(expr));
|
||||
return this.i32(_BinaryenConstGetValueI32(expr));
|
||||
}
|
||||
case NativeType.I64: {
|
||||
return this.createI64(
|
||||
return this.i64(
|
||||
_BinaryenConstGetValueI64Low(expr),
|
||||
_BinaryenConstGetValueI64High(expr)
|
||||
);
|
||||
}
|
||||
case NativeType.F32: {
|
||||
return this.createF32(_BinaryenConstGetValueF32(expr));
|
||||
return this.f32(_BinaryenConstGetValueF32(expr));
|
||||
}
|
||||
case NativeType.F64: {
|
||||
return this.createF64(_BinaryenConstGetValueF64(expr));
|
||||
return this.f64(_BinaryenConstGetValueF64(expr));
|
||||
}
|
||||
case NativeType.V128: {
|
||||
// TODO
|
||||
@ -1204,16 +1221,16 @@ export class Module {
|
||||
}
|
||||
}
|
||||
}
|
||||
case ExpressionId.GetLocal: {
|
||||
return _BinaryenGetLocal(this.ref,
|
||||
_BinaryenGetLocalGetIndex(expr),
|
||||
case ExpressionId.LocalGet: {
|
||||
return _BinaryenLocalGet(this.ref,
|
||||
_BinaryenLocalGetGetIndex(expr),
|
||||
_BinaryenExpressionGetType(expr)
|
||||
);
|
||||
}
|
||||
case ExpressionId.GetGlobal: {
|
||||
let globalName = _BinaryenGetGlobalGetName(expr);
|
||||
case ExpressionId.GlobalGet: {
|
||||
let globalName = _BinaryenGlobalGetGetName(expr);
|
||||
if (!globalName) break;
|
||||
return _BinaryenGetGlobal(this.ref, globalName, _BinaryenExpressionGetType(expr));
|
||||
return _BinaryenGlobalGet(this.ref, globalName, _BinaryenExpressionGetType(expr));
|
||||
}
|
||||
case ExpressionId.Load: {
|
||||
if (!(nested1 = this.cloneExpression(_BinaryenLoadGetPtr(expr), noSideEffects, maxDepth))) {
|
||||
@ -1312,24 +1329,24 @@ export function getConstValueF64(expr: ExpressionRef): f32 {
|
||||
return _BinaryenConstGetValueF64(expr);
|
||||
}
|
||||
|
||||
export function getGetLocalIndex(expr: ExpressionRef): Index {
|
||||
return _BinaryenGetLocalGetIndex(expr);
|
||||
export function getLocalGetIndex(expr: ExpressionRef): Index {
|
||||
return _BinaryenLocalGetGetIndex(expr);
|
||||
}
|
||||
|
||||
export function getSetLocalIndex(expr: ExpressionRef): Index {
|
||||
return _BinaryenSetLocalGetIndex(expr);
|
||||
export function getLocalSetIndex(expr: ExpressionRef): Index {
|
||||
return _BinaryenLocalSetGetIndex(expr);
|
||||
}
|
||||
|
||||
export function getSetLocalValue(expr: ExpressionRef): ExpressionRef {
|
||||
return _BinaryenSetLocalGetValue(expr);
|
||||
export function getLocalSetValue(expr: ExpressionRef): ExpressionRef {
|
||||
return _BinaryenLocalSetGetValue(expr);
|
||||
}
|
||||
|
||||
export function isTeeLocal(expr: ExpressionRef): bool {
|
||||
return _BinaryenSetLocalIsTee(expr);
|
||||
export function isLocalTee(expr: ExpressionRef): bool {
|
||||
return _BinaryenLocalSetIsTee(expr);
|
||||
}
|
||||
|
||||
export function getGetGlobalName(expr: ExpressionRef): string | null {
|
||||
return readString(_BinaryenGetGlobalGetName(expr));
|
||||
export function getGlobalGetName(expr: ExpressionRef): string | null {
|
||||
return readString(_BinaryenGlobalGetGetName(expr));
|
||||
}
|
||||
|
||||
export function getBinaryOp(expr: ExpressionRef): BinaryOp {
|
||||
@ -1448,6 +1465,14 @@ export function getCallTarget(expr: ExpressionRef): string | null {
|
||||
return readString(_BinaryenCallGetTarget(expr));
|
||||
}
|
||||
|
||||
export function getCallOperandCount(expr: ExpressionRef): i32 {
|
||||
return _BinaryenCallGetNumOperands(expr);
|
||||
}
|
||||
|
||||
export function getCallOperand(expr: ExpressionRef, index: Index): ExpressionRef {
|
||||
return _BinaryenCallGetOperand(expr, index);
|
||||
}
|
||||
|
||||
export function getHostOp(expr: ExpressionRef): ExpressionRef {
|
||||
return _BinaryenHostGetOp(expr);
|
||||
}
|
||||
@ -1765,3 +1790,177 @@ export function needsExplicitUnreachable(expr: ExpressionRef): bool {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Traverses all expression members of an expression, calling the given visitor. */
|
||||
export function traverse<T>(expr: ExpressionRef, data: T, visit: (expr: ExpressionRef, data: T) => void): bool {
|
||||
switch (getExpressionId(expr)) {
|
||||
case ExpressionId.Block: {
|
||||
for (let i = 0, n = _BinaryenBlockGetNumChildren(expr); i < n; ++i) {
|
||||
visit(_BinaryenBlockGetChild(expr, i), data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ExpressionId.If: {
|
||||
visit(_BinaryenIfGetCondition(expr), data);
|
||||
visit(_BinaryenIfGetIfTrue(expr), data);
|
||||
let ifFalse = _BinaryenIfGetIfFalse(expr);
|
||||
if (ifFalse) visit(ifFalse, data);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.Loop: {
|
||||
visit(_BinaryenLoopGetBody(expr), data);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.Break: {
|
||||
let condition = _BinaryenBreakGetCondition(expr);
|
||||
if (condition) visit(condition, data);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.Switch: {
|
||||
visit(_BinaryenSwitchGetCondition(expr), data);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.Call: {
|
||||
for (let i = 0, n = _BinaryenCallGetNumOperands(expr); i < n; ++i) {
|
||||
visit(_BinaryenCallGetOperand(expr, i), data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ExpressionId.CallIndirect: {
|
||||
for (let i = 0, n = _BinaryenCallIndirectGetNumOperands(expr); i < n; ++i) {
|
||||
visit(_BinaryenCallIndirectGetOperand(expr, i), data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ExpressionId.LocalGet: {
|
||||
break;
|
||||
}
|
||||
case ExpressionId.LocalSet: {
|
||||
visit(_BinaryenLocalSetGetValue(expr), data);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.GlobalGet: {
|
||||
break;
|
||||
}
|
||||
case ExpressionId.GlobalSet: {
|
||||
visit(_BinaryenGlobalSetGetValue(expr), data);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.Load: {
|
||||
visit(_BinaryenLoadGetPtr(expr), data);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.Store: {
|
||||
visit(_BinaryenStoreGetPtr(expr), data);
|
||||
visit(_BinaryenStoreGetValue(expr), data);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.AtomicRMW: {
|
||||
visit(_BinaryenAtomicRMWGetPtr(expr), data);
|
||||
visit(_BinaryenAtomicRMWGetValue(expr), data);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.AtomicCmpxchg: {
|
||||
visit(_BinaryenAtomicCmpxchgGetPtr(expr), data);
|
||||
visit(_BinaryenAtomicCmpxchgGetExpected(expr), data);
|
||||
visit(_BinaryenAtomicCmpxchgGetReplacement(expr), data);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.AtomicWait: {
|
||||
visit(_BinaryenAtomicWaitGetPtr(expr), data);
|
||||
visit(_BinaryenAtomicWaitGetExpected(expr), data);
|
||||
visit(_BinaryenAtomicWaitGetTimeout(expr), data);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.AtomicNotify: {
|
||||
visit(_BinaryenAtomicNotifyGetPtr(expr), data);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.SIMDExtract: {
|
||||
visit(_BinaryenSIMDExtractGetVec(expr), data);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.SIMDReplace: {
|
||||
visit(_BinaryenSIMDReplaceGetVec(expr), data);
|
||||
visit(_BinaryenSIMDReplaceGetValue(expr), data);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.SIMDShuffle: {
|
||||
visit(_BinaryenSIMDShuffleGetLeft(expr), data);
|
||||
visit(_BinaryenSIMDShuffleGetRight(expr), data);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.SIMDBitselect: {
|
||||
visit(_BinaryenSIMDBitselectGetLeft(expr), data);
|
||||
visit(_BinaryenSIMDBitselectGetRight(expr), data);
|
||||
visit(_BinaryenSIMDBitselectGetCond(expr), data);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.SIMDShift: {
|
||||
visit(_BinaryenSIMDShiftGetVec(expr), data);
|
||||
visit(_BinaryenSIMDShiftGetShift(expr), data);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.MemoryInit: {
|
||||
visit(_BinaryenMemoryInitGetDest(expr), data);
|
||||
visit(_BinaryenMemoryInitGetOffset(expr), data);
|
||||
visit(_BinaryenMemoryInitGetSize(expr), data);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.DataDrop: {
|
||||
break;
|
||||
}
|
||||
case ExpressionId.MemoryCopy: {
|
||||
visit(_BinaryenMemoryCopyGetDest(expr), data);
|
||||
visit(_BinaryenMemoryCopyGetSource(expr), data);
|
||||
visit(_BinaryenMemoryCopyGetSize(expr), data);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.MemoryFill: {
|
||||
visit(_BinaryenMemoryFillGetDest(expr), data);
|
||||
visit(_BinaryenMemoryFillGetValue(expr), data);
|
||||
visit(_BinaryenMemoryFillGetSize(expr), data);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.Const: {
|
||||
break;
|
||||
}
|
||||
case ExpressionId.Unary: {
|
||||
visit(_BinaryenUnaryGetValue(expr), data);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.Binary: {
|
||||
visit(_BinaryenBinaryGetLeft(expr), data);
|
||||
visit(_BinaryenBinaryGetRight(expr), data);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.Select: {
|
||||
visit(_BinaryenSelectGetIfTrue(expr), data);
|
||||
visit(_BinaryenSelectGetIfFalse(expr), data);
|
||||
visit(_BinaryenSelectGetCondition(expr), data);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.Drop: {
|
||||
visit(_BinaryenDropGetValue(expr), data);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.Return: {
|
||||
visit(_BinaryenReturnGetValue(expr), data);
|
||||
break;
|
||||
}
|
||||
case ExpressionId.Host: {
|
||||
for (let i = 0, n = _BinaryenHostGetNumOperands(expr); i < n; ++i) {
|
||||
visit(_BinaryenHostGetOperand(expr, i), data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ExpressionId.Nop: {
|
||||
break;
|
||||
}
|
||||
case ExpressionId.Unreachable: {
|
||||
break;
|
||||
}
|
||||
default: assert(false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
106
src/parser.ts
106
src/parser.ts
@ -171,31 +171,34 @@ export class Parser extends DiagnosticEmitter {
|
||||
// check modifiers
|
||||
var exportStart: i32 = 0;
|
||||
var exportEnd: i32 = 0;
|
||||
var defaultStart: i32 = 0;
|
||||
var defaultEnd: i32 = 0;
|
||||
if (tn.skip(Token.EXPORT)) {
|
||||
if (tn.skip(Token.DEFAULT)) {
|
||||
this.error(
|
||||
DiagnosticCode.Operation_not_supported,
|
||||
tn.range()
|
||||
);
|
||||
}
|
||||
if (startPos < 0) startPos = tn.tokenPos;
|
||||
flags |= CommonFlags.EXPORT;
|
||||
exportStart = tn.tokenPos;
|
||||
exportEnd = tn.pos;
|
||||
if (tn.skip(Token.DEFAULT)) {
|
||||
defaultStart = tn.tokenPos;
|
||||
defaultEnd = tn.pos;
|
||||
}
|
||||
}
|
||||
|
||||
var declareStart: i32 = 0;
|
||||
var declareEnd: i32 = 0;
|
||||
var contextIsAmbient = namespace != null && namespace.is(CommonFlags.AMBIENT);
|
||||
if (tn.skip(Token.DECLARE)) {
|
||||
if (startPos < 0) startPos = tn.tokenPos;
|
||||
if (contextIsAmbient) {
|
||||
this.error(
|
||||
DiagnosticCode.A_declare_modifier_cannot_be_used_in_an_already_ambient_context,
|
||||
tn.range()
|
||||
); // recoverable
|
||||
} else {
|
||||
if (startPos < 0) startPos = tn.tokenPos;
|
||||
declareStart = startPos;
|
||||
declareEnd = tn.pos;
|
||||
flags |= CommonFlags.DECLARE | CommonFlags.AMBIENT;
|
||||
}
|
||||
flags |= CommonFlags.DECLARE | CommonFlags.AMBIENT;
|
||||
} else if (contextIsAmbient) {
|
||||
flags |= CommonFlags.AMBIENT;
|
||||
}
|
||||
@ -297,7 +300,18 @@ export class Parser extends DiagnosticEmitter {
|
||||
|
||||
// handle plain exports
|
||||
if (flags & CommonFlags.EXPORT) {
|
||||
statement = this.parseExport(tn, startPos, (flags & CommonFlags.DECLARE) != 0);
|
||||
if (defaultEnd && tn.skipIdentifier(IdentifierHandling.PREFER)) {
|
||||
if (declareEnd) {
|
||||
this.error(
|
||||
DiagnosticCode.An_export_assignment_cannot_have_modifiers,
|
||||
tn.range(declareStart, declareEnd)
|
||||
);
|
||||
}
|
||||
statement = this.parseExportDefaultAlias(tn, startPos, defaultStart, defaultEnd);
|
||||
defaultStart = defaultEnd = 0; // consume
|
||||
} else {
|
||||
statement = this.parseExport(tn, startPos, (flags & CommonFlags.DECLARE) != 0);
|
||||
}
|
||||
|
||||
// handle non-declaration statements
|
||||
} else {
|
||||
@ -330,6 +344,25 @@ export class Parser extends DiagnosticEmitter {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// check if this an `export default` declaration
|
||||
if (defaultEnd && statement !== null) {
|
||||
switch (statement.kind) {
|
||||
case NodeKind.ENUMDECLARATION:
|
||||
case NodeKind.FUNCTIONDECLARATION:
|
||||
case NodeKind.CLASSDECLARATION:
|
||||
case NodeKind.INTERFACEDECLARATION:
|
||||
case NodeKind.NAMESPACEDECLARATION: {
|
||||
return Node.createExportDefaultStatement(<DeclarationStatement>statement, tn.range(startPos, tn.pos));
|
||||
}
|
||||
default: {
|
||||
this.error(
|
||||
DiagnosticCode._0_modifier_cannot_be_used_here,
|
||||
tn.range(defaultStart, defaultEnd), "default"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return statement;
|
||||
}
|
||||
|
||||
@ -1588,6 +1621,7 @@ export class Parser extends DiagnosticEmitter {
|
||||
}
|
||||
} while (!tn.skip(Token.CLOSEBRACE));
|
||||
}
|
||||
declaration.range.end = tn.pos;
|
||||
return declaration;
|
||||
}
|
||||
|
||||
@ -1639,6 +1673,7 @@ export class Parser extends DiagnosticEmitter {
|
||||
}
|
||||
} while (!tn.skip(Token.CLOSEBRACE));
|
||||
}
|
||||
declaration.range.end = tn.pos;
|
||||
return Node.createClassExpression(declaration);
|
||||
}
|
||||
|
||||
@ -2162,7 +2197,7 @@ export class Parser extends DiagnosticEmitter {
|
||||
let identifier = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
|
||||
if (tn.skip(Token.OPENBRACE)) {
|
||||
let members = new Array<Statement>();
|
||||
let ns = Node.createNamespaceDeclaration(
|
||||
let declaration = Node.createNamespaceDeclaration(
|
||||
identifier,
|
||||
members,
|
||||
decorators,
|
||||
@ -2170,7 +2205,7 @@ export class Parser extends DiagnosticEmitter {
|
||||
tn.range(startPos, tn.pos)
|
||||
);
|
||||
while (!tn.skip(Token.CLOSEBRACE)) {
|
||||
let member = this.parseTopLevelStatement(tn, ns);
|
||||
let member = this.parseTopLevelStatement(tn, declaration);
|
||||
if (member) members.push(member);
|
||||
else {
|
||||
this.skipStatement(tn);
|
||||
@ -2183,8 +2218,9 @@ export class Parser extends DiagnosticEmitter {
|
||||
}
|
||||
}
|
||||
}
|
||||
declaration.range.end = tn.pos;
|
||||
tn.skip(Token.SEMICOLON);
|
||||
return ns;
|
||||
return declaration;
|
||||
} else {
|
||||
this.error(
|
||||
DiagnosticCode._0_expected,
|
||||
@ -2318,6 +2354,28 @@ export class Parser extends DiagnosticEmitter {
|
||||
return null;
|
||||
}
|
||||
|
||||
parseExportDefaultAlias(
|
||||
tn: Tokenizer,
|
||||
startPos: i32,
|
||||
defaultStart: i32,
|
||||
defaultEnd: i32
|
||||
): ExportStatement {
|
||||
|
||||
// at 'export' 'default': [Known-To-Be-]Identifier
|
||||
|
||||
var name = tn.readIdentifier();
|
||||
var range = tn.range();
|
||||
var ret = Node.createExportStatement([
|
||||
Node.createExportMember(
|
||||
Node.createIdentifierExpression(name, range),
|
||||
Node.createIdentifierExpression("default", tn.range(defaultStart, defaultEnd)),
|
||||
range
|
||||
)
|
||||
], null, false, tn.range(startPos, tn.pos));
|
||||
tn.skip(Token.SEMICOLON);
|
||||
return ret;
|
||||
}
|
||||
|
||||
parseImport(
|
||||
tn: Tokenizer
|
||||
): ImportStatement | null {
|
||||
@ -2330,7 +2388,7 @@ export class Parser extends DiagnosticEmitter {
|
||||
var members: ImportDeclaration[] | null = null;
|
||||
var namespaceName: IdentifierExpression | null = null;
|
||||
var skipFrom = false;
|
||||
if (tn.skip(Token.OPENBRACE)) {
|
||||
if (tn.skip(Token.OPENBRACE)) { // import { ... } from "file"
|
||||
members = new Array();
|
||||
while (!tn.skip(Token.CLOSEBRACE)) {
|
||||
let member = this.parseImportDeclaration(tn);
|
||||
@ -2348,7 +2406,7 @@ export class Parser extends DiagnosticEmitter {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (tn.skip(Token.ASTERISK)) {
|
||||
} else if (tn.skip(Token.ASTERISK)) { // import * from "file"
|
||||
if (tn.skip(Token.AS)) {
|
||||
if (tn.skipIdentifier()) {
|
||||
namespaceName = Node.createIdentifierExpression(tn.readIdentifier(), tn.range());
|
||||
@ -2366,7 +2424,25 @@ export class Parser extends DiagnosticEmitter {
|
||||
);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
} else if (tn.skip(Token.IDENTIFIER, IdentifierHandling.PREFER)) { // import Name from "file"
|
||||
let name = tn.readIdentifier();
|
||||
let range = tn.range();
|
||||
members = [
|
||||
Node.createImportDeclaration(
|
||||
Node.createIdentifierExpression("default", range),
|
||||
Node.createIdentifierExpression(name, range),
|
||||
range
|
||||
)
|
||||
];
|
||||
if (tn.skip(Token.COMMA)) {
|
||||
// TODO: default + star, default + members
|
||||
this.error(
|
||||
DiagnosticCode.Operation_not_supported,
|
||||
tn.range()
|
||||
);
|
||||
return null;
|
||||
}
|
||||
} else { // import "file"
|
||||
skipFrom = true;
|
||||
}
|
||||
|
||||
|
769
src/program.ts
769
src/program.ts
File diff suppressed because it is too large
Load Diff
@ -82,6 +82,10 @@ import {
|
||||
Token
|
||||
} from "./tokenizer";
|
||||
|
||||
import {
|
||||
BuiltinSymbols
|
||||
} from "./builtins";
|
||||
|
||||
/** Indicates whether errors are reported or not. */
|
||||
export enum ReportMode {
|
||||
/** Report errors. */
|
||||
@ -368,12 +372,26 @@ export class Resolver extends DiagnosticEmitter {
|
||||
);
|
||||
// recoverable
|
||||
}
|
||||
return this.resolveType(
|
||||
let type = this.resolveType(
|
||||
(<TypeDefinition>element).typeNode,
|
||||
element,
|
||||
contextualTypeArguments,
|
||||
reportMode
|
||||
);
|
||||
if (!type) return null;
|
||||
if (node.isNullable) {
|
||||
if (!type.is(TypeFlags.REFERENCE)) {
|
||||
if (reportMode == ReportMode.REPORT) {
|
||||
this.error(
|
||||
DiagnosticCode.Basic_type_0_cannot_be_nullable,
|
||||
typeNode.name.range, typeName.identifier.text
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return type.asNullable();
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
if (reportMode == ReportMode.REPORT) {
|
||||
this.error(
|
||||
@ -438,7 +456,7 @@ export class Resolver extends DiagnosticEmitter {
|
||||
): Type[] | null {
|
||||
var minParameterCount = 0;
|
||||
var maxParameterCount = 0;
|
||||
for (let i = 0; i < typeParameters.length; ++i) {
|
||||
for (let i = 0, k = typeParameters.length; i < k; ++i) {
|
||||
if (!typeParameters[i].defaultType) ++minParameterCount;
|
||||
++maxParameterCount;
|
||||
}
|
||||
@ -610,25 +628,40 @@ export class Resolver extends DiagnosticEmitter {
|
||||
case ElementKind.CLASS: { // property access on element access?
|
||||
let elementExpression = this.currentElementExpression;
|
||||
if (elementExpression) {
|
||||
let indexedGet = (<Class>target).lookupOverload(OperatorKind.INDEXED_GET);
|
||||
if (!indexedGet) {
|
||||
this.error(
|
||||
DiagnosticCode.Index_signature_is_missing_in_type_0,
|
||||
elementExpression.range, (<Class>target).internalName
|
||||
);
|
||||
return null;
|
||||
}
|
||||
let returnType = indexedGet.signature.returnType;
|
||||
if (!(target = returnType.classReference)) {
|
||||
// let arrayType = this.program.determineBuiltinArrayType(<Class>target);
|
||||
// if (!arrayType) {
|
||||
let indexedGet = (<Class>target).lookupOverload(OperatorKind.INDEXED_GET);
|
||||
if (!indexedGet) {
|
||||
this.error(
|
||||
DiagnosticCode.Index_signature_is_missing_in_type_0,
|
||||
elementExpression.range, (<Class>target).internalName
|
||||
);
|
||||
return null;
|
||||
}
|
||||
let arrayType = indexedGet.signature.returnType;
|
||||
// }
|
||||
if (!(target = arrayType.classReference)) {
|
||||
this.error(
|
||||
DiagnosticCode.Property_0_does_not_exist_on_type_1,
|
||||
propertyAccess.property.range, propertyName, returnType.toString()
|
||||
propertyAccess.property.range, propertyName, arrayType.toString()
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ElementKind.FUNCTION_PROTOTYPE: { // function Symbol() + type Symbol = _Symbol
|
||||
let shadowType = target.shadowType;
|
||||
if (shadowType) {
|
||||
if (!shadowType.is(CommonFlags.RESOLVED)) {
|
||||
let resolvedType = this.resolveType(shadowType.typeNode, shadowType.parent, null, reportMode);
|
||||
if (resolvedType) shadowType.setType(resolvedType);
|
||||
}
|
||||
let classReference = shadowType.type.classReference;
|
||||
if (classReference) target = classReference.prototype;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look up the member within
|
||||
@ -672,6 +705,7 @@ export class Resolver extends DiagnosticEmitter {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.error(
|
||||
DiagnosticCode.Property_0_does_not_exist_on_type_1,
|
||||
propertyAccess.property.range, propertyName, target.internalName
|
||||
@ -706,19 +740,22 @@ export class Resolver extends DiagnosticEmitter {
|
||||
break;
|
||||
}
|
||||
case ElementKind.CLASS: {
|
||||
let indexedGet = (<Class>target).lookupOverload(OperatorKind.INDEXED_GET);
|
||||
if (!indexedGet) {
|
||||
if (reportMode == ReportMode.REPORT) {
|
||||
this.error(
|
||||
DiagnosticCode.Index_signature_is_missing_in_type_0,
|
||||
elementAccess.range, (<Class>target).internalName
|
||||
);
|
||||
// let arrayType = this.program.determineBuiltinArrayType(<Class>target);
|
||||
// if (!arrayType) {
|
||||
let indexedGet = (<Class>target).lookupOverload(OperatorKind.INDEXED_GET);
|
||||
if (!indexedGet) {
|
||||
if (reportMode == ReportMode.REPORT) {
|
||||
this.error(
|
||||
DiagnosticCode.Index_signature_is_missing_in_type_0,
|
||||
elementAccess.range, (<Class>target).internalName
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
let arrayType = indexedGet.signature.returnType;
|
||||
// }
|
||||
if (targetExpression.kind == NodeKind.ELEMENTACCESS) { // nested element access
|
||||
let returnType = indexedGet.signature.returnType;
|
||||
if (target = returnType.classReference) {
|
||||
if (target = arrayType.classReference) {
|
||||
this.currentThisExpression = targetExpression;
|
||||
this.currentElementExpression = elementAccess.elementExpression;
|
||||
return target;
|
||||
@ -1189,6 +1226,14 @@ export class Resolver extends DiagnosticEmitter {
|
||||
);
|
||||
if (!target) return null;
|
||||
if (target.kind == ElementKind.FUNCTION_PROTOTYPE) {
|
||||
// `unchecked(expr: *): *` is special
|
||||
if (
|
||||
(<FunctionPrototype>target).internalName == BuiltinSymbols.unchecked &&
|
||||
expression.arguments.length > 0
|
||||
) {
|
||||
return this.resolveExpression(expression.arguments[0], flow, contextualType, reportMode);
|
||||
}
|
||||
// otherwise resolve normally
|
||||
let instance = this.resolveFunctionInclTypeArguments(
|
||||
<FunctionPrototype>target,
|
||||
expression.typeArguments,
|
||||
|
133
src/tokenizer.ts
133
src/tokenizer.ts
@ -350,6 +350,7 @@ export function tokenIsAlsoIdentifier(token: Token): bool {
|
||||
case Token.FROM:
|
||||
case Token.FOR:
|
||||
case Token.GET:
|
||||
case Token.INSTANCEOF:
|
||||
case Token.IS:
|
||||
case Token.KEYOF:
|
||||
case Token.MODULE:
|
||||
@ -446,6 +447,7 @@ export class Range {
|
||||
get atStart(): Range {
|
||||
return new Range(this.source, this.start, this.start);
|
||||
}
|
||||
|
||||
get atEnd(): Range {
|
||||
return new Range(this.source, this.end, this.end);
|
||||
}
|
||||
@ -504,13 +506,14 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
this.source = source;
|
||||
this.pos = 0;
|
||||
this.end = source.text.length;
|
||||
this.diagnostics = diagnostics ? diagnostics : new Array();
|
||||
this.diagnostics = diagnostics || new Array();
|
||||
|
||||
var end = this.end;
|
||||
var text = source.text;
|
||||
|
||||
// skip bom
|
||||
if (
|
||||
this.pos < this.end &&
|
||||
this.pos < end &&
|
||||
text.charCodeAt(this.pos) == CharCode.BYTEORDERMARK
|
||||
) {
|
||||
++this.pos;
|
||||
@ -518,13 +521,13 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
|
||||
// skip shebang
|
||||
if (
|
||||
this.pos + 1 < this.end &&
|
||||
this.pos + 1 < end &&
|
||||
text.charCodeAt(this.pos) == CharCode.HASH &&
|
||||
text.charCodeAt(this.pos + 1) == CharCode.EXCLAMATION
|
||||
) {
|
||||
this.pos += 2;
|
||||
while (
|
||||
this.pos < this.end &&
|
||||
this.pos < end &&
|
||||
text.charCodeAt(this.pos) != CharCode.LINEFEED
|
||||
) {
|
||||
++this.pos;
|
||||
@ -542,14 +545,15 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
identifierHandling: IdentifierHandling = IdentifierHandling.DEFAULT,
|
||||
maxTokenLength: i32 = i32.MAX_VALUE
|
||||
): Token {
|
||||
var end = this.end;
|
||||
var text = this.source.text;
|
||||
while (this.pos < this.end) {
|
||||
while (this.pos < end) {
|
||||
this.tokenPos = this.pos;
|
||||
let c = text.charCodeAt(this.pos);
|
||||
switch (c) {
|
||||
case CharCode.CARRIAGERETURN: {
|
||||
if (!(
|
||||
++this.pos < this.end &&
|
||||
++this.pos < end &&
|
||||
text.charCodeAt(this.pos) == CharCode.LINEFEED
|
||||
)) break;
|
||||
// otherwise fall-through
|
||||
@ -565,12 +569,12 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
case CharCode.EXCLAMATION: {
|
||||
++this.pos;
|
||||
if (
|
||||
maxTokenLength > 1 && this.pos < this.end &&
|
||||
maxTokenLength > 1 && this.pos < end &&
|
||||
text.charCodeAt(this.pos) == CharCode.EQUALS
|
||||
) {
|
||||
++this.pos;
|
||||
if (
|
||||
maxTokenLength > 2 && this.pos < this.end &&
|
||||
maxTokenLength > 2 && this.pos < end &&
|
||||
text.charCodeAt(this.pos) == CharCode.EQUALS
|
||||
) {
|
||||
++this.pos;
|
||||
@ -588,7 +592,7 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
case CharCode.PERCENT: {
|
||||
++this.pos;
|
||||
if (
|
||||
maxTokenLength > 1 && this.pos < this.end &&
|
||||
maxTokenLength > 1 && this.pos < end &&
|
||||
text.charCodeAt(this.pos) == CharCode.EQUALS
|
||||
) {
|
||||
++this.pos;
|
||||
@ -598,7 +602,7 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
}
|
||||
case CharCode.AMPERSAND: {
|
||||
++this.pos;
|
||||
if (maxTokenLength > 1 && this.pos < this.end) {
|
||||
if (maxTokenLength > 1 && this.pos < end) {
|
||||
let chr = text.charCodeAt(this.pos);
|
||||
if (chr == CharCode.AMPERSAND) {
|
||||
++this.pos;
|
||||
@ -621,7 +625,7 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
}
|
||||
case CharCode.ASTERISK: {
|
||||
++this.pos;
|
||||
if (maxTokenLength > 1 && this.pos < this.end) {
|
||||
if (maxTokenLength > 1 && this.pos < end) {
|
||||
let chr = text.charCodeAt(this.pos);
|
||||
if (chr == CharCode.EQUALS) {
|
||||
++this.pos;
|
||||
@ -630,7 +634,7 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
if (chr == CharCode.ASTERISK) {
|
||||
++this.pos;
|
||||
if (
|
||||
maxTokenLength > 2 && this.pos < this.end &&
|
||||
maxTokenLength > 2 && this.pos < end &&
|
||||
text.charCodeAt(this.pos) == CharCode.EQUALS
|
||||
) {
|
||||
++this.pos;
|
||||
@ -643,7 +647,7 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
}
|
||||
case CharCode.PLUS: {
|
||||
++this.pos;
|
||||
if (maxTokenLength > 1 && this.pos < this.end) {
|
||||
if (maxTokenLength > 1 && this.pos < end) {
|
||||
let chr = text.charCodeAt(this.pos);
|
||||
if (chr == CharCode.PLUS) {
|
||||
++this.pos;
|
||||
@ -662,7 +666,7 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
}
|
||||
case CharCode.MINUS: {
|
||||
++this.pos;
|
||||
if (maxTokenLength > 1 && this.pos < this.end) {
|
||||
if (maxTokenLength > 1 && this.pos < end) {
|
||||
let chr = text.charCodeAt(this.pos);
|
||||
if (chr == CharCode.MINUS) {
|
||||
++this.pos;
|
||||
@ -677,14 +681,14 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
}
|
||||
case CharCode.DOT: {
|
||||
++this.pos;
|
||||
if (maxTokenLength > 1 && this.pos < this.end) {
|
||||
if (maxTokenLength > 1 && this.pos < end) {
|
||||
let chr = text.charCodeAt(this.pos);
|
||||
if (isDecimalDigit(chr)) {
|
||||
--this.pos;
|
||||
return Token.FLOATLITERAL; // expects a call to readFloat
|
||||
}
|
||||
if (
|
||||
maxTokenLength > 2 && this.pos + 1 < this.end &&
|
||||
maxTokenLength > 2 && this.pos + 1 < end &&
|
||||
chr == CharCode.DOT &&
|
||||
text.charCodeAt(this.pos + 1) == CharCode.DOT
|
||||
) {
|
||||
@ -697,18 +701,18 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
case CharCode.SLASH: {
|
||||
let commentStartPos = this.pos;
|
||||
++this.pos;
|
||||
if (maxTokenLength > 1 && this.pos < this.end) {
|
||||
if (maxTokenLength > 1 && this.pos < end) {
|
||||
let chr = text.charCodeAt(this.pos);
|
||||
if (chr == CharCode.SLASH) { // single-line
|
||||
let commentKind = CommentKind.LINE;
|
||||
if (
|
||||
this.pos + 1 < this.end &&
|
||||
this.pos + 1 < end &&
|
||||
text.charCodeAt(this.pos + 1) == CharCode.SLASH
|
||||
) {
|
||||
++this.pos;
|
||||
commentKind = CommentKind.TRIPLE;
|
||||
}
|
||||
while (++this.pos < this.end) {
|
||||
while (++this.pos < end) {
|
||||
if (text.charCodeAt(this.pos) == CharCode.LINEFEED) {
|
||||
++this.pos;
|
||||
break;
|
||||
@ -725,11 +729,11 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
}
|
||||
if (chr == CharCode.ASTERISK) { // multi-line
|
||||
let closed = false;
|
||||
while (++this.pos < this.end) {
|
||||
while (++this.pos < end) {
|
||||
c = text.charCodeAt(this.pos);
|
||||
if (
|
||||
c == CharCode.ASTERISK &&
|
||||
this.pos + 1 < this.end &&
|
||||
this.pos + 1 < end &&
|
||||
text.charCodeAt(this.pos + 1) == CharCode.SLASH
|
||||
) {
|
||||
this.pos += 2;
|
||||
@ -782,13 +786,13 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
}
|
||||
case CharCode.LESSTHAN: {
|
||||
++this.pos;
|
||||
if (maxTokenLength > 1 && this.pos < this.end) {
|
||||
if (maxTokenLength > 1 && this.pos < end) {
|
||||
let chr = text.charCodeAt(this.pos);
|
||||
if (chr == CharCode.LESSTHAN) {
|
||||
++this.pos;
|
||||
if (
|
||||
maxTokenLength > 2 &&
|
||||
this.pos < this.end &&
|
||||
this.pos < end &&
|
||||
text.charCodeAt(this.pos) == CharCode.EQUALS
|
||||
) {
|
||||
++this.pos;
|
||||
@ -805,13 +809,13 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
}
|
||||
case CharCode.EQUALS: {
|
||||
++this.pos;
|
||||
if (maxTokenLength > 1 && this.pos < this.end) {
|
||||
if (maxTokenLength > 1 && this.pos < end) {
|
||||
let chr = text.charCodeAt(this.pos);
|
||||
if (chr == CharCode.EQUALS) {
|
||||
++this.pos;
|
||||
if (
|
||||
maxTokenLength > 2 &&
|
||||
this.pos < this.end &&
|
||||
this.pos < end &&
|
||||
text.charCodeAt(this.pos) == CharCode.EQUALS
|
||||
) {
|
||||
++this.pos;
|
||||
@ -828,16 +832,16 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
}
|
||||
case CharCode.GREATERTHAN: {
|
||||
++this.pos;
|
||||
if (maxTokenLength > 1 && this.pos < this.end) {
|
||||
if (maxTokenLength > 1 && this.pos < end) {
|
||||
let chr = text.charCodeAt(this.pos);
|
||||
if (chr == CharCode.GREATERTHAN) {
|
||||
++this.pos;
|
||||
if (maxTokenLength > 2 && this.pos < this.end) {
|
||||
if (maxTokenLength > 2 && this.pos < end) {
|
||||
chr = text.charCodeAt(this.pos);
|
||||
if (chr == CharCode.GREATERTHAN) {
|
||||
++this.pos;
|
||||
if (
|
||||
maxTokenLength > 3 && this.pos < this.end &&
|
||||
maxTokenLength > 3 && this.pos < end &&
|
||||
text.charCodeAt(this.pos) == CharCode.EQUALS
|
||||
) {
|
||||
++this.pos;
|
||||
@ -874,7 +878,7 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
case CharCode.CARET: {
|
||||
++this.pos;
|
||||
if (
|
||||
maxTokenLength > 1 && this.pos < this.end &&
|
||||
maxTokenLength > 1 && this.pos < end &&
|
||||
text.charCodeAt(this.pos) == CharCode.EQUALS
|
||||
) {
|
||||
++this.pos;
|
||||
@ -888,7 +892,7 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
}
|
||||
case CharCode.BAR: {
|
||||
++this.pos;
|
||||
if (maxTokenLength > 1 && this.pos < this.end) {
|
||||
if (maxTokenLength > 1 && this.pos < end) {
|
||||
let chr = text.charCodeAt(this.pos);
|
||||
if (chr == CharCode.BAR) {
|
||||
++this.pos;
|
||||
@ -918,7 +922,7 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
if (isKeywordCharacter(c)) {
|
||||
let posBefore = this.pos;
|
||||
while (
|
||||
++this.pos < this.end &&
|
||||
++this.pos < end &&
|
||||
isIdentifierPart(c = text.charCodeAt(this.pos))
|
||||
) {
|
||||
if (!isKeywordCharacter(c)) {
|
||||
@ -1050,8 +1054,9 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
readIdentifier(): string {
|
||||
var text = this.source.text;
|
||||
var start = this.pos;
|
||||
var end = this.end;
|
||||
while (
|
||||
++this.pos < this.end &&
|
||||
++this.pos < end &&
|
||||
isIdentifierPart(text.charCodeAt(this.pos))
|
||||
);
|
||||
return text.substring(start, this.pos);
|
||||
@ -1061,13 +1066,14 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
var text = this.source.text;
|
||||
var quote = text.charCodeAt(this.pos++);
|
||||
var start = this.pos;
|
||||
var end = this.end;
|
||||
var result = "";
|
||||
while (true) {
|
||||
if (this.pos >= this.end) {
|
||||
if (this.pos >= end) {
|
||||
result += text.substring(start, this.pos);
|
||||
this.error(
|
||||
DiagnosticCode.Unterminated_string_literal,
|
||||
this.range(start - 1, this.end)
|
||||
this.range(start - 1, end)
|
||||
);
|
||||
break;
|
||||
}
|
||||
@ -1096,10 +1102,11 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
readEscapeSequence(): string {
|
||||
if (++this.pos >= this.end) {
|
||||
var end = this.end;
|
||||
if (++this.pos >= end) {
|
||||
this.error(
|
||||
DiagnosticCode.Unexpected_end_of_text,
|
||||
this.range(this.end)
|
||||
this.range(end)
|
||||
);
|
||||
return "";
|
||||
}
|
||||
@ -1118,7 +1125,7 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
case CharCode.DOUBLEQUOTE: return "\"";
|
||||
case CharCode.u: {
|
||||
if (
|
||||
this.pos < this.end &&
|
||||
this.pos < end &&
|
||||
text.charCodeAt(this.pos) == CharCode.OPENBRACE
|
||||
) {
|
||||
++this.pos;
|
||||
@ -1128,7 +1135,7 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
}
|
||||
case CharCode.CARRIAGERETURN: {
|
||||
if (
|
||||
this.pos < this.end &&
|
||||
this.pos < end &&
|
||||
text.charCodeAt(this.pos) == CharCode.LINEFEED
|
||||
) {
|
||||
++this.pos;
|
||||
@ -1145,12 +1152,13 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
readRegexpPattern(): string {
|
||||
var text = this.source.text;
|
||||
var start = this.pos;
|
||||
var end = this.end;
|
||||
var escaped = false;
|
||||
while (true) {
|
||||
if (this.pos >= this.end) {
|
||||
if (this.pos >= end) {
|
||||
this.error(
|
||||
DiagnosticCode.Unterminated_regular_expression_literal,
|
||||
this.range(start, this.end)
|
||||
this.range(start, end)
|
||||
);
|
||||
break;
|
||||
}
|
||||
@ -1177,8 +1185,9 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
readRegexpFlags(): string {
|
||||
var text = this.source.text;
|
||||
var start = this.pos;
|
||||
var end = this.end;
|
||||
var flags = 0;
|
||||
while (this.pos < this.end) {
|
||||
while (this.pos < end) {
|
||||
let c: i32 = text.charCodeAt(this.pos);
|
||||
if (!isIdentifierPart(c)) break;
|
||||
++this.pos;
|
||||
@ -1213,8 +1222,9 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
testInteger(): bool {
|
||||
var end = this.end;
|
||||
var text = this.source.text;
|
||||
if (this.pos + 1 < this.end && text.charCodeAt(this.pos) == CharCode._0) {
|
||||
if (this.pos + 1 < end && text.charCodeAt(this.pos) == CharCode._0) {
|
||||
switch (text.charCodeAt(this.pos + 2)) {
|
||||
case CharCode.x:
|
||||
case CharCode.X:
|
||||
@ -1225,11 +1235,9 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
}
|
||||
}
|
||||
var pos = this.pos;
|
||||
while (pos < this.end) {
|
||||
while (pos < end) {
|
||||
let c = text.charCodeAt(pos);
|
||||
if (c == CharCode.DOT || c == CharCode.e || c == CharCode.E) {
|
||||
return false;
|
||||
}
|
||||
if (c == CharCode.DOT || c == CharCode.e || c == CharCode.E) return false;
|
||||
if ((c < CharCode._0 || c > CharCode._9) && c != CharCode._) break;
|
||||
// does not validate separator placement (this is done in readXYInteger)
|
||||
pos++;
|
||||
@ -1277,7 +1285,8 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
var value = i64_new(0);
|
||||
var i64_4 = i64_new(4);
|
||||
var sepEnd = start;
|
||||
while (this.pos < this.end) {
|
||||
var end = this.end;
|
||||
while (this.pos < end) {
|
||||
let pos = this.pos;
|
||||
let c = text.charCodeAt(pos);
|
||||
if (c >= CharCode._0 && c <= CharCode._9) {
|
||||
@ -1330,10 +1339,11 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
readDecimalInteger(): I64 {
|
||||
var text = this.source.text;
|
||||
var start = this.pos;
|
||||
var end = this.end;
|
||||
var value = i64_new(0);
|
||||
var i64_10 = i64_new(10);
|
||||
var sepEnd = start;
|
||||
while (this.pos < this.end) {
|
||||
while (this.pos < end) {
|
||||
let pos = this.pos;
|
||||
let c = text.charCodeAt(pos);
|
||||
if (c >= CharCode._0 && c <= CharCode._9) {
|
||||
@ -1377,7 +1387,8 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
var value = i64_new(0);
|
||||
var i64_3 = i64_new(3);
|
||||
var sepEnd = start;
|
||||
while (this.pos < this.end) {
|
||||
var end = this.end;
|
||||
while (this.pos < end) {
|
||||
let pos = this.pos;
|
||||
let c = text.charCodeAt(pos);
|
||||
if (c >= CharCode._0 && c <= CharCode._7) {
|
||||
@ -1421,7 +1432,8 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
var value = i64_new(0);
|
||||
var i64_1 = i64_new(1);
|
||||
var sepEnd = start;
|
||||
while (this.pos < this.end) {
|
||||
var end = this.end;
|
||||
while (this.pos < end) {
|
||||
let pos = this.pos;
|
||||
let c = text.charCodeAt(pos);
|
||||
if (c == CharCode._0) {
|
||||
@ -1479,27 +1491,28 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
readDecimalFloat(): f64 {
|
||||
// TODO: numeric separators (parseFloat can't handle these)
|
||||
var start = this.pos;
|
||||
var end = this.end;
|
||||
var text = this.source.text;
|
||||
while (this.pos < this.end && isDecimalDigit(text.charCodeAt(this.pos))) {
|
||||
while (this.pos < end && isDecimalDigit(text.charCodeAt(this.pos))) {
|
||||
++this.pos;
|
||||
}
|
||||
if (this.pos < this.end && text.charCodeAt(this.pos) == CharCode.DOT) {
|
||||
if (this.pos < end && text.charCodeAt(this.pos) == CharCode.DOT) {
|
||||
++this.pos;
|
||||
while (this.pos < this.end && isDecimalDigit(text.charCodeAt(this.pos))) {
|
||||
while (this.pos < end && isDecimalDigit(text.charCodeAt(this.pos))) {
|
||||
++this.pos;
|
||||
}
|
||||
}
|
||||
if (this.pos < this.end) {
|
||||
if (this.pos < end) {
|
||||
let c = text.charCodeAt(this.pos);
|
||||
if (c == CharCode.e || c == CharCode.E) {
|
||||
if (
|
||||
++this.pos < this.end &&
|
||||
++this.pos < end &&
|
||||
(c = text.charCodeAt(this.pos)) == CharCode.MINUS || c == CharCode.PLUS &&
|
||||
isDecimalDigit(text.charCodeAt(this.pos + 1))
|
||||
) {
|
||||
++this.pos;
|
||||
}
|
||||
while (this.pos < this.end && isDecimalDigit(text.charCodeAt(this.pos))) {
|
||||
while (this.pos < end && isDecimalDigit(text.charCodeAt(this.pos))) {
|
||||
++this.pos;
|
||||
}
|
||||
}
|
||||
@ -1514,8 +1527,9 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
readUnicodeEscape(): string {
|
||||
var remain = 4;
|
||||
var value = 0;
|
||||
var end = this.end;
|
||||
var text = this.source.text;
|
||||
while (this.pos < this.end) {
|
||||
while (this.pos < end) {
|
||||
let c = text.charCodeAt(this.pos++);
|
||||
if (c >= CharCode._0 && c <= CharCode._9) {
|
||||
value = (value << 4) + c - CharCode._0;
|
||||
@ -1557,11 +1571,12 @@ export class Tokenizer extends DiagnosticEmitter {
|
||||
invalid = true;
|
||||
}
|
||||
|
||||
var end = this.end;
|
||||
var text = this.source.text;
|
||||
if (this.pos >= this.end) {
|
||||
if (this.pos >= end) {
|
||||
this.error(
|
||||
DiagnosticCode.Unexpected_end_of_text,
|
||||
this.range(start, this.end)
|
||||
this.range(start, end)
|
||||
);
|
||||
invalid = true;
|
||||
} else if (text.charCodeAt(this.pos) == CharCode.CLOSEBRACE) {
|
||||
|
@ -2,6 +2,7 @@
|
||||
"extends": "../std/portable.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out",
|
||||
"allowJs": false,
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": [
|
||||
|
52
src/types.ts
52
src/types.ts
@ -145,13 +145,21 @@ export class Type {
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets this type's logarithmic alignment in memory. */
|
||||
get alignLog2(): i32 {
|
||||
return 31 - clz<i32>(this.byteSize);
|
||||
}
|
||||
|
||||
/** Tests if this is a managed type that needs GC hooks. */
|
||||
isManaged(program: Program): bool {
|
||||
if (program.hasGC) {
|
||||
let classReference = this.classReference;
|
||||
return classReference !== null && !classReference.hasDecorator(DecoratorFlags.UNMANAGED);
|
||||
}
|
||||
return false;
|
||||
get isManaged(): bool {
|
||||
var classReference = this.classReference;
|
||||
return classReference !== null && !classReference.hasDecorator(DecoratorFlags.UNMANAGED);
|
||||
}
|
||||
|
||||
/** Tests if this is a class type explicitly annotated as unmanaged. */
|
||||
get isUnmanaged(): bool {
|
||||
var classReference = this.classReference;
|
||||
return classReference !== null && classReference.hasDecorator(DecoratorFlags.UNMANAGED);
|
||||
}
|
||||
|
||||
/** Computes the sign-extending shift in the target type. */
|
||||
@ -324,14 +332,14 @@ export class Type {
|
||||
toNativeZero(module: Module): ExpressionRef {
|
||||
switch (this.kind) {
|
||||
case TypeKind.VOID: assert(false);
|
||||
default: return module.createI32(0);
|
||||
default: return module.i32(0);
|
||||
case TypeKind.ISIZE:
|
||||
case TypeKind.USIZE: if (this.size != 64) return module.createI32(0);
|
||||
case TypeKind.USIZE: if (this.size != 64) return module.i32(0);
|
||||
case TypeKind.I64:
|
||||
case TypeKind.U64: return module.createI64(0);
|
||||
case TypeKind.F32: return module.createF32(0);
|
||||
case TypeKind.F64: return module.createF64(0);
|
||||
case TypeKind.V128: return module.createV128(v128_zero);
|
||||
case TypeKind.U64: return module.i64(0);
|
||||
case TypeKind.F32: return module.f32(0);
|
||||
case TypeKind.F64: return module.f64(0);
|
||||
case TypeKind.V128: return module.v128(v128_zero);
|
||||
}
|
||||
}
|
||||
|
||||
@ -340,13 +348,13 @@ export class Type {
|
||||
switch (this.kind) {
|
||||
case TypeKind.V128:
|
||||
case TypeKind.VOID: assert(false);
|
||||
default: return module.createI32(1);
|
||||
default: return module.i32(1);
|
||||
case TypeKind.ISIZE:
|
||||
case TypeKind.USIZE: if (this.size != 64) return module.createI32(1);
|
||||
case TypeKind.USIZE: if (this.size != 64) return module.i32(1);
|
||||
case TypeKind.I64:
|
||||
case TypeKind.U64: return module.createI64(1);
|
||||
case TypeKind.F32: return module.createF32(1);
|
||||
case TypeKind.F64: return module.createF64(1);
|
||||
case TypeKind.U64: return module.i64(1);
|
||||
case TypeKind.F32: return module.f32(1);
|
||||
case TypeKind.F64: return module.f64(1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -355,13 +363,13 @@ export class Type {
|
||||
switch (this.kind) {
|
||||
case TypeKind.V128:
|
||||
case TypeKind.VOID: assert(false);
|
||||
default: return module.createI32(-1);
|
||||
default: return module.i32(-1);
|
||||
case TypeKind.ISIZE:
|
||||
case TypeKind.USIZE: if (this.size != 64) return module.createI32(-1);
|
||||
case TypeKind.USIZE: if (this.size != 64) return module.i32(-1);
|
||||
case TypeKind.I64:
|
||||
case TypeKind.U64: return module.createI64(-1, -1);
|
||||
case TypeKind.F32: return module.createF32(-1);
|
||||
case TypeKind.F64: return module.createF64(-1);
|
||||
case TypeKind.U64: return module.i64(-1, -1);
|
||||
case TypeKind.F32: return module.f32(-1);
|
||||
case TypeKind.F64: return module.f64(-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,39 +0,0 @@
|
||||
/**
|
||||
* Arena Memory Allocator
|
||||
*
|
||||
* Provides a `memory.reset` function to reset the heap to its initial state. A user has to make
|
||||
* sure that there are no more references to cleared memory afterwards. Always aligns to 8 bytes.
|
||||
*
|
||||
* @module std/assembly/allocator/arena
|
||||
*//***/
|
||||
|
||||
import { AL_MASK, MAX_SIZE_32 } from "../internal/allocator";
|
||||
|
||||
var startOffset: usize = (HEAP_BASE + AL_MASK) & ~AL_MASK;
|
||||
var offset: usize = startOffset;
|
||||
|
||||
// Memory allocator interface
|
||||
|
||||
@global export function __memory_allocate(size: usize): usize {
|
||||
if (size > MAX_SIZE_32) unreachable();
|
||||
var ptr = offset;
|
||||
var newPtr = (ptr + max<usize>(size, 1) + AL_MASK) & ~AL_MASK;
|
||||
var pagesBefore = memory.size();
|
||||
if (newPtr > <usize>pagesBefore << 16) {
|
||||
let pagesNeeded = ((newPtr - ptr + 0xffff) & ~0xffff) >>> 16;
|
||||
let pagesWanted = max(pagesBefore, pagesNeeded); // double memory
|
||||
if (memory.grow(pagesWanted) < 0) {
|
||||
if (memory.grow(pagesNeeded) < 0) {
|
||||
unreachable(); // out of memory
|
||||
}
|
||||
}
|
||||
}
|
||||
offset = newPtr;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@global export function __memory_free(ptr: usize): void { /* nop */ }
|
||||
|
||||
@global export function __memory_reset(): void {
|
||||
offset = startOffset;
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
/**
|
||||
* Emscripten Memory Allocator.
|
||||
*
|
||||
* Uses Emscripten's exported _malloc and _free implementations, i.e., when linking with
|
||||
* Emscripten-compiled programs that already provide these. Differs from 'system' in that their
|
||||
* names are prefixed with an underscore.
|
||||
*
|
||||
* @module std/assembly/allocator/emscripten
|
||||
*//***/
|
||||
|
||||
declare function _malloc(size: usize): usize;
|
||||
declare function _free(ptr: usize): void;
|
||||
|
||||
// Memory allocator interface
|
||||
|
||||
@global export function __memory_allocate(size: usize): usize {
|
||||
return _malloc(size);
|
||||
}
|
||||
|
||||
@global export function __memory_free(ptr: usize): void {
|
||||
_free(ptr);
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
/**
|
||||
* System Memory Allocator.
|
||||
*
|
||||
* Uses the environment's malloc and free implementations, i.e., when linking with other C-like
|
||||
* programs that already provide these.
|
||||
*
|
||||
* @module std/assembly/allocator/system
|
||||
*//***/
|
||||
|
||||
declare function malloc(size: usize): usize;
|
||||
declare function free(ptr: usize): void;
|
||||
|
||||
// Memory allocator interface
|
||||
|
||||
@global export function __memory_allocate(size: usize): usize {
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
@global export function __memory_free(ptr: usize): void {
|
||||
free(ptr);
|
||||
}
|
@ -1,503 +0,0 @@
|
||||
/**
|
||||
* Two-Level Segregate Fit Memory Allocator.
|
||||
*
|
||||
* A general purpose dynamic memory allocator specifically designed to meet real-time requirements.
|
||||
* Always aligns to 8 bytes.
|
||||
*
|
||||
* @module std/assembly/allocator/tlsf
|
||||
*//***/
|
||||
|
||||
// ╒══════════════ Block size interpretation (32-bit) ═════════════╕
|
||||
// 3 2 1
|
||||
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 bits
|
||||
// ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┼─┴─┴─┴─┴─╫─┴─┴─┤
|
||||
// │ | FL │ SB = SL + AL │ ◄─ usize
|
||||
// └───────────────────────────────────────────────┴─────────╨─────┘
|
||||
// FL: first level, SL: second level, AL: alignment, SB: small block
|
||||
|
||||
import {
|
||||
AL_BITS,
|
||||
AL_SIZE,
|
||||
AL_MASK
|
||||
} from "../internal/allocator";
|
||||
|
||||
const SL_BITS: u32 = 5;
|
||||
const SL_SIZE: usize = 1 << <usize>SL_BITS;
|
||||
|
||||
const SB_BITS: usize = <usize>(SL_BITS + AL_BITS);
|
||||
const SB_SIZE: usize = 1 << <usize>SB_BITS;
|
||||
|
||||
const FL_BITS: u32 = (sizeof<usize>() == sizeof<u32>()
|
||||
? 30 // ^= up to 1GB per block
|
||||
: 32 // ^= up to 4GB per block
|
||||
) - SB_BITS;
|
||||
|
||||
// ╒════════════════ Block structure layout (32-bit) ══════════════╕
|
||||
// 3 2 1
|
||||
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 bits
|
||||
// ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┼─┼─┤
|
||||
// │ size │L│F│ ◄─┐ info
|
||||
// ╞═══════════════════════════════════════════════════════════╧═╧═╡ │ ┐
|
||||
// │ if free: ◄ prev │ ◄─┤ usize
|
||||
// ├───────────────────────────────────────────────────────────────┤ │
|
||||
// │ if free: next ► │ ◄─┤
|
||||
// ├───────────────────────────────────────────────────────────────┤ │
|
||||
// │ ... unused free space >= 0 ... │ │ = 0
|
||||
// ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ │
|
||||
// │ if free: jump ▲ │ ◄─┘
|
||||
// └───────────────────────────────────────────────────────────────┘ MIN SIZE ┘
|
||||
// F: FREE, L: LEFT_FREE
|
||||
|
||||
/** Tag indicating that this block is free. */
|
||||
const FREE: usize = 1 << 0;
|
||||
/** Tag indicating that this block's left block is free. */
|
||||
const LEFT_FREE: usize = 1 << 1;
|
||||
/** Mask to obtain all tags. */
|
||||
const TAGS: usize = FREE | LEFT_FREE;
|
||||
|
||||
/** Block structure. */
|
||||
@unmanaged
|
||||
class Block {
|
||||
|
||||
/** Info field holding this block's size and tags. */
|
||||
info: usize;
|
||||
|
||||
/** End offset of the {@link Block#info} field. User data starts here. */
|
||||
static readonly INFO: usize = (sizeof<usize>() + AL_MASK) & ~AL_MASK;
|
||||
|
||||
/** Previous free block, if any. Only valid if free. */
|
||||
prev: Block | null;
|
||||
/** Next free block, if any. Only valid if free. */
|
||||
next: Block | null;
|
||||
|
||||
/** Minimum size of a block, excluding {@link Block#info}. */
|
||||
static readonly MIN_SIZE: usize = (3 * sizeof<usize>() + AL_MASK) & ~AL_MASK;// prev + next + jump
|
||||
|
||||
/** Maximum size of a used block, excluding {@link Block#info}. */
|
||||
static readonly MAX_SIZE: usize = 1 << (FL_BITS + SB_BITS);
|
||||
|
||||
/** Gets this block's left (free) block in memory. */
|
||||
get left(): Block {
|
||||
assert(this.info & LEFT_FREE); // must be free to contain a jump
|
||||
return assert(
|
||||
load<Block>(changetype<usize>(this) - sizeof<usize>())
|
||||
); // can't be null
|
||||
}
|
||||
|
||||
/** Gets this block's right block in memory. */
|
||||
get right(): Block {
|
||||
assert(this.info & ~TAGS); // can't skip beyond the tail block
|
||||
return assert(
|
||||
changetype<Block>(
|
||||
changetype<usize>(this) + Block.INFO + (this.info & ~TAGS)
|
||||
)
|
||||
); // can't be null
|
||||
}
|
||||
}
|
||||
|
||||
// ╒════════════════ Root structure layout (32-bit) ═══════════════╕
|
||||
// 3 2 1
|
||||
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 bits
|
||||
// ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤ ┐
|
||||
// │ 0 | flMap S│ ◄────┐
|
||||
// ╞═══════════════════════════════════════════════════════════════╡ │
|
||||
// │ slMap[0] S │ ◄─┐ │
|
||||
// ├───────────────────────────────────────────────────────────────┤ │ │
|
||||
// │ slMap[1] │ ◄─┤ │
|
||||
// ├───────────────────────────────────────────────────────────────┤ u32 │
|
||||
// │ ... │ ◄─┤ │
|
||||
// ├───────────────────────────────────────────────────────────────┤ │ │
|
||||
// │ slMap[22] P │ ◄─┘ │
|
||||
// ╞═══════════════════════════════════════════════════════════════╡ usize
|
||||
// │ head[0] │ ◄────┤
|
||||
// ├───────────────────────────────────────────────────────────────┤ │
|
||||
// │ ... │ ◄────┤
|
||||
// ├───────────────────────────────────────────────────────────────┤ │
|
||||
// │ head[736] │ ◄────┤
|
||||
// ╞═══════════════════════════════════════════════════════════════╡ │
|
||||
// │ tailRef │ ◄────┘
|
||||
// └───────────────────────────────────────────────────────────────┘ SIZE ┘
|
||||
// S: Small blocks map, P: Possibly padded if 64-bit
|
||||
|
||||
assert((1 << SL_BITS) <= 32); // second level must fit into 32 bits
|
||||
|
||||
/** Root structure. */
|
||||
@unmanaged
|
||||
class Root {
|
||||
|
||||
/** First level bitmap. */
|
||||
flMap: usize = 0;
|
||||
|
||||
/** Start offset of second level maps. */
|
||||
private static readonly SL_START: usize = sizeof<usize>();
|
||||
|
||||
// Using *one* SL map per *FL bit*
|
||||
|
||||
/** Gets the second level map for the specified first level. */
|
||||
getSLMap(fl: usize): u32 {
|
||||
assert(fl < FL_BITS); // fl out of range
|
||||
return load<u32>(changetype<usize>(this) + fl * 4, Root.SL_START);
|
||||
}
|
||||
|
||||
/** Sets the second level map for the specified first level. */
|
||||
setSLMap(fl: usize, value: u32): void {
|
||||
assert(fl < FL_BITS); // fl out of range
|
||||
store<u32>(changetype<usize>(this) + fl * 4, value, Root.SL_START);
|
||||
}
|
||||
|
||||
/** End offset of second level maps. */
|
||||
private static readonly SL_END: usize = Root.SL_START + FL_BITS * 4;
|
||||
|
||||
// Using *number bits per SL* heads per *FL bit*
|
||||
|
||||
/** Start offset of FL/SL heads. */
|
||||
private static readonly HL_START: usize = (Root.SL_END + AL_MASK) & ~AL_MASK;
|
||||
|
||||
/** Gets the head of the specified first and second level index. */
|
||||
getHead(fl: usize, sl: u32): Block | null {
|
||||
assert(fl < FL_BITS); // fl out of range
|
||||
assert(sl < SL_SIZE); // sl out of range
|
||||
return changetype<Block>(load<usize>(
|
||||
changetype<usize>(this) + (fl * SL_SIZE + <usize>sl) * sizeof<usize>()
|
||||
, Root.HL_START));
|
||||
}
|
||||
|
||||
/** Sets the head of the specified first and second level index. */
|
||||
setHead(fl: usize, sl: u32, value: Block | null): void {
|
||||
assert(fl < FL_BITS); // fl out of range
|
||||
assert(sl < SL_SIZE); // sl out of range
|
||||
store<usize>(
|
||||
changetype<usize>(this) + (fl * SL_SIZE + <usize>sl) * sizeof<usize>()
|
||||
, changetype<usize>(value)
|
||||
, Root.HL_START);
|
||||
}
|
||||
|
||||
/** End offset of FL/SL heads. */
|
||||
private static readonly HL_END: usize = (
|
||||
Root.HL_START + FL_BITS * SL_SIZE * sizeof<usize>()
|
||||
);
|
||||
|
||||
get tailRef(): usize { return load<usize>(0, Root.HL_END); }
|
||||
set tailRef(value: usize) { store<usize>(0, value, Root.HL_END); }
|
||||
|
||||
/** Total size of the {@link Root} structure. */
|
||||
static readonly SIZE: usize = Root.HL_END + sizeof<usize>();
|
||||
|
||||
/** Inserts a previously used block back into the free list. */
|
||||
insert(block: Block): void {
|
||||
// check as much as possible here to prevent invalid free blocks
|
||||
assert(block); // cannot be null
|
||||
var blockInfo = block.info;
|
||||
assert(blockInfo & FREE); // must be free
|
||||
var size: usize;
|
||||
assert(
|
||||
(size = block.info & ~TAGS) >= Block.MIN_SIZE && size < Block.MAX_SIZE
|
||||
); // must be valid, not necessary to compute yet if noAssert=true
|
||||
|
||||
var right: Block = assert(block.right); // can't be null
|
||||
var rightInfo = right.info;
|
||||
|
||||
// merge with right block if also free
|
||||
if (rightInfo & FREE) {
|
||||
this.remove(right);
|
||||
block.info = (blockInfo += Block.INFO + (rightInfo & ~TAGS));
|
||||
right = block.right;
|
||||
rightInfo = right.info;
|
||||
// jump is set below
|
||||
}
|
||||
|
||||
// merge with left block if also free
|
||||
if (blockInfo & LEFT_FREE) {
|
||||
let left: Block = assert(block.left); // can't be null
|
||||
let leftInfo = left.info;
|
||||
assert(leftInfo & FREE); // must be free according to tags
|
||||
this.remove(left);
|
||||
left.info = (leftInfo += Block.INFO + (blockInfo & ~TAGS));
|
||||
block = left;
|
||||
blockInfo = leftInfo;
|
||||
// jump is set below
|
||||
}
|
||||
|
||||
right.info = rightInfo | LEFT_FREE;
|
||||
this.setJump(block, right);
|
||||
// right is no longer used now, hence rightInfo is not synced
|
||||
|
||||
size = blockInfo & ~TAGS;
|
||||
assert(size >= Block.MIN_SIZE && size < Block.MAX_SIZE); // must be valid
|
||||
|
||||
// mapping_insert
|
||||
var fl: usize, sl: u32;
|
||||
if (size < SB_SIZE) {
|
||||
fl = 0;
|
||||
sl = <u32>(size / AL_SIZE);
|
||||
} else {
|
||||
fl = fls<usize>(size);
|
||||
sl = <u32>((size >> (fl - SL_BITS)) ^ (1 << SL_BITS));
|
||||
fl -= SB_BITS - 1;
|
||||
}
|
||||
|
||||
// perform insertion
|
||||
var head = this.getHead(fl, sl);
|
||||
block.prev = null;
|
||||
block.next = head;
|
||||
if (head) head.prev = block;
|
||||
this.setHead(fl, sl, block);
|
||||
|
||||
// update first and second level maps
|
||||
this.flMap |= (1 << fl);
|
||||
this.setSLMap(fl, this.getSLMap(fl) | (1 << sl));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a free block from FL/SL maps. Does not alter left/jump because it
|
||||
* is likely that splitting is performed afterwards, invalidating any changes
|
||||
* again.
|
||||
*/
|
||||
private remove(block: Block): void {
|
||||
var blockInfo = block.info;
|
||||
assert(blockInfo & FREE); // must be free
|
||||
var size = blockInfo & ~TAGS;
|
||||
assert(size >= Block.MIN_SIZE && size < Block.MAX_SIZE); // must be valid
|
||||
|
||||
// mapping_insert
|
||||
var fl: usize, sl: u32;
|
||||
if (size < SB_SIZE) {
|
||||
fl = 0;
|
||||
sl = <u32>(size / AL_SIZE);
|
||||
} else {
|
||||
fl = fls<usize>(size);
|
||||
sl = <u32>((size >> (fl - SL_BITS)) ^ (1 << SL_BITS));
|
||||
fl -= SB_BITS - 1;
|
||||
}
|
||||
|
||||
// link previous and next free block
|
||||
var prev = block.prev;
|
||||
var next = block.next;
|
||||
if (prev) prev.next = next;
|
||||
if (next) next.prev = prev;
|
||||
|
||||
// update head if we are removing it
|
||||
if (block == this.getHead(fl, sl)) {
|
||||
this.setHead(fl, sl, next);
|
||||
|
||||
// clear second level map if head is empty now
|
||||
if (!next) {
|
||||
let slMap = this.getSLMap(fl);
|
||||
this.setSLMap(fl, slMap &= ~(1 << sl));
|
||||
|
||||
// clear first level map if second level is empty now
|
||||
if (!slMap) this.flMap &= ~(1 << fl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Searches for a free block of at least the specified size. */
|
||||
search(size: usize): Block | null {
|
||||
assert(size >= Block.MIN_SIZE && size < Block.MAX_SIZE);
|
||||
|
||||
// mapping_search
|
||||
var fl: usize, sl: u32;
|
||||
if (size < SB_SIZE) {
|
||||
fl = 0;
|
||||
sl = <u32>(size / AL_SIZE);
|
||||
} else {
|
||||
// (*) size += (1 << (fls<usize>(size) - SL_BITS)) - 1;
|
||||
fl = fls<usize>(size);
|
||||
sl = <u32>((size >> (fl - SL_BITS)) ^ (1 << SL_BITS));
|
||||
fl -= SB_BITS - 1;
|
||||
// (*) instead of rounding up, use next second level list for better fit
|
||||
if (sl < SL_SIZE - 1) ++sl;
|
||||
else ++fl, sl = 0;
|
||||
}
|
||||
|
||||
// search second level
|
||||
var slMap = this.getSLMap(fl) & (~0 << sl);
|
||||
var head: Block | null;
|
||||
if (!slMap) {
|
||||
// search next larger first level
|
||||
let flMap = this.flMap & (~0 << (fl + 1));
|
||||
if (!flMap) {
|
||||
head = null;
|
||||
} else {
|
||||
fl = ffs<usize>(flMap);
|
||||
slMap = assert(this.getSLMap(fl)); // can't be zero if fl points here
|
||||
head = this.getHead(fl, ffs<u32>(slMap));
|
||||
}
|
||||
} else {
|
||||
head = this.getHead(fl, ffs<u32>(slMap));
|
||||
}
|
||||
return head;
|
||||
}
|
||||
|
||||
/** Links a free left with its right block in memory. */
|
||||
private setJump(left: Block, right: Block): void {
|
||||
assert(left.info & FREE); // must be free
|
||||
assert(left.right == right); // right block must match
|
||||
assert(right.info & LEFT_FREE); // right block must be tagged as LEFT_FREE
|
||||
store<Block>(
|
||||
changetype<usize>(right) - sizeof<usize>()
|
||||
, left); // last word in left block's (free) data region
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the specified free block, removing it from internal maps and
|
||||
* splitting it if possible, and returns its data pointer.
|
||||
*/
|
||||
use(block: Block, size: usize): usize {
|
||||
var blockInfo = block.info;
|
||||
assert(blockInfo & FREE); // must be free so we can use it
|
||||
assert(size >= Block.MIN_SIZE && size < Block.MAX_SIZE); // must be valid
|
||||
assert(!(size & AL_MASK)); // size must be aligned so the new block is
|
||||
|
||||
this.remove(block);
|
||||
|
||||
// split if the block can hold another MIN_SIZE block
|
||||
var remaining = (blockInfo & ~TAGS) - size;
|
||||
if (remaining >= Block.INFO + Block.MIN_SIZE) {
|
||||
block.info = size | (blockInfo & LEFT_FREE); // also discards FREE
|
||||
|
||||
let spare = changetype<Block>(
|
||||
changetype<usize>(block) + Block.INFO + size
|
||||
);
|
||||
spare.info = (remaining - Block.INFO) | FREE; // not LEFT_FREE
|
||||
this.insert(spare); // also sets jump
|
||||
|
||||
// otherwise tag block as no longer FREE and right as no longer LEFT_FREE
|
||||
} else {
|
||||
block.info = blockInfo & ~FREE;
|
||||
let right: Block = assert(block.right); // can't be null (tail)
|
||||
right.info &= ~LEFT_FREE;
|
||||
}
|
||||
|
||||
return changetype<usize>(block) + Block.INFO;
|
||||
}
|
||||
|
||||
/** Adds more memory to the pool. */
|
||||
addMemory(start: usize, end: usize): bool {
|
||||
assert(start <= end);
|
||||
assert(!(start & AL_MASK)); // must be aligned
|
||||
assert(!(end & AL_MASK)); // must be aligned
|
||||
|
||||
var tailRef = this.tailRef;
|
||||
var tailInfo: usize = 0;
|
||||
if (tailRef) {
|
||||
assert(start >= tailRef + sizeof<usize>()); // starts after tail
|
||||
|
||||
// merge with current tail if adjacent
|
||||
if (start - Block.INFO == tailRef) {
|
||||
start -= Block.INFO;
|
||||
tailInfo = changetype<Block>(tailRef).info;
|
||||
}
|
||||
|
||||
} else {
|
||||
assert(start >= changetype<usize>(this) + Root.SIZE); // starts after root
|
||||
}
|
||||
|
||||
// check if size is large enough for a free block and the tail block
|
||||
var size = end - start;
|
||||
if (size < Block.INFO + Block.MIN_SIZE + Block.INFO) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// left size is total minus its own and the zero-length tail's header
|
||||
var leftSize = size - 2 * Block.INFO;
|
||||
var left = changetype<Block>(start);
|
||||
left.info = leftSize | FREE | (tailInfo & LEFT_FREE);
|
||||
left.prev = null;
|
||||
left.next = null;
|
||||
|
||||
// tail is a zero-length used block
|
||||
var tail = changetype<Block>(start + size - Block.INFO);
|
||||
tail.info = 0 | LEFT_FREE;
|
||||
this.tailRef = changetype<usize>(tail);
|
||||
|
||||
this.insert(left); // also merges with free left before tail / sets jump
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/** Determines the first (LSB to MSB) set bit's index of a word. */
|
||||
function ffs<T>(word: T): T {
|
||||
assert(word != 0); // word cannot be 0
|
||||
return ctz<T>(word); // differs from ffs only for 0
|
||||
}
|
||||
|
||||
/** Determines the last (LSB to MSB) set bit's index of a word. */
|
||||
function fls<T>(word: T): T {
|
||||
assert(word != 0); // word cannot be 0
|
||||
const inv: T = (sizeof<T>() << 3) - 1;
|
||||
return inv - clz<T>(word);
|
||||
}
|
||||
|
||||
/** Reference to the initialized {@link Root} structure, once initialized. */
|
||||
var ROOT: Root = changetype<Root>(0);
|
||||
|
||||
// Memory allocator interface
|
||||
|
||||
/** Allocates a chunk of memory. */
|
||||
@global export function __memory_allocate(size: usize): usize {
|
||||
|
||||
// initialize if necessary
|
||||
var root = ROOT;
|
||||
if (!root) {
|
||||
let rootOffset = (HEAP_BASE + AL_MASK) & ~AL_MASK;
|
||||
let pagesBefore = memory.size();
|
||||
let pagesNeeded = <i32>((((rootOffset + Root.SIZE) + 0xffff) & ~0xffff) >>> 16);
|
||||
if (pagesNeeded > pagesBefore && memory.grow(pagesNeeded - pagesBefore) < 0) unreachable();
|
||||
ROOT = root = changetype<Root>(rootOffset);
|
||||
root.tailRef = 0;
|
||||
root.flMap = 0;
|
||||
for (let fl: usize = 0; fl < FL_BITS; ++fl) {
|
||||
root.setSLMap(fl, 0);
|
||||
for (let sl: u32 = 0; sl < SL_SIZE; ++sl) {
|
||||
root.setHead(fl, sl, null);
|
||||
}
|
||||
}
|
||||
root.addMemory((rootOffset + Root.SIZE + AL_MASK) & ~AL_MASK, memory.size() << 16);
|
||||
}
|
||||
|
||||
// search for a suitable block
|
||||
if (size > Block.MAX_SIZE) unreachable();
|
||||
|
||||
// 32-bit MAX_SIZE is 1 << 30 and itself aligned, hence the following can't overflow MAX_SIZE
|
||||
size = max<usize>((size + AL_MASK) & ~AL_MASK, Block.MIN_SIZE);
|
||||
|
||||
var block = root.search(size);
|
||||
if (!block) {
|
||||
|
||||
// request more memory
|
||||
let pagesBefore = memory.size();
|
||||
let pagesNeeded = <i32>(((size + 0xffff) & ~0xffff) >>> 16);
|
||||
let pagesWanted = max(pagesBefore, pagesNeeded); // double memory
|
||||
if (memory.grow(pagesWanted) < 0) {
|
||||
if (memory.grow(pagesNeeded) < 0) {
|
||||
unreachable(); // out of memory
|
||||
}
|
||||
}
|
||||
let pagesAfter = memory.size();
|
||||
root.addMemory(<usize>pagesBefore << 16, <usize>pagesAfter << 16);
|
||||
block = assert(root.search(size)); // must be found now
|
||||
}
|
||||
|
||||
assert((block.info & ~TAGS) >= size);
|
||||
return root.use(<Block>block, size);
|
||||
}
|
||||
|
||||
/** Frees the chunk of memory at the specified address. */
|
||||
@global export function __memory_free(data: usize): void {
|
||||
if (data) {
|
||||
let root = ROOT;
|
||||
if (root) {
|
||||
let block = changetype<Block>(data - Block.INFO);
|
||||
let blockInfo = block.info;
|
||||
assert(!(blockInfo & FREE)); // must be used
|
||||
block.info = blockInfo | FREE;
|
||||
root.insert(changetype<Block>(data - Block.INFO));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@global export function __memory_reset(): void {
|
||||
unreachable();
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,67 +1,76 @@
|
||||
import {
|
||||
HEADER_SIZE,
|
||||
MAX_BLENGTH,
|
||||
allocateUnsafe
|
||||
} from "./internal/arraybuffer";
|
||||
/// <reference path="./rt/index.d.ts" />
|
||||
|
||||
import {
|
||||
Uint8ClampedArray,
|
||||
Uint8Array,
|
||||
Int8Array,
|
||||
Uint16Array,
|
||||
Int16Array,
|
||||
Uint32Array,
|
||||
Int32Array,
|
||||
Uint64Array,
|
||||
Int64Array
|
||||
} from "./typedarray";
|
||||
import { BLOCK, BLOCK_MAXSIZE, BLOCK_OVERHEAD } from "./rt/common";
|
||||
import { idof } from "./builtins";
|
||||
import { E_INVALIDLENGTH } from "./util/error";
|
||||
|
||||
import {
|
||||
DataView
|
||||
} from "./dataview";
|
||||
export abstract class ArrayBufferView {
|
||||
|
||||
@sealed
|
||||
export class ArrayBuffer {
|
||||
@unsafe data: ArrayBuffer;
|
||||
@unsafe dataStart: usize;
|
||||
@unsafe dataLength: u32;
|
||||
|
||||
readonly byteLength: i32; // capped to [0, MAX_LENGTH]
|
||||
protected constructor(length: i32, alignLog2: i32) {
|
||||
if (<u32>length > <u32>BLOCK_MAXSIZE >>> alignLog2) throw new RangeError(E_INVALIDLENGTH);
|
||||
var buffer = __alloc(length = length << alignLog2, idof<ArrayBuffer>());
|
||||
this.data = changetype<ArrayBuffer>(buffer); // retains
|
||||
this.dataStart = buffer;
|
||||
this.dataLength = length;
|
||||
}
|
||||
|
||||
@inline static isView<T>(value: T): bool {
|
||||
if (value === null) return false;
|
||||
if (value instanceof Uint8ClampedArray) return true;
|
||||
if (value instanceof Uint8Array) return true;
|
||||
if (value instanceof Int8Array) return true;
|
||||
if (value instanceof Uint16Array) return true;
|
||||
if (value instanceof Int16Array) return true;
|
||||
if (value instanceof Uint32Array) return true;
|
||||
if (value instanceof Int32Array) return true;
|
||||
if (value instanceof Uint64Array) return true;
|
||||
if (value instanceof Int64Array) return true;
|
||||
if (value instanceof DataView) return true;
|
||||
get byteOffset(): i32 {
|
||||
return <i32>(this.dataStart - changetype<usize>(this.data));
|
||||
}
|
||||
|
||||
get byteLength(): i32 {
|
||||
return this.dataLength;
|
||||
}
|
||||
|
||||
get length(): i32 {
|
||||
ERROR("missing implementation: subclasses must implement ArrayBufferView#length");
|
||||
return unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
@sealed export class ArrayBuffer {
|
||||
|
||||
static isView<T>(value: T): bool {
|
||||
if (value) {
|
||||
if (value instanceof Int8Array) return true;
|
||||
if (value instanceof Uint8Array) return true;
|
||||
if (value instanceof Uint8ClampedArray) return true;
|
||||
if (value instanceof Int16Array) return true;
|
||||
if (value instanceof Uint16Array) return true;
|
||||
if (value instanceof Int32Array) return true;
|
||||
if (value instanceof Uint32Array) return true;
|
||||
if (value instanceof Int64Array) return true;
|
||||
if (value instanceof Uint64Array) return true;
|
||||
if (value instanceof Float32Array) return true;
|
||||
if (value instanceof Float64Array) return true;
|
||||
if (value instanceof DataView) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// @unsafe
|
||||
@inline get data(): usize { return changetype<usize>(this) + HEADER_SIZE; }
|
||||
|
||||
constructor(length: i32, unsafe: bool = false) {
|
||||
if (<u32>length > <u32>MAX_BLENGTH) throw new RangeError("Invalid array buffer length");
|
||||
var buffer = allocateUnsafe(length);
|
||||
if (!unsafe) memory.fill(changetype<usize>(buffer) + HEADER_SIZE, 0, <usize>length);
|
||||
return buffer;
|
||||
constructor(length: i32) {
|
||||
if (<u32>length > <u32>BLOCK_MAXSIZE) throw new RangeError(E_INVALIDLENGTH);
|
||||
var buffer = __alloc(<usize>length, idof<ArrayBuffer>());
|
||||
memory.fill(buffer, 0, <usize>length);
|
||||
return changetype<ArrayBuffer>(buffer); // retains
|
||||
}
|
||||
|
||||
slice(begin: i32 = 0, end: i32 = MAX_BLENGTH): ArrayBuffer {
|
||||
var len = this.byteLength;
|
||||
begin = begin < 0 ? max(len + begin, 0) : min(begin, len);
|
||||
end = end < 0 ? max(len + end, 0) : min(end, len);
|
||||
len = max(end - begin, 0);
|
||||
var buffer = allocateUnsafe(len);
|
||||
memory.copy(
|
||||
changetype<usize>(buffer) + HEADER_SIZE,
|
||||
changetype<usize>(this) + HEADER_SIZE + begin,
|
||||
len
|
||||
);
|
||||
return buffer;
|
||||
get byteLength(): i32 {
|
||||
return changetype<BLOCK>(changetype<usize>(this) - BLOCK_OVERHEAD).rtSize;
|
||||
}
|
||||
|
||||
slice(begin: i32 = 0, end: i32 = BLOCK_MAXSIZE): ArrayBuffer {
|
||||
var length = this.byteLength;
|
||||
begin = begin < 0 ? max(length + begin, 0) : min(begin, length);
|
||||
end = end < 0 ? max(length + end , 0) : min(end , length);
|
||||
var outSize = <usize>max(end - begin, 0);
|
||||
var out = __alloc(outSize, idof<ArrayBuffer>());
|
||||
memory.copy(out, changetype<usize>(this) + <usize>begin, outSize);
|
||||
return changetype<ArrayBuffer>(out); // retains
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,244 +0,0 @@
|
||||
/**
|
||||
* Incremental Tri-Color-Marking Garbage Collector.
|
||||
*
|
||||
* @module std/assembly/collector/itcm
|
||||
*//***/
|
||||
|
||||
// Largely based on Bach Le's μgc, see: https://github.com/bullno1/ugc
|
||||
|
||||
@inline const TRACE = false;
|
||||
|
||||
/** Size of a managed object header. */
|
||||
@inline export const HEADER_SIZE: usize = (offsetof<ManagedObject>() + AL_MASK) & ~AL_MASK;
|
||||
|
||||
import { AL_MASK, MAX_SIZE_32 } from "../internal/allocator";
|
||||
import { iterateRoots } from "../gc";
|
||||
|
||||
/** Collector states. */
|
||||
const enum State {
|
||||
/** Not yet initialized. */
|
||||
INIT = 0,
|
||||
/** Currently transitioning from SWEEP to MARK state. */
|
||||
IDLE = 1,
|
||||
/** Currently marking reachable objects. */
|
||||
MARK = 2,
|
||||
/** Currently sweeping unreachable objects. */
|
||||
SWEEP = 3
|
||||
}
|
||||
|
||||
/** Current collector state. */
|
||||
var state = State.INIT;
|
||||
/** Current white color value. */
|
||||
var white = 0;
|
||||
|
||||
// From and to spaces
|
||||
var fromSpace: ManagedObjectList;
|
||||
var toSpace: ManagedObjectList;
|
||||
var iter: ManagedObject;
|
||||
|
||||
// ╒═══════════════ Managed object layout (32-bit) ════════════════╕
|
||||
// 3 2 1
|
||||
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 bits
|
||||
// ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┼─┼─┴─┤ ┐
|
||||
// │ next │0│ C │ ◄─┐ = nextWithColor
|
||||
// ├─────────────────────────────────────────────────────────┴─┴───┤ │ usize
|
||||
// │ prev │ ◄─┘
|
||||
// ├───────────────────────────────────────────────────────────────┤
|
||||
// │ hookFn │
|
||||
// ╞═══════════════════════════════════════════════════════════════╡ SIZE ┘ ◄─ user-space reference
|
||||
// │ ... data ... │
|
||||
// └───────────────────────────────────────────────────────────────┘
|
||||
// C: color
|
||||
|
||||
/** Represents a managed object in memory, consisting of a header followed by the object's data. */
|
||||
@unmanaged class ManagedObject {
|
||||
|
||||
/** Pointer to the next object with color flags stored in the alignment bits. */
|
||||
nextWithColor: usize;
|
||||
|
||||
/** Pointer to the previous object. */
|
||||
prev: ManagedObject;
|
||||
|
||||
/** Class-specific hook function called with the user-space reference. */
|
||||
hookFn: (ref: usize) => void;
|
||||
|
||||
/** Gets the pointer to the next object. */
|
||||
get next(): ManagedObject {
|
||||
return changetype<ManagedObject>(this.nextWithColor & ~3);
|
||||
}
|
||||
|
||||
/** Sets the pointer to the next object. */
|
||||
set next(obj: ManagedObject) {
|
||||
this.nextWithColor = changetype<usize>(obj) | (this.nextWithColor & 3);
|
||||
}
|
||||
|
||||
/** Gets this object's color. */
|
||||
get color(): i32 {
|
||||
return this.nextWithColor & 3;
|
||||
}
|
||||
|
||||
/** Sets this object's color. */
|
||||
set color(color: i32) {
|
||||
this.nextWithColor = (this.nextWithColor & ~3) | color;
|
||||
}
|
||||
|
||||
/** Unlinks this object from its list. */
|
||||
unlink(): void {
|
||||
var next = this.next;
|
||||
var prev = this.prev;
|
||||
if (TRACE) trace(" unlink", 3, objToRef(prev), objToRef(this), objToRef(next));
|
||||
next.prev = prev;
|
||||
prev.next = next;
|
||||
}
|
||||
|
||||
/** Marks this object as gray, that is reachable with unscanned children. */
|
||||
makeGray(): void {
|
||||
if (TRACE) trace(" makeGray", 1, objToRef(this));
|
||||
const gray = 2;
|
||||
if (this == iter) iter = this.prev;
|
||||
this.unlink();
|
||||
toSpace.push(this);
|
||||
this.nextWithColor = (this.nextWithColor & ~3) | gray;
|
||||
}
|
||||
}
|
||||
|
||||
/** A list of managed objects. Used for the from and to spaces. */
|
||||
@unmanaged class ManagedObjectList extends ManagedObject {
|
||||
|
||||
/** Inserts an object. */
|
||||
push(obj: ManagedObject): void {
|
||||
var prev = this.prev;
|
||||
if (TRACE) trace(" push", 3, objToRef(prev), objToRef(obj), objToRef(this));
|
||||
obj.next = this;
|
||||
obj.prev = prev;
|
||||
prev.next = obj;
|
||||
this.prev = obj;
|
||||
}
|
||||
|
||||
/** Clears this list. */
|
||||
clear(): void {
|
||||
if (TRACE) trace(" clear", 1, objToRef(this));
|
||||
this.nextWithColor = changetype<usize>(this);
|
||||
this.prev = this;
|
||||
}
|
||||
}
|
||||
|
||||
/** Performs a single step according to the current state. */
|
||||
function step(): void {
|
||||
var obj: ManagedObject;
|
||||
switch (state) {
|
||||
case State.INIT: {
|
||||
if (TRACE) trace("gc~step/INIT");
|
||||
fromSpace = changetype<ManagedObjectList>(memory.allocate(HEADER_SIZE));
|
||||
fromSpace.hookFn = changetype<(ref: usize) => void>(<u32>-1); // would error
|
||||
fromSpace.clear();
|
||||
toSpace = changetype<ManagedObjectList>(memory.allocate(HEADER_SIZE));
|
||||
toSpace.hookFn = changetype<(ref: usize) => void>(<u32>-1); // would error
|
||||
toSpace.clear();
|
||||
iter = toSpace;
|
||||
state = State.IDLE;
|
||||
if (TRACE) trace("gc~state = IDLE");
|
||||
// fall-through
|
||||
}
|
||||
case State.IDLE: {
|
||||
if (TRACE) trace("gc~step/IDLE");
|
||||
iterateRoots(__gc_mark);
|
||||
state = State.MARK;
|
||||
if (TRACE) trace("gc~state = MARK");
|
||||
break;
|
||||
}
|
||||
case State.MARK: {
|
||||
obj = iter.next;
|
||||
if (obj !== toSpace) {
|
||||
if (TRACE) trace("gc~step/MARK iterate", 1, objToRef(obj));
|
||||
iter = obj;
|
||||
obj.color = <i32>!white;
|
||||
// if (TRACE) {
|
||||
// trace(" next/prev/hook", 3,
|
||||
// changetype<usize>(obj.next),
|
||||
// changetype<usize>(obj.prev),
|
||||
// changetype<u32>(obj.hookFn)
|
||||
// );
|
||||
// }
|
||||
obj.hookFn(objToRef(obj));
|
||||
} else {
|
||||
if (TRACE) trace("gc~step/MARK finish");
|
||||
iterateRoots(__gc_mark);
|
||||
obj = iter.next;
|
||||
if (obj === toSpace) {
|
||||
let from = fromSpace;
|
||||
fromSpace = toSpace;
|
||||
toSpace = from;
|
||||
white = <i32>!white;
|
||||
iter = from.next;
|
||||
state = State.SWEEP;
|
||||
if (TRACE) trace("gc~state = SWEEP");
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case State.SWEEP: {
|
||||
obj = iter;
|
||||
if (obj !== toSpace) {
|
||||
if (TRACE) trace("gc~step/SWEEP free", 1, objToRef(obj));
|
||||
iter = obj.next;
|
||||
if (changetype<usize>(obj) >= HEAP_BASE) memory.free(changetype<usize>(obj));
|
||||
} else {
|
||||
if (TRACE) trace("gc~step/SWEEP finish");
|
||||
toSpace.clear();
|
||||
state = State.IDLE;
|
||||
if (TRACE) trace("gc~state = IDLE");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@inline function refToObj(ref: usize): ManagedObject {
|
||||
return changetype<ManagedObject>(ref - HEADER_SIZE);
|
||||
}
|
||||
|
||||
@inline function objToRef(obj: ManagedObject): usize {
|
||||
return changetype<usize>(obj) + HEADER_SIZE;
|
||||
}
|
||||
|
||||
// Garbage collector interface
|
||||
|
||||
@global export function __gc_allocate(
|
||||
size: usize,
|
||||
markFn: (ref: usize) => void
|
||||
): usize {
|
||||
if (TRACE) trace("gc.allocate", 1, size);
|
||||
if (size > MAX_SIZE_32 - HEADER_SIZE) unreachable();
|
||||
step(); // also makes sure it's initialized
|
||||
var obj = changetype<ManagedObject>(memory.allocate(HEADER_SIZE + size));
|
||||
obj.hookFn = markFn;
|
||||
obj.color = white;
|
||||
fromSpace.push(obj);
|
||||
return objToRef(obj);
|
||||
}
|
||||
|
||||
@global export function __gc_link(parentRef: usize, childRef: usize): void {
|
||||
if (TRACE) trace("gc.link", 2, parentRef, childRef);
|
||||
var parent = refToObj(parentRef);
|
||||
if (parent.color == <i32>!white && refToObj(childRef).color == white) parent.makeGray();
|
||||
}
|
||||
|
||||
@global export function __gc_mark(ref: usize): void {
|
||||
if (TRACE) trace("gc.mark", 1, ref);
|
||||
if (ref) {
|
||||
let obj = refToObj(ref);
|
||||
if (obj.color == white) obj.makeGray();
|
||||
}
|
||||
}
|
||||
|
||||
@global export function __gc_collect(): void {
|
||||
if (TRACE) trace("gc.collect");
|
||||
// begin collecting if not yet collecting
|
||||
switch (state) {
|
||||
case State.INIT:
|
||||
case State.IDLE: step();
|
||||
}
|
||||
// finish the cycle
|
||||
while (state != State.IDLE) step();
|
||||
}
|
@ -1,189 +1,213 @@
|
||||
import {
|
||||
HEADER_SIZE,
|
||||
MAX_BLENGTH
|
||||
} from "./internal/arraybuffer";
|
||||
import { BLOCK_MAXSIZE } from "./rt/common";
|
||||
import { ArrayBuffer } from "./arraybuffer";
|
||||
import { E_INDEXOUTOFRANGE, E_INVALIDLENGTH } from "./util/error";
|
||||
|
||||
// TODO: there is probably a smarter way to check byteOffset for accesses larger than 1 byte
|
||||
|
||||
export class DataView {
|
||||
|
||||
private data: ArrayBuffer;
|
||||
private dataStart: usize;
|
||||
private dataLength: i32;
|
||||
|
||||
constructor(
|
||||
readonly buffer: ArrayBuffer,
|
||||
readonly byteOffset: i32 = 0,
|
||||
readonly byteLength: i32 = i32.MIN_VALUE // FIXME
|
||||
buffer: ArrayBuffer,
|
||||
byteOffset: i32 = 0,
|
||||
byteLength: i32 = buffer.byteLength
|
||||
) {
|
||||
if (byteLength === i32.MIN_VALUE) byteLength = buffer.byteLength - byteOffset; // FIXME
|
||||
if (<u32>byteOffset > <u32>MAX_BLENGTH) throw new RangeError("Invalid byteOffset");
|
||||
if (<u32>byteLength > <u32>MAX_BLENGTH) throw new RangeError("Invalid byteLength");
|
||||
if (byteOffset + byteLength > buffer.byteLength) throw new RangeError("Invalid length");
|
||||
if (
|
||||
i32(<u32>byteLength > <u32>BLOCK_MAXSIZE) |
|
||||
i32(<u32>byteOffset + byteLength > <u32>buffer.byteLength)
|
||||
) throw new RangeError(E_INVALIDLENGTH);
|
||||
this.data = buffer; // retains
|
||||
var dataStart = changetype<usize>(buffer) + <usize>byteOffset;
|
||||
this.dataStart = dataStart;
|
||||
this.dataLength = byteLength;
|
||||
}
|
||||
|
||||
get buffer(): ArrayBuffer {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
get byteOffset(): i32 {
|
||||
return <i32>(this.dataStart - changetype<usize>(this.data));
|
||||
}
|
||||
|
||||
get byteLength(): i32 {
|
||||
return this.dataLength;
|
||||
}
|
||||
|
||||
getFloat32(byteOffset: i32, littleEndian: boolean = false): f32 {
|
||||
checkOffset(byteOffset, 4, this.byteLength);
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 4 > this.dataLength)
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
return littleEndian
|
||||
? load<f32>(changetype<usize>(this.buffer) + this.byteOffset + byteOffset, HEADER_SIZE)
|
||||
? load<f32>(this.dataStart + <usize>byteOffset)
|
||||
: reinterpret<f32>(
|
||||
bswap<u32>(
|
||||
load<u32>(changetype<usize>(this.buffer) + this.byteOffset + byteOffset, HEADER_SIZE)
|
||||
load<u32>(this.dataStart + <usize>byteOffset)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
getFloat64(byteOffset: i32, littleEndian: boolean = false): f64 {
|
||||
checkOffset(byteOffset, 8, this.byteLength);
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 8 > this.dataLength)
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
return littleEndian
|
||||
? load<f64>(changetype<usize>(this.buffer) + this.byteOffset + byteOffset, HEADER_SIZE)
|
||||
? load<f64>(this.dataStart + <usize>byteOffset)
|
||||
: reinterpret<f64>(
|
||||
bswap(
|
||||
load<u64>(changetype<usize>(this.buffer) + this.byteOffset + byteOffset, HEADER_SIZE)
|
||||
bswap<u64>(
|
||||
load<u64>(this.dataStart + <usize>byteOffset)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
getInt8(byteOffset: i32): i8 {
|
||||
checkOffset(byteOffset, 1, this.byteLength);
|
||||
return load<i8>(changetype<usize>(this.buffer) + this.byteOffset + byteOffset, HEADER_SIZE);
|
||||
if (<u32>byteOffset >= <u32>this.dataLength) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
return load<i8>(this.dataStart + <usize>byteOffset);
|
||||
}
|
||||
|
||||
getInt16(byteOffset: i32, littleEndian: boolean = false): i16 {
|
||||
checkOffset(byteOffset, 2, this.byteLength);
|
||||
var result: i16 = load<i16>(changetype<usize>(this.buffer) + this.byteOffset + byteOffset, HEADER_SIZE);
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 2 > this.dataLength)
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
var result: i16 = load<i16>(this.dataStart + <usize>byteOffset);
|
||||
return littleEndian ? result : bswap<i16>(result);
|
||||
}
|
||||
|
||||
getInt32(byteOffset: i32, littleEndian: boolean = false): i32 {
|
||||
checkOffset(byteOffset, 4, this.byteLength);
|
||||
var result = load<i32>(changetype<usize>(this.buffer) + this.byteOffset + byteOffset, HEADER_SIZE);
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 4 > this.dataLength)
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
var result: i32 = load<i32>(this.dataStart + <usize>byteOffset);
|
||||
return littleEndian ? result : bswap<i32>(result);
|
||||
}
|
||||
|
||||
getUint8(byteOffset: i32): u8 {
|
||||
checkOffset(byteOffset, 1, this.byteLength);
|
||||
return load<u8>(changetype<usize>(this.buffer) + this.byteOffset + byteOffset, HEADER_SIZE);
|
||||
if (<u32>byteOffset >= <u32>this.dataLength) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
return load<u8>(this.dataStart + <usize>byteOffset);
|
||||
}
|
||||
|
||||
getUint16(byteOffset: i32, littleEndian: boolean = false): u16 {
|
||||
checkOffset(byteOffset, 2, this.byteLength);
|
||||
var result: u16 = load<u16>(changetype<usize>(this.buffer) + this.byteOffset + byteOffset, HEADER_SIZE);
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 2 > this.dataLength)
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
var result: u16 = load<u16>(this.dataStart + <usize>byteOffset);
|
||||
return littleEndian ? result : bswap<u16>(result);
|
||||
}
|
||||
|
||||
getUint32(byteOffset: i32, littleEndian: boolean = false): u32 {
|
||||
checkOffset(byteOffset, 4, this.byteLength);
|
||||
var result = load<u32>(changetype<usize>(this.buffer) + this.byteOffset + byteOffset, HEADER_SIZE);
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 4 > this.dataLength)
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
var result: u32 = load<u32>(this.dataStart + <usize>byteOffset);
|
||||
return littleEndian ? result : bswap<u32>(result);
|
||||
}
|
||||
|
||||
setFloat32(byteOffset: i32, value: f32, littleEndian: boolean = false): void {
|
||||
checkOffset(byteOffset, 4, this.byteLength);
|
||||
if (littleEndian) {
|
||||
store<f32>(changetype<usize>(this.buffer) + this.byteOffset + byteOffset, value, HEADER_SIZE);
|
||||
} else {
|
||||
store<u32>(changetype<usize>(this.buffer) + this.byteOffset + byteOffset,
|
||||
bswap(
|
||||
reinterpret<u32>(value)
|
||||
),
|
||||
HEADER_SIZE
|
||||
);
|
||||
}
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 4 > this.dataLength)
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
if (littleEndian) store<f32>(this.dataStart + <usize>byteOffset, value);
|
||||
else store<u32>(this.dataStart + <usize>byteOffset, bswap<u32>(reinterpret<u32>(value)));
|
||||
}
|
||||
|
||||
setFloat64(byteOffset: i32, value: f64, littleEndian: boolean = false): void {
|
||||
checkOffset(byteOffset, 8, this.byteLength);
|
||||
if (littleEndian) {
|
||||
store<f64>(changetype<usize>(this.buffer) + this.byteOffset + byteOffset, value, HEADER_SIZE);
|
||||
} else {
|
||||
store<u64>(changetype<usize>(this.buffer) + this.byteOffset + byteOffset,
|
||||
bswap(
|
||||
reinterpret<u64>(value)
|
||||
),
|
||||
HEADER_SIZE
|
||||
);
|
||||
}
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 8 > this.dataLength)
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
if (littleEndian) store<f64>(this.dataStart + <usize>byteOffset, value);
|
||||
else store<u64>(this.dataStart + <usize>byteOffset, bswap<u64>(reinterpret<u64>(value)));
|
||||
}
|
||||
|
||||
setInt8(byteOffset: i32, value: i8): void {
|
||||
checkOffset(byteOffset, 1, this.byteLength);
|
||||
store<i8>(changetype<usize>(this.buffer) + this.byteOffset + byteOffset, value, HEADER_SIZE);
|
||||
if (<u32>byteOffset >= <u32>this.dataLength) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<i8>(this.dataStart + <usize>byteOffset, value);
|
||||
}
|
||||
|
||||
setInt16(byteOffset: i32, value: i16, littleEndian: boolean = false): void {
|
||||
checkOffset(byteOffset, 2, this.byteLength);
|
||||
store<i16>(
|
||||
changetype<usize>(this.buffer) + this.byteOffset + byteOffset,
|
||||
littleEndian ? value : bswap(value),
|
||||
HEADER_SIZE
|
||||
);
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 2 > this.dataLength)
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<i16>(this.dataStart + <usize>byteOffset, littleEndian ? value : bswap<i16>(value));
|
||||
}
|
||||
|
||||
setInt32(byteOffset: i32, value: i32, littleEndian: boolean = false): void {
|
||||
checkOffset(byteOffset, 4, this.byteLength);
|
||||
store<i32>(
|
||||
changetype<usize>(this.buffer) + this.byteOffset + byteOffset,
|
||||
littleEndian ? value : bswap(value),
|
||||
HEADER_SIZE
|
||||
);
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 4 > this.dataLength)
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<i32>(this.dataStart + <usize>byteOffset, littleEndian ? value : bswap<i32>(value));
|
||||
}
|
||||
|
||||
setUint8(byteOffset: i32, value: u8): void {
|
||||
checkOffset(byteOffset, 1, this.byteLength);
|
||||
store<u8>(changetype<usize>(this.buffer) + this.byteOffset + byteOffset, value, HEADER_SIZE);
|
||||
if (<u32>byteOffset >= <u32>this.dataLength) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<u8>(this.dataStart + <usize>byteOffset, value);
|
||||
}
|
||||
|
||||
setUint16(byteOffset: i32, value: u16, littleEndian: boolean = false): void {
|
||||
checkOffset(byteOffset, 2, this.byteLength);
|
||||
store<u16>(
|
||||
changetype<usize>(this.buffer) + this.byteOffset + byteOffset,
|
||||
littleEndian ? value : bswap(value),
|
||||
HEADER_SIZE
|
||||
);
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 2 > this.dataLength)
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<u16>(this.dataStart + <usize>byteOffset, littleEndian ? value : bswap<u16>(value));
|
||||
}
|
||||
|
||||
setUint32(byteOffset: i32, value: u32, littleEndian: boolean = false): void {
|
||||
checkOffset(byteOffset, 4, this.byteLength);
|
||||
store<u32>(
|
||||
changetype<usize>(this.buffer) + this.byteOffset + byteOffset,
|
||||
littleEndian ? value : bswap(value),
|
||||
HEADER_SIZE
|
||||
);
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 4 > this.dataLength)
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<u32>(this.dataStart + <usize>byteOffset, littleEndian ? value : bswap<u32>(value));
|
||||
}
|
||||
|
||||
// Non-standard additions that make sense in WebAssembly, but won't work in JS:
|
||||
|
||||
getInt64(byteOffset: i32, littleEndian: boolean = false): i64 {
|
||||
checkOffset(byteOffset, 8, this.byteLength);
|
||||
var result = load<i64>(changetype<usize>(this.buffer) + this.byteOffset + byteOffset, HEADER_SIZE);
|
||||
return littleEndian ? result : bswap(result);
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 8 > this.dataLength)
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
var result: i64 = load<i64>(this.dataStart + <usize>byteOffset);
|
||||
return littleEndian ? result : bswap<i64>(result);
|
||||
}
|
||||
|
||||
getUint64(byteOffset: i32, littleEndian: boolean = false): u64 {
|
||||
checkOffset(byteOffset, 8, this.byteLength);
|
||||
var result = load<u64>(changetype<usize>(this.buffer) + this.byteOffset + byteOffset, HEADER_SIZE);
|
||||
return littleEndian ? result : bswap(result);
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 8 > this.dataLength)
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
var result = load<u64>(this.dataStart + <usize>byteOffset);
|
||||
return littleEndian ? result : bswap<u64>(result);
|
||||
}
|
||||
|
||||
setInt64(byteOffset: i32, value: i64, littleEndian: boolean = false): void {
|
||||
checkOffset(byteOffset, 8, this.byteLength);
|
||||
store<i64>(
|
||||
changetype<usize>(this.buffer) + this.byteOffset + byteOffset,
|
||||
littleEndian ? value : bswap(value),
|
||||
HEADER_SIZE
|
||||
);
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 8 > this.dataLength)
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<i64>(this.dataStart + <usize>byteOffset, littleEndian ? value : bswap<i64>(value));
|
||||
}
|
||||
|
||||
setUint64(byteOffset: i32, value: u64, littleEndian: boolean = false): void {
|
||||
checkOffset(byteOffset, 8, this.byteLength);
|
||||
store<u64>(
|
||||
changetype<usize>(this.buffer) + this.byteOffset + byteOffset,
|
||||
littleEndian ? value : bswap(value),
|
||||
HEADER_SIZE
|
||||
);
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 8 > this.dataLength)
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<u64>(this.dataStart + <usize>byteOffset, littleEndian ? value : bswap<u64>(value));
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return "[object DataView]";
|
||||
}
|
||||
}
|
||||
|
||||
@inline function checkOffset(byteOffset: i32, n: i32, byteLength: i32): void {
|
||||
// n and byteLength must be known to be in bounds
|
||||
if (<u32>byteOffset > <u32>MAX_BLENGTH || byteOffset + n > byteLength) throw new Error("Offset out of bounds");
|
||||
}
|
||||
|
@ -1,5 +1,11 @@
|
||||
/* tslint:disable */
|
||||
// @ts-ignore: decorator
|
||||
@builtin
|
||||
export declare function ERROR(message?: string): void;
|
||||
|
||||
@builtin export declare function ERROR(message?: void): void;
|
||||
@builtin export declare function WARNING(message?: void): void;
|
||||
@builtin export declare function INFO(message?: void): void;
|
||||
// @ts-ignore: decorator
|
||||
@builtin
|
||||
export declare function WARNING(message?: string): void;
|
||||
|
||||
// @ts-ignore: decorator
|
||||
@builtin
|
||||
export declare function INFO(message?: string): void;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user