diff --git a/examples/loader/README.md b/examples/loader/README.md
new file mode 100644
index 0000000000..5872f82b58
--- /dev/null
+++ b/examples/loader/README.md
@@ -0,0 +1,38 @@
+Loader Basics
+=============
+
+An [AssemblyScript](http://assemblyscript.org) example. Utilizes the [loader](https://docs.assemblyscript.org/basics/loader) to perform various common tasks on the WebAssembly/JavaScript boundary, like passing along strings and arrays.
+
+Instructions
+------------
+
+Install the dependencies, build the WebAssembly module and verify that everything works:
+
+```
+$> npm install
+$> npm run asbuild
+$> npm test
+```
+
+The example consists of several files showing the different perspectives, in recommended reading order:
+
+* [assembly/index.ts](./assembly/index.ts)
+ The AssemblyScript sources we are going to compile to a WebAssembly module.
+ Contains the implementations we are going to call from JavaScript.
+
+* [tests/index.js](./tests/index.js)
+ A test loading our WebAssembly node module that will utilize the loader to
+ pass strings and arrays between WebAssembly and JavaScript.
+
+* [index.js](./index.js)
+ Instantiates the WebAssembly module and exposes it as a node module. Also
+ provides the imported functions used in Example 3.
+
+* [assembly/myConsole.ts](./assembly/myConsole.ts)
+ The import declarations of our custom console API used in Example 3.
+
+To rerun the tests:
+
+```
+$> npm test
+```
diff --git a/examples/loader/assembly/index.ts b/examples/loader/assembly/index.ts
new file mode 100644
index 0000000000..946e2243b8
--- /dev/null
+++ b/examples/loader/assembly/index.ts
@@ -0,0 +1,127 @@
+// Example 1: Passing a string from WebAssembly to JavaScript.
+
+// Under the hood, the following yields a WebAssembly function export returning
+// the pointer to a string within the module's memory. To obtain its contents,
+// we are going to read it from memory on the JavaScript side.
+
+// see: tests/index.js "Test for Example 1"
+
+export function getHello(): string {
+ return "Hello world (I am a WebAssembly string)";
+}
+
+// Example 2: Passing a string from JavaScript to WebAssembly.
+
+// Similarly, we'll call the following function with a pointer to a string in
+// the module's memory from JavaScript. To do so, the string will first be
+// allocated on the JavaScript side, while holding on to a reference to it.
+
+// see: tests/index.js "Test for Example 2"
+
+export function sayHello(s: string): void {
+ console.log(" " + s); // see Example 3
+}
+
+// Example 3: Calling a JavaScript import with a WebAssembly string.
+
+// see: assembly/myConsole.ts
+
+import * as console from "./myConsole";
+
+// Example 4: Passing an array from WebAssembly to JavaScript.
+
+// Analogous to the examples above working with strings, the following function
+// will return a pointer to an array within the module's memory. We can either
+// get a live view on it to modify, or obtain a copy.
+
+// see: tests/index.js "Test for Example 4"
+
+export function getMyArray(size: i32): Int32Array {
+ var arr = new Int32Array(size);
+ for (let i = 0; i < size; ++i) {
+ arr[i] = i;
+ }
+ return arr;
+}
+
+// Example 5: Passing an array from JavaScript to WebAssembly.
+
+// Likewise, we can also allocate an array on the JavaScript side and pass it
+// its pointer to WebAssembly, then doing something with it.
+
+// see: tests/index.js "Test for Example 5"
+
+export function computeSum(a: Int32Array): i32 {
+ console.time("sum"); // see Example 3
+ var sum = 0;
+ for (let i = 0, k = a.length; i < k; ++i) {
+ sum += a[i];
+ }
+ console.timeEnd("sum"); // see Example 3
+ return sum;
+}
+
+// See the comments in test/index.js "Test for Example 5" for why this is
+// necessary, and how to perform an Int32Array allocation using its runtime id.
+export const Int32Array_ID = idof();
+
+// Example 6: WebAssembly arrays of WebAssembly strings.
+
+// Let's get a little more serious with a combined example. We'd like to pass an
+// array of strings from JavaScript to WebAssembly, create a new array with all
+// strings converted to upper case, return it to JavaScript and print its contents.
+
+// see: tests/index.js "Test for Example 6"
+
+export function capitalize(a: string[]): string[] {
+ var length = a.length;
+ var b = new Array(length);
+ for (let i = 0; i < length; ++i) {
+ b[i] = a[i].toUpperCase();
+ }
+ return b;
+}
+
+export const ArrayOfStrings_ID = idof();
+
+// Example 7: Using custom classes.
+
+// The loader also understands exports of entire classes, and with the knowledge
+// obtained in the previous examples it becomes possible to interface with a
+// more complex program like the following in a nearly natural way.
+
+// see: tests/index.js "Test for Example 7"
+
+export namespace Game {
+ export class Player {
+ name: string;
+ position: Position | null;
+ constructor(name: string) {
+ this.name = name;
+ this.position = new Position();
+ }
+ move(x: i32, y: i32): void {
+ var position = assert(this.position);
+ position.x += x;
+ position.y += y;
+ }
+ kill(): void {
+ this.position = null;
+ }
+ toString(): string {
+ var position = this.position;
+ if (position) {
+ return this.name + " @ " + position.toString();
+ } else {
+ return this.name + " @ AWAITING ASSIGNMENT";
+ }
+ }
+ }
+ export class Position {
+ x: i32 = 0;
+ y: i32 = 0;
+ toString(): string {
+ return this.x.toString() + "/" + this.y.toString();
+ }
+ }
+}
diff --git a/examples/loader/assembly/myConsole.ts b/examples/loader/assembly/myConsole.ts
new file mode 100644
index 0000000000..4701132746
--- /dev/null
+++ b/examples/loader/assembly/myConsole.ts
@@ -0,0 +1,9 @@
+// Example 3: Calling JavaScript imports with WebAssembly strings.
+
+// Let's declare a `myConsole` import, with member functions we can call with
+// WebAssembly strings. Our imports, defined in the top-level index.js, will
+// translate the calls to JavaScript's console API using the loader.
+
+export declare function log(s: string): void;
+export declare function time(s: string): void;
+export declare function timeEnd(s: string): void;
diff --git a/examples/loader/assembly/tsconfig.json b/examples/loader/assembly/tsconfig.json
new file mode 100644
index 0000000000..449ca07c76
--- /dev/null
+++ b/examples/loader/assembly/tsconfig.json
@@ -0,0 +1,6 @@
+{
+ "extends": "../../../std/assembly.json",
+ "include": [
+ "./**/*.ts"
+ ]
+}
\ No newline at end of file
diff --git a/examples/loader/build/.gitignore b/examples/loader/build/.gitignore
new file mode 100644
index 0000000000..22b2ed20a7
--- /dev/null
+++ b/examples/loader/build/.gitignore
@@ -0,0 +1,3 @@
+*.wasm
+*.wasm.map
+*.asm.js
diff --git a/examples/loader/index.js b/examples/loader/index.js
new file mode 100644
index 0000000000..5b65d56af5
--- /dev/null
+++ b/examples/loader/index.js
@@ -0,0 +1,22 @@
+const fs = require("fs");
+const loader = require("@assemblyscript/loader");
+const myModule = module.exports = loader.instantiateSync(fs.readFileSync(__dirname + "/build/optimized.wasm"),
+
+ // These are the JavaScript imports to our WebAssembly module, translating
+ // from WebAssembly strings, received as a pointer into the module's memory,
+ // to JavaScript's console API as JavaScript strings.
+ {
+ // Example 3: Calling JavaScript imports with WebAssembly strings.
+ myConsole: {
+ log(messagePtr) { // Called as `console.log` in assembly/index.ts
+ console.log(myModule.__getString(messagePtr));
+ },
+ time(labelPtr) { // Called as `console.time` in assembly/index.ts
+ console.time(myModule.__getString(labelPtr));
+ },
+ timeEnd(labelPtr) { // Called as `console.timeEnd` in assembly/index.ts
+ console.timeEnd(myModule.__getString(labelPtr));
+ }
+ }
+ }
+);
diff --git a/examples/loader/package.json b/examples/loader/package.json
new file mode 100644
index 0000000000..dac32d3820
--- /dev/null
+++ b/examples/loader/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "@assemblyscript/loader-basics-example",
+ "version": "1.0.0",
+ "private": true,
+ "scripts": {
+ "asbuild:untouched": "asc assembly/index.ts -b build/untouched.wasm -t build/untouched.wat --validate --sourceMap --debug",
+ "asbuild:optimized": "asc assembly/index.ts -b build/optimized.wasm -t build/optimized.wat --validate --sourceMap --optimize",
+ "asbuild": "npm run asbuild:untouched && npm run asbuild:optimized",
+ "test": "node tests"
+ },
+ "dependencies": {
+ "@assemblyscript/loader": "^0.9.4"
+ },
+ "devDependencies": {
+ "assemblyscript": "^0.9.4"
+ }
+}
diff --git a/examples/loader/tests/index.js b/examples/loader/tests/index.js
new file mode 100644
index 0000000000..40db09c757
--- /dev/null
+++ b/examples/loader/tests/index.js
@@ -0,0 +1,151 @@
+// Load the node module exporting our WebAssembly module
+const myModule = require("../index");
+
+// Obtain the runtime helpers for
+const {
+ // memory management
+ __allocString, __allocArray,
+ // garbage collection
+ __retain, __release,
+ // and interop
+ __getString, __getArray, __getArrayView
+} = myModule;
+
+// Test for Example 1: Passing a string from WebAssembly to JavaScript.
+{
+ console.log("Example 1:");
+
+ // Obtain a pointer to our string in the module's memory. Note that `return`ing
+ // a string, or any other object, from WebAssembly to JavaScript automatically
+ // retains a reference for us, the caller, to release when we are done with it.
+ const ptr = myModule.getHello();
+
+ // Print its contents
+ console.log(" " + __getString(ptr));
+
+ __release(ptr); // we are done with the returned string but
+ // it might still be alive in WebAssembly
+}
+
+// Test for Example 2: Passing a string from JavaScript to WebAssembly.
+{
+ console.log("Example 2:");
+
+ // Allocate a string in the module's memory and retain a reference to our allocation
+ const ptr = __retain(__allocString("Hello world (I am a JavaScript string)"));
+
+ // Pass it to our WebAssembly export, which is going to print it using our custom console
+ myModule.sayHello(ptr);
+
+ __release(ptr); // we are done with the allocated string but
+ // it might still be alive in WebAssembly
+}
+
+// Test for Example 4: Passing an array from WebAssembly to JavaScript.
+{
+ console.log("Example 4:");
+
+ // Obtain a pointer to our array in the module's memory. Note that `return`ing
+ // an object from WebAssembly to JavaScript automatically retains a reference
+ // for us, the caller, to release when we are done with it.
+ const ptr = myModule.getMyArray(10);
+
+ // Obtain a live view on it
+ const view = __getArrayView(ptr);
+ console.log(" " + view + " (view)");
+
+ // Obtain a copy of it (modifying the live view does not modify the copy)
+ const copy = __getArray(ptr);
+ console.log(" " + copy + " (copy)");
+
+ __release(ptr); // we are done with the array
+}
+
+// Test for Example 5: Passing an array from JavaScript to WebAssembly.
+{
+ console.log("Example 5:");
+
+ // Allocate a new array in WebAssembly memory and get a view on it. Note that
+ // we have to specify the runtime id of the array type we want to allocate, so
+ // we export its id (`idof`) from the module to do so.
+ const ptr = __retain(__allocArray(myModule.Int32Array_ID, [ 1, 2, 3 ]));
+ const view = __getArrayView(ptr);
+ const copy = __getArray(ptr);
+
+ // Compute its sum
+ console.log(" Sum of " + view + " is " + myModule.computeSum(ptr));
+
+ // Modify the first element in place, and compute the new sum
+ view[0] = 42;
+ console.log(" Sum of " + view + " is " + myModule.computeSum(ptr));
+
+ // The initial copy remains unchanged and is not linked to `ptr`
+ console.log(" Unmodified copy: " + copy);
+
+ __release(ptr); // we are done with our allocated array but
+ // it might still be alive in WebAssembly
+}
+
+// Test for Example 6: WebAssembly arrays of WebAssembly strings.
+{
+ console.log("Example 6:");
+
+ // Allocate a new array, but this time its elements are pointers to strings.
+ // Note: Allocating an array of strings or other objects will automatically
+ // take care of retaining references to its elements, but the array itself
+ // must be dealt with as usual.
+ const inPtr = __retain(__allocArray(myModule.ArrayOfStrings_ID, [ "hello", "world" ].map(__allocString)));
+
+ // Provide our array of lowercase strings to WebAssembly, and obtain the new
+ // array of uppercase strings before printing it.
+ const outPtr = myModule.capitalize(inPtr);
+ console.log(" Uppercased: " + __getArray(outPtr).map(__getString));
+
+ __release(inPtr); // release our allocation and release
+ __release(outPtr); // the return value. you know the drill!
+
+ // Note that Example 6 is not an especially efficient use case and one would
+ // typically rather avoid the overhead and do this in JavaScript directly.
+}
+
+// Test for Example 7: Using custom classes.
+{
+ console.log("Example 7:");
+
+ // Create a new player. Note that the loader makes a nice object structure
+ // of our exports, here a class `Player` within the `Game` namespace. So
+ // let's call the `Player` constructor (this is also an allocation):
+ let player;
+ {
+ const namePtr = __retain(__allocString("Gordon Freeman"));
+ player = new myModule.Game.Player(namePtr);
+ __release(namePtr);
+ }
+ console.log(" Player (new): " + __getString(player.toString()));
+
+ // Move them and log again
+ player.move(10, 20);
+ console.log(" Player (moved): " + __getString(player.toString()));
+
+ // Obtaining just the position. Note that we can `wrap` any pointer with
+ // the matching class within the object structure made by the loader.
+ {
+ const positionPtr = player.position; // calls a getter (implicit `return`)
+ const position = myModule.Game.Position.wrap(positionPtr);
+ console.log(" Position (wrapped): " + position.x + "/" + position.y);
+
+ position.x -= 100;
+ position.y += 200;
+ console.log(" Position (moved): " + __getString(position.toString()));
+
+ __release(positionPtr); // we are done with the returned object
+ }
+
+ // Finish 'em
+ player.kill();
+ console.log(" Player (finished): " + __getString(player.toString()));
+
+ __release(player); // a tidy house, a tidy mind.
+}
+
+// Interested in all the details? https://docs.assemblyscript.org/details :)