mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 15:48:12 +00:00
Meta+LibWasm: Add support for module linking tests
This commit makes the linking tests in the wasm spec test run.
This commit is contained in:
parent
5c90c389c3
commit
9c5d38b7db
3 changed files with 218 additions and 36 deletions
|
@ -34,6 +34,8 @@ def parse(sexp):
|
||||||
stack[-2].append(stack.pop())
|
stack[-2].append(stack.pop())
|
||||||
elif c == '\\':
|
elif c == '\\':
|
||||||
i += 1
|
i += 1
|
||||||
|
if sexp[i] != '"':
|
||||||
|
stack[-1] += '\\'
|
||||||
stack[-1] += sexp[i]
|
stack[-1] += sexp[i]
|
||||||
else:
|
else:
|
||||||
stack[-1] += c
|
stack[-1] += c
|
||||||
|
@ -77,35 +79,107 @@ def generate_module_source_for_compilation(entries):
|
||||||
return s + ')'
|
return s + ')'
|
||||||
|
|
||||||
|
|
||||||
|
def generate_binary_source(chunks):
|
||||||
|
res = b''
|
||||||
|
for chunk in chunks:
|
||||||
|
i = 0
|
||||||
|
while i < len(chunk):
|
||||||
|
c = chunk[i]
|
||||||
|
if c == '\\':
|
||||||
|
res += bytes.fromhex(chunk[i + 1: i + 3])
|
||||||
|
i += 3
|
||||||
|
continue
|
||||||
|
res += c.encode('utf-8')
|
||||||
|
i += 1
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
named_modules = {}
|
||||||
|
named_modules_inverse = {}
|
||||||
|
registered_modules = {}
|
||||||
|
|
||||||
|
|
||||||
def generate(ast):
|
def generate(ast):
|
||||||
|
global named_modules, named_modules_inverse, registered_modules
|
||||||
|
|
||||||
if type(ast) != list:
|
if type(ast) != list:
|
||||||
return []
|
return []
|
||||||
tests = []
|
tests = []
|
||||||
for entry in ast:
|
for entry in ast:
|
||||||
if len(entry) > 0 and entry[0] == ('module',):
|
if len(entry) > 0 and entry[0] == ('module',):
|
||||||
|
name = None
|
||||||
|
mode = 'ast' # binary, quote
|
||||||
|
start_index = 1
|
||||||
|
if len(entry) > 1:
|
||||||
|
if isinstance(entry[1], tuple) and isinstance(entry[1][0], str) and entry[1][0].startswith('$'):
|
||||||
|
name = entry[1][0]
|
||||||
|
if len(entry) > 2:
|
||||||
|
if isinstance(entry[2], tuple) and entry[2][0] in ('binary', 'quote'):
|
||||||
|
mode = entry[2][0]
|
||||||
|
start_index = 3
|
||||||
|
else:
|
||||||
|
start_index = 2
|
||||||
|
elif isinstance(entry[1][0], str):
|
||||||
|
mode = entry[1][0]
|
||||||
|
start_index = 2
|
||||||
|
|
||||||
tests.append({
|
tests.append({
|
||||||
"module": generate_module_source_for_compilation(entry),
|
"module": {
|
||||||
|
'ast': lambda: ('parse', generate_module_source_for_compilation(entry)),
|
||||||
|
'binary': lambda: ('literal', generate_binary_source(entry[start_index:])),
|
||||||
|
# FIXME: Make this work when we have a WAT parser
|
||||||
|
'quote': lambda: ('literal', entry[start_index]),
|
||||||
|
}[mode](),
|
||||||
"tests": []
|
"tests": []
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if name is not None:
|
||||||
|
named_modules[name] = len(tests) - 1
|
||||||
|
named_modules_inverse[len(tests) - 1] = (name, None)
|
||||||
elif len(entry) in [2, 3] and entry[0][0].startswith('assert_'):
|
elif len(entry) in [2, 3] and entry[0][0].startswith('assert_'):
|
||||||
if entry[1][0] == ('invoke',):
|
if entry[1][0] == ('invoke',):
|
||||||
|
arg, name, module = 0, None, None
|
||||||
|
if isinstance(entry[1][1], str):
|
||||||
|
name = entry[1][1]
|
||||||
|
else:
|
||||||
|
name = entry[1][2]
|
||||||
|
module = named_modules[entry[1][1][0]]
|
||||||
|
arg = 1
|
||||||
tests[-1]["tests"].append({
|
tests[-1]["tests"].append({
|
||||||
"kind": entry[0][0][len('assert_'):],
|
"kind": entry[0][0][len('assert_'):],
|
||||||
"function": {
|
"function": {
|
||||||
"name": entry[1][1],
|
"module": module,
|
||||||
"args": list(parse_typed_value(x) for x in entry[1][2:])
|
"name": name,
|
||||||
|
"args": list(parse_typed_value(x) for x in entry[1][arg + 2:])
|
||||||
},
|
},
|
||||||
"result": parse_typed_value(entry[2]) if len(entry) == 3 else None
|
"result": parse_typed_value(entry[2]) if len(entry) == 3 + arg else None
|
||||||
|
})
|
||||||
|
elif entry[1][0] == ('get',):
|
||||||
|
arg, name, module = 0, None, None
|
||||||
|
if isinstance(entry[1][1], str):
|
||||||
|
name = entry[1][1]
|
||||||
|
else:
|
||||||
|
name = entry[1][2]
|
||||||
|
module = named_modules[entry[1][1][0]]
|
||||||
|
arg = 1
|
||||||
|
tests[-1]["tests"].append({
|
||||||
|
"kind": entry[0][0][len('assert_'):],
|
||||||
|
"get": {
|
||||||
|
"name": name,
|
||||||
|
"module": module,
|
||||||
|
},
|
||||||
|
"result": parse_typed_value(entry[2]) if len(entry) == 3 + arg else None
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
if not len(tests):
|
if not len(tests):
|
||||||
tests.append({
|
tests.append({
|
||||||
"module": "",
|
"module": ('literal', b""),
|
||||||
"tests": []
|
"tests": []
|
||||||
})
|
})
|
||||||
tests[-1]["tests"].append({
|
tests[-1]["tests"].append({
|
||||||
"kind": "testgen_fail",
|
"kind": "testgen_fail",
|
||||||
"function": {
|
"function": {
|
||||||
|
"module": None,
|
||||||
"name": "<unknown>",
|
"name": "<unknown>",
|
||||||
"args": []
|
"args": []
|
||||||
},
|
},
|
||||||
|
@ -113,23 +187,41 @@ def generate(ast):
|
||||||
})
|
})
|
||||||
elif len(entry) >= 2 and entry[0][0] == 'invoke':
|
elif len(entry) >= 2 and entry[0][0] == 'invoke':
|
||||||
# toplevel invoke :shrug:
|
# toplevel invoke :shrug:
|
||||||
|
arg, name, module = 0, None, None
|
||||||
|
if isinstance(entry[1][1], str):
|
||||||
|
name = entry[1][1]
|
||||||
|
else:
|
||||||
|
name = entry[1][2]
|
||||||
|
module = named_modules[entry[1][1][0]]
|
||||||
|
arg = 1
|
||||||
tests[-1]["tests"].append({
|
tests[-1]["tests"].append({
|
||||||
"kind": "ignore",
|
"kind": "ignore",
|
||||||
"function": {
|
"function": {
|
||||||
"name": entry[1][1],
|
"module": module,
|
||||||
"args": list(parse_typed_value(x) for x in entry[1][2:])
|
"name": name,
|
||||||
|
"args": list(parse_typed_value(x) for x in entry[1][arg + 2:])
|
||||||
},
|
},
|
||||||
"result": parse_typed_value(entry[2]) if len(entry) == 3 else None
|
"result": parse_typed_value(entry[2]) if len(entry) == 3 + arg else None
|
||||||
})
|
})
|
||||||
|
elif len(entry) > 1 and entry[0][0] == 'register':
|
||||||
|
if len(entry) == 3:
|
||||||
|
registered_modules[entry[1]] = named_modules[entry[2][0]]
|
||||||
|
x = named_modules_inverse[named_modules[entry[2][0]]]
|
||||||
|
named_modules_inverse[named_modules[entry[2][0]]] = (x[0], entry[1])
|
||||||
|
else:
|
||||||
|
index = len(tests) - 1
|
||||||
|
registered_modules[entry[1]] = index
|
||||||
|
named_modules_inverse[index] = (":" + entry[1], entry[1])
|
||||||
else:
|
else:
|
||||||
if not len(tests):
|
if not len(tests):
|
||||||
tests.append({
|
tests.append({
|
||||||
"module": "",
|
"module": ('literal', b""),
|
||||||
"tests": []
|
"tests": []
|
||||||
})
|
})
|
||||||
tests[-1]["tests"].append({
|
tests[-1]["tests"].append({
|
||||||
"kind": "testgen_fail",
|
"kind": "testgen_fail",
|
||||||
"function": {
|
"function": {
|
||||||
|
"module": None,
|
||||||
"name": "<unknown>",
|
"name": "<unknown>",
|
||||||
"args": []
|
"args": []
|
||||||
},
|
},
|
||||||
|
@ -190,22 +282,33 @@ all_names_in_main = {}
|
||||||
|
|
||||||
|
|
||||||
def genresult(ident, entry):
|
def genresult(ident, entry):
|
||||||
|
expectation = f'expect().fail("Unknown result structure " + {json.dumps(entry)})'
|
||||||
|
if "function" in entry:
|
||||||
|
tmodule = 'module'
|
||||||
|
if entry['function']['module'] is not None:
|
||||||
|
tmodule = f'namedModules[{json.dumps(named_modules_inverse[entry["function"]["module"]][0])}]'
|
||||||
|
expectation = (
|
||||||
|
f'{tmodule}.invoke({ident}, {", ".join(genarg(x) for x in entry["function"]["args"])})'
|
||||||
|
)
|
||||||
|
elif "get" in entry:
|
||||||
|
expectation = f'module.getExport({ident})'
|
||||||
|
|
||||||
if entry['kind'] == 'return':
|
if entry['kind'] == 'return':
|
||||||
return_check = f'expect({ident}_result).toBe({genarg(entry["result"])})' if entry["result"] is not None else ''
|
|
||||||
return (
|
return (
|
||||||
f'let {ident}_result ='
|
f'let {ident}_result = {expectation};\n '
|
||||||
f' module.invoke({ident}, {", ".join(genarg(x) for x in entry["function"]["args"])});\n '
|
f'expect({ident}_result).toBe({genarg(entry["result"])})\n ' if entry["result"] is not None else ''
|
||||||
f'{return_check};\n '
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if entry['kind'] == 'trap':
|
if entry['kind'] == 'trap':
|
||||||
return (
|
return (
|
||||||
f'expect(() => module.invoke({ident}, {", ".join(genarg(x) for x in entry["function"]["args"])}))'
|
f'expect(() => {expectation}).toThrow(TypeError, "Execution trapped");\n '
|
||||||
'.toThrow(TypeError, "Execution trapped");\n '
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if entry['kind'] == 'ignore':
|
if entry['kind'] == 'ignore':
|
||||||
return f'module.invoke({ident}, {", ".join(genarg(x) for x in entry["function"]["args"])});\n '
|
return expectation
|
||||||
|
|
||||||
|
if entry['kind'] == 'unlinkable':
|
||||||
|
return
|
||||||
|
|
||||||
if entry['kind'] == 'testgen_fail':
|
if entry['kind'] == 'testgen_fail':
|
||||||
return f'throw Exception("Test Generator Failure: " + {json.dumps(entry["reason"])});\n '
|
return f'throw Exception("Test Generator Failure: " + {json.dumps(entry["reason"])});\n '
|
||||||
|
@ -214,7 +317,8 @@ def genresult(ident, entry):
|
||||||
|
|
||||||
|
|
||||||
def gentest(entry, main_name):
|
def gentest(entry, main_name):
|
||||||
name = json.dumps(entry["function"]["name"])[1:-1]
|
isfunction = 'function' in entry
|
||||||
|
name = json.dumps((entry["function"] if isfunction else entry["get"])["name"])[1:-1]
|
||||||
if type(name) != str:
|
if type(name) != str:
|
||||||
print("Unsupported test case (call to", name, ")", file=stderr)
|
print("Unsupported test case (call to", name, ")", file=stderr)
|
||||||
return '\n '
|
return '\n '
|
||||||
|
@ -222,9 +326,13 @@ def gentest(entry, main_name):
|
||||||
count = all_names_in_main.get(name, 0)
|
count = all_names_in_main.get(name, 0)
|
||||||
all_names_in_main[name] = count + 1
|
all_names_in_main[name] = count + 1
|
||||||
test_name = f'execution of {main_name}: {name} (instance {count})'
|
test_name = f'execution of {main_name}: {name} (instance {count})'
|
||||||
|
tmodule = 'module'
|
||||||
|
key = "function" if "function" in entry else "get"
|
||||||
|
if entry[key]['module'] is not None:
|
||||||
|
tmodule = f'namedModules[{json.dumps(named_modules_inverse[entry[key]["module"]][0])}]'
|
||||||
source = (
|
source = (
|
||||||
f'test({json.dumps(test_name)}, () => {{\n'
|
f'test({json.dumps(test_name)}, () => {{\n'
|
||||||
f'let {ident} = module.getExport({json.dumps(name)});\n '
|
f'let {ident} = {tmodule}.getExport({json.dumps(name)});\n '
|
||||||
f'expect({ident}).not.toBeUndefined();\n '
|
f'expect({ident}).not.toBeUndefined();\n '
|
||||||
f'{genresult(ident, entry)}'
|
f'{genresult(ident, entry)}'
|
||||||
'});\n\n '
|
'});\n\n '
|
||||||
|
@ -232,33 +340,54 @@ def gentest(entry, main_name):
|
||||||
return source
|
return source
|
||||||
|
|
||||||
|
|
||||||
def gen_parse_module(name):
|
def gen_parse_module(name, index):
|
||||||
|
export_string = ''
|
||||||
|
if index in named_modules_inverse:
|
||||||
|
entry = named_modules_inverse[index]
|
||||||
|
export_string += f'namedModules[{json.dumps(entry[0])}] = module;\n '
|
||||||
|
if entry[1]:
|
||||||
|
export_string += f'globalImportObject[{json.dumps(entry[1])}] = module;\n '
|
||||||
|
|
||||||
return (
|
return (
|
||||||
f'let content = readBinaryWasmFile("Fixtures/SpecTests/{name}.wasm");\n '
|
f'let content = readBinaryWasmFile("Fixtures/SpecTests/{name}.wasm");\n '
|
||||||
f'const module = parseWebAssemblyModule(content)\n '
|
f'const module = parseWebAssemblyModule(content, globalImportObject)\n '
|
||||||
|
f'{export_string}\n '
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def nth(a, x, y=None):
|
||||||
|
if y:
|
||||||
|
return a[x:y]
|
||||||
|
return a[x]
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
with open(argv[1]) as f:
|
with open(argv[1]) as f:
|
||||||
sexp = f.read()
|
sexp = f.read()
|
||||||
name = argv[2]
|
name = argv[2]
|
||||||
module_output_path = argv[3]
|
module_output_path = argv[3]
|
||||||
ast = parse(sexp)
|
ast = parse(sexp)
|
||||||
|
print('let globalImportObject = {};')
|
||||||
|
print('let namedModules = {};\n')
|
||||||
for index, description in enumerate(generate(ast)):
|
for index, description in enumerate(generate(ast)):
|
||||||
testname = f'{name}_{index}'
|
testname = f'{name}_{index}'
|
||||||
outpath = path.join(module_output_path, f'{testname}.wasm')
|
outpath = path.join(module_output_path, f'{testname}.wasm')
|
||||||
with NamedTemporaryFile("w+") as temp:
|
mod = description["module"]
|
||||||
temp.write(description["module"])
|
if mod[0] == 'literal':
|
||||||
temp.flush()
|
with open('outpath', 'wb+') as f:
|
||||||
rc = call(["wasm-as", "-n", "-all", temp.name, "-o", outpath])
|
f.write(mod[1])
|
||||||
if rc != 0:
|
elif mod[0] == 'parse':
|
||||||
print("Failed to compile", name, "module index", index, "skipping that test", file=stderr)
|
with NamedTemporaryFile("w+") as temp:
|
||||||
continue
|
temp.write(mod[1])
|
||||||
|
temp.flush()
|
||||||
|
rc = call(["wasm-as", "-n", "-all", temp.name, "-o", outpath])
|
||||||
|
if rc != 0:
|
||||||
|
print("Failed to compile", name, "module index", index, "skipping that test", file=stderr)
|
||||||
|
continue
|
||||||
|
|
||||||
sep = ""
|
sep = ""
|
||||||
print(f'''describe({json.dumps(testname)}, () => {{
|
print(f'''describe({json.dumps(testname)}, () => {{
|
||||||
{gen_parse_module(testname)}
|
{gen_parse_module(testname, index)}
|
||||||
{sep.join(gentest(x, testname) for x in description["tests"])}
|
{sep.join(gentest(x, testname) for x in description["tests"])}
|
||||||
}});
|
}});
|
||||||
''')
|
''')
|
||||||
|
|
|
@ -40,14 +40,22 @@ public:
|
||||||
Wasm::Module& module() { return *m_module; }
|
Wasm::Module& module() { return *m_module; }
|
||||||
Wasm::ModuleInstance& module_instance() { return *m_module_instance; }
|
Wasm::ModuleInstance& module_instance() { return *m_module_instance; }
|
||||||
|
|
||||||
static WebAssemblyModule* create(JS::GlobalObject& global_object, Wasm::Module module)
|
static WebAssemblyModule* create(JS::GlobalObject& global_object, Wasm::Module module, HashMap<Wasm::Linker::Name, Wasm::ExternValue> const& imports)
|
||||||
{
|
{
|
||||||
auto instance = global_object.heap().allocate<WebAssemblyModule>(global_object, *global_object.object_prototype());
|
auto instance = global_object.heap().allocate<WebAssemblyModule>(global_object, *global_object.object_prototype());
|
||||||
instance->m_module = move(module);
|
instance->m_module = move(module);
|
||||||
if (auto result = machine().instantiate(*instance->m_module, {}); result.is_error())
|
Wasm::Linker linker(*instance->m_module);
|
||||||
global_object.vm().throw_exception<JS::TypeError>(global_object, result.release_error().error);
|
linker.link(imports);
|
||||||
else
|
linker.link(spec_test_namespace());
|
||||||
instance->m_module_instance = result.release_value();
|
auto link_result = linker.finish();
|
||||||
|
if (link_result.is_error()) {
|
||||||
|
global_object.vm().throw_exception<JS::TypeError>(global_object, "Link failed");
|
||||||
|
} else {
|
||||||
|
if (auto result = machine().instantiate(*instance->m_module, link_result.release_value()); result.is_error())
|
||||||
|
global_object.vm().throw_exception<JS::TypeError>(global_object, result.release_error().error);
|
||||||
|
else
|
||||||
|
instance->m_module_instance = result.release_value();
|
||||||
|
}
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
void initialize(JS::GlobalObject&) override;
|
void initialize(JS::GlobalObject&) override;
|
||||||
|
@ -58,12 +66,31 @@ private:
|
||||||
JS_DECLARE_NATIVE_FUNCTION(get_export);
|
JS_DECLARE_NATIVE_FUNCTION(get_export);
|
||||||
JS_DECLARE_NATIVE_FUNCTION(wasm_invoke);
|
JS_DECLARE_NATIVE_FUNCTION(wasm_invoke);
|
||||||
|
|
||||||
|
static HashMap<Wasm::Linker::Name, Wasm::ExternValue> const& spec_test_namespace()
|
||||||
|
{
|
||||||
|
if (!s_spec_test_namespace.is_empty())
|
||||||
|
return s_spec_test_namespace;
|
||||||
|
Wasm::FunctionType print_i32_type { { Wasm::ValueType(Wasm::ValueType::I32) }, {} };
|
||||||
|
|
||||||
|
auto address = m_machine.store().allocate(Wasm::HostFunction {
|
||||||
|
[](auto&, auto&) -> Wasm::Result {
|
||||||
|
// Noop, this just needs to exist.
|
||||||
|
return Wasm::Result { Vector<Wasm::Value> {} };
|
||||||
|
},
|
||||||
|
print_i32_type });
|
||||||
|
s_spec_test_namespace.set({ "spectest", "print_i32", print_i32_type }, Wasm::ExternValue { *address });
|
||||||
|
|
||||||
|
return s_spec_test_namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
static HashMap<Wasm::Linker::Name, Wasm::ExternValue> s_spec_test_namespace;
|
||||||
static Wasm::AbstractMachine m_machine;
|
static Wasm::AbstractMachine m_machine;
|
||||||
Optional<Wasm::Module> m_module;
|
Optional<Wasm::Module> m_module;
|
||||||
OwnPtr<Wasm::ModuleInstance> m_module_instance;
|
OwnPtr<Wasm::ModuleInstance> m_module_instance;
|
||||||
};
|
};
|
||||||
|
|
||||||
Wasm::AbstractMachine WebAssemblyModule::m_machine;
|
Wasm::AbstractMachine WebAssemblyModule::m_machine;
|
||||||
|
HashMap<Wasm::Linker::Name, Wasm::ExternValue> WebAssemblyModule::s_spec_test_namespace;
|
||||||
|
|
||||||
TESTJS_GLOBAL_FUNCTION(parse_webassembly_module, parseWebAssemblyModule)
|
TESTJS_GLOBAL_FUNCTION(parse_webassembly_module, parseWebAssemblyModule)
|
||||||
{
|
{
|
||||||
|
@ -86,7 +113,24 @@ TESTJS_GLOBAL_FUNCTION(parse_webassembly_module, parseWebAssemblyModule)
|
||||||
vm.throw_exception<JS::SyntaxError>(global_object, "Bianry stream contained errors");
|
vm.throw_exception<JS::SyntaxError>(global_object, "Bianry stream contained errors");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
return WebAssemblyModule::create(global_object, result.release_value());
|
|
||||||
|
HashMap<Wasm::Linker::Name, Wasm::ExternValue> imports;
|
||||||
|
auto import_value = vm.argument(1);
|
||||||
|
if (import_value.is_object()) {
|
||||||
|
auto& import_object = import_value.as_object();
|
||||||
|
for (auto& property : import_object.shape().property_table()) {
|
||||||
|
auto value = import_object.get_own_property(property.key, {}, JS::AllowSideEffects::No);
|
||||||
|
if (!value.is_object() || !is<WebAssemblyModule>(value.as_object()))
|
||||||
|
continue;
|
||||||
|
auto& module_object = static_cast<WebAssemblyModule&>(value.as_object());
|
||||||
|
for (auto& entry : module_object.module_instance().exports()) {
|
||||||
|
// FIXME: Don't pretend that everything is a function
|
||||||
|
imports.set({ property.key.as_string(), entry.name(), Wasm::TypeIndex(0) }, entry.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return WebAssemblyModule::create(global_object, result.release_value(), imports);
|
||||||
}
|
}
|
||||||
|
|
||||||
TESTJS_GLOBAL_FUNCTION(compare_typed_arrays, compareTypedArrays)
|
TESTJS_GLOBAL_FUNCTION(compare_typed_arrays, compareTypedArrays)
|
||||||
|
@ -136,7 +180,16 @@ JS_DEFINE_NATIVE_FUNCTION(WebAssemblyModule::get_export)
|
||||||
auto& value = entry.value();
|
auto& value = entry.value();
|
||||||
if (auto ptr = value.get_pointer<Wasm::FunctionAddress>())
|
if (auto ptr = value.get_pointer<Wasm::FunctionAddress>())
|
||||||
return JS::Value(static_cast<unsigned long>(ptr->value()));
|
return JS::Value(static_cast<unsigned long>(ptr->value()));
|
||||||
vm.throw_exception<JS::TypeError>(global_object, String::formatted("'{}' does not refer to a function", name));
|
if (auto v = value.get_pointer<Wasm::GlobalAddress>()) {
|
||||||
|
return m_machine.store().get(*v)->value().value().visit(
|
||||||
|
[&](const auto& value) -> JS::Value { return JS::Value(static_cast<double>(value)); },
|
||||||
|
[&](const Wasm::Reference& reference) -> JS::Value {
|
||||||
|
return reference.ref().visit(
|
||||||
|
[&](const Wasm::Reference::Null&) -> JS::Value { return JS::js_null(); },
|
||||||
|
[&](const auto& ref) -> JS::Value { return JS::Value(static_cast<double>(ref.address.value())); });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
vm.throw_exception<JS::TypeError>(global_object, String::formatted("'{}' does not refer to a function or a global", name));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ test("parsing can pass", () => {
|
||||||
0x01, 0x05, 0x63, 0x6c, 0x61, 0x6e, 0x67, 0x06, 0x31, 0x31, 0x2e, 0x31, 0x2e, 0x30,
|
0x01, 0x05, 0x63, 0x6c, 0x61, 0x6e, 0x67, 0x06, 0x31, 0x31, 0x2e, 0x31, 0x2e, 0x30,
|
||||||
]);
|
]);
|
||||||
// This just checks that the function actually works
|
// This just checks that the function actually works
|
||||||
parseWebAssemblyModule(binary);
|
expect(() => parseWebAssemblyModule(binary)).toThrowWithMessage(TypeError, "Link failed");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("parsing can fail", () => {
|
test("parsing can fail", () => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue