Skip to content

Commit 6615ff5

Browse files
authored
Merge pull request #70 from torch2424/type-embed
Type embeds
2 parents 78302f3 + b26ed1a commit 6615ff5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+16952
-3734
lines changed

README.md

Lines changed: 23 additions & 110 deletions
Large diffs are not rendered by default.

examples/browser-sdk/index.html

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
<title>AssemblyScript SDK Example</title>
6+
</head>
7+
<body>
8+
<!-- Load as-bind runtime -->
9+
<script src="/dist/as-bind.iife.js"></script>
10+
<!-- Load requirejs to handle the AMD version of the sdk -->
11+
<script src="/node_modules/requirejs/require.js"></script>
12+
<script>
13+
const SOURCE_CODE = `
14+
export function test(): string {
15+
return "ohai";
16+
}`;
17+
18+
require(["/node_modules/assemblyscript/dist/sdk.js"], ({
19+
asc,
20+
assemblyscript
21+
}) => {
22+
// Register the `assemblyscript` property as a module of its own
23+
// as is expected by most transform modules.
24+
define("assemblyscript", [], assemblyscript);
25+
// `visitor-as/as` is usually a facade for `assemblyscript` to avoid
26+
// multiple instances of `assemblyscript` being loaded.
27+
// So in this case we can take matters into our own hands and just replace
28+
// it with out `assemblyscript` instance.
29+
define("visitor-as/as", [], assemblyscript);
30+
// Load our ASBind transform...
31+
require(["/dist/transform.amd.js"], asbind => {
32+
asc.ready.then(() => {
33+
const stdout = asc.createMemoryStream();
34+
const stderr = asc.createMemoryStream();
35+
asc.main(
36+
// prettier-ignore
37+
[
38+
"module.ts",
39+
"-O3",
40+
"--exportRuntime",
41+
"--runtime", "stub",
42+
"--binaryFile", "module.wasm"
43+
],
44+
{
45+
stdout,
46+
stderr,
47+
transforms: [asbind],
48+
readFile(name, baseDir) {
49+
return name === "module.ts" ? SOURCE_CODE : null;
50+
},
51+
async writeFile(name, data, baseDir) {
52+
if (name.endsWith(".wasm")) {
53+
const instance = await AsBindIIFE.instantiate(data);
54+
console.log("Output:", instance.exports.test());
55+
}
56+
},
57+
listFiles(dirname, baseDir) {
58+
return [];
59+
}
60+
},
61+
err => {
62+
console.log(`>>> STDOUT >>>\n${stdout.toString()}`);
63+
console.log(`>>> STDERR >>>\n${stderr.toString()}`);
64+
if (err) {
65+
console.log(">>> THROWN >>>");
66+
console.log(err);
67+
}
68+
}
69+
);
70+
});
71+
});
72+
});
73+
</script>
74+
<p>See the browser console!</p>
75+
</body>
76+
</html>

examples/markdown-parser/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { h, render, Component } from "preact";
2-
import asbind from "../../dist/as-bind.esm";
2+
import * as asbind from "../../dist/as-bind.esm";
33

44
// Import our TypeScript equivalent
55
import { convertMarkdownToHTML } from "../../dist/ts/index";

lib/asbind-instance/asbind-instance.js

Lines changed: 101 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,41 @@ import { asbindInstantiate, asbindInstantiateSync } from "./instantiate";
44
import { bindImportFunction, bindExportFunction } from "./bind-function";
55
import { isReservedExportKey } from "./reserved-export-keys";
66

