зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1715275 - Add support for a JS WebAssembly.Function object r=rhunt
This introduces a new Constructor WebAssembly.Function which is part of the WASM JS-API type reflection work. This allows normal JS functions to be wrapped with a type and used with WebAssembly objects (e.g. Tables). All exported WebAssembly functions will now be of this type, which nows provides a .type() method which will expose the arguments and results to JS. Differential Revision: https://phabricator.services.mozilla.com/D118712
This commit is contained in:
Родитель
1acb393f08
Коммит
41f7cdd040
|
@ -122,6 +122,7 @@
|
|||
REAL(WasmTable, OCLASP(WasmTable)) \
|
||||
REAL(WasmGlobal, OCLASP(WasmGlobal)) \
|
||||
REAL(WasmTag, OCLASP(WasmTag)) \
|
||||
REAL(WasmFunction, CLASP(WasmFunction)) \
|
||||
REAL(WasmException, OCLASP(WasmException)) \
|
||||
REAL(FinalizationRegistry, OCLASP(FinalizationRegistry)) \
|
||||
REAL(WeakRef, OCLASP(WeakRef)) \
|
||||
|
|
|
@ -470,6 +470,8 @@ MSG_DEF(JSMSG_WASM_BAD_EXN_ARG, 0, JSEXN_TYPEERR, "first argument mus
|
|||
MSG_DEF(JSMSG_WASM_BAD_EXN_PAYLOAD, 0, JSEXN_TYPEERR, "second argument must be an object")
|
||||
MSG_DEF(JSMSG_WASM_BAD_EXN_PAYLOAD_LEN, 2, JSEXN_TYPEERR, "expected {0} values but got {1}")
|
||||
MSG_DEF(JSMSG_WASM_BAD_EXN_TAG, 0, JSEXN_TYPEERR, "exception's tag did not match the provided exception tag")
|
||||
MSG_DEF(JSMSG_WASM_BAD_FUNCTION_VALUE, 0, JSEXN_TYPEERR, "second argument must be a function")
|
||||
MSG_DEF(JSMSG_WASM_BAD_ARG_TYPE, 0, JSEXN_TYPEERR, "parameters and results must be an iterator of value types")
|
||||
MSG_DEF(JSMSG_WASM_NO_TRANSFER, 0, JSEXN_TYPEERR, "cannot transfer WebAssembly/asm.js ArrayBuffer")
|
||||
MSG_DEF(JSMSG_WASM_TEXT_FAIL, 1, JSEXN_SYNTAXERR, "wasm text error: {0}")
|
||||
MSG_DEF(JSMSG_WASM_MISSING_MAXIMUM, 0, JSEXN_TYPEERR, "'shared' is true but maximum is not specified")
|
||||
|
|
|
@ -405,6 +405,7 @@
|
|||
"RequireObjectCoercible") \
|
||||
MACRO_(resolve, resolve, "resolve") \
|
||||
MACRO_(result, result, "result") \
|
||||
MACRO_(results, results, "results") \
|
||||
MACRO_(resumeGenerator, resumeGenerator, "resumeGenerator") \
|
||||
MACRO_(return, return_, "return") \
|
||||
MACRO_(revoke, revoke, "revoke") \
|
||||
|
|
|
@ -162,6 +162,7 @@ bool GlobalObject::skipDeselectedConstructor(JSContext* cx, JSProtoKey key) {
|
|||
case JSProto_WasmTable:
|
||||
case JSProto_WasmGlobal:
|
||||
case JSProto_WasmTag:
|
||||
case JSProto_WasmFunction:
|
||||
case JSProto_WasmException:
|
||||
return false;
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "vm/GlobalObject.h" // js::GlobalObject
|
||||
#include "vm/HelperThreadState.h" // js::PromiseHelperTask
|
||||
#include "vm/Interpreter.h"
|
||||
#include "vm/JSFunction.h"
|
||||
#include "vm/PlainObject.h" // js::PlainObject
|
||||
#include "vm/PromiseObject.h" // js::PromiseObject
|
||||
#include "vm/StringType.h"
|
||||
|
@ -1050,6 +1051,32 @@ static JSObject* GetWasmConstructorPrototype(JSContext* cx,
|
|||
return proto;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool ParseValTypeArguments(JSContext* cx, HandleValue src,
|
||||
ValTypeVector& dest) {
|
||||
JS::ForOfIterator iterator(cx);
|
||||
|
||||
if (!iterator.init(src, JS::ForOfIterator::ThrowOnNonIterable)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedValue nextParam(cx);
|
||||
while (true) {
|
||||
bool done;
|
||||
if (!iterator.next(&nextParam, &done)) {
|
||||
return false;
|
||||
}
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
|
||||
ValType valType;
|
||||
if (!ToValType(cx, nextParam, &valType) || !dest.append(valType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// WebAssembly.Module class and methods
|
||||
|
||||
|
@ -2201,10 +2228,16 @@ bool WasmInstanceObject::getExportedFunction(
|
|||
if (!name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fun.set(NewNativeFunction(cx, WasmCall, numArgs, name,
|
||||
gc::AllocKind::FUNCTION_EXTENDED, TenuredObject,
|
||||
FunctionFlags::WASM));
|
||||
RootedObject proto(cx);
|
||||
#ifdef ENABLE_WASM_TYPE_REFLECTIONS
|
||||
proto = GlobalObject::getOrCreatePrototype(cx, JSProto_WasmFunction);
|
||||
if (!proto) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
fun.set(NewFunctionWithProto(
|
||||
cx, WasmCall, numArgs, FunctionFlags::WASM, nullptr, name, proto,
|
||||
gc::AllocKind::FUNCTION_EXTENDED, TenuredObject));
|
||||
if (!fun) {
|
||||
return false;
|
||||
}
|
||||
|
@ -3610,26 +3643,9 @@ bool WasmTagObject::construct(JSContext* cx, unsigned argc, Value* vp) {
|
|||
return false;
|
||||
}
|
||||
|
||||
JS::ForOfIterator iterator(cx);
|
||||
if (!iterator.init(paramsVal, JS::ForOfIterator::ThrowOnNonIterable)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ValTypeVector params;
|
||||
RootedValue nextParam(cx);
|
||||
while (true) {
|
||||
bool done;
|
||||
if (!iterator.next(&nextParam, &done)) {
|
||||
return false;
|
||||
}
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
|
||||
ValType argType;
|
||||
if (!ToValType(cx, nextParam, &argType) || !params.append(argType)) {
|
||||
return false;
|
||||
}
|
||||
if (!ParseValTypeArguments(cx, paramsVal, params)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject proto(cx);
|
||||
|
@ -4045,6 +4061,257 @@ ArrayObject& WasmExceptionObject::refs() const {
|
|||
return getReservedSlot(REFS_SLOT).toObject().as<ArrayObject>();
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// WebAssembly.Function and methods
|
||||
#ifdef ENABLE_WASM_TYPE_REFLECTIONS
|
||||
static JSObject* CreateWasmFunctionPrototype(JSContext* cx, JSProtoKey key) {
|
||||
// WasmFunction's prototype should inherit from JSFunction's prototype.
|
||||
RootedObject jsProto(
|
||||
cx, GlobalObject::getOrCreatePrototype(cx, JSProto_Function));
|
||||
if (!jsProto) {
|
||||
return nullptr;
|
||||
}
|
||||
return GlobalObject::createBlankPrototypeInheriting(cx, &PlainObject::class_,
|
||||
jsProto);
|
||||
}
|
||||
|
||||
[[nodiscard]] static bool IsWasmFunction(HandleValue v) {
|
||||
if (!v.isObject()) {
|
||||
return false;
|
||||
}
|
||||
if (!v.toObject().is<JSFunction>()) {
|
||||
return false;
|
||||
}
|
||||
return v.toObject().as<JSFunction>().isWasm();
|
||||
}
|
||||
|
||||
/* static */
|
||||
static bool ValTypesToArray(JSContext* cx, Handle<jsid> key,
|
||||
const ValTypeVector& value,
|
||||
MutableHandle<IdValueVector> dest) {
|
||||
RootedArrayObject result(cx, NewDenseEmptyArray(cx));
|
||||
for (ValType v : value) {
|
||||
RootedString type(cx, UTF8CharsToString(cx, ToString(v).get()));
|
||||
if (!type) {
|
||||
return false;
|
||||
}
|
||||
if (!NewbornArrayPush(cx, result, StringValue(type))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return dest.append(IdValuePair(key, ObjectValue(*result)));
|
||||
}
|
||||
|
||||
bool WasmFunctionTypeImpl(JSContext* cx, const CallArgs& args) {
|
||||
RootedFunction function(cx, &args.thisv().toObject().as<JSFunction>());
|
||||
|
||||
// Lookup the type information.
|
||||
RootedWasmInstanceObject instanceObj(
|
||||
cx, ExportedFunctionToInstanceObject(function));
|
||||
uint32_t funcIndex = ExportedFunctionToFuncIndex(function);
|
||||
Instance& instance = instanceObj->instance();
|
||||
const FuncType& ft = instance.metadata(instance.code().bestTier())
|
||||
.lookupFuncExport(funcIndex)
|
||||
.funcType();
|
||||
|
||||
const ValTypeVector& parameters = ft.args();
|
||||
RootedId parametersId(cx, NameToId(cx->names().parameters));
|
||||
Rooted<IdValueVector> props(cx, IdValueVector(cx));
|
||||
if (!ValTypesToArray(cx, parametersId, parameters, &props)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ValTypeVector& results = ft.results();
|
||||
RootedId resultsId(cx, NameToId(cx->names().results));
|
||||
if (!ValTypesToArray(cx, resultsId, results, &props)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject functionType(
|
||||
cx, NewPlainObjectWithProperties(cx, props.begin(), props.length(),
|
||||
GenericObject));
|
||||
if (!functionType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
args.rval().setObject(*functionType);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WasmFunctionType(JSContext* cx, unsigned argc, Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod<IsWasmFunction, WasmFunctionTypeImpl>(cx, args);
|
||||
}
|
||||
|
||||
JSFunction* WasmFunctionCreate(JSContext* cx, HandleFunction fun,
|
||||
wasm::ValTypeVector&& params,
|
||||
wasm::ValTypeVector&& results,
|
||||
HandleObject proto) {
|
||||
MOZ_RELEASE_ASSERT(!IsWasmExportedFunction(fun));
|
||||
|
||||
// We want to import the function to a wasm module and then export it again so
|
||||
// that it behaves exactly like a normal wasm function and can be used like
|
||||
// one in wasm tables. Below we create the wasm module with fun as it's import
|
||||
// then exporting it.
|
||||
FeatureOptions options;
|
||||
ScriptedCaller scriptedCaller;
|
||||
SharedCompileArgs compileArgs =
|
||||
CompileArgs::build(cx, std::move(scriptedCaller), options);
|
||||
ModuleEnvironment moduleEnv(compileArgs->features);
|
||||
CompilerEnvironment compilerEnv(CompileMode::Once, Tier::Optimized,
|
||||
OptimizedBackend::Ion, DebugEnabled::False);
|
||||
compilerEnv.computeParameters();
|
||||
|
||||
// Add the Import for the function
|
||||
FuncType funcType = FuncType(std::move(params), std::move(results));
|
||||
TypeDef funcTypeDef = TypeDef(std::move(funcType));
|
||||
if (!moduleEnv.types.append(std::move(funcTypeDef))) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!moduleEnv.typeIds.resize(1)) {
|
||||
return nullptr;
|
||||
}
|
||||
FuncDesc funcDesc =
|
||||
FuncDesc(&moduleEnv.types[0].funcType(), &moduleEnv.typeIds[0], 0);
|
||||
if (!moduleEnv.funcs.append(funcDesc) ||
|
||||
!moduleEnv.funcImportGlobalDataOffsets.resize(1)) {
|
||||
return nullptr;
|
||||
}
|
||||
moduleEnv.declareFuncExported(0, false, false);
|
||||
|
||||
// We will be looking up and using the function in the future by index so the
|
||||
// name doesn't matter.
|
||||
CacheableChars fieldName = DuplicateString("");
|
||||
if (!moduleEnv.exports.emplaceBack(std::move(fieldName), 0,
|
||||
DefinitionKind::Function)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ModuleGenerator mg(*compileArgs, &moduleEnv, &compilerEnv, nullptr, nullptr);
|
||||
if (!mg.init(nullptr)) {
|
||||
return nullptr;
|
||||
}
|
||||
ShareableBytes* sharableBytes = cx->new_<ShareableBytes>();
|
||||
// We're not compiling any function definitions.
|
||||
if (!mg.finishFuncDefs()) {
|
||||
return nullptr;
|
||||
}
|
||||
SharedModule wasmModule = mg.finishModule(*sharableBytes);
|
||||
// Instantiate the module.
|
||||
Rooted<ImportValues> imports(cx);
|
||||
imports.get().funcs.append(fun);
|
||||
RootedWasmInstanceObject instanceObj(cx);
|
||||
if (!wasmModule->instantiate(cx, imports.get(), nullptr, &instanceObj)) {
|
||||
MOZ_ASSERT(cx->isThrowingOutOfMemory());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get the exported function which wraps the JS function to return.
|
||||
RootedFunction exportedFun(cx);
|
||||
instanceObj->getExportedFunction(cx, instanceObj, 0, &exportedFun);
|
||||
return exportedFun;
|
||||
}
|
||||
|
||||
bool WasmFunctionConstruct(JSContext* cx, unsigned argc, Value* vp) {
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
if (!ThrowIfNotConstructing(cx, args, "WebAssembly.Function")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args.requireAtLeast(cx, "WebAssembly.Function", 2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ValTypeVector params;
|
||||
ValTypeVector results;
|
||||
if (!args[0].isObject()) {
|
||||
return false;
|
||||
}
|
||||
RootedObject obj(cx, &args[0].toObject());
|
||||
|
||||
RootedId parametersId(cx, NameToId(cx->names().parameters));
|
||||
RootedValue parametersVal(cx);
|
||||
if (!GetProperty(cx, obj, obj, parametersId, ¶metersVal)) {
|
||||
return false;
|
||||
}
|
||||
if (!ParseValTypeArguments(cx, parametersVal, params)) {
|
||||
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_WASM_BAD_ARG_TYPE);
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedId resultsId(cx, NameToId(cx->names().results));
|
||||
RootedValue resultsVal(cx);
|
||||
if (!GetProperty(cx, obj, obj, resultsId, &resultsVal)) {
|
||||
return false;
|
||||
}
|
||||
if (!ParseValTypeArguments(cx, resultsVal, results)) {
|
||||
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_WASM_BAD_ARG_TYPE);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[1].isObject() || !args[1].toObject().is<JSFunction>()) {
|
||||
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_WASM_BAD_FUNCTION_VALUE);
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedFunction funcObj(cx, &args[1].toObject().as<JSFunction>());
|
||||
|
||||
RootedObject proto(cx);
|
||||
if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_WasmFunction,
|
||||
&proto)) {
|
||||
return false;
|
||||
}
|
||||
if (!proto) {
|
||||
proto = GlobalObject::getOrCreatePrototype(cx, JSProto_WasmFunction);
|
||||
}
|
||||
|
||||
RootedFunction function(cx, WasmFunctionCreate(cx, funcObj, std::move(params),
|
||||
std::move(results), proto));
|
||||
if (!function) {
|
||||
return false;
|
||||
}
|
||||
args.rval().setObject(*function);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSObject* CreateWasmFunctionConstructor(JSContext* cx, JSProtoKey key) {
|
||||
RootedObject proto(
|
||||
cx, GlobalObject::getOrCreateFunctionConstructor(cx, cx->global()));
|
||||
if (!proto) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HandlePropertyName name = cx->names().WasmFunction;
|
||||
return NewFunctionWithProto(cx, WasmFunctionConstruct, 1,
|
||||
FunctionFlags::NATIVE_CTOR, nullptr, name, proto,
|
||||
gc::AllocKind::FUNCTION, TenuredObject);
|
||||
}
|
||||
|
||||
const JSFunctionSpec WasmFunctionMethods[] = {
|
||||
JS_FN("type", WasmFunctionType, 0, 0), JS_FS_END};
|
||||
|
||||
const ClassSpec WasmFunctionClassSpec = {CreateWasmFunctionConstructor,
|
||||
CreateWasmFunctionPrototype,
|
||||
nullptr,
|
||||
nullptr,
|
||||
WasmFunctionMethods,
|
||||
nullptr,
|
||||
nullptr,
|
||||
ClassSpec::DontDefineConstructor};
|
||||
|
||||
const JSClass js::WasmFunctionClass = {
|
||||
"WebAssembly.Function", 0, JS_NULL_CLASS_OPS, &WasmFunctionClassSpec};
|
||||
|
||||
#else
|
||||
const JSClass js::WasmFunctionClass = {"WebAssembly.Function", 0,
|
||||
JS_NULL_CLASS_OPS, JS_NULL_CLASS_SPEC};
|
||||
#endif
|
||||
|
||||
// ============================================================================
|
||||
// WebAssembly class and static methods
|
||||
|
||||
|
@ -5067,6 +5334,9 @@ static bool WebAssemblyClassFinish(JSContext* cx, HandleObject object,
|
|||
{"CompileError", GetExceptionProtoKey(JSEXN_WASMCOMPILEERROR)},
|
||||
{"LinkError", GetExceptionProtoKey(JSEXN_WASMLINKERROR)},
|
||||
{"RuntimeError", GetExceptionProtoKey(JSEXN_WASMRUNTIMEERROR)},
|
||||
#ifdef ENABLE_WASM_TYPE_REFLECTIONS
|
||||
{"Function", JSProto_WasmFunction},
|
||||
#endif
|
||||
};
|
||||
RootedValue ctorValue(cx);
|
||||
RootedId id(cx);
|
||||
|
|
|
@ -573,6 +573,8 @@ class WasmNamespaceObject : public NativeObject {
|
|||
static const ClassSpec classSpec_;
|
||||
};
|
||||
|
||||
extern const JSClass WasmFunctionClass;
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif // wasm_js_h
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
[call.tentative.any.worker.html]
|
||||
[test calling function]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[call.tentative.any.html]
|
||||
[test calling function]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[call.tentative.any.js]
|
||||
[test calling function]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
|
@ -0,0 +1,57 @@
|
|||
[constructor.tentative.any.worker.html]
|
||||
[construct with JS function]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[fail with missing parameters]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[fail with missing results]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[fail with non-string parameters & results]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[fail with non-existant parameter and result type]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[fail with non-function object]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[constructor.tentative.any.html]
|
||||
[construct with JS function]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[fail with missing parameters]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[fail with missing results]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[fail with non-string parameters & results]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[fail with non-existant parameter and result type]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[fail with non-function object]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[constructor.tentative.any.js]
|
||||
[construct with JS function]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[fail with missing parameters]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[fail with missing results]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[fail with non-string parameters & results]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[fail with non-existant parameter and result type]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[fail with non-function object]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
|
@ -0,0 +1,12 @@
|
|||
[table.tentative.any.worker.html]
|
||||
[Test insertion into table]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[table.tentative.any.html]
|
||||
[Test insertion into table]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[table.tentative.any.js]
|
||||
[Test insertion into table]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
|
@ -0,0 +1,21 @@
|
|||
[type.tentative.any.worker.html]
|
||||
[Check empty results and parameters]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[Check all types]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[type.tentative.any.html]
|
||||
[Check empty results and parameters]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[Check all types]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[type.tentative.any.js]
|
||||
[Check empty results and parameters]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
||||
[Check all types]
|
||||
expected:
|
||||
if release_or_beta: FAIL
|
|
@ -17,8 +17,13 @@ function assert_function_length(fn, length, description) {
|
|||
}
|
||||
|
||||
function assert_exported_function(fn, { name, length }, description) {
|
||||
assert_equals(Object.getPrototypeOf(fn), Function.prototype,
|
||||
`${description}: prototype`);
|
||||
if (WebAssembly.Function === undefined) {
|
||||
assert_equals(Object.getPrototypeOf(fn), Function.prototype,
|
||||
`${description}: prototype`);
|
||||
} else {
|
||||
assert_equals(Object.getPrototypeOf(fn), WebAssembly.Function.prototype,
|
||||
`${description}: prototype`);
|
||||
}
|
||||
|
||||
assert_function_name(fn, name, description);
|
||||
assert_function_length(fn, length, description);
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
// META: global=window,dedicatedworker,jsshell
|
||||
// META: script=/wasm/jsapi/assertions.js
|
||||
|
||||
function addxy(x, y) {
|
||||
return x + y
|
||||
}
|
||||
|
||||
test(() => {
|
||||
var fun = new WebAssembly.Function({parameters: ["i32", "i32"], results: ["i32"]}, addxy);
|
||||
assert_equals(fun(1, 2), 3)
|
||||
}, "test calling function")
|
|
@ -0,0 +1,35 @@
|
|||
// META: global=window,dedicatedworker,jsshell
|
||||
// META: script=/wasm/jsapi/assertions.js
|
||||
|
||||
function addxy(x, y) {
|
||||
return x + y
|
||||
}
|
||||
|
||||
test(() => {
|
||||
var fun = new WebAssembly.Function({parameters: ["i32", "i32"], results: ["i32"]}, addxy);
|
||||
assert_true(fun instanceof WebAssembly.Function)
|
||||
}, "construct with JS function")
|
||||
|
||||
test(() => {
|
||||
assert_throws_js(TypeError, () => new WebAssembly.Function({parameters: []}, addxy))
|
||||
}, "fail with missing results")
|
||||
|
||||
test(() => {
|
||||
assert_throws_js(TypeError, () => new WebAssembly.Function({results: []}, addxy))
|
||||
}, "fail with missing parameters")
|
||||
|
||||
test(() => {
|
||||
assert_throws_js(TypeError, () => new WebAssembly.Function({parameters: [1], results: [true]}, addxy))
|
||||
}, "fail with non-string parameters & results")
|
||||
|
||||
test(() => {
|
||||
assert_throws_js(TypeError, () => new WebAssembly.Function({parameters: ["invalid"], results: ["invalid"]}, addxy))
|
||||
}, "fail with non-existent parameter and result type")
|
||||
|
||||
test(() => {
|
||||
assert_throws_js(TypeError, () => new WebAssembly.Function({parameters: [], results: []}, 72))
|
||||
}, "fail with non-function object")
|
||||
|
||||
test(() => {
|
||||
assert_throws_js(TypeError, () => new WebAssembly.Function({parameters: [], results: []}, {}))
|
||||
}, "fail to construct with non-callable object")
|
|
@ -0,0 +1,30 @@
|
|||
// META: global=window,dedicatedworker,jsshell
|
||||
// META: script=/wasm/jsapi/assertions.js
|
||||
|
||||
function testfunc(n) {}
|
||||
|
||||
test(() => {
|
||||
var table = new WebAssembly.Table({element: "anyfunc", initial: 3})
|
||||
var func1 = new WebAssembly.Function({parameters: ["i32"], results: []}, testfunc)
|
||||
table.set(0, func1)
|
||||
var func2 = new WebAssembly.Function({parameters: ["f32"], results: []}, testfunc)
|
||||
table.set(1, func2)
|
||||
var func3 = new WebAssembly.Function({parameters: ["i64"], results: []}, testfunc)
|
||||
table.set(2, func3)
|
||||
|
||||
var first = table.get(0)
|
||||
assert_true(first instanceof WebAssembly.Function)
|
||||
assert_equals(first, func1)
|
||||
assert_equals(first.type().parameters[0], func1.type().parameters[0])
|
||||
|
||||
var second = table.get(1)
|
||||
assert_true(second instanceof WebAssembly.Function)
|
||||
assert_equals(second, func2)
|
||||
assert_equals(second.type().parameters[0], func2.type().parameters[0])
|
||||
|
||||
var third = table.get(2)
|
||||
assert_true(third instanceof WebAssembly.Function)
|
||||
assert_equals(third, func3)
|
||||
assert_equals(third.type().parameters[0], func3.type().parameters[0])
|
||||
|
||||
}, "Test insertion into table")
|
|
@ -0,0 +1,28 @@
|
|||
// META: global=window,dedicatedworker,jsshell
|
||||
// META: script=/wasm/jsapi/assertions.js
|
||||
|
||||
function addNumbers(x, y, z) {
|
||||
return x+y+z;
|
||||
}
|
||||
|
||||
function doNothing() {}
|
||||
|
||||
function assert_function(functype, func) {
|
||||
var wasmFunc = new WebAssembly.Function(functype, func);
|
||||
assert_equals(functype.parameters.length, wasmFunc.type().parameters.length);
|
||||
for(let i = 0; i < functype.parameters.length; i++) {
|
||||
assert_equals(functype.parameters[i], wasmFunc.type().parameters[i]);
|
||||
}
|
||||
assert_equals(functype.results.length, wasmFunc.type().results.length);
|
||||
for(let i = 0; i < functype.results.length; i++) {
|
||||
assert_equals(functype.results[i], wasmFunc.type().results[i]);
|
||||
}
|
||||
}
|
||||
|
||||
test(() => {
|
||||
assert_function({results: [], parameters: []}, doNothing);
|
||||
}, "Check empty results and parameters")
|
||||
|
||||
test(() => {
|
||||
assert_function({results: ["f64"], parameters: ["i32", "i64", "f32"]}, addNumbers)
|
||||
}, "Check all types")
|
Загрузка…
Ссылка в новой задаче