使用导入函数从嵌入式 v8 调用 webassembly
Call webassembly from embedded v8 with imported function
从嵌入式 v8(无 JS)使用 import 语句调用 webassembly
在线程 之后,我能够直接从 C++ 调用 WebAssembly 代码。当我尝试 运行 包含导入语句的更多 "complex" 代码(参见附加代码)时,我的问题就开始了。当尝试 运行 这段代码时,我得到一个 v8 错误 WebAssembly.Instance(): Imports argument must be present and must be an object.
我研究了 v8 代码,发现当模块的 import_table 为空时会发生此错误(v8/src/wasm/module-instantiate.cc#276)。
我想我需要提供导入函数的实现,但我不知道该怎么做。
#include <include/v8.h>
#include <include/libplatform/libplatform.h>
using v8::HandleScope;
using v8::Isolate;
using v8::Local;
using v8::Promise;
using v8::WasmModuleObjectBuilderStreaming;
using v8::WasmCompiledModule;
using v8::Context;
using v8::Local;
using v8::Value;
using v8::String;
using v8::Object;
using v8::Function;
using v8::Int32;
using args_type = Local<Value>[];
int main(int argc, char* argv[]) {
v8::V8::InitializeICUDefaultLocation(argv[0]);
v8::V8::InitializeExternalStartupData(argv[0]);
std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
v8::V8::InitializePlatform(platform.get());
v8::V8::Initialize();
Isolate::CreateParams create_params;
create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
Isolate* isolate = Isolate::New(create_params);
Isolate::Scope isolate_scope(isolate);
HandleScope scope(isolate);
Local<Context> context = Context::New(isolate);
Context::Scope context_scope(context);
WasmModuleObjectBuilderStreaming stream(isolate);
// Use the v8 API to generate a WebAssembly module.
// compiled from the following c code
//
// #include <stdlib.h>
// int add(int x, int y) {
// return x + rand();
// }
//
// produce the following wasm code
//
// (module
// (type $FUNCSIG$i(func(result i32)))
// (import "env" "rand" (func $rand(result i32)))
// (table 0 anyfunc)
// (memory [=10=] 1)
// (export "memory" (memory [=10=]))
// (export "add" (func $add))
// (func $add(; 1;) (param [=10=] i32) (param i32) (result i32)
// (i32.add
// (call $rand)
// (get_local [=10=])
// )
// )
// )
//
// binary representation of the above code
std::vector<uint8_t> wasmbin{
0x00 ,0x61 ,0x73 ,0x6d ,0x01 ,0x00 ,0x00 ,0x00 ,0x01 ,0x8b ,0x80 ,0x80 ,0x80 ,0x00 ,0x02 ,0x60 ,0x00 ,0x01 ,
0x7f ,0x60 ,0x02 ,0x7f ,0x7f ,0x01 ,0x7f ,0x02 ,0x8c ,0x80 ,0x80 ,0x80 ,0x00 ,0x01 ,0x03 ,0x65 ,0x6e ,0x76 ,
0x04 ,0x72 ,0x61 ,0x6e ,0x64 ,0x00 ,0x00 ,0x03 ,0x82 ,0x80 ,0x80 ,0x80 ,0x00 ,0x01 ,0x01 ,0x04 ,0x84 ,0x80 ,
0x80 ,0x80 ,0x00 ,0x01 ,0x70 ,0x00 ,0x00 ,0x05 ,0x83 ,0x80 ,0x80 ,0x80 ,0x00 ,0x01 ,0x00 ,0x01 ,0x06 ,0x81 ,
0x80 ,0x80 ,0x80 ,0x00 ,0x00 ,0x07 ,0x90 ,0x80 ,0x80 ,0x80 ,0x00 ,0x02 ,0x06 ,0x6d ,0x65 ,0x6d ,0x6f ,0x72 ,
0x79 ,0x02 ,0x00 ,0x03 ,0x61 ,0x64 ,0x64 ,0x00 ,0x01 ,0x0a ,0x8d ,0x80 ,0x80 ,0x80 ,0x00 ,0x01 ,0x87 ,0x80 ,
0x80 ,0x80 ,0x00 ,0x00 ,0x10 ,0x00 ,0x20 ,0x00 ,0x6a ,0x0b
};
// same as calling:
// let module = new WebAssembly.Module(bytes);
Local<WasmCompiledModule> module = WasmCompiledModule::DeserializeOrCompile(isolate,
WasmCompiledModule::BufferReference(0, 0),
WasmCompiledModule::BufferReference(wasmbin.data(), wasmbin.size())
).ToLocalChecked();
// same as calling:
// let module_instance_exports = new WebAssembly.Instance(module).exports;
args_type instance_args{module};
Local<Object> module_instance_exports = context->Global()
->Get(context, String::NewFromUtf8(isolate, "WebAssembly"))
.ToLocalChecked().As<Object>()
->Get(context, String::NewFromUtf8(isolate, "Instance"))
.ToLocalChecked().As<Object>()
->CallAsConstructor(context, 1, instance_args)
.ToLocalChecked().As<Object>()
->Get(context, String::NewFromUtf8(isolate, "exports"))
.ToLocalChecked().As<Object>()
;
// same as calling:
// module_instance_exports.add(77, 0)
args_type add_args{Int32::New(isolate, 77), Int32::New(isolate, 0)};
Local<Int32> adder_res = module_instance_exports
->Get(context, String::NewFromUtf8(isolate, "add"))
.ToLocalChecked().As<Function>()
->Call(context, context->Global(), 2, add_args)
.ToLocalChecked().As<Int32>();
printf("77 + rand() = %d\n", adder_res->Value());
return 0;
}
欢迎任何帮助。
我建议使用 WebAssembly C/C++ API, which V8 implements in the form of a library called "libwee8". The documentation for that is currently here。这应该是从 C++ 到 运行 WebAssembly 模块的最简单方法,而不涉及 JavaScript。作为一个额外的好处,它可以很容易地切换底层实现,如果在你的产品的未来生命中出于任何原因 V8 将不是给定用例的正确解决方案。
就是说,如果您更喜欢使用(JavaScript-专注!)传统 V8 API 自己完成所有事情,那么您可以查看映射 Wasm C++ API 到(稍作修改,查看该目录中的其他文件)V8 API 此处:https://github.com/WebAssembly/wasm-c-api/blob/master/src/wasm-v8.cc。请注意,由于需要间接访问,这将比使用 libwee8
慢很多,但如果您出于教育目的而重新发明轮子,那对您来说可能无关紧要 ;-)
具体来说,在 https://github.com/WebAssembly/wasm-c-api/blob/master/src/wasm-v8.cc#L2106 中,您可以看到实例化调用如何将 imports
对象作为其第二个参数。正如您收到的错误消息通知您的那样,它必须存在(并且必须是一个 JavaScript 对象),即使它是空的。
从嵌入式 v8(无 JS)使用 import 语句调用 webassembly
在线程
#include <include/v8.h>
#include <include/libplatform/libplatform.h>
using v8::HandleScope;
using v8::Isolate;
using v8::Local;
using v8::Promise;
using v8::WasmModuleObjectBuilderStreaming;
using v8::WasmCompiledModule;
using v8::Context;
using v8::Local;
using v8::Value;
using v8::String;
using v8::Object;
using v8::Function;
using v8::Int32;
using args_type = Local<Value>[];
int main(int argc, char* argv[]) {
v8::V8::InitializeICUDefaultLocation(argv[0]);
v8::V8::InitializeExternalStartupData(argv[0]);
std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
v8::V8::InitializePlatform(platform.get());
v8::V8::Initialize();
Isolate::CreateParams create_params;
create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
Isolate* isolate = Isolate::New(create_params);
Isolate::Scope isolate_scope(isolate);
HandleScope scope(isolate);
Local<Context> context = Context::New(isolate);
Context::Scope context_scope(context);
WasmModuleObjectBuilderStreaming stream(isolate);
// Use the v8 API to generate a WebAssembly module.
// compiled from the following c code
//
// #include <stdlib.h>
// int add(int x, int y) {
// return x + rand();
// }
//
// produce the following wasm code
//
// (module
// (type $FUNCSIG$i(func(result i32)))
// (import "env" "rand" (func $rand(result i32)))
// (table 0 anyfunc)
// (memory [=10=] 1)
// (export "memory" (memory [=10=]))
// (export "add" (func $add))
// (func $add(; 1;) (param [=10=] i32) (param i32) (result i32)
// (i32.add
// (call $rand)
// (get_local [=10=])
// )
// )
// )
//
// binary representation of the above code
std::vector<uint8_t> wasmbin{
0x00 ,0x61 ,0x73 ,0x6d ,0x01 ,0x00 ,0x00 ,0x00 ,0x01 ,0x8b ,0x80 ,0x80 ,0x80 ,0x00 ,0x02 ,0x60 ,0x00 ,0x01 ,
0x7f ,0x60 ,0x02 ,0x7f ,0x7f ,0x01 ,0x7f ,0x02 ,0x8c ,0x80 ,0x80 ,0x80 ,0x00 ,0x01 ,0x03 ,0x65 ,0x6e ,0x76 ,
0x04 ,0x72 ,0x61 ,0x6e ,0x64 ,0x00 ,0x00 ,0x03 ,0x82 ,0x80 ,0x80 ,0x80 ,0x00 ,0x01 ,0x01 ,0x04 ,0x84 ,0x80 ,
0x80 ,0x80 ,0x00 ,0x01 ,0x70 ,0x00 ,0x00 ,0x05 ,0x83 ,0x80 ,0x80 ,0x80 ,0x00 ,0x01 ,0x00 ,0x01 ,0x06 ,0x81 ,
0x80 ,0x80 ,0x80 ,0x00 ,0x00 ,0x07 ,0x90 ,0x80 ,0x80 ,0x80 ,0x00 ,0x02 ,0x06 ,0x6d ,0x65 ,0x6d ,0x6f ,0x72 ,
0x79 ,0x02 ,0x00 ,0x03 ,0x61 ,0x64 ,0x64 ,0x00 ,0x01 ,0x0a ,0x8d ,0x80 ,0x80 ,0x80 ,0x00 ,0x01 ,0x87 ,0x80 ,
0x80 ,0x80 ,0x00 ,0x00 ,0x10 ,0x00 ,0x20 ,0x00 ,0x6a ,0x0b
};
// same as calling:
// let module = new WebAssembly.Module(bytes);
Local<WasmCompiledModule> module = WasmCompiledModule::DeserializeOrCompile(isolate,
WasmCompiledModule::BufferReference(0, 0),
WasmCompiledModule::BufferReference(wasmbin.data(), wasmbin.size())
).ToLocalChecked();
// same as calling:
// let module_instance_exports = new WebAssembly.Instance(module).exports;
args_type instance_args{module};
Local<Object> module_instance_exports = context->Global()
->Get(context, String::NewFromUtf8(isolate, "WebAssembly"))
.ToLocalChecked().As<Object>()
->Get(context, String::NewFromUtf8(isolate, "Instance"))
.ToLocalChecked().As<Object>()
->CallAsConstructor(context, 1, instance_args)
.ToLocalChecked().As<Object>()
->Get(context, String::NewFromUtf8(isolate, "exports"))
.ToLocalChecked().As<Object>()
;
// same as calling:
// module_instance_exports.add(77, 0)
args_type add_args{Int32::New(isolate, 77), Int32::New(isolate, 0)};
Local<Int32> adder_res = module_instance_exports
->Get(context, String::NewFromUtf8(isolate, "add"))
.ToLocalChecked().As<Function>()
->Call(context, context->Global(), 2, add_args)
.ToLocalChecked().As<Int32>();
printf("77 + rand() = %d\n", adder_res->Value());
return 0;
}
欢迎任何帮助。
我建议使用 WebAssembly C/C++ API, which V8 implements in the form of a library called "libwee8". The documentation for that is currently here。这应该是从 C++ 到 运行 WebAssembly 模块的最简单方法,而不涉及 JavaScript。作为一个额外的好处,它可以很容易地切换底层实现,如果在你的产品的未来生命中出于任何原因 V8 将不是给定用例的正确解决方案。
就是说,如果您更喜欢使用(JavaScript-专注!)传统 V8 API 自己完成所有事情,那么您可以查看映射 Wasm C++ API 到(稍作修改,查看该目录中的其他文件)V8 API 此处:https://github.com/WebAssembly/wasm-c-api/blob/master/src/wasm-v8.cc。请注意,由于需要间接访问,这将比使用 libwee8
慢很多,但如果您出于教育目的而重新发明轮子,那对您来说可能无关紧要 ;-)
具体来说,在 https://github.com/WebAssembly/wasm-c-api/blob/master/src/wasm-v8.cc#L2106 中,您可以看到实例化调用如何将 imports
对象作为其第二个参数。正如您收到的错误消息通知您的那样,它必须存在(并且必须是一个 JavaScript 对象),即使它是空的。