Как включить и использовать модули ES6 в движке JavaScript V8?

Я использую встроенный движок V8 в моем (Windows Desktop) приложении C ++. Я так понимаю, что V8 поддерживает модули ES6. Как мне активировать и использовать эту функцию в моем приложении?

Я не ожидал, что у кого-то будет полный рабочий пример того, как это работает, но ответ на высоком уровне, указывающий мне (и будущим читателям) в правильном направлении, полностью оправдает мои надежды и чаяния по этому вопросу.


person Boinst    schedule 26.08.2018    source источник
comment
Вы пробовали некоторые функции es6, но они не работают? Как вы встраиваете двигатель V8?   -  person Anas    schedule 26.08.2018
comment
Я не пробовал просто включать функции @Anas, кажется, что после включения мне нужно выполнить некоторую конфигурацию, прежде чем она действительно заработает. Как еще V8 мог бы знать, где искать файл в моей файловой системе? Если я использую следующий код JS, например: import {x} from "./y.js"; Я не думаю, что V8 знает, где искать y.js.   -  person Boinst    schedule 26.08.2018
comment
@Anas RE, как вы встраиваете, я думаю, что встраиваю довольно стандартным способом. Все, что я делаю, согласуется с Руководством по внедрению.   -  person Boinst    schedule 26.08.2018
comment
Мне было очень полезно (учитывая отсутствие документации) обнаружить, что реализация V8 для модулей ужасно близка к буквальному тексту / языку спецификации. ecma-international.org/ecma-262/# сек-источник-текст-модуль-записи   -  person Ron Burk    schedule 25.05.2020


Ответы (1)


Помимо реальных примеров из V8 (я действительно планировал написать некоторые из них в какой-то момент), я напишу здесь один. Для некоторых примеров использования в дикой природе я рекомендую реализацию Node.js, или мой собственный, оба используют очень похожие макеты (написано теми же людьми). Также имеется реализация в D8ugger8 в CL debugger..

Local<String> source_text = String::NewFromUtf8(
    isolate, "import 'some thing'; 1 + 1");

ScriptOrigin origin(String::NewFromUtf8("main.mjs"),      // specifier
                    Integer::New(isolate, 0),             // line offset
                    Integer::New(isolate, 0),             // column offset
                    False(isolate),                       // is cross origin
                    Local<Integer>(),                     // script id
                    Local<Value>(),                       // source map URL
                    False(isolate),                       // is opaque
                    False(isolate),                       // is WASM
                    True(isolate));                       // is ES6 module
Context::Scope context_scope(context);
ScriptCompiler::Source source(source_text, origin);
Local<Module> module;
if (!ScriptCompiler::CompileModule(isolate, &source).ToLocal(&module)) {
  // if you have a v8::TryCatch, you should check it here.
  return;
}

// You can resolve import requests ahead of time (useful for async)
for (int i = 0; i < module->GetModuleRequestsLength(); i++) {
  Local<String> specifier = module->GetModuleRequest(i); // "some thing"
}

// or you can resolve them sync in the InstantiateModule callback
module->InstantiateModule(context, [](Local<Context> context, // "main.mjs"
                                      Local<String> specifier, // "some thing"
                                      Local<Module> referrer) {
  return Local<Module>();
});

// setting this callback enables dynamic import
isolate->SetImportModuleDynamicallyCallback([](Local<Context> context,
                                               Local<ScriptOrModule> referrer,
                                               Local<String> specifier) {
  return MaybeLocal<Promise>();
});

// setting this callback enables import.meta
isolate->SetHostInitializeImportMetaObjectCallback([](Local<Context> context,
                                                      Local<Module> module,
                                                      Local<Object> meta) {
  // meta->Set(key, value); you could set import.meta.url here
});

Local<Value> result;
if (module->Evaluate(context).ToLocal(&result)) {
  String::Utf8Value utf8(isolate, result);
  printf("module eval result: %s\n", *utf8);
} else {
  // once again, if you have a v8::TryCatch, use it here.
}
person snek    schedule 27.08.2018
comment
Как бы вы расширили свой пример для поддержки нескольких модулей, которые импортируют и экспортируют друг друга? Конечно, также будет один основной модуль точки входа. - person synchronizer; 31.12.2019
comment
@synchronizer, вы должны вернуть запрошенные модули из InstantiateModule. - person snek; 02.01.2020
comment
Вы не просто загружаете одновременно массив файлов или что-то в этом роде? Вы имеете в виду, что мне нужно самому выяснить зависимости? - person synchronizer; 02.01.2020
comment
Спецификаторы отображения @synchronizer в модули полностью оставлены на усмотрение хоста. Вы несете ответственность за решение, как разрешать запросы на импорт, откуда загружается источник и т. Д. - person snek; 02.01.2020
comment
@snek Я не уверен, что понимаю, почему сценарий должен быть скомпилирован как модуль, если семантика должна быть другой? - person user64204; 12.07.2020
comment
@ user64204 Я не понимаю, о чем вы спрашиваете. - person snek; 13.07.2020
comment
@snek смотрит на интерфейс v8 для загрузки модуля. Кажется, что любой сценарий, использующий import, является модулем. Т.е. он должен выполняться один раз за время выполнения изолята. Теперь я пытаюсь понять, как парадигма «один раз выполнить несколько раз» работает в новом мире, основанном на модулях. И похоже, что это вообще не работает. Вы больше не можете скомпилировать функцию синхронизации, которая использует библиотеку. Вся спецификация непригодна для использования в мире встраивающего устройства ... - person user64204; 15.07.2020
comment
@ user64204 устройство для внедрения явно выбирает, является ли данный исходный текст сценарием или модулем. Видите вызов ScriptCompiler :: CompileModule () в моем примере кода? если бы я хотел запустить скрипт, я бы вместо этого использовал ScriptCompiler :: Compile (). - person snek; 16.07.2020
comment
@snek Ага, но import нельзя использовать в скрипте. Это означает, что либо все является модулем (запускается один раз, подходит для длительного процесса, например, сервера), либо вам нужно придерживаться require(), либо использовать import(), который является просто уродливым псевдонимом, предназначенным только для асинхронного программирования, который требует ... - person user64204; 17.07.2020
comment
Возможно, я слепой, но могу ли я попросить помощи о том, как на самом деле разрешить import запрос? Вы перебираете все спецификаторы в ModuleRequests, но я не могу понять, что мне делать дальше. Как мне сообщить V8, что этот модуль должен быть связан с этим спецификатором? - person Cisco Ortega; 26.07.2021