@next2d/builder 0.1.7 → 0.1.10

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.
Files changed (75) hide show
  1. package/dist/cli.d.ts +59 -0
  2. package/dist/cli.js +141 -0
  3. package/dist/constants.d.ts +53 -0
  4. package/dist/constants.js +64 -0
  5. package/dist/context.d.ts +29 -0
  6. package/dist/context.js +19 -0
  7. package/dist/electron.d.ts +10 -0
  8. package/dist/electron.js +142 -0
  9. package/dist/index.js +18 -754
  10. package/dist/native.d.ts +27 -0
  11. package/dist/native.js +123 -0
  12. package/dist/utils.d.ts +27 -0
  13. package/dist/utils.js +40 -0
  14. package/dist/web.d.ts +18 -0
  15. package/dist/web.js +70 -0
  16. package/dist/xbox.d.ts +62 -0
  17. package/dist/xbox.js +426 -0
  18. package/package.json +12 -9
  19. package/templates/xbox/CMakeLists.txt +116 -11
  20. package/templates/xbox/README.md +34 -8
  21. package/templates/xbox/cmake/FetchDawn.cmake +47 -6
  22. package/templates/xbox/cmake/FindV8.cmake +15 -0
  23. package/templates/xbox/js/bootstrap.js +151 -28
  24. package/templates/xbox/js/selftest.js +740 -90
  25. package/templates/xbox/src/AssetLoader.cpp +115 -12
  26. package/templates/xbox/src/AssetLoader.h +4 -0
  27. package/templates/xbox/src/EmbeddedAssets.cpp +149 -0
  28. package/templates/xbox/src/EmbeddedAssets.h +60 -0
  29. package/templates/xbox/src/EventLoop.cpp +13 -1
  30. package/templates/xbox/src/EventLoop.h +12 -0
  31. package/templates/xbox/src/bindings/Audio.cpp +211 -31
  32. package/templates/xbox/src/bindings/Bindings.h +10 -0
  33. package/templates/xbox/src/bindings/Canvas.cpp +77 -2
  34. package/templates/xbox/src/bindings/Canvas2D.cpp +496 -43
  35. package/templates/xbox/src/bindings/Console.cpp +6 -1
  36. package/templates/xbox/src/bindings/DomShims.cpp +143 -0
  37. package/templates/xbox/src/bindings/EventTarget.cpp +11 -0
  38. package/templates/xbox/src/bindings/Fetch.cpp +2 -8
  39. package/templates/xbox/src/bindings/Image.cpp +243 -15
  40. package/templates/xbox/src/bindings/Polyfills.cpp +1226 -0
  41. package/templates/xbox/src/bindings/RasterCore.h +105 -1
  42. package/templates/xbox/src/bindings/Video.cpp +153 -10
  43. package/templates/xbox/src/bindings/webgpu/WebGPU.cpp +844 -18
  44. package/templates/xbox/src/bindings/webgpu/WebGPUCommon.h +7 -9
  45. package/templates/xbox/src/bindings/webgpu/WebGPUEnums.h +32 -0
  46. package/templates/xbox/src/gpu/DawnContext.cpp +245 -7
  47. package/templates/xbox/src/gpu/DawnContext.h +18 -0
  48. package/templates/xbox/src/main.cpp +459 -8
  49. package/templates/xbox/src/platform/AudioDecoder.cpp +121 -0
  50. package/templates/xbox/src/platform/AudioDecoder.h +18 -0
  51. package/templates/xbox/src/platform/AudioEngine.cpp +37 -6
  52. package/templates/xbox/src/platform/DecodeQueue.cpp +136 -0
  53. package/templates/xbox/src/platform/DecodeQueue.h +27 -0
  54. package/templates/xbox/src/platform/ImageDecoder.cpp +44 -0
  55. package/templates/xbox/src/platform/ImageDecoder.h +18 -0
  56. package/templates/xbox/src/platform/StbTextRasterizer.cpp +324 -0
  57. package/templates/xbox/src/platform/StbTextRasterizer.h +37 -0
  58. package/templates/xbox/src/platform/TextRasterizer.cpp +32 -0
  59. package/templates/xbox/src/platform/WicDecoder.cpp +19 -0
  60. package/templates/xbox/src/v8/CodeCache.h +223 -0
  61. package/templates/xbox/src/v8/V8Runtime.cpp +76 -11
  62. package/templates/xbox/src/v8/V8Util.h +79 -0
  63. package/templates/xbox/src/v8/WeakHandle.h +45 -0
  64. package/templates/xbox/src/worker/WorkerRuntime.cpp +432 -48
  65. package/templates/xbox/src/worker/WorkerRuntime.h +43 -13
  66. package/templates/xbox/tests/check_local.sh +7 -2
  67. package/templates/xbox/tests/gamecore_api_probe.cpp +172 -0
  68. package/templates/xbox/tests/raster_test.cpp +50 -0
  69. package/templates/xbox/tests/windows_platform_test.cpp +75 -3
  70. package/templates/xbox/third_party/LICENSES.md +14 -0
  71. package/templates/xbox/third_party/dr_mp3.h +5412 -0
  72. package/templates/xbox/third_party/dr_wav.h +9131 -0
  73. package/templates/xbox/third_party/stb_image.h +7988 -0
  74. package/templates/xbox/third_party/stb_truetype.h +5079 -0
  75. package/templates/xbox/third_party/stb_vorbis.c +5584 -0
