Skip to content

Commit e23db93

Browse files
committed
Merge branch 'master' of https://github.com/torch2424/as-bind into mitigate-as-bind-typings
the commit.
2 parents 59e9202 + 107707e commit e23db93

File tree

6 files changed

+108
-38
lines changed

6 files changed

+108
-38
lines changed

.travis.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
notifications:
22
email: false
33
language: node_js
4-
sudo: false
54
node_js:
65
- "node"
76
install:

README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,8 @@ asyncTask();
111111

112112
_Did the quick start not work for you, or you are noticing some weird behavior? Please see the [FAQ and Common Issues](#faq-and-common-issues)_
113113

114+
_Want to use `as-bind` in production? Please see the [Production section in the FAQ and Common Issues](#production)_
115+
114116
## Additional Examples
115117

116118
## Passing a high-level type to a an exported function, and returning a high-level type
@@ -193,6 +195,26 @@ The `AsBind` class is meant to vaugely act as the [WebAssembly](https://develope
193195

194196
Value that is the current version of your imported AsBind.
195197

198+
##### RETURN_TYPES
199+
200+
`AsBind.RETURN_TYPES`
201+
202+
Constants (represented as JSON) of the supported return types on bound export functions. This is useful for forcing the return type on [bound export functions](#exports).
203+
204+
For example, this could be used like:
205+
206+
```typescript
207+
// Force our return type to our expected string
208+
asBindInstance.exports.myExportedFunctionThatReturnsAString.returnType =
209+
AsBind.RETURN_TYPES.STRING;
210+
const myString = asBindInstance.exports.myExportedFunctionThatReturnsAString();
211+
212+
// Force our return type to return a number (The pointer to the string)
213+
asBindInstance.exports.myExportedFunctionThatReturnsAString.returnType =
214+
AsBind.RETURN_TYPES.NUMBER;
215+
const myNumber = asBindInstance.exports.myExportedFunctionThatReturnsAString();
216+
```
217+
196218
##### instantiate
197219

198220
```typescript
@@ -239,6 +261,8 @@ Each **exported function** has the properties:
239261
240262
- `shouldCacheTypes`
241263
- If you would like to disable type caching (speculative execution) for a particular function, you can do: `asBindInstance.exports.myFunction.shouldCacheTypes = false;`. Or set to true, to re-enable type caching.
264+
- `returnType`
265+
- (Reccomended for production usage) Set this value on a bound export function, to force it's return type. This should be set to a constant found on: [`AsBind.RETURN_TYPES`](#return_types). Defaults to `null`.
242266
- `unsafeReturnValue`
243267
- By default, all values (in particular [TypedArrays](https://www.assemblyscript.org/stdlib/typedarray.html#typedarray)) will be copied out of Wasm Memory, instead of giving direct read/write access. If you would like to use a view of the returned memory, you can do: `asBindInstance.exports.myFunction.unsafeReturnValue = true;`. For More context, please see the [AssemblyScript loader documentation](https://www.assemblyscript.org/loader.html#module-instance-utility) on array views.
244268
- After settings this flag on a function, it will then return it's values wrapped in an object, like so: `{ptr: /* The pointer or index in wasm memory the view is reffering to */, value: /* The returned value (TypedArray) that is backed directly by Wasm Memory */}`
@@ -301,6 +325,12 @@ Eventually for the most performant option, we would want to do some JavaScript c
301325
302326
In the future, these types of high-level data passing tools will not be needed for WebAssembly toolchains, once the [WebAssembly Inteface Types proposal](https://github.com/WebAssembly/interface-types/blob/master/proposals/interface-types/Explainer.md) lands, and this functionality is handled by the runtime / toolchain.
303327
328+
## Production
329+
330+
`as-bind` works by abstracting away using the [AssemblyScript Loader](https://www.assemblyscript.org/loader.html). For passing values into your AssemblyScript, it uses the Loader on your half to allocate memory, and then passes the pointer to the allocated memory. However, to pass a value back from AssemblyScript to JavaScript, AsBind will iterate through all the supported types until it finds a match (or doesn't in which case it just returns the number). However, returning a value _can sometimes_ conflict with something in AssemblyScript memory, as discovered in [#50](https://github.com/torch2424/as-bind/issues/50).
331+
332+
Thus, for production usage we highly reccomend that you set the [`returnType` property on your bound export functions](#exports) to ensure that this conflict does not happen. 😄
333+
304334
## Projects using as-bind
305335
306336
- The as-bind example is a Markdown Parser, in which as-bind takes in a string, passes it to a rough markdown parser / compiler written in AssemblyScript, and returns a string. [(Live Demo)](https://torch2424.github.io/as-bind/), [(Source Code)](https://github.com/torch2424/as-bind/tree/master/examples/markdown-parser)

lib/asbind-instance/bind-function.js

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { validateExportsAndFunction } from "./validate";
2-
import SUPPORTED_REF_TYPES from "./supported-ref-types";
2+
import { SUPPORTED_REF_TYPES } from "./supported-ref-types";
33

44
// Function that takes in an asbindInstance, and importObject, and the path to the import function on the object
55
// (E.g ["env", "myObject", "myFunction"] for {env: myObject: {myFunction: () => {}}})
@@ -195,13 +195,21 @@ export function bindExportFunction(asbindInstance, exportFunctionKey) {
195195
// Find our supported type
196196
let supportedType = undefined;
197197

198-
// Check if we cached the return type
199-
if (functionThis.shouldCacheTypes && functionThis.cachedReturnTypes[0]) {
198+
if (functionThis.returnType) {
199+
// Check if the return type was manually set
200+
if (SUPPORTED_REF_TYPES[functionThis.returnType]) {
201+
supportedType = SUPPORTED_REF_TYPES[functionThis.returnType];
202+
}
203+
} else if (
204+
functionThis.shouldCacheTypes &&
205+
functionThis.cachedReturnTypes[0]
206+
) {
207+
// Check if we cached the return type
208+
200209
if (functionThis.cachedReturnTypes[0].type === "ref") {
201-
supportedType = supportedType =
210+
supportedType =
202211
SUPPORTED_REF_TYPES[functionThis.cachedReturnTypes[0].key];
203212
}
204-
// Let it fall through the if and handle the primitive (number) logic
205213
} else {
206214
// We need to find / cache the type
207215
Object.keys(SUPPORTED_REF_TYPES).some(key => {
@@ -242,7 +250,9 @@ export function bindExportFunction(asbindInstance, exportFunctionKey) {
242250
}
243251
} else if (typeof exportFunctionResponse === "number") {
244252
response = exportFunctionResponse;
245-
if (functionThis.shouldCacheTypes) {
253+
// Need to check if we are caching types
254+
// And, if the type was forced to a number, and we fell through, don't cache it
255+
if (functionThis.shouldCacheTypes && !functionThis.returnType) {
246256
functionThis.cachedReturnTypes[0] = {
247257
type: "number"
248258
};
@@ -257,6 +267,7 @@ export function bindExportFunction(asbindInstance, exportFunctionKey) {
257267
// Initialize the state of our function
258268
boundExport.shouldCacheTypes = true;
259269
boundExport.unsafeReturnValue = false;
270+
boundExport.returnType = null;
260271
boundExport.cachedArgTypes = [];
261272
boundExport.cachedReturnTypes = [];
262273

lib/asbind-instance/supported-ref-types.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const getUnsafeResponse = (value, ptr) => {
55
};
66
};
77

8-
const SUPPORTED_REF_TYPES = {
8+
export const SUPPORTED_REF_TYPES = {
99
STRING: {
1010
isTypeFromArgument: arg => {
1111
return typeof arg === "string";
@@ -204,4 +204,16 @@ const SUPPORTED_REF_TYPES = {
204204
}
205205
};
206206

207-
export default SUPPORTED_REF_TYPES;
207+
// Our return type constant
208+
export const RETURN_TYPES = {
209+
NUMBER: "NUMBER",
210+
STRING: "STRING",
211+
INT8ARRAY: "INT8ARRAY",
212+
UINT8ARRAY: "UINT8ARRAY",
213+
INT16ARRAY: "INT16ARRAY",
214+
UINT16ARRAY: "UINT16ARRAY",
215+
INT32ARRAY: "INT32ARRAY",
216+
UINT32ARRAY: "UINT32ARRAY",
217+
FLOAT32ARRAY: "FLOAT32ARRAY",
218+
FLOAT64ARRAY: "FLOAT64ARRAY"
219+
};

lib/lib.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import packageJson from "../package.json";
22
import AsbindInstance from "./asbind-instance/asbind-instance";
3+
import { RETURN_TYPES } from "./asbind-instance/supported-ref-types";
34

45
export const AsBind = {
5-
// General asbind versionn
6+
// General asbind version
67
version: packageJson.version,
78

9+
// Constants
10+
RETURN_TYPES: RETURN_TYPES,
11+
812
// Our APIs
913
instantiate: async (source, importObject) => {
1014
let asbindInstance = new AsbindInstance();

test/test.js

Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -626,36 +626,9 @@ describe("asbind", () => {
626626

627627
describe("Unsafe Return Value", () => {
628628
let asbindInstance;
629-
let testImportCalledWith = [];
630629

631630
beforeEach(async () => {
632-
const importObjectFunction = value => {
633-
testImportCalledWith = [value];
634-
};
635-
636-
wrappedBaseImportObject = {
637-
...baseImportObject,
638-
test: {
639-
testImportString: importObjectFunction,
640-
testImportTwoStrings: (value1, value2) => {
641-
testImportCalledWith = [value1, value2];
642-
},
643-
testImportReturnNumber: () => -1,
644-
testImportInt8Array: importObjectFunction,
645-
testImportUint8Array: importObjectFunction,
646-
testImportInt16Array: importObjectFunction,
647-
testImportUint16Array: importObjectFunction,
648-
testImportInt32Array: importObjectFunction,
649-
testImportUint32Array: importObjectFunction,
650-
testImportFloat32Array: importObjectFunction,
651-
testImportFloat64Array: importObjectFunction
652-
}
653-
};
654-
655-
asbindInstance = await AsBind.instantiate(
656-
wasmBytes,
657-
wrappedBaseImportObject
658-
);
631+
asbindInstance = await AsBind.instantiate(wasmBytes, baseImportObject);
659632
});
660633

661634
it("should not break strings", () => {
@@ -706,4 +679,45 @@ describe("asbind", () => {
706679
});
707680
});
708681
});
682+
683+
describe("Forcing Return Types", () => {
684+
let asbindInstance;
685+
686+
beforeEach(async () => {
687+
asbindInstance = await AsBind.instantiate(wasmBytes, baseImportObject);
688+
});
689+
690+
it("should allow setting the returnType on a bound export function", () => {
691+
// Make sure the return type is null
692+
assert.equal(asbindInstance.exports.helloWorld.returnType, null);
693+
694+
// Call the export
695+
const defaultResponse = asbindInstance.exports.helloWorld("returnType");
696+
assert.equal(typeof defaultResponse, "string");
697+
698+
// Set the return type to a number
699+
asbindInstance.exports.helloWorld.returnType = AsBind.RETURN_TYPES.NUMBER;
700+
701+
// Call the export
702+
const numberResponse = asbindInstance.exports.helloWorld("returnType");
703+
assert.equal(typeof numberResponse, "number");
704+
705+
// Set the return type to a string
706+
asbindInstance.exports.helloWorld.returnType = AsBind.RETURN_TYPES.STRING;
707+
708+
// Call the export
709+
const stringResponse = asbindInstance.exports.helloWorld("returnType");
710+
assert.equal(typeof stringResponse, "string");
711+
712+
// Remove the returnType
713+
asbindInstance.exports.helloWorld.returnType = null;
714+
715+
// Call the export
716+
const nullReturnTypeResponse = asbindInstance.exports.helloWorld(
717+
"returnType"
718+
);
719+
assert.equal(typeof nullReturnTypeResponse, "string");
720+
});
721+
});
722+
// Done!
709723
});

0 commit comments

Comments
 (0)