Skip to content

Commit 21b1883

Browse files
authored
Merge pull request #48 from torch2424/unsafe-array-views
Allowed Accessing Unsafe Values from bound Asbind Functions
2 parents 9ab3ece + 4034096 commit 21b1883

File tree

5 files changed

+188
-14
lines changed

5 files changed

+188
-14
lines changed

README.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,13 @@ An AsBindInstance is vaugley similar to a [WebAssembly instance](https://develop
235235
236236
Similar to to [WebAssembly.Instance.prototype.exports](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/Instance/exports), this is an object containing all of the exported fields from the WebAssembly module. However, **exported functions** are bound / wrapped in which they will handle passing the supported high-level data types to the exported AssemblyScript function.
237237
238-
Each **exported function** has the property: `shouldCacheTypes`. 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.
238+
Each **exported function** has the properties:
239+
240+
- `shouldCacheTypes`
241+
- 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.
242+
- `unsafeReturnValue`
243+
- 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.
244+
- 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 */}`
239245
240246
##### unboundExports
241247
@@ -255,6 +261,14 @@ Calling this method will (re-)enable type caching (speculative execution) for AL
255261
256262
Calling this method will disable type caching (speculative execution) for ALL exported functions on the AsBindInstance.
257263
264+
##### enableExportFunctionUnsafeReturnValue
265+
266+
Calling this method will (re-)enable unsafe return types for ALL exported functions on the AsBindInstance.
267+
268+
##### disableExportFunctionUnsafeReturnValue
269+
270+
Calling this method will disable unsafe return types for ALL exported functions on the AsBindInstance.
271+
258272
##### enableImportFunctionTypeCaching
259273
260274
Calling this method will (re-)enable type caching (speculative execution) for ALL importObject functions on the AsBindInstance.

lib/asbind-instance/asbind-instance.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,18 @@ export default class AsbindInstance {
122122
});
123123
}
124124

125+
enableExportFunctionUnsafeReturnValue() {
126+
Object.keys(this.exports).forEach(exportKey => {
127+
this.exports[exportKey].unsafeReturnValue = true;
128+
});
129+
}
130+
131+
disableExportFunctionUnsafeReturnValue() {
132+
Object.keys(this.exports).forEach(exportKey => {
133+
this.exports[exportKey].unsafeReturnValue = false;
134+
});
135+
}
136+
125137
enableImportFunctionTypeCaching() {
126138
// Need to traverse the importObject and bind all import functions
127139
traverseObjectAndRunCallbackForFunctions(

lib/asbind-instance/bind-function.js

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -226,10 +226,20 @@ export function bindExportFunction(asbindInstance, exportFunctionKey) {
226226
}
227227

228228
if (supportedType) {
229-
response = supportedType.getValueFromRef(
230-
exports,
231-
exportFunctionResponse
232-
);
229+
if (
230+
functionThis.unsafeReturnValue &&
231+
supportedType.getUnsafeValueFromRef
232+
) {
233+
response = supportedType.getUnsafeValueFromRef(
234+
exports,
235+
exportFunctionResponse
236+
);
237+
} else {
238+
response = supportedType.getValueFromRef(
239+
exports,
240+
exportFunctionResponse
241+
);
242+
}
233243
} else if (typeof exportFunctionResponse === "number") {
234244
response = exportFunctionResponse;
235245
if (functionThis.shouldCacheTypes) {
@@ -246,6 +256,7 @@ export function bindExportFunction(asbindInstance, exportFunctionKey) {
246256

247257
// Initialize the state of our function
248258
boundExport.shouldCacheTypes = true;
259+
boundExport.unsafeReturnValue = false;
249260
boundExport.cachedArgTypes = [];
250261
boundExport.cachedReturnTypes = [];
251262

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

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
const getUnsafeResponse = (value, ptr) => {
2+
return {
3+
ptr: ptr,
4+
value: value
5+
};
6+
};
7+
18
const SUPPORTED_REF_TYPES = {
29
STRING: {
310
isTypeFromArgument: arg => {
@@ -26,7 +33,13 @@ const SUPPORTED_REF_TYPES = {
2633
);
2734
},
2835
getValueFromRef: (wasmExports, responseRef) => {
29-
return wasmExports.__getInt8Array(responseRef).slice();
36+
return wasmExports.__getInt8Array(responseRef);
37+
},
38+
getUnsafeValueFromRef: (wasmExports, responseRef) => {
39+
return getUnsafeResponse(
40+
wasmExports.__getInt8ArrayView(responseRef),
41+
responseRef
42+
);
3043
}
3144
},
3245
UINT8ARRAY: {
@@ -42,7 +55,13 @@ const SUPPORTED_REF_TYPES = {
4255
);
4356
},
4457
getValueFromRef: (wasmExports, responseRef) => {
45-
return wasmExports.__getUint8Array(responseRef).slice();
58+
return wasmExports.__getUint8Array(responseRef);
59+
},
60+
getUnsafeValueFromRef: (wasmExports, responseRef) => {
61+
return getUnsafeResponse(
62+
wasmExports.__getUint8ArrayView(responseRef),
63+
responseRef
64+
);
4665
}
4766
},
4867
INT16ARRAY: {
@@ -58,7 +77,13 @@ const SUPPORTED_REF_TYPES = {
5877
);
5978
},
6079
getValueFromRef: (wasmExports, responseRef) => {
61-
return wasmExports.__getInt16Array(responseRef).slice();
80+
return wasmExports.__getInt16Array(responseRef);
81+
},
82+
getUnsafeValueFromRef: (wasmExports, responseRef) => {
83+
return getUnsafeResponse(
84+
wasmExports.__getInt16ArrayView(responseRef),
85+
responseRef
86+
);
6287
}
6388
},
6489
UINT16ARRAY: {
@@ -74,7 +99,13 @@ const SUPPORTED_REF_TYPES = {
7499
);
75100
},
76101
getValueFromRef: (wasmExports, responseRef) => {
77-
return wasmExports.__getUint16Array(responseRef).slice();
102+
return wasmExports.__getUint16Array(responseRef);
103+
},
104+
getUnsafeValueFromRef: (wasmExports, responseRef) => {
105+
return getUnsafeResponse(
106+
wasmExports.__getUint16ArrayView(responseRef),
107+
responseRef
108+
);
78109
}
79110
},
80111
INT32ARRAY: {
@@ -90,7 +121,13 @@ const SUPPORTED_REF_TYPES = {
90121
);
91122
},
92123
getValueFromRef: (wasmExports, responseRef) => {
93-
return wasmExports.__getInt32Array(responseRef).slice();
124+
return wasmExports.__getInt32Array(responseRef);
125+
},
126+
getUnsafeValueFromRef: (wasmExports, responseRef) => {
127+
return getUnsafeResponse(
128+
wasmExports.__getInt32ArrayView(responseRef),
129+
responseRef
130+
);
94131
}
95132
},
96133
UINT32ARRAY: {
@@ -106,7 +143,13 @@ const SUPPORTED_REF_TYPES = {
106143
);
107144
},
108145
getValueFromRef: (wasmExports, responseRef) => {
109-
return wasmExports.__getUint32Array(responseRef).slice();
146+
return wasmExports.__getUint32Array(responseRef);
147+
},
148+
getUnsafeValueFromRef: (wasmExports, responseRef) => {
149+
return getUnsafeResponse(
150+
wasmExports.__getUint32ArrayView(responseRef),
151+
responseRef
152+
);
110153
}
111154
},
112155
FLOAT32ARRAY: {
@@ -125,7 +168,13 @@ const SUPPORTED_REF_TYPES = {
125168
);
126169
},
127170
getValueFromRef: (wasmExports, responseRef) => {
128-
return wasmExports.__getFloat32Array(responseRef).slice();
171+
return wasmExports.__getFloat32Array(responseRef);
172+
},
173+
getUnsafeValueFromRef: (wasmExports, responseRef) => {
174+
return getUnsafeResponse(
175+
wasmExports.__getFloat32ArrayView(responseRef),
176+
responseRef
177+
);
129178
}
130179
},
131180
FLOAT64ARRAY: {
@@ -144,7 +193,13 @@ const SUPPORTED_REF_TYPES = {
144193
);
145194
},
146195
getValueFromRef: (wasmExports, responseRef) => {
147-
return wasmExports.__getFloat64Array(responseRef).slice();
196+
return wasmExports.__getFloat64Array(responseRef);
197+
},
198+
getUnsafeValueFromRef: (wasmExports, responseRef) => {
199+
return getUnsafeResponse(
200+
wasmExports.__getFloat64ArrayView(responseRef),
201+
responseRef
202+
);
148203
}
149204
}
150205
};

test/test.js

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,6 @@ describe("asbind", () => {
313313
describe("type caching", () => {
314314
let asbindInstance;
315315
let testImportCalledWith = [];
316-
let weappedBaseImportObject = {};
317316

318317
beforeEach(async () => {
319318
const importObjectFunction = value => {
@@ -624,4 +623,87 @@ describe("asbind", () => {
624623
);
625624
});
626625
});
626+
627+
describe("Unsafe Return Value", () => {
628+
let asbindInstance;
629+
let testImportCalledWith = [];
630+
631+
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+
);
659+
});
660+
661+
it("should not break strings", () => {
662+
asbindInstance.exports.helloWorld.unsafeReturnValue = true;
663+
const response = asbindInstance.exports.helloWorld("asbind");
664+
assert.equal(response, "Hello asbind!");
665+
});
666+
667+
// TypedArrays
668+
[
669+
"Int8Array",
670+
"Uint8Array",
671+
"Int16Array",
672+
"Uint16Array",
673+
"Int32Array",
674+
"Uint32Array",
675+
"Float32Array",
676+
"Float64Array"
677+
].forEach(typedArrayKey => {
678+
it(`should handle ${typedArrayKey} being returned unsafe`, () => {
679+
const exportName = `map${typedArrayKey}`;
680+
681+
assert.equal(
682+
asbindInstance.exports[exportName].unsafeReturnValue,
683+
false
684+
);
685+
686+
let randomValue;
687+
let array;
688+
let arrayMapResponse;
689+
690+
randomValue = Math.floor(Math.random() * 10) + 1;
691+
array = global[typedArrayKey].from([randomValue]);
692+
arrayMapResponse = asbindInstance.exports[exportName](array);
693+
694+
// Check to make sure it returns an arrary
695+
assert(arrayMapResponse.length > 0);
696+
697+
asbindInstance.exports[exportName].unsafeReturnValue = true;
698+
699+
randomValue = Math.floor(Math.random() * 10) + 1;
700+
array = global[typedArrayKey].from([randomValue]);
701+
arrayMapResponse = asbindInstance.exports[exportName](array);
702+
703+
// Assert it now returns a pointer and a value
704+
assert(arrayMapResponse.ptr !== undefined);
705+
assert(arrayMapResponse.value !== undefined);
706+
});
707+
});
708+
});
627709
});

0 commit comments

Comments
 (0)