mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-25 15:12:12 +00:00
Initial implementation if ugc, see #16; Fix tests
This commit is contained in:
parent
461daab2a2
commit
9cdfa35938
@ -20,7 +20,10 @@ A few early examples to get an idea:
|
||||
A PSON decoder implemented in AssemblyScript.
|
||||
|
||||
* **[TLSF memory allocator](./examples/tlsf)**<br />
|
||||
An early port of TLSF to AssemblyScript.
|
||||
An port of TLSF to AssemblyScript.
|
||||
|
||||
* **[μgc garbage collector](./examples/ugc)**<br />
|
||||
An port of μgc to AssemblyScript.
|
||||
|
||||
Or browse the [compiler tests](./tests/compiler) for a more in-depth overview of what's supported already. One of them is a [showcase](./tests/compiler/showcase.ts).
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
TLSF
|
||||
====
|
||||
TLSF memory allocator
|
||||
=====================
|
||||
|
||||
A port of [Matt Conte's implementation](https://github.com/mattconte/tlsf) of the [TLSF](http://www.gii.upv.es/tlsf/) memory allocator to AssemblyScript.
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
tlsf.ts is based on:
|
||||
tlsf.ts is based on https://github.com/mattconte/tlsf
|
||||
|
||||
Two Level Segregated Fit memory allocator, version 3.1.
|
||||
Written by Matthew Conte
|
||||
|
@ -35,7 +35,7 @@ class BlockHeader {
|
||||
static readonly OVERHEAD: usize = sizeof<usize>();
|
||||
|
||||
// User data starts directly after the size field in a used block.
|
||||
static readonly USERDATA_OFFSET: usize = sizeof<usize>() + sizeof<usize>();
|
||||
static readonly DATA_OFFSET: usize = sizeof<usize>() + sizeof<usize>();
|
||||
|
||||
// A free block must be large enough to store its header minus the size of
|
||||
// the prev_phys_block field, and no larger than the number of addressable
|
||||
@ -114,12 +114,12 @@ class BlockHeader {
|
||||
|
||||
/** Gets the block header matching the specified data pointer. */
|
||||
static fromDataPtr(ptr: usize): BlockHeader {
|
||||
return changetype<BlockHeader>(ptr - BlockHeader.USERDATA_OFFSET);
|
||||
return changetype<BlockHeader>(ptr - BlockHeader.DATA_OFFSET);
|
||||
}
|
||||
|
||||
/** Returns the address of this block's data. */
|
||||
toDataPtr(): usize {
|
||||
return changetype<usize>(this) + BlockHeader.USERDATA_OFFSET;
|
||||
return changetype<usize>(this) + BlockHeader.DATA_OFFSET;
|
||||
}
|
||||
|
||||
/** Gets the next block after this one using the specified size. */
|
||||
@ -174,7 +174,7 @@ class BlockHeader {
|
||||
return this.size >= BlockHeader.SIZE + size;
|
||||
}
|
||||
|
||||
/* Splits a block into two, the second of which is free. */
|
||||
/** Splits a block into two, the second of which is free. */
|
||||
split(size: usize): BlockHeader {
|
||||
// Calculate the amount of space left in the remaining block.
|
||||
var remain = BlockHeader.fromOffset(
|
||||
@ -194,7 +194,7 @@ class BlockHeader {
|
||||
return remain;
|
||||
}
|
||||
|
||||
/* Absorb a free block's storage into this (adjacent previous) free block. */
|
||||
/** Absorb a free block's storage into this (adjacent previous) free block. */
|
||||
absorb(block: BlockHeader): void {
|
||||
assert(!this.isLast,
|
||||
"previous block can't be last"
|
||||
@ -205,7 +205,7 @@ class BlockHeader {
|
||||
}
|
||||
}
|
||||
|
||||
/* The TLSF control structure. */
|
||||
/** The TLSF control structure. */
|
||||
@explicit
|
||||
class Control extends BlockHeader { // Empty lists point here, indicating free
|
||||
|
||||
@ -289,7 +289,7 @@ class Control extends BlockHeader { // Empty lists point here, indicating free
|
||||
this.insertFreeBlock(block, fl_out, sl_out);
|
||||
}
|
||||
|
||||
/* Inserts a free block into the free block list. */
|
||||
/** Inserts a free block into the free block list. */
|
||||
insertFreeBlock(block: BlockHeader, fl: i32, sl: i32): void {
|
||||
var current = this.blocks(fl, sl);
|
||||
assert(current,
|
||||
@ -311,7 +311,7 @@ class Control extends BlockHeader { // Empty lists point here, indicating free
|
||||
this.sl_bitmap_set(fl, this.sl_bitmap(fl) | (1 << sl))
|
||||
}
|
||||
|
||||
/* Removes a free block from the free list.*/
|
||||
/** Removes a free block from the free list.*/
|
||||
removeFreeBlock(block: BlockHeader, fl: i32, sl: i32): void {
|
||||
var prev = block.prev_free;
|
||||
var next = block.next_free;
|
||||
|
20
examples/ugc/README.md
Normal file
20
examples/ugc/README.md
Normal file
@ -0,0 +1,20 @@
|
||||
μgc garbage collector
|
||||
=====================
|
||||
|
||||
A port of [Bach Le's μgc garbage collector library](https://github.com/bullno1/ugc) to AssemblyScript.
|
||||
|
||||
Instructions
|
||||
------------
|
||||
|
||||
To build [assembly/ugc.ts](./assembly/ugc.ts) to an untouched and an optimized `.wasm` including their respective `.wast` representations, run:
|
||||
|
||||
```
|
||||
$> npm run build
|
||||
```
|
||||
|
||||
Afterwards, to run the included [test](./tests/index.js):
|
||||
|
||||
```
|
||||
$> npm install
|
||||
$> npm test
|
||||
```
|
25
examples/ugc/assembly/LICENSE
Normal file
25
examples/ugc/assembly/LICENSE
Normal file
@ -0,0 +1,25 @@
|
||||
ugc.ts is based on https://github.com/bullno1/ugc
|
||||
|
||||
Copyright (c) 2017, Bach Le
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
6
examples/ugc/assembly/tsconfig.json
Normal file
6
examples/ugc/assembly/tsconfig.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "../../../std/assembly.json",
|
||||
"include": [
|
||||
"./**/*.ts"
|
||||
]
|
||||
}
|
287
examples/ugc/assembly/ugc.ts
Normal file
287
examples/ugc/assembly/ugc.ts
Normal file
@ -0,0 +1,287 @@
|
||||
/////////////////////////// μgc Garbage Collector /////////////////////////////
|
||||
// based on https://github.com/bullno1/ugc - BSD (see LICENSE file) //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// States
|
||||
const IDLE: u8 = 0;
|
||||
const MARK: u8 = 1;
|
||||
const SWEEP: u8 = 2;
|
||||
|
||||
// Gray tag
|
||||
const GRAY: u32 = 2;
|
||||
|
||||
/** Header for a managed object. */
|
||||
@explicit
|
||||
class ObjectHeader {
|
||||
|
||||
/////////////////////////////// Constants ///////////////////////////////////
|
||||
|
||||
static readonly SIZE: usize = 2 * sizeof<usize>();
|
||||
|
||||
///////////////////////////////// Fields ////////////////////////////////////
|
||||
|
||||
tagged_next: usize;
|
||||
tagged_prev: usize;
|
||||
|
||||
get next(): ObjectHeader {
|
||||
return changetype<ObjectHeader>(this.tagged_next & ~3);
|
||||
}
|
||||
|
||||
set next(value: ObjectHeader) {
|
||||
this.tagged_next = changetype<usize>(value) | (this.tagged_next & 3);
|
||||
}
|
||||
|
||||
get prev(): ObjectHeader {
|
||||
return changetype<ObjectHeader>(this.tagged_prev & ~3);
|
||||
}
|
||||
|
||||
set prev(value: ObjectHeader) {
|
||||
this.tagged_prev = changetype<usize>(value) | (this.tagged_prev & 3);
|
||||
}
|
||||
|
||||
get color(): u32 {
|
||||
return this.tagged_next & 3;
|
||||
}
|
||||
|
||||
set color(value: u32) {
|
||||
assert(value < 3);
|
||||
this.tagged_next = this.tagged_next | value;
|
||||
}
|
||||
|
||||
///////////////////////////////// Methods ///////////////////////////////////
|
||||
|
||||
push(element: ObjectHeader): void {
|
||||
element.next = this;
|
||||
element.prev = this.prev;
|
||||
this.prev.next = element;
|
||||
this.prev = element;
|
||||
}
|
||||
|
||||
unlink(): void {
|
||||
var next = this.next;
|
||||
var prev = this.prev;
|
||||
next.prev = prev;
|
||||
prev.next = next;
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.next = this;
|
||||
this.prev = this;
|
||||
}
|
||||
}
|
||||
|
||||
/** Garbage collector data. */
|
||||
@explicit
|
||||
class Control {
|
||||
|
||||
/////////////////////////////// Constants ///////////////////////////////////
|
||||
|
||||
static readonly SIZE: usize = 7 * sizeof<usize>() + 2 * sizeof<u8>();
|
||||
static readonly PAUSED_BIT: u8 = 1 << 7;
|
||||
|
||||
///////////////////////////////// Fields ////////////////////////////////////
|
||||
|
||||
// 'from' and 'to' point here
|
||||
private __set1_tagged_next: usize;
|
||||
private __set1_tagged_prev: usize;
|
||||
private __set2_tagged_next: usize;
|
||||
private __set2_tagged_prev: usize;
|
||||
|
||||
from: ObjectHeader;
|
||||
to: ObjectHeader;
|
||||
iterator: ObjectHeader;
|
||||
state: u8; // MSB indicates paused
|
||||
white: u8;
|
||||
|
||||
/** Tests whether the collector is currently paused. */
|
||||
get paused(): bool { return (this.state & Control.PAUSED_BIT) != 0; }
|
||||
/** Sets whether the collector is currently paused. */
|
||||
set paused(paused: bool) { this.state = paused ? this.state |= Control.PAUSED_BIT : this.state &= ~Control.PAUSED_BIT; }
|
||||
|
||||
///////////////////////////////// Methods ///////////////////////////////////
|
||||
|
||||
/** Creates a new instance. */
|
||||
static create(mem: usize): Control {
|
||||
var control = changetype<Control>(mem);
|
||||
var set1 = changetype<ObjectHeader>(mem);
|
||||
var set2 = changetype<ObjectHeader>(mem + 2 * sizeof<usize>());
|
||||
set1.clear();
|
||||
set2.clear();
|
||||
control.state = IDLE;
|
||||
control.white = 0;
|
||||
control.from = set1;
|
||||
control.to = set2;
|
||||
control.iterator = control.to;
|
||||
return control;
|
||||
}
|
||||
|
||||
/** Registers a new object to be managed. */
|
||||
register(obj: ObjectHeader): void {
|
||||
this.from.push(obj);
|
||||
obj.color = this.white;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new reference from one object to another.
|
||||
*
|
||||
* Whenever an object stores a reference to another object, this function
|
||||
* MUST be called to ensure that the GC works correctly.
|
||||
*
|
||||
* Root objects (stack, globals) are treated differently so there is no need
|
||||
* to call this function when a store to them occurs.
|
||||
*/
|
||||
addRef(parent: ObjectHeader, child: ObjectHeader): void {
|
||||
var parent_color = parent.color;
|
||||
var child_color = child.color;
|
||||
var white = this.white;
|
||||
var black = white ^ 1;
|
||||
if (parent_color == black && child_color == white) {
|
||||
this.makeGray(parent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the GC perform one unit of work.
|
||||
*
|
||||
* What happens depends on the current GC's state.
|
||||
*
|
||||
* - In IDLE state, it will scan the root by calling the scan callback then
|
||||
* switch to MARK state.
|
||||
* - In MARK state, it will mark one object and discover its children using
|
||||
* the scan callback. When there is no object left to mark, the GC will
|
||||
* scan the root once more to account for changes during the mark phase.
|
||||
* When all live objects are marked, it will switch to SWEEP state.
|
||||
* - In SWEEP state, it will release one object. When all garbage are
|
||||
* released, it wil switch to UGC_IDLE state.
|
||||
*/
|
||||
step(): void {
|
||||
var obj: ObjectHeader;
|
||||
switch (this.state) {
|
||||
|
||||
case IDLE:
|
||||
gc_scan_fn(this, null);
|
||||
this.state = MARK;
|
||||
break;
|
||||
|
||||
case MARK:
|
||||
obj = this.iterator.next;
|
||||
var white = this.white;
|
||||
|
||||
if (obj != this.to) {
|
||||
this.iterator = obj;
|
||||
obj.color = white ^ 1;
|
||||
gc_scan_fn(this, obj);
|
||||
} else {
|
||||
gc_scan_fn(this, null);
|
||||
obj = this.iterator.next;
|
||||
if (obj == this.to) {
|
||||
var from = this.from;
|
||||
this.from = this.to;
|
||||
this.to = from;
|
||||
this.white = white ^ 1;
|
||||
this.iterator = from.next;
|
||||
this.state = SWEEP;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SWEEP:
|
||||
obj = this.iterator;
|
||||
if (obj != this.to) {
|
||||
this.iterator = obj.next;
|
||||
gc_free_fn(this, obj);
|
||||
} else {
|
||||
this.to.clear();
|
||||
this.state = IDLE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a collection cycle.
|
||||
*
|
||||
* Start the GC if it's not already running and only return once the GC has
|
||||
* finished collecting all garbage identified at the point of calling.
|
||||
*
|
||||
* If the GC is already in the SWEEP state, it will leave newly created
|
||||
* garbage for the next cycle.
|
||||
*/
|
||||
collect(): void {
|
||||
if (this.state == IDLE)
|
||||
this.step();
|
||||
while (this.state != IDLE)
|
||||
this.step();
|
||||
}
|
||||
|
||||
/** Informs the GC of a referred object during the mark phase. */
|
||||
visit(obj: ObjectHeader): void {
|
||||
if (this.state == SWEEP)
|
||||
return;
|
||||
if (obj.color == this.white)
|
||||
this.makeGray(obj);
|
||||
}
|
||||
|
||||
makeGray(obj: ObjectHeader): void {
|
||||
if (obj != this.iterator) {
|
||||
obj.unlink();
|
||||
this.to.push(obj);
|
||||
} else {
|
||||
this.iterator = this.iterator.prev;
|
||||
}
|
||||
obj.color = GRAY;
|
||||
}
|
||||
}
|
||||
|
||||
var GC = Control.create(HEAP_BASE);
|
||||
var GC_BASE = HEAP_BASE + Control.SIZE;
|
||||
|
||||
GC.register(changetype<ObjectHeader>(GC_BASE));
|
||||
|
||||
// Exported interface
|
||||
|
||||
/** Pauses automatic garbage collection. */
|
||||
export function gc_pause(): void {
|
||||
GC.paused = true;
|
||||
}
|
||||
|
||||
/** Resumes automatic garbage collection. */
|
||||
export function gc_resume(): void {
|
||||
GC.paused = false;
|
||||
}
|
||||
|
||||
/** Performs a collection cycle. Ignores pauses. */
|
||||
export function gc_collect(): void {
|
||||
var paused = GC.paused;
|
||||
GC.paused = false;
|
||||
GC.collect();
|
||||
GC.paused = paused;
|
||||
}
|
||||
|
||||
// TODO: these functions must be generated by the compiler and combined by
|
||||
// any potential linker. They live here for now to document their structure.
|
||||
|
||||
function gc_scan_fn(control: Control, header: ObjectHeader | null): void {
|
||||
if (!header) {
|
||||
// visit all global vars referencing managed objects
|
||||
} else {
|
||||
// visit all referenced objects using the compiler's knowledge of this
|
||||
// object's layout
|
||||
var classId = load<u32>(changetype<usize>(header) + ObjectHeader.SIZE);
|
||||
// switch (classId) {
|
||||
// arrays
|
||||
// strings
|
||||
// user-defined
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
function gc_free_fn(control: Control, header: ObjectHeader): void {
|
||||
// finalize the given object using the compiler's knowledge of its layout
|
||||
var classId = load<u32>(changetype<usize>(header) + ObjectHeader.SIZE);
|
||||
// switch (classId) {
|
||||
// array, string: free their data segments
|
||||
// TODO: might make sense to provide @finalize or similar
|
||||
// }
|
||||
free_memory(changetype<usize>(header));
|
||||
}
|
11
examples/ugc/package.json
Normal file
11
examples/ugc/package.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "@assemblyscript/ugc",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "npm run build:untouched && npm run build:optimized",
|
||||
"build:untouched": "asc assembly/ugc.ts -t ugc.untouched.wast -b ugc.untouched.wasm --validate",
|
||||
"build:optimized": "asc -O assembly/ugc.ts -b ugc.optimized.wasm -t ugc.optimized.wast --validate --noAssert --runPasses inlining",
|
||||
"test": "node tests"
|
||||
}
|
||||
}
|
26
examples/ugc/tests/index.js
Normal file
26
examples/ugc/tests/index.js
Normal file
@ -0,0 +1,26 @@
|
||||
var fs = require("fs");
|
||||
|
||||
// NOTE that this doesn't do anything useful, yet
|
||||
|
||||
var ugc = new WebAssembly.Instance(new WebAssembly.Module(fs.readFileSync(__dirname + "/../ugc.untouched.wasm"))).exports;
|
||||
|
||||
function mem(memory, offset, count) {
|
||||
if (!offset) offset = 0;
|
||||
if (!count) count = 1024;
|
||||
var mem = new Uint8Array(memory.buffer, offset);
|
||||
var stackTop = new Uint32Array(memory.buffer, 4, 1)[0];
|
||||
var hex = [];
|
||||
for (var i = 0; i < count; ++i) {
|
||||
var o = (offset + i).toString(16);
|
||||
while (o.length < 3) o = "0" + o;
|
||||
if ((i & 15) === 0) {
|
||||
hex.push("\n" + o + ":");
|
||||
}
|
||||
var h = mem[i].toString(16);
|
||||
if (h.length < 2) h = "0" + h;
|
||||
hex.push(h);
|
||||
}
|
||||
console.log(hex.join(" ") + " ...");
|
||||
}
|
||||
|
||||
mem(ugc.memory, 0, 1024);
|
66
src/ast.ts
66
src/ast.ts
@ -1050,12 +1050,60 @@ export abstract class DeclarationStatement extends Statement {
|
||||
/** Array of decorators. */
|
||||
decorators: Decorator[] | null = null;
|
||||
|
||||
protected _cachedInternalName: string | null = null;
|
||||
protected cachedProgramLevelInternalName: string | null = null;
|
||||
protected cachedFileLevelInternalName: string | null = null;
|
||||
|
||||
/** Gets the mangled internal name of this declaration. */
|
||||
get internalName(): string { return this._cachedInternalName === null ? this._cachedInternalName = mangleInternalName(this) : this._cachedInternalName; }
|
||||
/** Tests if this is a top-level declaration. */
|
||||
get isTopLevel(): bool { return this.parent != null && this.parent.kind == NodeKind.SOURCE; }
|
||||
/** Gets the mangled program-level internal name of this declaration. */
|
||||
get programLevelInternalName(): string {
|
||||
if (!this.cachedProgramLevelInternalName)
|
||||
this.cachedProgramLevelInternalName = mangleInternalName(this, true);
|
||||
return this.cachedProgramLevelInternalName;
|
||||
}
|
||||
|
||||
/** Gets the mangled file-level internal name of this declaration. */
|
||||
get fileLevelInternalName(): string {
|
||||
if (!this.cachedFileLevelInternalName)
|
||||
this.cachedFileLevelInternalName = mangleInternalName(this, false);
|
||||
return this.cachedFileLevelInternalName;
|
||||
}
|
||||
|
||||
/** Tests if this is a top-level declaration within its source file. */
|
||||
get isTopLevel(): bool {
|
||||
var parent = this.parent;
|
||||
if (!parent)
|
||||
return false;
|
||||
if (parent.kind == NodeKind.VARIABLE)
|
||||
if (!(parent = parent.parent))
|
||||
return false;
|
||||
return parent.kind == NodeKind.SOURCE;
|
||||
}
|
||||
|
||||
/** Tests if this declaration is a top-level export within its source file. */
|
||||
get isTopLevelExport(): bool {
|
||||
var parent = this.parent;
|
||||
if (!parent)
|
||||
return false;
|
||||
if (parent.kind == NodeKind.VARIABLE)
|
||||
if (!(parent = parent.parent))
|
||||
return false;
|
||||
if (parent.kind == NodeKind.NAMESPACEDECLARATION)
|
||||
return hasModifier(ModifierKind.EXPORT, this.modifiers) && (<NamespaceDeclaration>parent).isTopLevelExport;
|
||||
if (parent.kind == NodeKind.CLASSDECLARATION)
|
||||
return hasModifier(ModifierKind.STATIC, this.modifiers) && (<ClassDeclaration>parent).isTopLevelExport;
|
||||
return parent.kind == NodeKind.SOURCE && hasModifier(ModifierKind.EXPORT, this.modifiers);
|
||||
}
|
||||
|
||||
/** Tests if this declaration exported by the given member needs an explicit export. */
|
||||
needsExplicitExport(member: ExportMember): bool {
|
||||
// This is necessary because module-level exports are automatically created for
|
||||
// exported top level declarations of all sorts. In other words this function
|
||||
// tests that this condition doesn't apply so the export isn't a duplicate.
|
||||
return (
|
||||
member.identifier.name != member.externalIdentifier.name || // if aliased
|
||||
this.range.source != member.range.source || // if a re-export
|
||||
!this.isTopLevelExport // if not top-level
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** Base class of all variable-like declaration statements with a type and initializer. */
|
||||
@ -1470,7 +1518,7 @@ export function mangleInternalPath(path: string): string {
|
||||
}
|
||||
|
||||
/** Mangles a declaration's name to an internal name. */
|
||||
export function mangleInternalName(declaration: DeclarationStatement): string {
|
||||
export function mangleInternalName(declaration: DeclarationStatement, asGlobal: bool = false): string {
|
||||
var name = declaration.name.name;
|
||||
var parent = declaration.parent;
|
||||
if (!parent)
|
||||
@ -1479,8 +1527,10 @@ export function mangleInternalName(declaration: DeclarationStatement): string {
|
||||
if (!(parent = parent.parent))
|
||||
return name;
|
||||
if (parent.kind == NodeKind.CLASSDECLARATION)
|
||||
return (<ClassDeclaration>parent).internalName + (hasModifier(ModifierKind.STATIC, declaration.modifiers) ? STATIC_DELIMITER : INSTANCE_DELIMITER) + name;
|
||||
return mangleInternalName(<ClassDeclaration>parent, asGlobal) + (hasModifier(ModifierKind.STATIC, declaration.modifiers) ? STATIC_DELIMITER : INSTANCE_DELIMITER) + name;
|
||||
if (parent.kind == NodeKind.NAMESPACEDECLARATION || parent.kind == NodeKind.ENUMDECLARATION)
|
||||
return (<DeclarationStatement>parent).internalName + STATIC_DELIMITER + name;
|
||||
return mangleInternalName(<DeclarationStatement>parent, asGlobal) + STATIC_DELIMITER + name;
|
||||
if (asGlobal)
|
||||
return name;
|
||||
return declaration.range.source.internalPath + PATH_DELIMITER + name;
|
||||
}
|
||||
|
100
src/compiler.ts
100
src/compiler.ts
@ -326,17 +326,11 @@ export class Compiler extends DiagnosticEmitter {
|
||||
// globals
|
||||
|
||||
compileGlobalDeclaration(declaration: VariableDeclaration, isConst: bool): Global | null {
|
||||
var element = this.program.elements.get(declaration.internalName);
|
||||
var element = this.program.elements.get(declaration.fileLevelInternalName);
|
||||
if (!element || element.kind != ElementKind.GLOBAL)
|
||||
throw new Error("global expected");
|
||||
if (!this.compileGlobal(<Global>element)) // reports
|
||||
return null;
|
||||
if (isModuleExport(element, declaration)) {
|
||||
if ((<Global>element).hasConstantValue)
|
||||
this.module.addGlobalExport(element.internalName, declaration.name.name);
|
||||
else
|
||||
this.warning(DiagnosticCode.Cannot_export_a_mutable_global, declaration.range);
|
||||
}
|
||||
return <Global>element;
|
||||
}
|
||||
|
||||
@ -416,7 +410,6 @@ export class Compiler extends DiagnosticEmitter {
|
||||
if (!this.module.noEmit)
|
||||
this.startFunctionBody.push(setExpr);
|
||||
} else {
|
||||
// TODO: not necessary to create a global if constant and not a file-level export anyway
|
||||
if (!global.isMutable) {
|
||||
if (!this.module.noEmit) {
|
||||
var exprType = _BinaryenExpressionGetType(initExpr);
|
||||
@ -441,11 +434,14 @@ export class Compiler extends DiagnosticEmitter {
|
||||
default:
|
||||
throw new Error("concrete type expected");
|
||||
}
|
||||
global.hasConstantValue = true;
|
||||
if (!declaration || isModuleExport(global, declaration))
|
||||
this.module.addGlobal(internalName, nativeType, global.isMutable, initExpr);
|
||||
}
|
||||
} else if (!this.module.noEmit)
|
||||
global.hasConstantValue = true;
|
||||
if (!declaration || declaration.isTopLevel) { // might be re-exported
|
||||
this.module.addGlobal(internalName, nativeType, global.isMutable, initExpr);
|
||||
}
|
||||
if (declaration && declaration.range.source.isEntry && declaration.isTopLevelExport)
|
||||
this.module.addGlobalExport(global.internalName, declaration.programLevelInternalName);
|
||||
} else
|
||||
this.module.addGlobal(internalName, nativeType, global.isMutable, initExpr);
|
||||
}
|
||||
global.isCompiled = true;
|
||||
@ -454,11 +450,11 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
// enums
|
||||
|
||||
compileEnumDeclaration(declaration: EnumDeclaration): void {
|
||||
var element = this.program.elements.get(declaration.internalName);
|
||||
compileEnumDeclaration(declaration: EnumDeclaration): Enum | null {
|
||||
var element = this.program.elements.get(declaration.fileLevelInternalName);
|
||||
if (!element || element.kind != ElementKind.ENUM)
|
||||
throw new Error("enum expected");
|
||||
this.compileEnum(<Enum>element);
|
||||
return this.compileEnum(<Enum>element) ? <Enum>element : null;
|
||||
}
|
||||
|
||||
compileEnum(element: Enum): bool {
|
||||
@ -473,10 +469,11 @@ export class Compiler extends DiagnosticEmitter {
|
||||
continue;
|
||||
var initInStart = false;
|
||||
var val = <EnumValue>member;
|
||||
var valueDeclaration = val.declaration;
|
||||
if (val.hasConstantValue) {
|
||||
this.module.addGlobal(val.internalName, NativeType.I32, false, this.module.createI32(val.constantValue));
|
||||
} else if (val.declaration) {
|
||||
var valueDeclaration = val.declaration;
|
||||
if (!element.declaration || element.declaration.isTopLevelExport)
|
||||
this.module.addGlobal(val.internalName, NativeType.I32, false, this.module.createI32(val.constantValue));
|
||||
} else if (valueDeclaration) {
|
||||
var initExpr: ExpressionRef;
|
||||
if (valueDeclaration.value) {
|
||||
initExpr = this.compileExpression(<Expression>valueDeclaration.value, Type.i32);
|
||||
@ -519,25 +516,26 @@ export class Compiler extends DiagnosticEmitter {
|
||||
}
|
||||
} else
|
||||
throw new Error("declaration expected");
|
||||
if (element.declaration && isModuleExport(element, element.declaration) && !initInStart)
|
||||
this.module.addGlobalExport(member.internalName, member.internalName);
|
||||
previousValue = <EnumValue>val;
|
||||
|
||||
// export values if the enum is exported
|
||||
if (element.declaration && element.declaration.range.source.isEntry && element.declaration.isTopLevelExport) {
|
||||
if (member.hasConstantValue)
|
||||
this.module.addGlobalExport(member.internalName, member.internalName);
|
||||
else if (valueDeclaration)
|
||||
this.warning(DiagnosticCode.Cannot_export_a_mutable_global, valueDeclaration.range);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// functions
|
||||
|
||||
compileFunctionDeclaration(declaration: FunctionDeclaration, typeArguments: TypeNode[], contextualTypeArguments: Map<string,Type> | null = null, alternativeReportNode: Node | null = null): void {
|
||||
var internalName = declaration.internalName;
|
||||
var element = this.program.elements.get(internalName);
|
||||
compileFunctionDeclaration(declaration: FunctionDeclaration, typeArguments: TypeNode[], contextualTypeArguments: Map<string,Type> | null = null, alternativeReportNode: Node | null = null): Function | null {
|
||||
var element = this.program.elements.get(declaration.fileLevelInternalName);
|
||||
if (!element || element.kind != ElementKind.FUNCTION_PROTOTYPE)
|
||||
throw new Error("function expected");
|
||||
var instance = this.compileFunctionUsingTypeArguments(<FunctionPrototype>element, typeArguments, contextualTypeArguments, alternativeReportNode); // reports
|
||||
if (!instance)
|
||||
return;
|
||||
if (isModuleExport(instance, declaration))
|
||||
this.module.addFunctionExport(instance.internalName, declaration.name.name);
|
||||
return this.compileFunctionUsingTypeArguments(<FunctionPrototype>element, typeArguments, contextualTypeArguments, alternativeReportNode); // reports
|
||||
}
|
||||
|
||||
compileFunctionUsingTypeArguments(prototype: FunctionPrototype, typeArguments: TypeNode[], contextualTypeArguments: Map<string,Type> | null = null, alternativeReportNode: Node | null = null): Function | null {
|
||||
@ -606,6 +604,9 @@ export class Compiler extends DiagnosticEmitter {
|
||||
this.module.addFunction(instance.internalName, typeRef, typesToNativeTypes(instance.additionalLocals), this.module.createBlock(null, <ExpressionRef[]>stmts, NativeType.None));
|
||||
}
|
||||
instance.finalize();
|
||||
if (declaration.range.source.isEntry && declaration.isTopLevelExport) {
|
||||
this.module.addFunctionExport(instance.internalName, declaration.name.name);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -709,19 +710,25 @@ export class Compiler extends DiagnosticEmitter {
|
||||
break;
|
||||
|
||||
case ElementKind.FUNCTION_PROTOTYPE:
|
||||
if (!(<FunctionPrototype>element).isGeneric) {
|
||||
if (!(<FunctionPrototype>element).isGeneric && statement.range.source.isEntry) {
|
||||
var functionInstance = this.compileFunctionUsingTypeArguments(<FunctionPrototype>element, []);
|
||||
if (functionInstance && statement.range.source.isEntry)
|
||||
this.module.addFunctionExport(functionInstance.internalName, member.externalIdentifier.name);
|
||||
if (functionInstance) {
|
||||
var functionDeclaration = functionInstance.prototype.declaration;
|
||||
if (functionDeclaration && functionDeclaration.needsExplicitExport(member))
|
||||
this.module.addFunctionExport(functionInstance.internalName, member.externalIdentifier.name);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ElementKind.GLOBAL:
|
||||
if (this.compileGlobal(<Global>element) && statement.range.source.isEntry) {
|
||||
if ((<Global>element).hasConstantValue)
|
||||
this.module.addGlobalExport(element.internalName, member.externalIdentifier.name);
|
||||
else
|
||||
this.warning(DiagnosticCode.Cannot_export_a_mutable_global, member.range);
|
||||
var globalDeclaration = (<Global>element).declaration;
|
||||
if (globalDeclaration && globalDeclaration.needsExplicitExport(member)) {
|
||||
if ((<Global>element).hasConstantValue)
|
||||
this.module.addGlobalExport(element.internalName, member.externalIdentifier.name);
|
||||
else
|
||||
this.warning(DiagnosticCode.Cannot_export_a_mutable_global, member.range);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@ -735,8 +742,7 @@ export class Compiler extends DiagnosticEmitter {
|
||||
// classes
|
||||
|
||||
compileClassDeclaration(declaration: ClassDeclaration, typeArguments: TypeNode[], contextualTypeArguments: Map<string,Type> | null = null, alternativeReportNode: Node | null = null): void {
|
||||
var internalName = declaration.internalName;
|
||||
var element = this.program.elements.get(internalName);
|
||||
var element = this.program.elements.get(declaration.fileLevelInternalName);
|
||||
if (!element || element.kind != ElementKind.CLASS_PROTOTYPE)
|
||||
throw new Error("class expected");
|
||||
this.compileClassUsingTypeArguments(<ClassPrototype>element, typeArguments, contextualTypeArguments, alternativeReportNode);
|
||||
@ -3063,26 +3069,6 @@ export class Compiler extends DiagnosticEmitter {
|
||||
|
||||
// helpers
|
||||
|
||||
/** Tests whether an element is a module-level export from the entry file. */
|
||||
function isModuleExport(element: Element, declaration: DeclarationStatement): bool {
|
||||
if (!element.isExported)
|
||||
return false;
|
||||
var parentNode = declaration.parent;
|
||||
if (!parentNode)
|
||||
return false;
|
||||
if (declaration.range.source.isEntry && parentNode.kind != NodeKind.NAMESPACEDECLARATION)
|
||||
return true;
|
||||
if (parentNode.kind == NodeKind.VARIABLE)
|
||||
if (!(parentNode = parentNode.parent))
|
||||
return false;
|
||||
if (parentNode.kind != NodeKind.NAMESPACEDECLARATION && parentNode.kind != NodeKind.CLASSDECLARATION)
|
||||
return false;
|
||||
var parent = element.program.elements.get((<DeclarationStatement>parentNode).internalName);
|
||||
if (!parent)
|
||||
return false;
|
||||
return isModuleExport(parent, <DeclarationStatement>parentNode);
|
||||
}
|
||||
|
||||
/** Creates an inlined expression of a constant variable-like element. */
|
||||
function makeInlineConstant(element: VariableLikeElement, module: Module): ExpressionRef {
|
||||
assert(element.hasConstantValue);
|
||||
|
@ -180,7 +180,7 @@ export abstract class DiagnosticEmitter {
|
||||
this.diagnostics.push(message);
|
||||
if (!this.silentDiagnostics) {
|
||||
console.log(formatDiagnosticMessage(message, true, true) + "\n"); // temporary
|
||||
console.log(<string>new Error("stack").stack);
|
||||
// console.log(<string>new Error("stack").stack);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,7 +110,7 @@ export class Program extends DiagnosticEmitter {
|
||||
types: Map<string,Type> = noTypesYet;
|
||||
/** Declared type aliases. */
|
||||
typeAliases: Map<string,TypeNode> = new Map();
|
||||
/** Exports of individual files by internal name. Not global exports. */
|
||||
/** Exports of individual files by exported internal name. Not global exports. */
|
||||
exports: Map<string,Element> = new Map();
|
||||
|
||||
/** Constructs a new program, optionally inheriting parser diagnostics. */
|
||||
@ -283,7 +283,7 @@ export class Program extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
private initializeClass(declaration: ClassDeclaration, queuedDerivedClasses: ClassPrototype[], namespace: Element | null = null): void {
|
||||
var internalName = declaration.internalName;
|
||||
var internalName = declaration.fileLevelInternalName;
|
||||
if (this.elements.has(internalName)) {
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName);
|
||||
return;
|
||||
@ -351,17 +351,17 @@ export class Program extends DiagnosticEmitter {
|
||||
|
||||
private initializeField(declaration: FieldDeclaration, classPrototype: ClassPrototype): void {
|
||||
var name = declaration.name.name;
|
||||
var internalName = declaration.internalName;
|
||||
var internalName = declaration.fileLevelInternalName;
|
||||
|
||||
// static fields become global variables
|
||||
if (hasModifier(ModifierKind.STATIC, declaration.modifiers)) {
|
||||
if (this.elements.has(internalName)) {
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, declaration.internalName);
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName);
|
||||
return;
|
||||
}
|
||||
if (classPrototype.members) {
|
||||
if (classPrototype.members.has(name)) {
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, declaration.internalName);
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName);
|
||||
return;
|
||||
}
|
||||
} else
|
||||
@ -374,7 +374,7 @@ export class Program extends DiagnosticEmitter {
|
||||
} else {
|
||||
if (classPrototype.instanceMembers) {
|
||||
if (classPrototype.instanceMembers.has(name)) {
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, declaration.internalName);
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName);
|
||||
return;
|
||||
}
|
||||
} else
|
||||
@ -386,19 +386,19 @@ export class Program extends DiagnosticEmitter {
|
||||
|
||||
private initializeMethod(declaration: MethodDeclaration, classPrototype: ClassPrototype): void {
|
||||
var name = declaration.name.name;
|
||||
var internalName = declaration.internalName;
|
||||
var internalName = declaration.fileLevelInternalName;
|
||||
var instancePrototype: FunctionPrototype | null = null;
|
||||
|
||||
// static methods become global functions
|
||||
if (hasModifier(ModifierKind.STATIC, declaration.modifiers)) {
|
||||
|
||||
if (this.elements.has(internalName)) {
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, declaration.internalName);
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName);
|
||||
return;
|
||||
}
|
||||
if (classPrototype.members) {
|
||||
if (classPrototype.members.has(name)) {
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, declaration.internalName);
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName);
|
||||
return;
|
||||
}
|
||||
} else
|
||||
@ -411,7 +411,7 @@ export class Program extends DiagnosticEmitter {
|
||||
} else {
|
||||
if (classPrototype.instanceMembers) {
|
||||
if (classPrototype.instanceMembers.has(name)) {
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, declaration.internalName);
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName);
|
||||
return;
|
||||
}
|
||||
} else
|
||||
@ -470,7 +470,7 @@ export class Program extends DiagnosticEmitter {
|
||||
|
||||
private initializeAccessor(declaration: MethodDeclaration, classPrototype: ClassPrototype, isGetter: bool): void {
|
||||
var propertyName = declaration.name.name;
|
||||
var internalPropertyName = declaration.internalName;
|
||||
var internalPropertyName = declaration.fileLevelInternalName;
|
||||
|
||||
var propertyElement = this.elements.get(internalPropertyName);
|
||||
if (propertyElement) {
|
||||
@ -505,7 +505,7 @@ export class Program extends DiagnosticEmitter {
|
||||
var internalInstanceName = classPrototype.internalName + INSTANCE_DELIMITER + name;
|
||||
if (classPrototype.instanceMembers) {
|
||||
if (classPrototype.instanceMembers.has(name)) {
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, declaration.internalName);
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalPropertyName);
|
||||
return;
|
||||
}
|
||||
} else
|
||||
@ -521,7 +521,7 @@ export class Program extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
private initializeEnum(declaration: EnumDeclaration, namespace: Element | null = null): void {
|
||||
var internalName = declaration.internalName;
|
||||
var internalName = declaration.fileLevelInternalName;
|
||||
if (this.elements.has(internalName)) {
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName);
|
||||
return;
|
||||
@ -556,7 +556,7 @@ export class Program extends DiagnosticEmitter {
|
||||
|
||||
private initializeEnumValue(declaration: EnumValueDeclaration, enm: Enum): void {
|
||||
var name = declaration.name.name;
|
||||
var internalName = declaration.internalName;
|
||||
var internalName = declaration.fileLevelInternalName;
|
||||
if (enm.members) {
|
||||
if (enm.members.has(name)) {
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName);
|
||||
@ -649,7 +649,7 @@ export class Program extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
private initializeFunction(declaration: FunctionDeclaration, namespace: Element | null = null): void {
|
||||
var internalName = declaration.internalName;
|
||||
var internalName = declaration.fileLevelInternalName;
|
||||
if (this.elements.has(internalName)) {
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName);
|
||||
return;
|
||||
@ -694,7 +694,7 @@ export class Program extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
private initializeImport(declaration: ImportDeclaration, internalPath: string, queuedExports: Map<string,QueuedExport>, queuedImports: QueuedImport[]): void {
|
||||
var internalName = declaration.internalName;
|
||||
var internalName = declaration.fileLevelInternalName;
|
||||
if (this.elements.has(internalName)) {
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName);
|
||||
return;
|
||||
@ -739,7 +739,7 @@ export class Program extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
private initializeInterface(declaration: InterfaceDeclaration, namespace: Element | null = null): void {
|
||||
var internalName = declaration.internalName;
|
||||
var internalName = declaration.fileLevelInternalName;
|
||||
if (this.elements.has(internalName)) {
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName);
|
||||
return;
|
||||
@ -791,7 +791,7 @@ export class Program extends DiagnosticEmitter {
|
||||
}
|
||||
|
||||
private initializeNamespace(declaration: NamespaceDeclaration, queuedExtendingClasses: ClassPrototype[], parentNamespace: Element | null = null): void {
|
||||
var internalName = declaration.internalName;
|
||||
var internalName = declaration.fileLevelInternalName;
|
||||
|
||||
var namespace = this.elements.get(internalName);
|
||||
if (!namespace) {
|
||||
@ -873,7 +873,7 @@ export class Program extends DiagnosticEmitter {
|
||||
var declarations = statement.declarations;
|
||||
for (var i = 0, k = declarations.length; i < k; ++i) {
|
||||
var declaration = declarations[i];
|
||||
var internalName = declaration.internalName;
|
||||
var internalName = declaration.fileLevelInternalName;
|
||||
if (this.elements.has(internalName)) {
|
||||
this.error(DiagnosticCode.Duplicate_identifier_0, declaration.name.range, internalName);
|
||||
continue;
|
||||
|
@ -3,14 +3,16 @@
|
||||
(type $v (func))
|
||||
(global $export/a i32 (i32.const 1))
|
||||
(global $export/b i32 (i32.const 2))
|
||||
(global $export/c i32 (i32.const 3))
|
||||
(memory $0 1)
|
||||
(export "add" (func $export/add))
|
||||
(export "renamed_sub" (func $export/sub))
|
||||
(export "sub" (func $export/sub))
|
||||
(export "renamed_mul" (func $export/mul))
|
||||
(export "a" (global $export/a))
|
||||
(export "renamed_b" (global $export/b))
|
||||
(export "b" (global $export/b))
|
||||
(export "renamed_c" (global $export/c))
|
||||
(export "two" (func $export/ns.two))
|
||||
(export "memory" (memory $0))
|
||||
(start $export/ns.two)
|
||||
(func $export/add (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
|
||||
(i32.add
|
||||
(get_local $0)
|
||||
@ -23,7 +25,13 @@
|
||||
(get_local $1)
|
||||
)
|
||||
)
|
||||
(func $export/ns.two (; 2 ;) (type $v)
|
||||
(func $export/mul (; 2 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
|
||||
(i32.mul
|
||||
(get_local $0)
|
||||
(get_local $1)
|
||||
)
|
||||
)
|
||||
(func $export/ns.two (; 3 ;) (type $v)
|
||||
(nop)
|
||||
)
|
||||
)
|
||||
|
@ -6,14 +6,23 @@ function sub(a: i32, b: i32): i32 {
|
||||
return a - b;
|
||||
}
|
||||
|
||||
export { sub as renamed_sub };
|
||||
export { sub };
|
||||
|
||||
function mul(a: i32, b: i32): i32 { // not exported as "mul"
|
||||
return a * b;
|
||||
}
|
||||
|
||||
export { mul as renamed_mul };
|
||||
|
||||
export const a: i32 = 1;
|
||||
|
||||
const b: i32 = 2;
|
||||
b;
|
||||
|
||||
export { b as renamed_b };
|
||||
export { b };
|
||||
|
||||
const c: i32 = 3; // not exported as "c"
|
||||
|
||||
export { c as renamed_c };
|
||||
|
||||
export namespace ns {
|
||||
function one(): void {}
|
||||
|
@ -3,15 +3,17 @@
|
||||
(type $v (func))
|
||||
(global $export/a i32 (i32.const 1))
|
||||
(global $export/b i32 (i32.const 2))
|
||||
(global $export/c i32 (i32.const 3))
|
||||
(global $HEAP_BASE i32 (i32.const 4))
|
||||
(memory $0 1)
|
||||
(export "add" (func $export/add))
|
||||
(export "renamed_sub" (func $export/sub))
|
||||
(export "sub" (func $export/sub))
|
||||
(export "renamed_mul" (func $export/mul))
|
||||
(export "a" (global $export/a))
|
||||
(export "renamed_b" (global $export/b))
|
||||
(export "b" (global $export/b))
|
||||
(export "renamed_c" (global $export/c))
|
||||
(export "two" (func $export/ns.two))
|
||||
(export "memory" (memory $0))
|
||||
(start $start)
|
||||
(func $export/add (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
|
||||
(return
|
||||
(i32.add
|
||||
@ -28,13 +30,16 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(func $export/ns.two (; 2 ;) (type $v)
|
||||
)
|
||||
(func $start (; 3 ;) (type $v)
|
||||
(drop
|
||||
(i32.const 2)
|
||||
(func $export/mul (; 2 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
|
||||
(return
|
||||
(i32.mul
|
||||
(get_local $0)
|
||||
(get_local $1)
|
||||
)
|
||||
)
|
||||
)
|
||||
(func $export/ns.two (; 3 ;) (type $v)
|
||||
)
|
||||
)
|
||||
(;
|
||||
[program.elements]
|
||||
@ -82,15 +87,19 @@
|
||||
GLOBAL: HEAP_BASE
|
||||
FUNCTION_PROTOTYPE: export/add
|
||||
FUNCTION_PROTOTYPE: export/sub
|
||||
FUNCTION_PROTOTYPE: export/mul
|
||||
GLOBAL: export/a
|
||||
GLOBAL: export/b
|
||||
GLOBAL: export/c
|
||||
NAMESPACE: export/ns
|
||||
FUNCTION_PROTOTYPE: export/ns.one
|
||||
FUNCTION_PROTOTYPE: export/ns.two
|
||||
[program.exports]
|
||||
FUNCTION_PROTOTYPE: export/add
|
||||
FUNCTION_PROTOTYPE: export/renamed_sub
|
||||
FUNCTION_PROTOTYPE: export/sub
|
||||
FUNCTION_PROTOTYPE: export/renamed_mul
|
||||
GLOBAL: export/a
|
||||
GLOBAL: export/renamed_b
|
||||
GLOBAL: export/b
|
||||
GLOBAL: export/renamed_c
|
||||
NAMESPACE: export/ns
|
||||
;)
|
||||
|
@ -9,33 +9,51 @@
|
||||
(local $1 i32)
|
||||
(local $2 i32)
|
||||
(local $3 i32)
|
||||
(local $4 i32)
|
||||
(local $5 i32)
|
||||
(drop
|
||||
(i32.add
|
||||
(block (result i32)
|
||||
(block $__inlined_func$export/add (result i32)
|
||||
(set_local $0
|
||||
(i32.const 1)
|
||||
(i32.add
|
||||
(block (result i32)
|
||||
(block $__inlined_func$export/add (result i32)
|
||||
(set_local $0
|
||||
(i32.const 1)
|
||||
)
|
||||
(set_local $1
|
||||
(i32.const 2)
|
||||
)
|
||||
(i32.add
|
||||
(get_local $0)
|
||||
(get_local $1)
|
||||
)
|
||||
)
|
||||
(set_local $1
|
||||
(i32.const 2)
|
||||
)
|
||||
(i32.add
|
||||
(get_local $0)
|
||||
(get_local $1)
|
||||
)
|
||||
(block (result i32)
|
||||
(block $__inlined_func$export/sub (result i32)
|
||||
(set_local $2
|
||||
(i32.const 2)
|
||||
)
|
||||
(set_local $3
|
||||
(i32.const 3)
|
||||
)
|
||||
(i32.sub
|
||||
(get_local $2)
|
||||
(get_local $3)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(block (result i32)
|
||||
(block $__inlined_func$export/sub (result i32)
|
||||
(set_local $2
|
||||
(i32.const 2)
|
||||
(block $__inlined_func$export/mul (result i32)
|
||||
(set_local $4
|
||||
(i32.const 3)
|
||||
)
|
||||
(set_local $3
|
||||
(set_local $5
|
||||
(i32.const 1)
|
||||
)
|
||||
(i32.sub
|
||||
(get_local $2)
|
||||
(get_local $3)
|
||||
(i32.mul
|
||||
(get_local $4)
|
||||
(get_local $5)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -16,18 +16,30 @@
|
||||
(get_local $1)
|
||||
)
|
||||
)
|
||||
(func $export/ns.two (; 2 ;) (type $v)
|
||||
(func $export/mul (; 2 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
|
||||
(i32.mul
|
||||
(get_local $0)
|
||||
(get_local $1)
|
||||
)
|
||||
)
|
||||
(func $export/ns.two (; 3 ;) (type $v)
|
||||
(nop)
|
||||
)
|
||||
(func $start (; 3 ;) (type $v)
|
||||
(func $start (; 4 ;) (type $v)
|
||||
(drop
|
||||
(i32.add
|
||||
(call $export/add
|
||||
(i32.const 1)
|
||||
(i32.const 2)
|
||||
(i32.add
|
||||
(call $export/add
|
||||
(i32.const 1)
|
||||
(i32.const 2)
|
||||
)
|
||||
(call $export/sub
|
||||
(i32.const 2)
|
||||
(i32.const 3)
|
||||
)
|
||||
)
|
||||
(call $export/sub
|
||||
(i32.const 2)
|
||||
(call $export/mul
|
||||
(i32.const 3)
|
||||
(i32.const 1)
|
||||
)
|
||||
)
|
||||
|
@ -1,5 +1,13 @@
|
||||
import { add, renamed_sub as sub, a, renamed_b as b, ns as renamed_ns } from "./export";
|
||||
import {
|
||||
add,
|
||||
sub as sub,
|
||||
renamed_mul as mul,
|
||||
a,
|
||||
b as b,
|
||||
renamed_c as c,
|
||||
ns as renamed_ns
|
||||
} from "./export";
|
||||
|
||||
add(a, b) + sub(b, a);
|
||||
add(a, b) + sub(b, c) + mul(c, a);
|
||||
|
||||
renamed_ns.two();
|
||||
|
@ -3,6 +3,7 @@
|
||||
(type $v (func))
|
||||
(global $export/a i32 (i32.const 1))
|
||||
(global $export/b i32 (i32.const 2))
|
||||
(global $export/c i32 (i32.const 3))
|
||||
(global $HEAP_BASE i32 (i32.const 4))
|
||||
(memory $0 1)
|
||||
(export "memory" (memory $0))
|
||||
@ -23,20 +24,31 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(func $export/ns.two (; 2 ;) (type $v)
|
||||
)
|
||||
(func $start (; 3 ;) (type $v)
|
||||
(drop
|
||||
(i32.const 2)
|
||||
(func $export/mul (; 2 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
|
||||
(return
|
||||
(i32.mul
|
||||
(get_local $0)
|
||||
(get_local $1)
|
||||
)
|
||||
)
|
||||
)
|
||||
(func $export/ns.two (; 3 ;) (type $v)
|
||||
)
|
||||
(func $start (; 4 ;) (type $v)
|
||||
(drop
|
||||
(i32.add
|
||||
(call $export/add
|
||||
(i32.const 1)
|
||||
(i32.const 2)
|
||||
(i32.add
|
||||
(call $export/add
|
||||
(i32.const 1)
|
||||
(i32.const 2)
|
||||
)
|
||||
(call $export/sub
|
||||
(i32.const 2)
|
||||
(i32.const 3)
|
||||
)
|
||||
)
|
||||
(call $export/sub
|
||||
(i32.const 2)
|
||||
(call $export/mul
|
||||
(i32.const 3)
|
||||
(i32.const 1)
|
||||
)
|
||||
)
|
||||
@ -90,20 +102,26 @@
|
||||
GLOBAL: HEAP_BASE
|
||||
FUNCTION_PROTOTYPE: export/add
|
||||
FUNCTION_PROTOTYPE: export/sub
|
||||
FUNCTION_PROTOTYPE: export/mul
|
||||
GLOBAL: export/a
|
||||
GLOBAL: export/b
|
||||
GLOBAL: export/c
|
||||
NAMESPACE: export/ns
|
||||
FUNCTION_PROTOTYPE: export/ns.one
|
||||
FUNCTION_PROTOTYPE: export/ns.two
|
||||
FUNCTION_PROTOTYPE: import/add
|
||||
FUNCTION_PROTOTYPE: import/sub
|
||||
FUNCTION_PROTOTYPE: import/mul
|
||||
GLOBAL: import/a
|
||||
GLOBAL: import/b
|
||||
GLOBAL: import/c
|
||||
NAMESPACE: import/renamed_ns
|
||||
[program.exports]
|
||||
FUNCTION_PROTOTYPE: export/add
|
||||
FUNCTION_PROTOTYPE: export/renamed_sub
|
||||
FUNCTION_PROTOTYPE: export/sub
|
||||
FUNCTION_PROTOTYPE: export/renamed_mul
|
||||
GLOBAL: export/a
|
||||
GLOBAL: export/renamed_b
|
||||
GLOBAL: export/b
|
||||
GLOBAL: export/renamed_c
|
||||
NAMESPACE: export/ns
|
||||
;)
|
||||
|
@ -3,13 +3,18 @@
|
||||
(type $v (func))
|
||||
(global $export/a i32 (i32.const 1))
|
||||
(global $export/b i32 (i32.const 2))
|
||||
(global $export/c i32 (i32.const 3))
|
||||
(memory $0 1)
|
||||
(export "add" (func $export/add))
|
||||
(export "renamed_sub" (func $export/sub))
|
||||
(export "renamed_a" (global $export/a))
|
||||
(export "rerenamed_b" (global $export/b))
|
||||
(export "renamed_mul" (func $export/mul))
|
||||
(export "rerenamed_mul" (func $export/mul))
|
||||
(export "a" (global $export/a))
|
||||
(export "renamed_b" (global $export/b))
|
||||
(export "renamed_c" (global $export/c))
|
||||
(export "rerenamed_c" (global $export/c))
|
||||
(export "renamed_add" (func $export/add))
|
||||
(export "rerenamed_sub" (func $export/sub))
|
||||
(export "rerenamed_sub" (func $export/mul))
|
||||
(export "memory" (memory $0))
|
||||
(start $start)
|
||||
(func $export/add (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
|
||||
@ -24,14 +29,20 @@
|
||||
(get_local $1)
|
||||
)
|
||||
)
|
||||
(func $start (; 2 ;) (type $v)
|
||||
(func $export/mul (; 2 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
|
||||
(i32.mul
|
||||
(get_local $0)
|
||||
(get_local $1)
|
||||
)
|
||||
)
|
||||
(func $start (; 3 ;) (type $v)
|
||||
(drop
|
||||
(i32.add
|
||||
(call $export/add
|
||||
(i32.const 1)
|
||||
(i32.const 2)
|
||||
)
|
||||
(call $export/sub
|
||||
(call $export/mul
|
||||
(i32.const 3)
|
||||
(i32.const 4)
|
||||
)
|
||||
|
@ -1,8 +1,25 @@
|
||||
export { add, renamed_sub, a as renamed_a, renamed_b as rerenamed_b } from "./export";
|
||||
export {
|
||||
add,
|
||||
sub as renamed_sub,
|
||||
renamed_mul,
|
||||
renamed_mul as rerenamed_mul,
|
||||
|
||||
import { add as imported_add, renamed_sub as imported_sub, ns as imported_ns } from "./export";
|
||||
a,
|
||||
b as renamed_b,
|
||||
renamed_c,
|
||||
renamed_c as rerenamed_c
|
||||
} from "./export";
|
||||
|
||||
export { imported_add as renamed_add, imported_sub as rerenamed_sub };
|
||||
import {
|
||||
add as imported_add,
|
||||
renamed_mul as imported_sub,
|
||||
ns as imported_ns
|
||||
} from "./export";
|
||||
|
||||
export {
|
||||
imported_add as renamed_add,
|
||||
imported_sub as rerenamed_sub
|
||||
};
|
||||
|
||||
imported_add(1, 2) + imported_sub(3, 4);
|
||||
|
||||
|
@ -3,14 +3,19 @@
|
||||
(type $v (func))
|
||||
(global $export/a i32 (i32.const 1))
|
||||
(global $export/b i32 (i32.const 2))
|
||||
(global $export/c i32 (i32.const 3))
|
||||
(global $HEAP_BASE i32 (i32.const 4))
|
||||
(memory $0 1)
|
||||
(export "add" (func $export/add))
|
||||
(export "renamed_sub" (func $export/sub))
|
||||
(export "renamed_a" (global $export/a))
|
||||
(export "rerenamed_b" (global $export/b))
|
||||
(export "renamed_mul" (func $export/mul))
|
||||
(export "rerenamed_mul" (func $export/mul))
|
||||
(export "a" (global $export/a))
|
||||
(export "renamed_b" (global $export/b))
|
||||
(export "renamed_c" (global $export/c))
|
||||
(export "rerenamed_c" (global $export/c))
|
||||
(export "renamed_add" (func $export/add))
|
||||
(export "rerenamed_sub" (func $export/sub))
|
||||
(export "rerenamed_sub" (func $export/mul))
|
||||
(export "memory" (memory $0))
|
||||
(start $start)
|
||||
(func $export/add (; 0 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
|
||||
@ -29,19 +34,24 @@
|
||||
)
|
||||
)
|
||||
)
|
||||
(func $export/ns.two (; 2 ;) (type $v)
|
||||
)
|
||||
(func $start (; 3 ;) (type $v)
|
||||
(drop
|
||||
(i32.const 2)
|
||||
(func $export/mul (; 2 ;) (type $iii) (param $0 i32) (param $1 i32) (result i32)
|
||||
(return
|
||||
(i32.mul
|
||||
(get_local $0)
|
||||
(get_local $1)
|
||||
)
|
||||
)
|
||||
)
|
||||
(func $export/ns.two (; 3 ;) (type $v)
|
||||
)
|
||||
(func $start (; 4 ;) (type $v)
|
||||
(drop
|
||||
(i32.add
|
||||
(call $export/add
|
||||
(i32.const 1)
|
||||
(i32.const 2)
|
||||
)
|
||||
(call $export/sub
|
||||
(call $export/mul
|
||||
(i32.const 3)
|
||||
(i32.const 4)
|
||||
)
|
||||
@ -95,8 +105,10 @@
|
||||
GLOBAL: HEAP_BASE
|
||||
FUNCTION_PROTOTYPE: export/add
|
||||
FUNCTION_PROTOTYPE: export/sub
|
||||
FUNCTION_PROTOTYPE: export/mul
|
||||
GLOBAL: export/a
|
||||
GLOBAL: export/b
|
||||
GLOBAL: export/c
|
||||
NAMESPACE: export/ns
|
||||
FUNCTION_PROTOTYPE: export/ns.one
|
||||
FUNCTION_PROTOTYPE: export/ns.two
|
||||
@ -105,14 +117,20 @@
|
||||
NAMESPACE: reexport/imported_ns
|
||||
[program.exports]
|
||||
FUNCTION_PROTOTYPE: export/add
|
||||
FUNCTION_PROTOTYPE: export/renamed_sub
|
||||
FUNCTION_PROTOTYPE: export/sub
|
||||
FUNCTION_PROTOTYPE: export/renamed_mul
|
||||
GLOBAL: export/a
|
||||
GLOBAL: export/renamed_b
|
||||
GLOBAL: export/b
|
||||
GLOBAL: export/renamed_c
|
||||
NAMESPACE: export/ns
|
||||
FUNCTION_PROTOTYPE: reexport/add
|
||||
FUNCTION_PROTOTYPE: reexport/renamed_sub
|
||||
GLOBAL: reexport/renamed_a
|
||||
GLOBAL: reexport/rerenamed_b
|
||||
FUNCTION_PROTOTYPE: reexport/renamed_mul
|
||||
FUNCTION_PROTOTYPE: reexport/rerenamed_mul
|
||||
GLOBAL: reexport/a
|
||||
GLOBAL: reexport/renamed_b
|
||||
GLOBAL: reexport/renamed_c
|
||||
GLOBAL: reexport/rerenamed_c
|
||||
FUNCTION_PROTOTYPE: reexport/renamed_add
|
||||
FUNCTION_PROTOTYPE: reexport/rerenamed_sub
|
||||
NAMESPACE: reexport/renamed_ns
|
||||
|
Loading…
x
Reference in New Issue
Block a user