@@ -1,4 +1,5 @@
1
1
  #include "AssetLoader.h"
2
+ #include "EmbeddedAssets.h"
2
3
 
3
4
  #include <filesystem>
4
5
  #include <fstream>
@@ -14,9 +15,10 @@ AssetLoader::AssetLoader(std::string root)
14
15
  {
15
16
  }
16
17
 
17
- std::string AssetLoader::Resolve(const std::string& url) const
18
+ std::string AssetLoader::RelativeKey(const std::string& url) const
18
19
  {
19
- // 先頭の "/" や "./"、絶対 URL のオリジンを剥がして assets/app 基準に寄せる。
20
+ // 先頭の "/" や "./"、絶対 URL のオリジンを剥がして assets/app 基準の相対パスにする。
21
+ // 埋め込み pak のキー(フォワードスラッシュ相対)と一致させるためのもの。
20
22
  std::string path = url;
21
23
 
22
24
  // スキーム付き (file://, http://localhost/...) はパス部だけ取り出す
@@ -39,11 +41,101 @@ std::string AssetLoader::Resolve(const std::string& url) const
39
41
  path = path.substr(2);
40
42
  }
41
43
 
42
- return (fs::path(root_) / path).string();
44
+ // バックスラッシュはフォワードスラッシュへ正規化 (pak キーと一致させる)。
45
+ for (char& c : path) {
46
+ if (c == '\\') c = '/';
47
+ }
48
+ return path;
49
+ }
50
+
51
+ std::string AssetLoader::Resolve(const std::string& url) const
52
+ {
53
+ return (fs::path(root_) / RelativeKey(url)).string();
54
+ }
55
+
56
+ // base64 デコード (data: URI 用)。Image.cpp の同等実装と同じ仕様。
57
+ static bool DecodeBase64(const std::string& in, std::vector<uint8_t>* out)
58
+ {
59
+ auto val = [](char c) -> int {
60
+ if (c >= 'A' && c <= 'Z') return c - 'A';
61
+ if (c >= 'a' && c <= 'z') return c - 'a' + 26;
62
+ if (c >= '0' && c <= '9') return c - '0' + 52;
63
+ if (c == '+') return 62;
64
+ if (c == '/') return 63;
65
+ return -1;
66
+ };
67
+ out->clear();
68
+ out->reserve(in.size() / 4 * 3);
69
+ int buf = 0, bits = 0;
70
+ for (char c : in) {
71
+ if (c == '=' || c == '\r' || c == '\n' || c == ' ') continue;
72
+ const int v = val(c);
73
+ if (v < 0) return false;
74
+ buf = buf << 6 | v;
75
+ bits += 6;
76
+ if (bits >= 8) {
77
+ bits -= 8;
78
+ out->push_back(static_cast<uint8_t>(buf >> bits & 0xFF));
79
+ }
80
+ }
81
+ return true;
82
+ }
83
+
84
+ // data: URI を「%xx」percent-decode する (base64 でない稀なケース用)。
85
+ static std::vector<uint8_t> PercentDecode(const std::string& in)
86
+ {
87
+ std::vector<uint8_t> out;
88
+ out.reserve(in.size());
89
+ auto hex = [](char c) -> int {
90
+ if (c >= '0' && c <= '9') return c - '0';
91
+ if (c >= 'a' && c <= 'f') return c - 'a' + 10;
92
+ if (c >= 'A' && c <= 'F') return c - 'A' + 10;
93
+ return -1;
94
+ };
95
+ for (size_t i = 0; i < in.size(); ++i) {
96
+ if (in[i] == '%' && i + 2 < in.size()) {
97
+ const int hi = hex(in[i + 1]);
98
+ const int lo = hex(in[i + 2]);
99
+ if (hi >= 0 && lo >= 0) {
100
+ out.push_back(static_cast<uint8_t>(hi << 4 | lo));
101
+ i += 2;
102
+ continue;
103
+ }
104
+ }
105
+ out.push_back(static_cast<uint8_t>(in[i]));
106
+ }
107
+ return out;
43
108
  }
