@next2d/builder 0.1.8 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.ts +59 -0
- package/dist/cli.js +141 -0
- package/dist/constants.d.ts +53 -0
- package/dist/constants.js +64 -0
- package/dist/context.d.ts +29 -0
- package/dist/context.js +19 -0
- package/dist/electron.d.ts +10 -0
- package/dist/electron.js +142 -0
- package/dist/index.js +18 -790
- package/dist/native.d.ts +27 -0
- package/dist/native.js +123 -0
- package/dist/utils.d.ts +27 -0
- package/dist/utils.js +40 -0
- package/dist/web.d.ts +18 -0
- package/dist/web.js +70 -0
- package/dist/xbox.d.ts +62 -0
- package/dist/xbox.js +426 -0
- package/package.json +11 -9
- package/templates/xbox/CMakeLists.txt +47 -11
- package/templates/xbox/README.md +18 -0
- package/templates/xbox/js/bootstrap.js +120 -0
- package/templates/xbox/src/AssetLoader.cpp +115 -12
- package/templates/xbox/src/AssetLoader.h +4 -0
- package/templates/xbox/src/EmbeddedAssets.cpp +149 -0
- package/templates/xbox/src/EmbeddedAssets.h +60 -0
- package/templates/xbox/src/EventLoop.cpp +0 -15
- package/templates/xbox/src/bindings/Audio.cpp +118 -22
- package/templates/xbox/src/bindings/Canvas.cpp +32 -2
- package/templates/xbox/src/bindings/Canvas2D.cpp +495 -39
- package/templates/xbox/src/bindings/Console.cpp +6 -1
- package/templates/xbox/src/bindings/DomShims.cpp +18 -2
- package/templates/xbox/src/bindings/EventTarget.cpp +9 -0
- package/templates/xbox/src/bindings/Image.cpp +83 -57
- package/templates/xbox/src/bindings/Polyfills.cpp +671 -236
- package/templates/xbox/src/bindings/RasterCore.h +105 -1
- package/templates/xbox/src/bindings/Video.cpp +57 -3
- package/templates/xbox/src/bindings/webgpu/WebGPU.cpp +29 -3
- package/templates/xbox/src/bindings/webgpu/WebGPUCommon.h +4 -1
- package/templates/xbox/src/bindings/webgpu/WebGPUEnums.h +32 -0
- package/templates/xbox/src/gpu/DawnContext.cpp +167 -16
- package/templates/xbox/src/main.cpp +385 -16
- package/templates/xbox/src/platform/AudioDecoder.cpp +121 -0
- package/templates/xbox/src/platform/AudioDecoder.h +18 -0
- package/templates/xbox/src/platform/AudioEngine.cpp +28 -3
- package/templates/xbox/src/platform/DecodeQueue.cpp +136 -0
- package/templates/xbox/src/platform/DecodeQueue.h +27 -0
- package/templates/xbox/src/platform/ImageDecoder.cpp +44 -0
- package/templates/xbox/src/platform/ImageDecoder.h +18 -0
- package/templates/xbox/src/platform/StbTextRasterizer.cpp +324 -0
- package/templates/xbox/src/platform/StbTextRasterizer.h +37 -0
- package/templates/xbox/src/platform/TextRasterizer.cpp +32 -0
- package/templates/xbox/src/platform/WicDecoder.cpp +19 -0
- package/templates/xbox/src/v8/CodeCache.h +223 -0
- package/templates/xbox/src/v8/V8Runtime.cpp +34 -33
- package/templates/xbox/src/v8/V8Util.h +54 -4
- package/templates/xbox/src/worker/WorkerRuntime.cpp +165 -65
- package/templates/xbox/src/worker/WorkerRuntime.h +23 -3
- package/templates/xbox/tests/check_local.sh +5 -1
- package/templates/xbox/tests/gamecore_api_probe.cpp +172 -0
- package/templates/xbox/tests/raster_test.cpp +50 -0
- package/templates/xbox/tests/windows_platform_test.cpp +75 -3
- package/templates/xbox/third_party/LICENSES.md +14 -0
- package/templates/xbox/third_party/dr_mp3.h +5412 -0
- package/templates/xbox/third_party/dr_wav.h +9131 -0
- package/templates/xbox/third_party/stb_image.h +7988 -0
- package/templates/xbox/third_party/stb_truetype.h +5079 -0
- package/templates/xbox/third_party/stb_vorbis.c +5584 -0
|
@@ -7,9 +7,11 @@
|
|
|
7
7
|
#include "bindings/ImageSource.h"
|
|
8
8
|
#include "platform/ImageTypes.h"
|
|
9
9
|
#include "v8/V8Util.h"
|
|
10
|
+
#include "v8/CodeCache.h"
|
|
10
11
|
|
|
11
12
|
#include <cstdlib>
|
|
12
13
|
#include <cstring>
|
|
14
|
+
#include <filesystem>
|
|
13
15
|
#include <iostream>
|
|
14
16
|
|
|
15
17
|
namespace next2d {
|
|
@@ -117,15 +119,6 @@ bool IsImageBitmapObject(v8::Isolate* isolate, v8::Local<v8::Object> obj)
|
|
|
117
119
|
constexpr uint32_t kHostObjectOffscreenCanvas = 1;
|
|
118
120
|
constexpr uint32_t kHostObjectImageBitmap = 2;
|
|
119
121
|
|
|
120
|
-
// メッセージトレース: 定常時のログ洪水を防ぐため、最初の 120 件のあとは
|
|
121
|
-
// 200 件ごとに 1 回だけ出力する (render は 60fps で双方向に流れる)。
|
|
122
|
-
bool TraceMessage()
|
|
123
|
-
{
|
|
124
|
-
static uint64_t count = 0;
|
|
125
|
-
++count;
|
|
126
|
-
return count <= 120 || count % 200 == 0;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
122
|
class SerializerDelegate : public v8::ValueSerializer::Delegate {
|
|
130
123
|
public:
|
|
131
124
|
explicit SerializerDelegate(v8::Isolate* isolate) : isolate_(isolate) {}
|
|
@@ -361,11 +354,35 @@ void AddEventListener(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
|
361
354
|
// ===========================================================================
|
|
362
355
|
// WorkerInstance
|
|
363
356
|
// ===========================================================================
|
|
364
|
-
WorkerInstance::WorkerInstance(WorkerRuntime* runtime, v8::Isolate* isolate, std::string url
|
|
365
|
-
|
|
357
|
+
WorkerInstance::WorkerInstance(WorkerRuntime* runtime, v8::Isolate* isolate, std::string url,
|
|
358
|
+
bool is_module)
|
|
359
|
+
: runtime_(runtime), isolate_(isolate), url_(std::move(url)), module_(is_module)
|
|
366
360
|
{
|
|
367
361
|
}
|
|
368
362
|
|
|
363
|
+
namespace {
|
|
364
|
+
|
|
365
|
+
// import 指定子を referrer の URL からの相対で解決する。
|
|
366
|
+
// data:/blob: の referrer(インライン worker)は基準ディレクトリを持たないため
|
|
367
|
+
// 指定子をそのまま返す(assets 参照は失敗するが、インライン ESM は import を持たない)。
|
|
368
|
+
std::string ResolveModulePath(const std::string& referrer_url, const std::string& specifier)
|
|
369
|
+
{
|
|
370
|
+
// 絶対 URL / bare specifier はそのまま
|
|
371
|
+
if (specifier.rfind("/", 0) == 0 ||
|
|
372
|
+
specifier.rfind("http:", 0) == 0 || specifier.rfind("https:", 0) == 0 ||
|
|
373
|
+
specifier.rfind("data:", 0) == 0 || specifier.rfind("blob:", 0) == 0) {
|
|
374
|
+
return specifier;
|
|
375
|
+
}
|
|
376
|
+
if (referrer_url.rfind("data:", 0) == 0 || referrer_url.rfind("blob:", 0) == 0) {
|
|
377
|
+
return specifier;
|
|
378
|
+
}
|
|
379
|
+
namespace fs = std::filesystem;
|
|
380
|
+
const fs::path dir = fs::path(referrer_url).parent_path();
|
|
381
|
+
return (dir / specifier).lexically_normal().generic_string();
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
} // namespace
|
|
385
|
+
|
|
369
386
|
WorkerInstance::~WorkerInstance() = default;
|
|
370
387
|
|
|
371
388
|
v8::Local<v8::Context> WorkerInstance::context() const
|
|
@@ -383,10 +400,6 @@ void WorkerSelfPostMessage(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
|
383
400
|
v8::Local<v8::Value> msg = args.Length() > 0 ? args[0] : v8::Undefined(isolate).As<v8::Value>();
|
|
384
401
|
v8::Local<v8::Value> transfer = args.Length() > 1 ? args[1] : v8::Undefined(isolate).As<v8::Value>();
|
|
385
402
|
auto message = SerializeMessage(isolate, isolate->GetCurrentContext(), msg, transfer);
|
|
386
|
-
if (TraceMessage()) {
|
|
387
|
-
std::cerr << "[Worker] worker->main postMessage: " << message.data.size()
|
|
388
|
-
<< " bytes (+" << message.transfers.size() << " transfers)" << std::endl;
|
|
389
|
-
}
|
|
390
403
|
if (!message.data.empty()) {
|
|
391
404
|
instance->PostToMain(std::move(message));
|
|
392
405
|
}
|
|
@@ -420,10 +433,6 @@ void MainWorkerPostMessage(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
|
420
433
|
v8::Local<v8::Value> msg = args.Length() > 0 ? args[0] : v8::Undefined(isolate).As<v8::Value>();
|
|
421
434
|
v8::Local<v8::Value> transfer = args.Length() > 1 ? args[1] : v8::Undefined(isolate).As<v8::Value>();
|
|
422
435
|
auto message = SerializeMessage(isolate, isolate->GetCurrentContext(), msg, transfer);
|
|
423
|
-
if (TraceMessage()) {
|
|
424
|
-
std::cerr << "[Worker] main->worker postMessage: " << message.data.size()
|
|
425
|
-
<< " bytes (+" << message.transfers.size() << " transfers)" << std::endl;
|
|
426
|
-
}
|
|
427
436
|
if (!message.data.empty()) {
|
|
428
437
|
instance->PostToWorker(std::move(message));
|
|
429
438
|
}
|
|
@@ -433,23 +442,22 @@ void MainWorkerPostMessage(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
|
433
442
|
|
|
434
443
|
bool WorkerInstance::Start()
|
|
435
444
|
{
|
|
436
|
-
std::cerr << "[Worker] start: " << ShortUrl(url_) << std::endl;
|
|
437
445
|
v8::HandleScope handle_scope(isolate_);
|
|
438
446
|
|
|
439
447
|
v8::Local<v8::Context> context = v8::Context::New(isolate_);
|
|
440
448
|
context_.Reset(isolate_, context);
|
|
441
|
-
std::cerr << "[Worker] context created" << std::endl;
|
|
442
449
|
|
|
443
450
|
// worker 専用 EventLoop を Context の embedder slot に登録
|
|
444
451
|
loop_ = std::make_unique<EventLoop>(isolate_);
|
|
445
452
|
context->SetAlignedPointerInEmbedderData(kEventLoopEmbedderSlot, loop_.get());
|
|
453
|
+
// ESM の ResolveModule コールバックが context から自身を引けるように登録
|
|
454
|
+
context->SetAlignedPointerInEmbedderData(kWorkerInstanceEmbedderSlot, this);
|
|
446
455
|
|
|
447
456
|
v8::Context::Scope context_scope(context);
|
|
448
457
|
v8::Local<v8::Object> global = context->Global();
|
|
449
458
|
|
|
450
459
|
// ブラウザ相当環境一式 (console/timers/fetch/image/audio/webgpu/canvas/dom)
|
|
451
460
|
InstallGlobalBindings(isolate_, global, HostContext::From(isolate_));
|
|
452
|
-
std::cerr << "[Worker] bindings installed" << std::endl;
|
|
453
461
|
|
|
454
462
|
// worker スコープには document/window を出さない (ライブラリの環境判定対策)。
|
|
455
463
|
global->Delete(context, Str(isolate_, "document")).Check();
|
|
@@ -467,7 +475,7 @@ bool WorkerInstance::Start()
|
|
|
467
475
|
// 入れ子 Worker を許可 (ZlibInflate 等)。
|
|
468
476
|
runtime_->InstallOnGlobal(global);
|
|
469
477
|
|
|
470
|
-
//
|
|
478
|
+
// ワーカースクリプトを読み込む (クラシック / ESM は module_ で分岐)。
|
|
471
479
|
// Vite の `?worker&inline` は data:application/javascript;base64,... を渡す
|
|
472
480
|
// (バージョンによっては blob: URL)。data: / blob: / assets の順で解決する。
|
|
473
481
|
HostContext* host = HostContext::From(isolate_);
|
|
@@ -501,37 +509,23 @@ bool WorkerInstance::Start()
|
|
|
501
509
|
}
|
|
502
510
|
source = std::move(*src);
|
|
503
511
|
}
|
|
504
|
-
std::cerr << "[Worker] source resolved (" << source.size() << " bytes)" << std::endl;
|
|
505
512
|
|
|
506
|
-
//
|
|
507
|
-
|
|
508
|
-
|
|
513
|
+
// ESM worker (new Worker(url, { type: "module" }))。モジュールとして評価する。
|
|
514
|
+
if (module_) {
|
|
515
|
+
return EvaluateModule(context, source);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// «自己修復» CommandController.execute() は try/catch を持たないため、描画中の例外が
|
|
519
|
+
// 1 度起きると this.state が "active" のまま固まり、以後 onmessage は queue に積むだけで
|
|
520
|
+
// execute を再開せず worker が永久停止する。元の execute をリネームし、例外を捕捉して
|
|
521
|
+
// state を戻すラッパを被せて凍結を防ぐ (例外は [rendererr] で出力)。安全網として残す。
|
|
509
522
|
if (source.find("getCurrentTexture") != std::string::npos) {
|
|
510
523
|
const auto patch = [&source](const std::string& from, const std::string& to) {
|
|
511
524
|
const auto pos = source.find(from);
|
|
512
525
|
if (pos != std::string::npos) {
|
|
513
526
|
source.replace(pos, from.size(), to);
|
|
514
|
-
} else {
|
|
515
|
-
std::cerr << "[Worker] trace patch not applied: " << from.substr(0, 40) << std::endl;
|
|
516
527
|
}
|
|
517
528
|
};
|
|
518
|
-
source.insert(0,
|
|
519
|
-
"var __n2dT={};function __n2dTrace(k,s){var n=(__n2dT[k]=(__n2dT[k]||0)+1);"
|
|
520
|
-
"if(n<=30||n%300===0)console.info(\"[trace]\",k,\"#\"+n,s||\"\")}\n");
|
|
521
|
-
patch("clearTransferBounds(){this.beginFrame()}",
|
|
522
|
-
"clearTransferBounds(){__n2dTrace(\"CTB\",\"fs=\"+this.frameStarted+\" mt=\"+!!this.mainTexture);this.beginFrame()}");
|
|
523
|
-
patch("ensureMainTexture(){this.mainTexture||",
|
|
524
|
-
"ensureMainTexture(){__n2dTrace(\"EMT\",\"mt=\"+!!this.mainTexture+\" rc=\"+this.$needsReconfigure);this.mainTexture||");
|
|
525
|
-
patch("endFrame(){if(this.frameStarted){",
|
|
526
|
-
"endFrame(){__n2dTrace(\"EF\",\"fs=\"+this.frameStarted+\" enc=\"+!!this.commandEncoder);if(this.frameStarted){");
|
|
527
|
-
patch("transferMainCanvas(){if(!this.$mainAttachmentObject||!this.$mainAttachmentObject.texture)",
|
|
528
|
-
"transferMainCanvas(){__n2dTrace(\"TMC\",\"att=\"+!!(this.$mainAttachmentObject&&this.$mainAttachmentObject.texture));if(!this.$mainAttachmentObject||!this.$mainAttachmentObject.texture)");
|
|
529
|
-
// «診断/自己修復» CommandController.execute() は try/catch を持たないため、
|
|
530
|
-
// 描画中の例外が 1 度起きると this.state が "active" のまま固まり、
|
|
531
|
-
// 以後 onmessage は queue に積むだけで execute を再開せず worker が永久停止する
|
|
532
|
-
// (画面が ~15 フレームで凍結する現象の直接原因)。元の execute をリネームし、
|
|
533
|
-
// 例外を捕捉して message+stack を出力しつつ state を戻すラッパを被せる。
|
|
534
|
-
// これで「実際に何が throw しているか」を CI ログで確定でき、かつ凍結も防げる。
|
|
535
529
|
patch("async execute(){for(this.state=\"active\"",
|
|
536
530
|
"async execute(){try{await this.__n2dExec()}catch(__ee){"
|
|
537
531
|
"var __n=(globalThis.__rerr=(globalThis.__rerr||0)+1);"
|
|
@@ -542,8 +536,10 @@ bool WorkerInstance::Start()
|
|
|
542
536
|
v8::TryCatch tc(isolate_);
|
|
543
537
|
v8::ScriptOrigin origin(Str(isolate_, url_));
|
|
544
538
|
v8::Local<v8::String> code = Str(isolate_, source);
|
|
539
|
+
// バイトコードキャッシュ込みでコンパイル。レンダラ worker (player の描画バンドル)
|
|
540
|
+
// が最大のパース対象。キーは自己修復パッチ適用後のソースなので整合する。
|
|
545
541
|
v8::Local<v8::Script> script;
|
|
546
|
-
if (!
|
|
542
|
+
if (!codecache::Compile(context, code, origin, url_, source).ToLocal(&script) ||
|
|
547
543
|
script->Run(context).IsEmpty()) {
|
|
548
544
|
std::cerr << "[Worker] script eval failed: " << ShortUrl(url_) << std::endl;
|
|
549
545
|
if (tc.HasCaught()) {
|
|
@@ -560,7 +556,115 @@ bool WorkerInstance::Start()
|
|
|
560
556
|
// NOTE: ここで PerformMicrotaskCheckpoint は呼ばない。Start は JS コールバック
|
|
561
557
|
// (new Worker) の最中に呼ばれるため、JS 実行中のチェックポイントは V8 の規約違反。
|
|
562
558
|
// マイクロタスクはメインループの PumpMicrotasks で処理される。
|
|
563
|
-
|
|
559
|
+
return true;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// --- ESM worker ------------------------------------------------------------
|
|
563
|
+
// 依存モジュールを assets から読み、コンパイルして再帰ロードする。
|
|
564
|
+
v8::MaybeLocal<v8::Module> WorkerInstance::LoadModule(const std::string& url)
|
|
565
|
+
{
|
|
566
|
+
auto cached = modules_.find(url);
|
|
567
|
+
if (cached != modules_.end()) {
|
|
568
|
+
return cached->second.Get(isolate_);
|
|
569
|
+
}
|
|
570
|
+
HostContext* host = HostContext::From(isolate_);
|
|
571
|
+
auto src = host->assets->ReadText(url);
|
|
572
|
+
if (!src) {
|
|
573
|
+
std::cerr << "[Worker] module not found: " << ShortUrl(url) << std::endl;
|
|
574
|
+
return v8::MaybeLocal<v8::Module>();
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
v8::ScriptOrigin origin(Str(isolate_, url), 0, 0, false, -1,
|
|
578
|
+
v8::Local<v8::Value>(), false, false, /*is_module*/ true);
|
|
579
|
+
v8::Local<v8::Module> module;
|
|
580
|
+
if (!codecache::CompileModule(isolate_, Str(isolate_, *src), origin, url, *src)
|
|
581
|
+
.ToLocal(&module)) {
|
|
582
|
+
std::cerr << "[Worker] module compile failed: " << ShortUrl(url) << std::endl;
|
|
583
|
+
return v8::MaybeLocal<v8::Module>();
|
|
584
|
+
}
|
|
585
|
+
modules_[url].Reset(isolate_, module);
|
|
586
|
+
module_paths_[module->GetIdentityHash()] = url;
|
|
587
|
+
|
|
588
|
+
v8::Local<v8::Context> ctx = context();
|
|
589
|
+
v8::Local<v8::FixedArray> requests = module->GetModuleRequests();
|
|
590
|
+
for (int i = 0; i < requests->Length(); ++i) {
|
|
591
|
+
v8::Local<v8::ModuleRequest> request =
|
|
592
|
+
requests->Get(ctx, i).As<v8::ModuleRequest>();
|
|
593
|
+
const std::string specifier = ToStdString(isolate_, request->GetSpecifier());
|
|
594
|
+
if (LoadModule(ResolveModulePath(url, specifier)).IsEmpty()) {
|
|
595
|
+
return v8::MaybeLocal<v8::Module>();
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
return module;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
v8::MaybeLocal<v8::Module> WorkerInstance::ResolveModule(
|
|
602
|
+
v8::Local<v8::Context> context,
|
|
603
|
+
v8::Local<v8::String> specifier,
|
|
604
|
+
v8::Local<v8::FixedArray> /*import_attributes*/,
|
|
605
|
+
v8::Local<v8::Module> referrer)
|
|
606
|
+
{
|
|
607
|
+
v8::Isolate* isolate = context->GetIsolate();
|
|
608
|
+
auto* self = static_cast<WorkerInstance*>(
|
|
609
|
+
context->GetAlignedPointerFromEmbedderData(kWorkerInstanceEmbedderSlot));
|
|
610
|
+
if (!self) {
|
|
611
|
+
return v8::MaybeLocal<v8::Module>();
|
|
612
|
+
}
|
|
613
|
+
auto it = self->module_paths_.find(referrer->GetIdentityHash());
|
|
614
|
+
const std::string referrer_url =
|
|
615
|
+
(it != self->module_paths_.end()) ? it->second : self->url_;
|
|
616
|
+
const std::string spec = ToStdString(isolate, specifier);
|
|
617
|
+
return self->LoadModule(ResolveModulePath(referrer_url, spec));
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
bool WorkerInstance::EvaluateModule(v8::Local<v8::Context> context, const std::string& source)
|
|
621
|
+
{
|
|
622
|
+
v8::TryCatch tc(isolate_);
|
|
623
|
+
|
|
624
|
+
// 入口モジュールはインライン(data:/blob:)の可能性があるため、渡された source を
|
|
625
|
+
// 直接コンパイルする(assets 経由の LoadModule は依存モジュール用)。
|
|
626
|
+
v8::ScriptOrigin origin(Str(isolate_, url_), 0, 0, false, -1,
|
|
627
|
+
v8::Local<v8::Value>(), false, false, /*is_module*/ true);
|
|
628
|
+
v8::Local<v8::Module> module;
|
|
629
|
+
if (!codecache::CompileModule(isolate_, Str(isolate_, source), origin, url_, source)
|
|
630
|
+
.ToLocal(&module)) {
|
|
631
|
+
std::cerr << "[Worker] module compile failed: " << ShortUrl(url_) << std::endl;
|
|
632
|
+
return false;
|
|
633
|
+
}
|
|
634
|
+
modules_[url_].Reset(isolate_, module);
|
|
635
|
+
module_paths_[module->GetIdentityHash()] = url_;
|
|
636
|
+
|
|
637
|
+
// 静的 import を再帰ロード(インライン worker は import 無しでスキップ)。
|
|
638
|
+
v8::Local<v8::FixedArray> requests = module->GetModuleRequests();
|
|
639
|
+
for (int i = 0; i < requests->Length(); ++i) {
|
|
640
|
+
v8::Local<v8::ModuleRequest> request =
|
|
641
|
+
requests->Get(context, i).As<v8::ModuleRequest>();
|
|
642
|
+
const std::string specifier = ToStdString(isolate_, request->GetSpecifier());
|
|
643
|
+
if (LoadModule(ResolveModulePath(url_, specifier)).IsEmpty()) {
|
|
644
|
+
return false;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
if (module->InstantiateModule(context, ResolveModule).IsNothing()) {
|
|
649
|
+
std::cerr << "[Worker] module instantiate failed: " << ShortUrl(url_) << std::endl;
|
|
650
|
+
if (tc.HasCaught()) {
|
|
651
|
+
v8::String::Utf8Value ex(isolate_, tc.Exception());
|
|
652
|
+
std::cerr << "[Worker] exception: " << (*ex ? *ex : "?") << std::endl;
|
|
653
|
+
}
|
|
654
|
+
return false;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// NOTE: Start と同じく PerformMicrotaskCheckpoint は呼ばない(new Worker の JS 実行中)。
|
|
658
|
+
// トップレベル await が返す Promise はメインループの PumpMicrotasks で解決される。
|
|
659
|
+
v8::Local<v8::Value> result;
|
|
660
|
+
if (!module->Evaluate(context).ToLocal(&result)) {
|
|
661
|
+
std::cerr << "[Worker] module evaluate failed: " << ShortUrl(url_) << std::endl;
|
|
662
|
+
if (tc.HasCaught()) {
|
|
663
|
+
v8::String::Utf8Value ex(isolate_, tc.Exception());
|
|
664
|
+
std::cerr << "[Worker] exception: " << (*ex ? *ex : "?") << std::endl;
|
|
665
|
+
}
|
|
666
|
+
return false;
|
|
667
|
+
}
|
|
564
668
|
return true;
|
|
565
669
|
}
|
|
566
670
|
|
|
@@ -585,16 +689,9 @@ void WorkerInstance::DeliverToWorker(double now_ms)
|
|
|
585
689
|
while (!to_worker_.empty()) {
|
|
586
690
|
WorkerMessage m = std::move(to_worker_.front());
|
|
587
691
|
to_worker_.pop_front();
|
|
588
|
-
const bool trace = TraceMessage();
|
|
589
|
-
if (trace) {
|
|
590
|
-
std::cerr << "[Worker] deliver main->worker (" << m.data.size() << " bytes)" << std::endl;
|
|
591
|
-
}
|
|
592
692
|
v8::Local<v8::Value> data;
|
|
593
693
|
if (DeserializeMessage(isolate_, ctx, m).ToLocal(&data)) {
|
|
594
694
|
DispatchMessage(isolate_, ctx, ctx->Global(), data);
|
|
595
|
-
if (trace) {
|
|
596
|
-
std::cerr << "[Worker] worker onmessage done" << std::endl;
|
|
597
|
-
}
|
|
598
695
|
} else {
|
|
599
696
|
std::cerr << "[Worker] main->worker deserialize failed ("
|
|
600
697
|
<< m.data.size() << " bytes)" << std::endl;
|
|
@@ -619,16 +716,9 @@ void WorkerInstance::DeliverToMain()
|
|
|
619
716
|
while (!to_main_.empty()) {
|
|
620
717
|
WorkerMessage m = std::move(to_main_.front());
|
|
621
718
|
to_main_.pop_front();
|
|
622
|
-
const bool trace = TraceMessage();
|
|
623
|
-
if (trace) {
|
|
624
|
-
std::cerr << "[Worker] deliver worker->main (" << m.data.size() << " bytes)" << std::endl;
|
|
625
|
-
}
|
|
626
719
|
v8::Local<v8::Value> data;
|
|
627
720
|
if (DeserializeMessage(isolate_, main_ctx, m).ToLocal(&data)) {
|
|
628
721
|
DispatchMessage(isolate_, main_ctx, worker_obj, data);
|
|
629
|
-
if (trace) {
|
|
630
|
-
std::cerr << "[Worker] main onmessage done" << std::endl;
|
|
631
|
-
}
|
|
632
722
|
} else {
|
|
633
723
|
std::cerr << "[Worker] worker->main deserialize failed ("
|
|
634
724
|
<< m.data.size() << " bytes)" << std::endl;
|
|
@@ -652,11 +742,11 @@ WorkerRuntime::WorkerRuntime(v8::Isolate* isolate, HostContext* host)
|
|
|
652
742
|
|
|
653
743
|
WorkerRuntime::~WorkerRuntime() = default;
|
|
654
744
|
|
|
655
|
-
v8::Local<v8::Object> WorkerRuntime::CreateWorker(const std::string& url)
|
|
745
|
+
v8::Local<v8::Object> WorkerRuntime::CreateWorker(const std::string& url, bool is_module)
|
|
656
746
|
{
|
|
657
747
|
v8::Local<v8::Context> ctx = isolate_->GetCurrentContext();
|
|
658
748
|
|
|
659
|
-
auto instance = std::make_unique<WorkerInstance>(this, isolate_, url);
|
|
749
|
+
auto instance = std::make_unique<WorkerInstance>(this, isolate_, url, is_module);
|
|
660
750
|
WorkerInstance* ptr = instance.get();
|
|
661
751
|
|
|
662
752
|
// main 側 Worker オブジェクト
|
|
@@ -702,7 +792,17 @@ void WorkerConstructor(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
|
702
792
|
// new Worker(new URL(...)) にも対応するため toString で URL 文字列化
|
|
703
793
|
url = ToStdString(isolate, args[0]);
|
|
704
794
|
}
|
|
705
|
-
|
|
795
|
+
// new Worker(url, { type: "module" }) → ESM として評価する
|
|
796
|
+
bool is_module = false;
|
|
797
|
+
if (args.Length() > 1 && args[1]->IsObject()) {
|
|
798
|
+
v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
|
|
799
|
+
v8::Local<v8::Value> type;
|
|
800
|
+
if (args[1].As<v8::Object>()->Get(ctx, Str(isolate, "type")).ToLocal(&type) &&
|
|
801
|
+
type->IsString()) {
|
|
802
|
+
is_module = (ToStdString(isolate, type) == "module");
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
args.GetReturnValue().Set(runtime->CreateWorker(url, is_module));
|
|
706
806
|
}
|
|
707
807
|
|
|
708
808
|
} // namespace
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
#include <cstdint>
|
|
17
17
|
#include <deque>
|
|
18
|
+
#include <map>
|
|
18
19
|
#include <memory>
|
|
19
20
|
#include <string>
|
|
20
21
|
#include <vector>
|
|
@@ -25,6 +26,10 @@ class HostContext;
|
|
|
25
26
|
class EventLoop;
|
|
26
27
|
class WorkerRuntime;
|
|
27
28
|
|
|
29
|
+
// worker context の embedder slot。1 は EventLoop (kEventLoopEmbedderSlot) が使用。
|
|
30
|
+
// ESM の ResolveModule コールバックが context から WorkerInstance を引くために使う。
|
|
31
|
+
constexpr int kWorkerInstanceEmbedderSlot = 2;
|
|
32
|
+
|
|
28
33
|
// 1 メッセージ = ValueSerializer 出力バイト列 + transfer された ArrayBuffer の実体。
|
|
29
34
|
// transfer_list の ArrayBuffer は中身を bytes に埋め込まず所有権ごと移す
|
|
30
35
|
// (render キューは数十 MB になるため、コピーするとフレームレートが崩壊する)。
|
|
@@ -35,7 +40,8 @@ struct WorkerMessage {
|
|
|
35
40
|
|
|
36
41
|
class WorkerInstance {
|
|
37
42
|
public:
|
|
38
|
-
WorkerInstance(WorkerRuntime* runtime, v8::Isolate* isolate, std::string url
|
|
43
|
+
WorkerInstance(WorkerRuntime* runtime, v8::Isolate* isolate, std::string url,
|
|
44
|
+
bool is_module = false);
|
|
39
45
|
~WorkerInstance();
|
|
40
46
|
|
|
41
47
|
bool Start(); // Context 生成 + ワーカースクリプト評価
|
|
@@ -62,14 +68,28 @@ private:
|
|
|
62
68
|
void DeliverToWorker(double now_ms);
|
|
63
69
|
void DeliverToMain();
|
|
64
70
|
|
|
71
|
+
// ESM worker (type: "module")。分類スクリプトは Script、モジュールは Module として評価。
|
|
72
|
+
bool EvaluateModule(v8::Local<v8::Context> context, const std::string& source);
|
|
73
|
+
v8::MaybeLocal<v8::Module> LoadModule(const std::string& url);
|
|
74
|
+
static v8::MaybeLocal<v8::Module> ResolveModule(
|
|
75
|
+
v8::Local<v8::Context> context,
|
|
76
|
+
v8::Local<v8::String> specifier,
|
|
77
|
+
v8::Local<v8::FixedArray> import_attributes,
|
|
78
|
+
v8::Local<v8::Module> referrer);
|
|
79
|
+
|
|
65
80
|
WorkerRuntime* runtime_;
|
|
66
81
|
v8::Isolate* isolate_;
|
|
67
82
|
std::string url_;
|
|
83
|
+
bool module_ = false;
|
|
68
84
|
v8::Global<v8::Context> context_;
|
|
69
85
|
std::unique_ptr<EventLoop> loop_;
|
|
70
86
|
std::deque<WorkerMessage> to_worker_;
|
|
71
87
|
std::deque<WorkerMessage> to_main_;
|
|
72
88
|
bool terminated_ = false;
|
|
89
|
+
|
|
90
|
+
// ESM 解決: url -> Module、Module identity hash -> url (referrer 特定用)。
|
|
91
|
+
std::map<std::string, v8::Global<v8::Module>> modules_;
|
|
92
|
+
std::map<int, std::string> module_paths_;
|
|
73
93
|
};
|
|
74
94
|
|
|
75
95
|
class WorkerRuntime {
|
|
@@ -83,8 +103,8 @@ public:
|
|
|
83
103
|
// main / worker グローバルに Worker コンストラクタ等を設置する。
|
|
84
104
|
void InstallOnGlobal(v8::Local<v8::Object> global);
|
|
85
105
|
|
|
86
|
-
// new Worker(url) の実体
|
|
87
|
-
v8::Local<v8::Object> CreateWorker(const std::string& url);
|
|
106
|
+
// new Worker(url, { type }) の実体
|
|
107
|
+
v8::Local<v8::Object> CreateWorker(const std::string& url, bool is_module = false);
|
|
88
108
|
|
|
89
109
|
// 全 worker のメッセージ配送とイベントループ前進
|
|
90
110
|
void Pump(double now_ms);
|
|
@@ -47,9 +47,13 @@ FILES=(
|
|
|
47
47
|
src/bindings/Canvas.cpp
|
|
48
48
|
src/bindings/Audio.cpp
|
|
49
49
|
src/bindings/Gamepad.cpp
|
|
50
|
+
src/platform/DecodeQueue.cpp
|
|
51
|
+
src/platform/AudioDecoder.cpp
|
|
52
|
+
src/platform/ImageDecoder.cpp
|
|
53
|
+
src/platform/StbTextRasterizer.cpp
|
|
50
54
|
)
|
|
51
55
|
for f in "${FILES[@]}"; do
|
|
52
|
-
c++ -fsyntax-only -std=c++20 -Isrc -isystem "${V8INC}" \
|
|
56
|
+
c++ -fsyntax-only -std=c++20 -Isrc -I. -isystem "${V8INC}" \
|
|
53
57
|
-DV8_COMPRESS_POINTERS "$f"
|
|
54
58
|
echo "OK $f"
|
|
55
59
|
done
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// Game Core (Xbox コンソール) API パーティション監査プローブ。
|
|
2
|
+
//
|
|
3
|
+
// Windows SDK (19041+) は各 API 宣言を WINAPI_PARTITION_* でガードしており、
|
|
4
|
+
// `cl /DWINAPI_FAMILY=WINAPI_FAMILY_GAMES` でコンパイルすると GAMES パーティションに
|
|
5
|
+
// 属さない API は「未宣言」エラーになる。= NDA 資料なしで、公開 SDK だけを使って
|
|
6
|
+
// 「コンソールで使えない API」を正確に列挙できる (プリプロセッサが公式のゲート
|
|
7
|
+
// ロジックそのものを評価するため、テキスト解析より確実)。
|
|
8
|
+
//
|
|
9
|
+
// 使い方 (xbox-host-ci の gamecore-api-audit ジョブが実行):
|
|
10
|
+
// cl /c /DWINAPI_FAMILY=WINAPI_FAMILY_GAMES tests\gamecore_api_probe.cpp
|
|
11
|
+
// -> ホストがコンソールで使う API の存在検証 (エラー = 直ちに修正が必要)
|
|
12
|
+
// cl /c /DWINAPI_FAMILY=WINAPI_FAMILY_GAMES /DPROBE_V8 tests\gamecore_api_probe.cpp
|
|
13
|
+
// -> V8 の platform 層 (src/base/platform/*-win32) が使う API の存在検証
|
|
14
|
+
// (エラー = V8 Game Core 移植時に置換が必要な TODO リスト。情報提供扱い)
|
|
15
|
+
//
|
|
16
|
+
// 注意: これは「宣言がパーティションに含まれるか」の静的検査。実機での挙動
|
|
17
|
+
// (リンク先 lib の有無・実行時の権限) は devkit での検証が別途必要。
|
|
18
|
+
//
|
|
19
|
+
// WIN32_LEAN_AND_MEAN は必須: GAMES ファミリではフル Windows.h が引き込む
|
|
20
|
+
// 周辺ヘッダ (mmsystem/ole2/winsock 等) に GAMES 非対応部分があり
|
|
21
|
+
// コンパイルが壊れる (vcpkg の Xbox トリプレットも同様の前提)。
|
|
22
|
+
// 必要なヘッダは個別に include する。
|
|
23
|
+
#define WIN32_LEAN_AND_MEAN
|
|
24
|
+
#define NOMINMAX
|
|
25
|
+
#include <Windows.h>
|
|
26
|
+
|
|
27
|
+
#include <objbase.h> // CoInitializeEx (DecodeQueue のスレッド COM 初期化)
|
|
28
|
+
#include <bcrypt.h> // BCryptGenRandom (crypto.getRandomValues)
|
|
29
|
+
#include <xaudio2.h> // XAudio2Create (音声出力)
|
|
30
|
+
|
|
31
|
+
#ifdef PROBE_V8
|
|
32
|
+
#include <timeapi.h> // timeGetTime (V8 time.cc — GAMES 外の想定 = TODO 検出対象)
|
|
33
|
+
#include <psapi.h> // GetProcessMemoryInfo (V8 OS::GetPeakMemoryUsageKb)
|
|
34
|
+
#include <process.h> // _beginthreadex (V8 Thread)
|
|
35
|
+
#endif
|
|
36
|
+
|
|
37
|
+
// 関数アドレスを取るだけの配列。宣言が無ければコンパイルエラーになる。
|
|
38
|
+
#define PROBE(fn) reinterpret_cast<void*>(&fn)
|
|
39
|
+
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// ホストがコンソールビルドでも使う API (NEXT2D_XBOX_CONSOLE ガード外のもの)。
|
|
42
|
+
// ここのエラーはホスト側の修正が必要 (ジョブを fail させる)。
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
void* const g_host_apis[] = {
|
|
45
|
+
// ウィンドウ / メッセージループ (main.cpp。GDK コンソールサンプルも同構成)
|
|
46
|
+
PROBE(RegisterClassExW),
|
|
47
|
+
PROBE(CreateWindowExW),
|
|
48
|
+
PROBE(DefWindowProcW),
|
|
49
|
+
PROBE(PeekMessageW),
|
|
50
|
+
PROBE(TranslateMessage),
|
|
51
|
+
PROBE(DispatchMessageW),
|
|
52
|
+
PROBE(PostQuitMessage),
|
|
53
|
+
PROBE(LoadCursorW),
|
|
54
|
+
PROBE(GetClientRect),
|
|
55
|
+
|
|
56
|
+
// 時刻 / ペーシング (EventLoop::Now, メインループ)
|
|
57
|
+
PROBE(QueryPerformanceCounter),
|
|
58
|
+
PROBE(QueryPerformanceFrequency),
|
|
59
|
+
PROBE(Sleep),
|
|
60
|
+
|
|
61
|
+
// モジュール / 環境 (ExeDir, DomShims の storage パス解決)
|
|
62
|
+
PROBE(GetModuleFileNameW),
|
|
63
|
+
PROBE(GetModuleHandleW),
|
|
64
|
+
PROBE(GetEnvironmentVariableW),
|
|
65
|
+
PROBE(GetLastError),
|
|
66
|
+
|
|
67
|
+
// COM (DecodeQueue プールスレッド。コンソールでは WIC/MF は使わないが
|
|
68
|
+
// CoInitializeEx 自体は呼ぶ)
|
|
69
|
+
PROBE(CoInitializeEx),
|
|
70
|
+
PROBE(CoUninitialize),
|
|
71
|
+
|
|
72
|
+
// 乱数 (crypto.getRandomValues)
|
|
73
|
+
PROBE(BCryptGenRandom),
|
|
74
|
+
|
|
75
|
+
// 音声 (XAudio2 はコンソールでは xaudio2_9)
|
|
76
|
+
PROBE(XAudio2Create),
|
|
77
|
+
|
|
78
|
+
// リソース (EmbeddedAssets の RCDATA 読み込み)
|
|
79
|
+
PROBE(FindResourceW),
|
|
80
|
+
PROBE(LoadResource),
|
|
81
|
+
PROBE(LockResource),
|
|
82
|
+
PROBE(SizeofResource),
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
#ifdef PROBE_V8
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
// V8 13.7 の platform 層 (src/base/platform/{platform-win32,time,mutex,
|
|
88
|
+
// condition-variable,semaphore}.cc) から機械抽出した Win32 API。
|
|
89
|
+
// ここのエラー = V8 Game Core 移植時の置換 TODO (情報提供、ジョブは fail しない)。
|
|
90
|
+
// ---------------------------------------------------------------------------
|
|
91
|
+
void* const g_v8_apis[] = {
|
|
92
|
+
// 仮想メモリ (焦点: コンソールでは XMemVirtualAlloc への置換が推奨とされる)
|
|
93
|
+
PROBE(VirtualAlloc),
|
|
94
|
+
PROBE(VirtualFree),
|
|
95
|
+
PROBE(VirtualProtect),
|
|
96
|
+
PROBE(VirtualQuery),
|
|
97
|
+
PROBE(DiscardVirtualMemory),
|
|
98
|
+
// VirtualAlloc2 / MapViewOfFile3 / UnmapViewOfFile2 は V8 が GetProcAddress で
|
|
99
|
+
// 動的解決するが、宣言の有無がパーティション対応の指標になるため含める
|
|
100
|
+
PROBE(VirtualAlloc2),
|
|
101
|
+
PROBE(MapViewOfFile3),
|
|
102
|
+
PROBE(UnmapViewOfFile2),
|
|
103
|
+
|
|
104
|
+
// ファイルマッピング (OS::MemoryMappedFile)
|
|
105
|
+
PROBE(CreateFileMappingW),
|
|
106
|
+
PROBE(CreateFileW),
|
|
107
|
+
PROBE(MapViewOfFile),
|
|
108
|
+
PROBE(MapViewOfFileEx),
|
|
109
|
+
PROBE(UnmapViewOfFile),
|
|
110
|
+
PROBE(GetFileSize),
|
|
111
|
+
PROBE(GetFileType),
|
|
112
|
+
PROBE(DeleteFileA),
|
|
113
|
+
PROBE(GetTempPathA),
|
|
114
|
+
PROBE(GetTempFileNameA),
|
|
115
|
+
|
|
116
|
+
// スレッド / TLS / 同期
|
|
117
|
+
PROBE(GetCurrentThread),
|
|
118
|
+
PROBE(GetCurrentThreadId),
|
|
119
|
+
PROBE(SetThreadPriority),
|
|
120
|
+
PROBE(TlsAlloc),
|
|
121
|
+
PROBE(TlsFree),
|
|
122
|
+
PROBE(TlsGetValue),
|
|
123
|
+
PROBE(TlsSetValue),
|
|
124
|
+
PROBE(CreateSemaphoreA),
|
|
125
|
+
PROBE(ReleaseSemaphore),
|
|
126
|
+
PROBE(WaitForSingleObject),
|
|
127
|
+
PROBE(CloseHandle),
|
|
128
|
+
PROBE(CreateWaitableTimerExW),
|
|
129
|
+
PROBE(SetWaitableTimer),
|
|
130
|
+
|
|
131
|
+
// 時刻 (焦点: timeGetTime は winmm — デスクトップ専用の可能性が高い)
|
|
132
|
+
PROBE(timeGetTime),
|
|
133
|
+
PROBE(GetSystemTime),
|
|
134
|
+
PROBE(GetSystemTimeAsFileTime),
|
|
135
|
+
PROBE(SystemTimeToFileTime),
|
|
136
|
+
PROBE(GetThreadTimes),
|
|
137
|
+
PROBE(GetTimeZoneInformation),
|
|
138
|
+
|
|
139
|
+
// プロセス / システム情報
|
|
140
|
+
PROBE(GetCurrentProcess),
|
|
141
|
+
PROBE(GetCurrentProcessId),
|
|
142
|
+
PROBE(GetSystemInfo),
|
|
143
|
+
PROBE(ExitProcess),
|
|
144
|
+
PROBE(TerminateProcess),
|
|
145
|
+
PROBE(GetProcessMemoryInfo),
|
|
146
|
+
PROBE(SetErrorMode),
|
|
147
|
+
PROBE(GetStdHandle),
|
|
148
|
+
|
|
149
|
+
// 文字列変換 / デバッグ / モジュール
|
|
150
|
+
PROBE(MultiByteToWideChar),
|
|
151
|
+
PROBE(WideCharToMultiByte),
|
|
152
|
+
PROBE(OutputDebugStringA),
|
|
153
|
+
PROBE(DebugBreak),
|
|
154
|
+
PROBE(IsDebuggerPresent),
|
|
155
|
+
PROBE(LoadLibraryW),
|
|
156
|
+
PROBE(GetProcAddress),
|
|
157
|
+
PROBE(VerifyVersionInfoW), // IsWindows10OrGreater (versionhelpers) の実体
|
|
158
|
+
|
|
159
|
+
// CRT (参考: パーティション対象外のはずだが確認)
|
|
160
|
+
reinterpret_cast<void*>(&_beginthreadex),
|
|
161
|
+
};
|
|
162
|
+
#endif // PROBE_V8
|
|
163
|
+
|
|
164
|
+
// リンク不要 (cl /c のみ)。配列が最適化で消えないよう参照を残す。
|
|
165
|
+
void* GameCoreApiProbeAnchor()
|
|
166
|
+
{
|
|
167
|
+
void* acc = const_cast<void**>(g_host_apis)[0];
|
|
168
|
+
#ifdef PROBE_V8
|
|
169
|
+
acc = const_cast<void**>(g_v8_apis)[0];
|
|
170
|
+
#endif
|
|
171
|
+
return acc;
|
|
172
|
+
}
|