diff --git a/README.md b/README.md
index 893c406c..b561ab2a 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,10 @@ A few early examples to get an idea:
A PSON decoder implemented in AssemblyScript.
* **[TLSF memory allocator](./examples/tlsf)**
- An early port of TLSF to AssemblyScript.
+ An port of TLSF to AssemblyScript.
+
+* **[μgc garbage collector](./examples/ugc)**
+ 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).
diff --git a/examples/tlsf/README.md b/examples/tlsf/README.md
index 6af330a7..2ff61a18 100644
--- a/examples/tlsf/README.md
+++ b/examples/tlsf/README.md
@@ -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.
diff --git a/examples/tlsf/assembly/LICENSE b/examples/tlsf/assembly/LICENSE
index 7caf9595..a1544a95 100644
--- a/examples/tlsf/assembly/LICENSE
+++ b/examples/tlsf/assembly/LICENSE
@@ -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
diff --git a/examples/tlsf/assembly/tlsf.ts b/examples/tlsf/assembly/tlsf.ts
index c9eaf9df..16b2a412 100644
--- a/examples/tlsf/assembly/tlsf.ts
+++ b/examples/tlsf/assembly/tlsf.ts
@@ -35,7 +35,7 @@ class BlockHeader {
static readonly OVERHEAD: usize = sizeof();
// User data starts directly after the size field in a used block.
- static readonly USERDATA_OFFSET: usize = sizeof() + sizeof();
+ static readonly DATA_OFFSET: usize = sizeof() + sizeof();
// 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(ptr - BlockHeader.USERDATA_OFFSET);
+ return changetype(ptr - BlockHeader.DATA_OFFSET);
}
/** Returns the address of this block's data. */
toDataPtr(): usize {
- return changetype(this) + BlockHeader.USERDATA_OFFSET;
+ return changetype(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;
diff --git a/examples/ugc/README.md b/examples/ugc/README.md
new file mode 100644
index 00000000..80fb613d
--- /dev/null
+++ b/examples/ugc/README.md
@@ -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
+```
diff --git a/examples/ugc/assembly/LICENSE b/examples/ugc/assembly/LICENSE
new file mode 100644
index 00000000..16f15994
--- /dev/null
+++ b/examples/ugc/assembly/LICENSE
@@ -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.
diff --git a/examples/ugc/assembly/tsconfig.json b/examples/ugc/assembly/tsconfig.json
new file mode 100644
index 00000000..6e52b21c
--- /dev/null
+++ b/examples/ugc/assembly/tsconfig.json
@@ -0,0 +1,6 @@
+{
+ "extends": "../../../std/assembly.json",
+ "include": [
+ "./**/*.ts"
+ ]
+}
diff --git a/examples/ugc/assembly/ugc.ts b/examples/ugc/assembly/ugc.ts
new file mode 100644
index 00000000..90696948
--- /dev/null
+++ b/examples/ugc/assembly/ugc.ts
@@ -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();
+
+ ///////////////////////////////// Fields ////////////////////////////////////
+
+ tagged_next: usize;
+ tagged_prev: usize;
+
+ get next(): ObjectHeader {
+ return changetype(this.tagged_next & ~3);
+ }
+
+ set next(value: ObjectHeader) {
+ this.tagged_next = changetype(value) | (this.tagged_next & 3);
+ }
+
+ get prev(): ObjectHeader {
+ return changetype(this.tagged_prev & ~3);
+ }
+
+ set prev(value: ObjectHeader) {
+ this.tagged_prev = changetype(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() + 2 * sizeof();
+ 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(mem);
+ var set1 = changetype(mem);
+ var set2 = changetype(mem + 2 * sizeof());
+ 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(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(changetype(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(changetype(header) + ObjectHeader.SIZE);
+ // switch (classId) {
+ // array, string: free their data segments
+ // TODO: might make sense to provide @finalize or similar
+ // }
+ free_memory(changetype(header));
+}
diff --git a/examples/ugc/package.json b/examples/ugc/package.json
new file mode 100644
index 00000000..138aef2a
--- /dev/null
+++ b/examples/ugc/package.json
@@ -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"
+ }
+}
diff --git a/examples/ugc/tests/index.js b/examples/ugc/tests/index.js
new file mode 100644
index 00000000..ddecb3d0
--- /dev/null
+++ b/examples/ugc/tests/index.js
@@ -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);
diff --git a/src/ast.ts b/src/ast.ts
index cec50177..239d93ff 100644
--- a/src/ast.ts
+++ b/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) && (parent).isTopLevelExport;
+ if (parent.kind == NodeKind.CLASSDECLARATION)
+ return hasModifier(ModifierKind.STATIC, this.modifiers) && (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 (parent).internalName + (hasModifier(ModifierKind.STATIC, declaration.modifiers) ? STATIC_DELIMITER : INSTANCE_DELIMITER) + name;
+ return mangleInternalName(parent, asGlobal) + (hasModifier(ModifierKind.STATIC, declaration.modifiers) ? STATIC_DELIMITER : INSTANCE_DELIMITER) + name;
if (parent.kind == NodeKind.NAMESPACEDECLARATION || parent.kind == NodeKind.ENUMDECLARATION)
- return (parent).internalName + STATIC_DELIMITER + name;
+ return mangleInternalName(parent, asGlobal) + STATIC_DELIMITER + name;
+ if (asGlobal)
+ return name;
return declaration.range.source.internalPath + PATH_DELIMITER + name;
}
diff --git a/src/compiler.ts b/src/compiler.ts
index cba39226..74e371de 100644
--- a/src/compiler.ts
+++ b/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(element)) // reports
return null;
- if (isModuleExport(element, declaration)) {
- if ((element).hasConstantValue)
- this.module.addGlobalExport(element.internalName, declaration.name.name);
- else
- this.warning(DiagnosticCode.Cannot_export_a_mutable_global, declaration.range);
- }
return 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(element);
+ return this.compileEnum(element) ? element : null;
}
compileEnum(element: Enum): bool {
@@ -473,10 +469,11 @@ export class Compiler extends DiagnosticEmitter {
continue;
var initInStart = false;
var val = 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(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 = 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 | 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 | 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(element, typeArguments, contextualTypeArguments, alternativeReportNode); // reports
- if (!instance)
- return;
- if (isModuleExport(instance, declaration))
- this.module.addFunctionExport(instance.internalName, declaration.name.name);
+ return this.compileFunctionUsingTypeArguments(element, typeArguments, contextualTypeArguments, alternativeReportNode); // reports
}
compileFunctionUsingTypeArguments(prototype: FunctionPrototype, typeArguments: TypeNode[], contextualTypeArguments: Map | 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, 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 (!(element).isGeneric) {
+ if (!(element).isGeneric && statement.range.source.isEntry) {
var functionInstance = this.compileFunctionUsingTypeArguments(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(element) && statement.range.source.isEntry) {
- if ((element).hasConstantValue)
- this.module.addGlobalExport(element.internalName, member.externalIdentifier.name);
- else
- this.warning(DiagnosticCode.Cannot_export_a_mutable_global, member.range);
+ var globalDeclaration = (element).declaration;
+ if (globalDeclaration && globalDeclaration.needsExplicitExport(member)) {
+ if ((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 | 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(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((parentNode).internalName);
- if (!parent)
- return false;
- return isModuleExport(parent, parentNode);
-}
-
/** Creates an inlined expression of a constant variable-like element. */
function makeInlineConstant(element: VariableLikeElement, module: Module): ExpressionRef {
assert(element.hasConstantValue);
diff --git a/src/diagnostics.ts b/src/diagnostics.ts
index 304d7fa8..cee2717e 100644
--- a/src/diagnostics.ts
+++ b/src/diagnostics.ts
@@ -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(new Error("stack").stack);
+ // console.log(new Error("stack").stack);
}
}
diff --git a/src/program.ts b/src/program.ts
index 691b1032..5cc0c421 100644
--- a/src/program.ts
+++ b/src/program.ts
@@ -110,7 +110,7 @@ export class Program extends DiagnosticEmitter {
types: Map = noTypesYet;
/** Declared type aliases. */
typeAliases: Map = 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 = 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, 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;
diff --git a/tests/compiler/export.optimized.wast b/tests/compiler/export.optimized.wast
index 285e3c9f..2ca90ee7 100644
--- a/tests/compiler/export.optimized.wast
+++ b/tests/compiler/export.optimized.wast
@@ -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)
)
)
diff --git a/tests/compiler/export.ts b/tests/compiler/export.ts
index e7857e2f..ef50645f 100644
--- a/tests/compiler/export.ts
+++ b/tests/compiler/export.ts
@@ -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 {}
diff --git a/tests/compiler/export.wast b/tests/compiler/export.wast
index 55cc6737..96d443f1 100644
--- a/tests/compiler/export.wast
+++ b/tests/compiler/export.wast
@@ -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
;)
diff --git a/tests/compiler/import.optimized-inlined.wast b/tests/compiler/import.optimized-inlined.wast
index abbb867f..bae846e6 100644
--- a/tests/compiler/import.optimized-inlined.wast
+++ b/tests/compiler/import.optimized-inlined.wast
@@ -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)
)
)
)
diff --git a/tests/compiler/import.optimized.wast b/tests/compiler/import.optimized.wast
index 678d3079..39a84b87 100644
--- a/tests/compiler/import.optimized.wast
+++ b/tests/compiler/import.optimized.wast
@@ -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)
)
)
diff --git a/tests/compiler/import.ts b/tests/compiler/import.ts
index b6ef62ff..fcbaa9cc 100644
--- a/tests/compiler/import.ts
+++ b/tests/compiler/import.ts
@@ -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();
diff --git a/tests/compiler/import.wast b/tests/compiler/import.wast
index 8a9b4f46..b9d50e52 100644
--- a/tests/compiler/import.wast
+++ b/tests/compiler/import.wast
@@ -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
;)
diff --git a/tests/compiler/reexport.optimized.wast b/tests/compiler/reexport.optimized.wast
index f3fc9b36..5ef0b907 100644
--- a/tests/compiler/reexport.optimized.wast
+++ b/tests/compiler/reexport.optimized.wast
@@ -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)
)
diff --git a/tests/compiler/reexport.ts b/tests/compiler/reexport.ts
index 495c7c84..c48dbf71 100644
--- a/tests/compiler/reexport.ts
+++ b/tests/compiler/reexport.ts
@@ -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);
diff --git a/tests/compiler/reexport.wast b/tests/compiler/reexport.wast
index fecce9bd..b8511b89 100644
--- a/tests/compiler/reexport.wast
+++ b/tests/compiler/reexport.wast
@@ -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