44
109
 
45
110
  std::optional<std::vector<uint8_t>> AssetLoader::ReadBinary(const std::string& url) const
46
111
  {
112
+ // data: URI (Vite の ?inline はアセットを data:<mime>;base64,<payload> に埋め込む)。
113
+ // このゲームは全サウンドを ?inline で取り込むため実体は data URI。画像は Image.cpp が
114
+ // 対応済みだが、音声 (new Audio / Sound の XHR fetch) もこの ReadBinary を通るので
115
+ // ここで一括デコードする。未対応だと data: をファイルパス扱いして開けず、全 SFX/BGM が
116
+ // 無音になる (XAudio2 の可否とは無関係に実機でも無音)。
117
+ if (url.rfind("data:", 0) == 0) {
118
+ const auto comma = url.find(',');
119
+ if (comma == std::string::npos) {
120
+ return std::nullopt;
121
+ }
122
+ const std::string meta = url.substr(5, comma - 5); // 例: "audio/mpeg;base64"
123
+ const std::string payload = url.substr(comma + 1);
124
+ if (meta.find("base64") != std::string::npos) {
125
+ std::vector<uint8_t> out;
126
+ if (!DecodeBase64(payload, &out)) {
127
+ return std::nullopt;
128
+ }
129
+ return out;
130
+ }
131
+ return PercentDecode(payload);
132
+ }
133
+
134
+ // 埋め込みモード優先: exe 内 pak に同じ相対キーがあれば実ファイルより先に返す。
135
+ if (const auto* embedded = GetEmbeddedAsset(RelativeKey(url))) {
136
+ return *embedded;
137
+ }
138
+
47
139
  const std::string path = Resolve(url);
48
140
  std::ifstream ifs(path, std::ios::binary | std::ios::ate);
49
141
  if (!ifs) {
@@ -61,6 +153,10 @@ std::optional<std::vector<uint8_t>> AssetLoader::ReadBinary(const std::string& u
61
153
 
62
154
  std::optional<std::string> AssetLoader::ReadText(const std::string& url) const
63
155
  {
156
+ if (const auto* embedded = GetEmbeddedAsset(RelativeKey(url))) {
157
+ return std::string(embedded->begin(), embedded->end());
158
+ }
159
+
64
160
  const std::string path = Resolve(url);
65
161
  std::ifstream ifs(path, std::ios::binary);
66
162
  if (!ifs) {
@@ -74,22 +170,29 @@ std::optional<std::string> AssetLoader::ReadText(const std::string& url) const
74
170
  std::optional<std::string> AssetLoader::ResolveEntryModule() const
75
171
  {
76
172
  // vite が出力する index.html から <script type="module" src="..."> を抽出する。
77
- const fs::path index = fs::path(root_) / "index.html";
78
- std::ifstream ifs(index, std::ios::binary);
79
- if (!ifs) {
80
- return std::nullopt;
173
+ // 埋め込みモードでは pak から、無ければ実ファイルから読む。
174
+ std::string html;
175
+ if (const auto* embedded = GetEmbeddedAsset("index.html")) {
176
+ html.assign(embedded->begin(), embedded->end());
177
+ } else {
178
+ const fs::path index = fs::path(root_) / "index.html";
179
+ std::ifstream ifs(index, std::ios::binary);
180
+ if (!ifs) {
181
+ return std::nullopt;
182
+ }
183
+ std::ostringstream oss;
184
+ oss << ifs.rdbuf();
185
+ html = oss.str();
81
186
  }
82
- std::ostringstream oss;
83
- oss << ifs.rdbuf();
84
- const std::string html = oss.str();
85
187
 
86
188
  // type="module" を含む <script ... src="..."> を優先して拾う。
189
+ // 返す値は assets/app 基準の絶対パス。モジュールローダ側で埋め込み/実ファイルを解決する。
87
190
  std::regex module_re(
88
191
  R"(<script[^>]*type=["']module["'][^>]*src=["']([^"']+)["'])",
89
192
  std::regex::icase);
90
193
  std::smatch m;
91
194
  if (std::regex_search(html, m, module_re)) {
92
- return (fs::path(root_) / Resolve(m[1].str()).substr(root_.size() + 1)).string();
195
+ return Resolve(m[1].str());
93
196
  }
94
197
 
95
198
  // 逆順 (src が先) も試す
@@ -97,7 +200,7 @@ std::optional<std::string> AssetLoader::ResolveEntryModule() const
97
200
  R"(<script[^>]*src=["']([^"']+)["'][^>]*type=["']module["'])",
98
201
  std::regex::icase);
99
202
  if (std::regex_search(html, m, module_re2)) {
100
- return (fs::path(root_) / Resolve(m[1].str()).substr(root_.size() + 1)).string();
203
+ return Resolve(m[1].str());
101
204
  }
102
205
 
103
206
  return std::nullopt;
@@ -20,6 +20,10 @@ public:
20
20
  // 相対 URL/パスを assets/app 基準の絶対パスへ解決する。
21
21
  std::string Resolve(const std::string& url) const;
22
22
 
23
+ // URL を assets/app 基準の相対キー(フォワードスラッシュ)へ正規化する。
24
+ // 埋め込み pak のキーと一致させるために使う。
25
+ std::string RelativeKey(const std::string& url) const;
26
+
23
27
  // バイナリ読み込み。存在しなければ nullopt。
24
28
  std::optional<std::vector<uint8_t>> ReadBinary(const std::string& url) const;
25
29
 
@@ -0,0 +1,149 @@
1
+ #include "EmbeddedAssets.h"
2
+
3
+ #include <filesystem>
4
+ #include <map>
5
+ #include <utility>
6
+
7
+ #ifdef _WIN32
8
+ #ifndef WIN32_LEAN_AND_MEAN
9
+ #define WIN32_LEAN_AND_MEAN
10
+ #endif
11
+ #include <Windows.h>
12
+ #endif
13
+
14
+ namespace fs = std::filesystem;
15
+
16
+ namespace next2d {
17
+
18
+ namespace {
19
+
20
+ std::map<std::string, std::vector<uint8_t>> g_assets;
21
+ std::string g_root; // assets/app の正規化済み絶対パス
22
+ bool g_loaded = false;
23
+ bool g_has = false;
24
+
25
+ inline uint32_t Read32LE(const uint8_t* p)
26
+ {
27
+ return static_cast<uint32_t>(p[0])
28
+ | static_cast<uint32_t>(p[1]) << 8
29
+ | static_cast<uint32_t>(p[2]) << 16
30
+ | static_cast<uint32_t>(p[3]) << 24;
31
+ }
32
+
33
+ } // namespace
34
+
35
+ bool ParseEmbeddedPak(const uint8_t* data, std::size_t size,
36
+ std::vector<std::pair<std::string, std::vector<uint8_t>>>* out)
37
+ {
38
+ if (!data || size < 12) {
39
+ return false;
40
+ }
41
+ if (data[0] != 'N' || data[1] != '2' || data[2] != 'D' || data[3] != 'A') {
42
+ return false;
43
+ }
44
+ // data[4..7] = version (現状 1 のみ。将来の互換のため値は検査しない)
45
+ const uint32_t count = Read32LE(data + 8);
46
+ std::size_t off = 12;
47
+ for (uint32_t i = 0; i < count; ++i) {
48
+ if (off + 4 > size) return false;
49
+ const uint32_t key_len = Read32LE(data + off);
50
+ off += 4;
51
+ if (key_len > size || off + key_len > size) return false;
52
+ std::string key(reinterpret_cast<const char*>(data + off), key_len);
53
+ off += key_len;
54
+
55
+ if (off + 4 > size) return false;
56
+ const uint32_t data_len = Read32LE(data + off);
57
+ off += 4;
58
+ if (data_len > size || off + data_len > size) return false;
59
+ std::vector<uint8_t> bytes(data + off, data + off + data_len);
60
+ off += data_len;
61
+
62
+ if (out) {
63
+ out->emplace_back(std::move(key), std::move(bytes));
64
+ }
65
+ }
66
+ return true;
67
+ }
68
+
69
+ bool InitEmbeddedAssets()
70
+ {
71
+ if (g_loaded) {
72
+ return g_has;
73
+ }
74
+ g_loaded = true;
75
+
76
+ #ifdef _WIN32
77
+ // 実行ファイル自身のリソースから "N2DASSETS" (RCDATA) を取り出す。
78
+ HRSRC res = FindResourceW(nullptr, L"N2DASSETS", RT_RCDATA);
79
+ if (res) {
80
+ HGLOBAL handle = LoadResource(nullptr, res);
81
+ const DWORD sz = SizeofResource(nullptr, res);
82
+ const void* ptr = handle ? LockResource(handle) : nullptr;
83
+ if (ptr && sz >= 12) {
84
+ std::vector<std::pair<std::string, std::vector<uint8_t>>> entries;
85
+ if (ParseEmbeddedPak(static_cast<const uint8_t*>(ptr), sz, &entries)) {
86
+ for (auto& e : entries) {
87
+ g_assets.emplace(std::move(e.first), std::move(e.second));
88
+ }
89
+ g_has = true;
90
+ }
91
+ }
92
+ }
93
+ #endif
94
+
95
+ return g_has;
96
+ }
97
+
98
+ bool HasEmbeddedAssets()
99
+ {
100
+ return g_has;
101
+ }
102
+
103
+ void SetEmbeddedAssetsRoot(const std::string& abs_root)
104
+ {
105
+ std::error_code ec;
106
+ fs::path canon = fs::weakly_canonical(fs::path(abs_root), ec);
107
+ g_root = ec ? abs_root : canon.string();
108
+ }
109
+
110
+ const std::vector<uint8_t>* GetEmbeddedAsset(const std::string& key)
111
+ {
112
+ if (!g_has) {
113
+ return nullptr;
114
+ }
115
+ auto it = g_assets.find(key);
116
+ return it == g_assets.end() ? nullptr : &it->second;
117
+ }
118
+
119
+ void ForEachEmbeddedAsset(
120
+ const std::function<void(const std::string& key,
121
+ const std::vector<uint8_t>& data)>& callback)
122
+ {
123
+ if (!g_has) {
124
+ return;
125
+ }
126
+ for (const auto& [key, data] : g_assets) {
127
+ callback(key, data);
128
+ }
129
+ }
130
+
131
+ const std::vector<uint8_t>* GetEmbeddedAssetByAbsPath(const std::string& abs)
132
+ {
133
+ if (!g_has || g_root.empty()) {
134
+ return nullptr;
135
+ }
136
+ // 純粋に字句的な相対化 (FS 参照なし)。abs も root も weakly_canonical 済みを前提。
137
+ const fs::path rel = fs::path(abs).lexically_relative(fs::path(g_root));
138
+ if (rel.empty()) {
139
+ return nullptr;
140
+ }
141
+ const std::string key = rel.generic_string();
142
+ // root 外 ("../..") は埋め込み対象外。
143
+ if (key.rfind("..", 0) == 0) {
144
+ return nullptr;
145
+ }
146
+ return GetEmbeddedAsset(key);
147
+ }
148
+
149
+ } // namespace next2d
@@ -0,0 +1,60 @@
1
+ // EmbeddedAssets: exe に埋め込んだ assets pak (Windows RCDATA) を解析して提供する。
2
+ //
3
+ // 目的: 書き出した *.exe の隣に平文の JS/HTML を置かず、ゲーム資産(assets/app 一式 +
4
+ // js/bootstrap.js)を exe 内リソースへ格納する。埋め込みが無い(開発ビルド等)場合は
5
+ // 全 API が「見つからない」を返し、呼び出し側はファイルシステムへフォールバックする。
6
+ // これにより埋め込みは純粋に加算的で、既存のファイル読み経路を壊さない。
7
+ //
8
+ // pak フォーマット (リトルエンディアン uint32):
9
+ // [4] magic "N2DA"
10
+ // [4] uint32 version (=1)
11
+ // [4] uint32 count
12
+ // count 回:
13
+ // [4] uint32 key_len
14
+ // [key_len] key (UTF-8, フォワードスラッシュ相対パス)
15
+ // [4] uint32 data_len
16
+ // [data_len] data
17
+ //
18
+ // key は次のいずれか:
19
+ // - assets/app 基準の相対パス (例 "app.js", "index.html", "assets/xxx.js")
20
+ // - "js/bootstrap.js" / "js/selftest.js" (exe_dir 基準)
21
+ #pragma once
22
+
23
+ #include <cstddef>
24
+ #include <cstdint>
25
+ #include <functional>
26
+ #include <string>
27
+ #include <vector>
28
+
29
+ namespace next2d {
30
+
31
+ // exe リソースから pak をロードして解析する (プロセスで一度だけ)。
32
+ // 埋め込みが存在し解析に成功したとき true。以後の Has/Get はこの結果に従う。
33
+ bool InitEmbeddedAssets();
34
+
35
+ // 埋め込みが利用可能か。
36
+ bool HasEmbeddedAssets();
37
+
38
+ // assets/app のルート絶対パスを登録する。GetEmbeddedAssetByAbsPath の
39
+ // 相対キー算出に使う。main で AssetLoader の root と同じ値を渡す。
40
+ void SetEmbeddedAssetsRoot(const std::string& abs_root);
41
+
42
+ // 相対キー直接指定で検索する。見つかればバイト列ポインタ(プロセス生存中有効)、無ければ nullptr。
43
+ const std::vector<uint8_t>* GetEmbeddedAsset(const std::string& key);
44
+
45
+ // 全エントリを列挙する (フォント等、キーが事前に分からない資材の収集用)。
46
+ // callback には key とデータ (プロセス生存中有効) が渡る。
47
+ void ForEachEmbeddedAsset(
48
+ const std::function<void(const std::string& key,
49
+ const std::vector<uint8_t>& data)>& callback);
50
+
51
+ // モジュールローダ用: assets/app 配下の絶対パスを相対キーへ変換して検索する。
52
+ // root 未登録 / abs が root 配下でない場合は nullptr。
53
+ const std::vector<uint8_t>* GetEmbeddedAssetByAbsPath(const std::string& abs);
54
+
55
+ // テスト用: 生の pak バッファを解析して (key -> data) を out へ格納する。
56
+ // プラットフォーム非依存で macOS 上でも検証できる。成功時 true。
57
+ bool ParseEmbeddedPak(const uint8_t* data, std::size_t size,
58
+ std::vector<std::pair<std::string, std::vector<uint8_t>>>* out);
59
+
60
+ } // namespace next2d
@@ -63,6 +63,7 @@ void EventLoop::ClearTimer(uint32_t id)
63
63
  uint32_t EventLoop::RequestAnimationFrame(v8::Local<v8::Function> callback)
64
64
  {
65
65
  const uint32_t id = next_raf_id_++;
66
+
66
67
  v8::Global<v8::Function> global;
67
68
  global.Reset(isolate_, callback);
68
69
  raf_callbacks_.emplace_back(id, std::move(global));
@@ -71,6 +72,16 @@ uint32_t EventLoop::RequestAnimationFrame(v8::Local<v8::Function> callback)
71
72
 
72
73
  void EventLoop::CancelAnimationFrame(uint32_t id)
73
74
  {
75
+ // 未実行分は即座に取り除く。raf_cancelled_ だけに頼ると、RunAnimationFrame の
76
+ // 実行中に登録されたコールバックへのキャンセルが同フレーム末尾の clear で失われ、
77
+ // キャンセル済みのはずの rAF が翌フレームに実行されてしまう (ticker/Tween の多重化)。
78
+ for (auto it = raf_callbacks_.begin(); it != raf_callbacks_.end(); ++it) {
79
+ if (it->first == id) {
80
+ raf_callbacks_.erase(it);
81
+ return;
82
+ }
83
+ }
84
+ // 実行中バッチ (RunAnimationFrame の current) 内のものはフラグで無効化する
74
85
  raf_cancelled_.push_back(id);
75
86
  }
76
87
 
@@ -104,7 +115,7 @@ void EventLoop::PumpTimers()
104
115
 
105
116
  v8::TryCatch try_catch(isolate_);
106
117
  (void) fn->Call(ctx, global, 0, nullptr);
107
- // 例外はコンソールに委譲 (V8Runtime のメッセージハンドラが拾う)
118
+ v8util::ReportCaught(isolate_, &try_catch, "setTimeout/setInterval callback");
108
119
 
109
120
  if (!it->second.repeat) {
110
121
  // 単発は実行後に破棄
@@ -149,6 +160,7 @@ void EventLoop::RunAnimationFrame(double timestamp_ms)
149
160
  v8::TryCatch try_catch(isolate_);
150
161
  v8::Local<v8::Value> args[1] = { arg };
151
162
  (void) fn->Call(ctx, global, 1, args);
163
+ v8util::ReportCaught(isolate_, &try_catch, "requestAnimationFrame callback");
152
164
  }
153
165
 
154
166
  raf_cancelled_.clear();
@@ -50,6 +50,18 @@ public:
50
50
  // 保留タスクが残っているか (終了判定用)
51
51
  bool HasPendingWork() const;
52
52
 
53
+ // 診断用: 登録中の rAF コールバック数 / タイマー数
54
+ size_t PendingAnimationFrameCount() const { return raf_callbacks_.size(); }
55
+ size_t PendingTimerCount() const { return timers_.size(); }
56
+
57
+ // 全タイマー/rAF コールバック (v8::Global) を解放する。Isolate::Dispose より
58
+ // 前に必ず呼ぶこと (スタック巻き戻しでの破棄は Isolate 破棄後になり fail-fast)。
59
+ void Shutdown() {
60
+ timers_.clear();
61
+ raf_callbacks_.clear();
62
+ raf_cancelled_.clear();
63
+ }
64
+
53
65
  private:
54
66
  struct TimerEntry {
55
67
  v8::Global<v8::Function> callback;