Вызов веб-сборки из встроенного v8 с импортированной функцией - PullRequest
0 голосов
/ 03 ноября 2019

Вызов веб-сборки с оператором импорта из встроенного v8 (без JS)

Следуя потоку Вызов веб-сборки из встроенного v8 без JS Мне удалось вызвать код WebAssembly напрямую из c ++. Моя проблема началась, когда я попытался запустить более «сложный» код (см. Прилагаемый код), который включает оператор импорта. При попытке запустить этот код я получаю ошибку v8. WebAssembly.Instance (): должен присутствовать аргумент Imports, который должен быть объектом. Я копался в коде 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 $0 1)
    //      (export "memory" (memory $0))
    //      (export "add" (func $add))
    //      (func $add(; 1;) (param $0 i32) (param $1 i32) (result i32)
    //      (i32.add
    //      (call $rand)
    //          (get_local $0)
    //          )
    //          )
    //      )
    // 
    // 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;
}

Любая помощь будет приветствоваться.

1 Ответ

0 голосов
/ 03 ноября 2019

Я бы порекомендовал использовать WebAssembly C / C ++ API , который V8 реализует в форме библиотеки под названием "libwee8". Документация для этого в настоящее время здесь . Это должен быть самый простой способ запуска модулей WebAssembly из C ++ без использования JavaScript. В качестве дополнительного бонуса, это облегчает переключение базовой реализации, если по какой-либо причине в будущем жизнь вашего продукта V8 не будет правильным решением для данного варианта использования.

Тем не менее,если вы предпочитаете делать все самостоятельно, используя (ориентированный на JavaScript!) традиционный API V8, то вы можете посмотреть на реализацию прототипа, которая отображает API Wasm C ++ на (немного измененный, см. другие файлы в этом каталоге) API V8 здесь: 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), даже если оно пустое.

...