7-
// Need to traverse the importObject and bind all import functions
8-
const traverseObjectAndRunCallbackForFunctions = (
9-
baseObject,
10-
keys,
11-
callback
12-
) => {
13-
if (!baseObject) {
14-
return;
7+
const SECTION_NAME = "as-bind_bindings";
8+
9+
// Basically a deep-copy, but can be limited in levels.
10+
function copyObject(obj, { depth = Number.POSITIVE_INFINITY } = {}) {
11+
if (depth <= 0 || !obj || typeof obj !== "object") {
12+
return obj;
1513
}
14+
return Object.fromEntries(
15+
Object.entries(obj).map(([key, val]) => [
16+
key,
17+
copyObject(val, { depth: depth - 1 })
18+
])
19+
);
20+
}
1621

17-
Object.keys(baseObject).forEach(baseObjectKey => {
18-
if (typeof baseObject[baseObjectKey] === "function") {
19-
// Call the callback
20-
callback(baseObject, keys, baseObjectKey);
21-
} else if (typeof baseObject[baseObjectKey] === "object") {
22-
traverseObjectAndRunCallbackForFunctions(
23-
baseObject[baseObjectKey],
24-
[...keys, baseObjectKey],
25-
callback
26-
);
22+
async function compileStreaming(source) {
23+
source = await Promise.resolve(source);
24+
if (typeof Response !== "undefined" && source instanceof Response) {
25+
if (WebAssembly.compileStreaming) {
26+
return WebAssembly.compileStreaming(source);
2727
}
28-
});
29-
};
28+
source = await source.arrayBuffer();
29+
}
30+
return WebAssembly.compile(source);
31+
}
32+
33+
function extractTypeDescriptor(module) {
34+
const sections = WebAssembly.Module.customSections(module, SECTION_NAME);
35+
const str = new TextDecoder("utf8").decode(new Uint8Array(sections[0]));
36+
try {
37+
return JSON.parse(str);
38+
} catch (e) {
39+
throw Error(`Couldn’t decode type descriptor: ${e.message}`);
40+
}
41+
}
3042

3143
export default class AsbindInstance {
3244
constructor() {
@@ -35,136 +47,94 @@ export default class AsbindInstance {
3547
this.importObject = {};
3648
}
3749

38-
async _instantiate(source, importObject) {
39-
// Bind our import function
40-
this._instantiateBindImportFunctions(importObject);
41-
42-
// Instantiate the module through the loader
43-
const unboundExports = await asbindInstantiate(source, this.importObject);
44-
45-
// Bind our unbound exports
46-
this._instantiateBindUnboundExports(unboundExports);
47-
}
48-
49-
_instantiateSync(source, importObject) {
50-
// Bind our import function
51-
this._instantiateBindImportFunctions(importObject);
52-
53-
// Instantiate the module through the loader
54-
const unboundExports = asbindInstantiateSync(source, this.importObject);
55-
56-
// Bind our unbound exports
57-
this._instantiateBindUnboundExports(unboundExports);
50+
getTypeId(typeName) {
51+
if (typeName in this.typeDescriptor.typeIds) {
52+
return this.typeDescriptor.typeIds[typeName].id;
53+
}
54+
throw Error(`Unknown type ${JSON.stringify(typeName)}`);
5855
}
5956

60-
_instantiateBindImportFunctions(importObject) {
61-
// Set our import object, as we will need it to store type caching
62-
this.importObject = importObject;
63-
64-
// Need to traverse the importObject and bind all import functions
65-
traverseObjectAndRunCallbackForFunctions(
66-
this.importObject,
67-
[],
68-
(baseObject, keys, baseObjectKey) => {
69-
// Wrap the original key, but then expose a new key for the unbound import
70-
let importFunction = baseObject[baseObjectKey];
71-
baseObject[`__asbind_unbound_${baseObjectKey}`] = importFunction;
72-
baseObject[baseObjectKey] = bindImportFunction(
73-
this,
74-
this.importObject,
75-
[...keys, baseObjectKey]
76-
);
77-
}
78-
);
57+
getTypeSize(typeName) {
58+
if (typeName in this.typeDescriptor.typeIds) {
59+
return this.typeDescriptor.typeIds[typeName].byteSize;
60+
}
61+
throw Error(`Unknown type ${JSON.stringify(typeName)}`);
7962
}
8063

81-
_instantiateBindUnboundExports(unboundExports) {
82-
// Set our unbound exports
83-
this.unboundExports = unboundExports;
84-
85-
// Ensure that the AS Wasm was built with the as-bind entryfile
64+
_validate() {
65+
if (
66+
!WebAssembly.Module.exports(this.module).find(exp => exp.name === "__new")
67+
) {
68+
throw Error(
69+
"The AssemblyScript wasm module was not built with --exportRuntime, which is required."
70+
);
71+
}
8672
if (
87-
!this.unboundExports.__asbind_entryfile_flag ||
88-
this.unboundExports.__asbind_entryfile_flag === 0
73+
WebAssembly.Module.customSections(this.module, SECTION_NAME).length !== 1
8974
) {
9075
throw new Error(
91-
"as-bind: The instantiated AssemblyScript wasm module was not built with the as-bind entryfile. Please see the as-bind documentation (Quick Start), and rebuild your AssemblyScript wasm module."
76+
"The AssemblyScript wasm module was not built with the as-bind transform."
9277
);
9378
}
94-
95-
// Wrap appropriate the appropriate export functions
96-
this.exports = {};
97-
Object.keys(this.unboundExports).forEach(unboundExportKey => {
98-
if (
99-
typeof this.unboundExports[unboundExportKey] === "function" &&
100-
!isReservedExportKey(unboundExportKey)
101-
) {
102-
// Wrap the export
103-
this.exports[unboundExportKey] = bindExportFunction(
104-
this,
105-
unboundExportKey
106-
);
107-
} else {
108-
this.exports[unboundExportKey] = this.unboundExports[unboundExportKey];
109-
}
110-
});
11179
}
11280

113-
enableExportFunctionTypeCaching() {
114-
Object.keys(this.exports).forEach(exportKey => {
115-
if (this.exports[exportKey]) {
116-
this.exports[exportKey].shouldCacheTypes = true;
117-
}
118-
});
119-
}
81+
async _instantiate(source, importObject) {
82+
this.module = await compileStreaming(source);
12083

121-
disableExportFunctionTypeCaching() {
122-
Object.keys(this.exports).forEach(exportKey => {
123-
if (this.exports[exportKey]) {
124-
this.exports[exportKey].shouldCacheTypes = false;
125-
}
126-
});
84+
this._validate();
85+
this.typeDescriptor = extractTypeDescriptor(this.module);
86+
this._instantiateBindImportFunctions(importObject);
87+
// Instantiate the module through the loader
88+
this.loadedModule = await asbindInstantiate(this.module, this.importObject);
89+
this._instantiateBindUnboundExports();
12790
}
12891

129-
enableExportFunctionUnsafeReturnValue() {
130-
Object.keys(this.exports).forEach(exportKey => {
131-
if (this.exports[exportKey]) {
132-
this.exports[exportKey].unsafeReturnValue = true;
133-
}
134-
});
135-
}
92+
_instantiateSync(source, importObject) {
93+
this.module = new WebAssembly.Module(source);
13694

137-
disableExportFunctionUnsafeReturnValue() {
138-
Object.keys(this.exports).forEach(exportKey => {
139-
if (this.exports[exportKey]) {
140-
this.exports[exportKey].unsafeReturnValue = false;
141-
}
142-
});
95+
this._validate();
96+
this.typeDescriptor = extractTypeDescriptor(this.module);
97+
this._instantiateBindImportFunctions(importObject);
98+
this.loadedModule = asbindInstantiateSync(this.module, this.importObject);
99+
this._instantiateBindUnboundExports();
143100
}
144101

145-
enableImportFunctionTypeCaching() {
146-
// Need to traverse the importObject and bind all import functions
147-
traverseObjectAndRunCallbackForFunctions(
148-
this.importObject,
149-
[],
150-
(baseObject, keys, baseObjectKey) => {
151-
// Wrap the original key, but then expose a new key for the unbound import
152-
let importFunction = baseObject[baseObjectKey];
153-
importFunction.shouldCacheTypes = true;
102+
_instantiateBindImportFunctions(importObject) {
103+
this.importObject = copyObject(importObject, { depth: 2 });
104+
105+
for (const [moduleName, moduleDescriptor] of Object.entries(
106+
this.typeDescriptor.importedFunctions
107+
)) {
108+
for (const [importedFunctionName, descriptor] of Object.entries(
109+
moduleDescriptor
110+
)) {
111+
this.importObject[moduleName][
112+
`__asbind_unbound_${importedFunctionName}`
113+
] = importObject[moduleName][importedFunctionName];
114+
this.importObject[moduleName][
115+
importedFunctionName
116+
] = bindImportFunction(
117+
this,
118+
importObject[moduleName][importedFunctionName],
119+
descriptor
120+
);
154121
}
155-
);
122+
}
156123
}
157124

158-
disableImportFunctionTypeCaching() {
159-
// Need to traverse the importObject and bind all import functions
160-
traverseObjectAndRunCallbackForFunctions(
161-
this.importObject,
162-
[],
163-
(baseObject, keys, baseObjectKey) => {
164-
// Wrap the original key, but then expose a new key for the unbound import
165-
let importFunction = baseObject[baseObjectKey];
166-
importFunction.shouldCacheTypes = false;
167-
}
168-
);
125+
_instantiateBindUnboundExports() {
126+
// Wrap appropriate the appropriate export functions
127+
const unboundExports = this.loadedModule.exports;
128+
this.exports = copyObject(unboundExports, { depth: 1 });
129+
130+
for (const [exportedFunctionName, descriptor] of Object.entries(
131+
this.typeDescriptor.exportedFunctions
132+
)) {
133+
this.exports[exportedFunctionName] = bindExportFunction(
134+
this,
135+
unboundExports[exportedFunctionName],
136+
descriptor
137+
);
138+
}
169139
}
170140
}

0 commit comments

Comments
 (0)