@next2d/builder 0.1.6 → 0.1.8
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/README.md +5 -1
- package/dist/index.js +140 -36
- package/package.json +3 -2
- package/templates/xbox/CMakeLists.txt +74 -2
- package/templates/xbox/README.md +73 -12
- package/templates/xbox/cmake/FetchDawn.cmake +47 -6
- package/templates/xbox/cmake/FindV8.cmake +22 -1
- package/templates/xbox/js/bootstrap.js +31 -28
- package/templates/xbox/js/selftest.js +740 -90
- package/templates/xbox/src/EventLoop.cpp +28 -1
- package/templates/xbox/src/EventLoop.h +12 -0
- package/templates/xbox/src/bindings/Audio.cpp +99 -15
- package/templates/xbox/src/bindings/Bindings.h +10 -0
- package/templates/xbox/src/bindings/Canvas.cpp +45 -0
- package/templates/xbox/src/bindings/Canvas2D.cpp +2 -5
- package/templates/xbox/src/bindings/DomShims.cpp +127 -0
- package/templates/xbox/src/bindings/EventTarget.cpp +2 -0
- package/templates/xbox/src/bindings/Fetch.cpp +2 -8
- package/templates/xbox/src/bindings/Image.cpp +212 -10
- package/templates/xbox/src/bindings/Polyfills.cpp +791 -0
- package/templates/xbox/src/bindings/Video.cpp +96 -7
- package/templates/xbox/src/bindings/webgpu/WebGPU.cpp +815 -15
- package/templates/xbox/src/bindings/webgpu/WebGPUCommon.h +3 -8
- package/templates/xbox/src/gpu/DawnContext.cpp +90 -3
- package/templates/xbox/src/gpu/DawnContext.h +18 -0
- package/templates/xbox/src/main.cpp +88 -6
- package/templates/xbox/src/platform/AudioEngine.cpp +9 -3
- package/templates/xbox/src/v8/V8Runtime.cpp +69 -4
- package/templates/xbox/src/v8/V8Util.h +29 -0
- package/templates/xbox/src/v8/WeakHandle.h +45 -0
- package/templates/xbox/src/worker/WorkerRuntime.cpp +325 -41
- package/templates/xbox/src/worker/WorkerRuntime.h +20 -10
- package/templates/xbox/tests/check_local.sh +3 -2
- package/templates/xbox/tests/raster_test.bin +0 -0
|
@@ -63,6 +63,22 @@ 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
|
+
|
|
67
|
+
// 診断: 誰が rAF を登録しているか (関数名 + minify 後スクリプト位置)。
|
|
68
|
+
// 最初の 40 件 + 以後 300 件毎にサンプルし、凍結後も生きている登録元を特定する。
|
|
69
|
+
static uint64_t reg_count = 0;
|
|
70
|
+
++reg_count;
|
|
71
|
+
if (reg_count <= 40 || reg_count % 300 == 0) {
|
|
72
|
+
const std::string name =
|
|
73
|
+
v8util::ToStdString(isolate_, callback->GetDebugName());
|
|
74
|
+
v8::Local<v8::Value> res = callback->GetScriptOrigin().ResourceName();
|
|
75
|
+
std::cerr << "[Loop] rAF register #" << reg_count
|
|
76
|
+
<< " fn=" << (name.empty() ? "(anon)" : name)
|
|
77
|
+
<< " at " << v8util::ToStdString(isolate_, res)
|
|
78
|
+
<< ":" << callback->GetScriptLineNumber() + 1
|
|
79
|
+
<< ":" << callback->GetScriptColumnNumber() + 1 << std::endl;
|
|
80
|
+
}
|
|
81
|
+
|
|
66
82
|
v8::Global<v8::Function> global;
|
|
67
83
|
global.Reset(isolate_, callback);
|
|
68
84
|
raf_callbacks_.emplace_back(id, std::move(global));
|
|
@@ -71,6 +87,16 @@ uint32_t EventLoop::RequestAnimationFrame(v8::Local<v8::Function> callback)
|
|
|
71
87
|
|
|
72
88
|
void EventLoop::CancelAnimationFrame(uint32_t id)
|
|
73
89
|
{
|
|
90
|
+
// 未実行分は即座に取り除く。raf_cancelled_ だけに頼ると、RunAnimationFrame の
|
|
91
|
+
// 実行中に登録されたコールバックへのキャンセルが同フレーム末尾の clear で失われ、
|
|
92
|
+
// キャンセル済みのはずの rAF が翌フレームに実行されてしまう (ticker/Tween の多重化)。
|
|
93
|
+
for (auto it = raf_callbacks_.begin(); it != raf_callbacks_.end(); ++it) {
|
|
94
|
+
if (it->first == id) {
|
|
95
|
+
raf_callbacks_.erase(it);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// 実行中バッチ (RunAnimationFrame の current) 内のものはフラグで無効化する
|
|
74
100
|
raf_cancelled_.push_back(id);
|
|
75
101
|
}
|
|
76
102
|
|
|
@@ -104,7 +130,7 @@ void EventLoop::PumpTimers()
|
|
|
104
130
|
|
|
105
131
|
v8::TryCatch try_catch(isolate_);
|
|
106
132
|
(void) fn->Call(ctx, global, 0, nullptr);
|
|
107
|
-
|
|
133
|
+
v8util::ReportCaught(isolate_, &try_catch, "setTimeout/setInterval callback");
|
|
108
134
|
|
|
109
135
|
if (!it->second.repeat) {
|
|
110
136
|
// 単発は実行後に破棄
|
|
@@ -149,6 +175,7 @@ void EventLoop::RunAnimationFrame(double timestamp_ms)
|
|
|
149
175
|
v8::TryCatch try_catch(isolate_);
|
|
150
176
|
v8::Local<v8::Value> args[1] = { arg };
|
|
151
177
|
(void) fn->Call(ctx, global, 1, args);
|
|
178
|
+
v8util::ReportCaught(isolate_, &try_catch, "requestAnimationFrame callback");
|
|
152
179
|
}
|
|
153
180
|
|
|
154
181
|
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;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
#include "EventTarget.h"
|
|
6
6
|
#include "platform/AudioEngine.h"
|
|
7
7
|
#include "v8/V8Util.h"
|
|
8
|
+
#include "v8/WeakHandle.h"
|
|
8
9
|
|
|
9
10
|
#include <memory>
|
|
10
11
|
#include <vector>
|
|
@@ -22,11 +23,6 @@ struct GainParam {
|
|
|
22
23
|
std::shared_ptr<AudioVoice> voice; // source.connect(gain) 時にリンクされる
|
|
23
24
|
};
|
|
24
25
|
|
|
25
|
-
void ReleaseGainParam(const v8::WeakCallbackInfo<GainParam>& info)
|
|
26
|
-
{
|
|
27
|
-
delete info.GetParameter();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
26
|
// 再生中(非ループ)の source ノード。毎フレーム IsFinished を見て "ended" を発火する。
|
|
31
27
|
std::vector<v8::Global<v8::Object>>& PlayingSources()
|
|
32
28
|
{
|
|
@@ -40,19 +36,12 @@ struct Holder {
|
|
|
40
36
|
std::shared_ptr<T> ptr;
|
|
41
37
|
};
|
|
42
38
|
|
|
43
|
-
template <typename T>
|
|
44
|
-
void ReleaseHolder(const v8::WeakCallbackInfo<Holder<T>>& info)
|
|
45
|
-
{
|
|
46
|
-
delete info.GetParameter();
|
|
47
|
-
}
|
|
48
|
-
|
|
49
39
|
template <typename T>
|
|
50
40
|
void Attach(v8::Isolate* isolate, v8::Local<v8::Object> obj, std::shared_ptr<T> ptr)
|
|
51
41
|
{
|
|
52
42
|
auto* holder = new Holder<T>{std::move(ptr)};
|
|
53
43
|
obj->SetInternalField(0, v8::External::New(isolate, holder));
|
|
54
|
-
|
|
55
|
-
handle->SetWeak(holder, ReleaseHolder<T>, v8::WeakCallbackType::kParameter);
|
|
44
|
+
v8util::AttachWeak(isolate, obj, holder);
|
|
56
45
|
}
|
|
57
46
|
|
|
58
47
|
template <typename T>
|
|
@@ -198,8 +187,7 @@ void CreateGain(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
|
198
187
|
v8::Local<v8::Object> gain = InternalTemplate(isolate)->NewInstance(ctx).ToLocalChecked();
|
|
199
188
|
auto* param = new GainParam();
|
|
200
189
|
gain->SetInternalField(0, v8::External::New(isolate, param));
|
|
201
|
-
|
|
202
|
-
handle->SetWeak(param, ReleaseGainParam, v8::WeakCallbackType::kParameter);
|
|
190
|
+
v8util::AttachWeak(isolate, gain, param);
|
|
203
191
|
|
|
204
192
|
gain->SetNativeDataProperty(ctx, Str(isolate, "value"),
|
|
205
193
|
[](v8::Local<v8::Name>, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
@@ -226,6 +214,16 @@ void CreateGain(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
|
226
214
|
// 毎フレーム呼ばれ、再生終了した source ノードへ "ended" を発火する (main.cpp のループから)。
|
|
227
215
|
} // namespace
|
|
228
216
|
|
|
217
|
+
// V8 破棄前に呼ぶ。再生追跡リストの v8::Global を解放する。
|
|
218
|
+
// (放置すると static デストラクタが V8 破棄後に走りアクセス違反になる)
|
|
219
|
+
void ShutdownAudioEvents()
|
|
220
|
+
{
|
|
221
|
+
for (auto& g : PlayingSources()) {
|
|
222
|
+
g.Reset();
|
|
223
|
+
}
|
|
224
|
+
PlayingSources().clear();
|
|
225
|
+
}
|
|
226
|
+
|
|
229
227
|
void PumpAudioEvents(v8::Isolate* isolate)
|
|
230
228
|
{
|
|
231
229
|
auto& sources = PlayingSources();
|
|
@@ -285,6 +283,83 @@ void DecodeAudioData(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
|
285
283
|
resolver->Resolve(ctx, buffer).Check();
|
|
286
284
|
}
|
|
287
285
|
|
|
286
|
+
// --- Audio (HTMLAudioElement 最小実装) -------------------------------------
|
|
287
|
+
// ゲームコードが `new Audio(url)` で効果音/BGM を再生する経路。
|
|
288
|
+
// play/pause/paused/loop/volume/currentTime/preload をサポートする。
|
|
289
|
+
// pause→play は先頭からの再生になる (位置レジュームは未対応の近似)。
|
|
290
|
+
|
|
291
|
+
void AudioElementPlay(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
292
|
+
{
|
|
293
|
+
v8::Isolate* isolate = args.GetIsolate();
|
|
294
|
+
v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
|
|
295
|
+
v8::Local<v8::Object> self = args.This();
|
|
296
|
+
|
|
297
|
+
auto resolver = v8::Promise::Resolver::New(ctx).ToLocalChecked();
|
|
298
|
+
args.GetReturnValue().Set(resolver->GetPromise());
|
|
299
|
+
|
|
300
|
+
auto voice = Get<AudioVoice>(self);
|
|
301
|
+
if (voice) {
|
|
302
|
+
bool loop = false;
|
|
303
|
+
double volume = 1.0;
|
|
304
|
+
v8::Local<v8::Value> v;
|
|
305
|
+
if (self->Get(ctx, Str(isolate, "loop")).ToLocal(&v)) {
|
|
306
|
+
loop = v->BooleanValue(isolate);
|
|
307
|
+
}
|
|
308
|
+
if (self->Get(ctx, Str(isolate, "volume")).ToLocal(&v) && v->IsNumber()) {
|
|
309
|
+
volume = v.As<v8::Number>()->Value();
|
|
310
|
+
}
|
|
311
|
+
voice->SetVolume(static_cast<float>(volume));
|
|
312
|
+
voice->Start(loop);
|
|
313
|
+
}
|
|
314
|
+
v8util::SetValue(isolate, self, "paused", v8::Boolean::New(isolate, false));
|
|
315
|
+
resolver->Resolve(ctx, v8::Undefined(isolate)).Check();
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
void AudioElementPause(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
319
|
+
{
|
|
320
|
+
v8::Isolate* isolate = args.GetIsolate();
|
|
321
|
+
auto voice = Get<AudioVoice>(args.This());
|
|
322
|
+
if (voice) {
|
|
323
|
+
voice->Stop();
|
|
324
|
+
}
|
|
325
|
+
v8util::SetValue(isolate, args.This(), "paused", v8::Boolean::New(isolate, true));
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
void AudioElementConstructor(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
329
|
+
{
|
|
330
|
+
v8::Isolate* isolate = args.GetIsolate();
|
|
331
|
+
if (!args.IsConstructCall()) {
|
|
332
|
+
v8util::ThrowTypeError(isolate, "Audio must be called with new");
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
v8::Local<v8::Object> self = args.This();
|
|
336
|
+
Attach<AudioVoice>(isolate, self, nullptr);
|
|
337
|
+
|
|
338
|
+
// URL 指定があれば同期でロード+デコードして voice を作る (失敗時は無音の no-op)
|
|
339
|
+
if (args.Length() > 0 && args[0]->IsString()) {
|
|
340
|
+
const std::string src = ToStdString(isolate, args[0]);
|
|
341
|
+
v8util::SetValue(isolate, self, "src", args[0]);
|
|
342
|
+
HostContext* host = HostContext::From(isolate);
|
|
343
|
+
auto bytes = host->assets->ReadBinary(src);
|
|
344
|
+
if (bytes) {
|
|
345
|
+
auto pcm = std::make_shared<PcmBuffer>();
|
|
346
|
+
if (AudioEngine::Decode(*bytes, *pcm)) {
|
|
347
|
+
Attach<AudioVoice>(isolate, self, host->audio->CreateVoice(pcm));
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
v8util::SetValue(isolate, self, "loop", v8::Boolean::New(isolate, false));
|
|
353
|
+
v8util::SetValue(isolate, self, "volume", v8::Number::New(isolate, 1.0));
|
|
354
|
+
v8util::SetValue(isolate, self, "paused", v8::Boolean::New(isolate, true));
|
|
355
|
+
v8util::SetValue(isolate, self, "preload", Str(isolate, "auto"));
|
|
356
|
+
v8util::SetValue(isolate, self, "currentTime", v8::Number::New(isolate, 0));
|
|
357
|
+
v8util::SetMethod(isolate, self, "play", AudioElementPlay);
|
|
358
|
+
v8util::SetMethod(isolate, self, "pause", AudioElementPause);
|
|
359
|
+
InstallEventTarget(isolate, self);
|
|
360
|
+
args.GetReturnValue().Set(self);
|
|
361
|
+
}
|
|
362
|
+
|
|
288
363
|
// AudioContext コンストラクタ
|
|
289
364
|
void AudioContextConstructor(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
290
365
|
{
|
|
@@ -315,6 +390,15 @@ void InstallAudio(v8::Isolate* isolate, v8::Local<v8::Object> global, HostContex
|
|
|
315
390
|
|
|
316
391
|
global->Set(ctx, Str(isolate, "AudioContext"), fn).Check();
|
|
317
392
|
global->Set(ctx, Str(isolate, "webkitAudioContext"), fn).Check();
|
|
393
|
+
|
|
394
|
+
// Audio (HTMLAudioElement 最小): ゲームコードの `new Audio(url)` 用。
|
|
395
|
+
// インスタンスは内部フィールドに AudioVoice ホルダを持つ。
|
|
396
|
+
v8::Local<v8::FunctionTemplate> audio_tmpl =
|
|
397
|
+
v8::FunctionTemplate::New(isolate, AudioElementConstructor);
|
|
398
|
+
audio_tmpl->SetClassName(Str(isolate, "Audio"));
|
|
399
|
+
audio_tmpl->InstanceTemplate()->SetInternalFieldCount(1);
|
|
400
|
+
global->Set(ctx, Str(isolate, "Audio"),
|
|
401
|
+
audio_tmpl->GetFunction(ctx).ToLocalChecked()).Check();
|
|
318
402
|
}
|
|
319
403
|
|
|
320
404
|
} // namespace next2d
|
|
@@ -41,6 +41,12 @@ void InstallAudio(v8::Isolate* isolate, v8::Local<v8::Object> global, HostContex
|
|
|
41
41
|
// 再生終了した AudioBufferSourceNode へ "ended" を発火する。main.cpp のループから毎フレーム呼ぶ。
|
|
42
42
|
void PumpAudioEvents(v8::Isolate* isolate);
|
|
43
43
|
|
|
44
|
+
// V8 破棄前に呼ぶ。音声の再生追跡リスト (v8::Global) を解放する。
|
|
45
|
+
void ShutdownAudioEvents();
|
|
46
|
+
|
|
47
|
+
// V8 破棄前に呼ぶ。WebGPU テンプレートの v8::Global (static) を解放する。
|
|
48
|
+
void ShutdownWebGPU();
|
|
49
|
+
|
|
44
50
|
// window / document / navigator / screen 等の DOM 相当スタブと canvas 生成
|
|
45
51
|
void InstallDomShims(v8::Isolate* isolate, v8::Local<v8::Object> global, HostContext* host);
|
|
46
52
|
|
|
@@ -69,6 +75,10 @@ bool GetVideoFramePixels(v8::Isolate* isolate, v8::Local<v8::Object> obj,
|
|
|
69
75
|
// blob: URL のバイト列をテキストとして取得する (Network.cpp)。
|
|
70
76
|
bool ResolveObjectURL(const std::string& url, std::string* out_text);
|
|
71
77
|
|
|
78
|
+
// TextEncoder/TextDecoder/queueMicrotask 等の JS ポリフィルを評価する (Polyfills.cpp)。
|
|
79
|
+
// bootstrap.js はメインのみのため、worker を含む全コンテキストにはこちらで注入する。
|
|
80
|
+
void InstallPolyfills(v8::Isolate* isolate, v8::Local<v8::Context> context);
|
|
81
|
+
|
|
72
82
|
// 上記をまとめてインストールする
|
|
73
83
|
void InstallGlobalBindings(v8::Isolate* isolate, v8::Local<v8::Object> global, HostContext* host);
|
|
74
84
|
|
|
@@ -135,6 +135,9 @@ v8::Local<v8::Object> CreateCanvasElement(v8::Isolate* isolate, HostContext* hos
|
|
|
135
135
|
int width, int height)
|
|
136
136
|
{
|
|
137
137
|
v8::Local<v8::Object> canvas = v8::Object::New(isolate);
|
|
138
|
+
// player の canvasSetPositionService は children[0].localName === "canvas" を確認する
|
|
139
|
+
SetValue(isolate, canvas, "tagName", Str(isolate, "CANVAS"));
|
|
140
|
+
SetValue(isolate, canvas, "localName", Str(isolate, "canvas"));
|
|
138
141
|
SetValue(isolate, canvas, "width",
|
|
139
142
|
v8::Integer::New(isolate, width ? width : host->viewport_width));
|
|
140
143
|
SetValue(isolate, canvas, "height",
|
|
@@ -146,6 +149,48 @@ v8::Local<v8::Object> CreateCanvasElement(v8::Isolate* isolate, HostContext* hos
|
|
|
146
149
|
SetMethod(isolate, canvas, "transferControlToOffscreen", TransferControlToOffscreen);
|
|
147
150
|
InstallEventTarget(isolate, canvas); // pointer/wheel リスナを保持
|
|
148
151
|
SetMethod(isolate, canvas, "getBoundingClientRect", GetBoundingClientRect);
|
|
152
|
+
|
|
153
|
+
// player の CanvasInitializeService が canvas.setAttribute("style", ...) を呼ぶ。
|
|
154
|
+
// 属性は __attrs に保存する (見た目の CSS はネイティブ描画では不要)。
|
|
155
|
+
SetMethod(isolate, canvas, "setAttribute",
|
|
156
|
+
[](const v8::FunctionCallbackInfo<v8::Value>& a) {
|
|
157
|
+
if (a.Length() < 2) return;
|
|
158
|
+
v8::Isolate* iso = a.GetIsolate();
|
|
159
|
+
v8::Local<v8::Context> c = iso->GetCurrentContext();
|
|
160
|
+
v8::Local<v8::Value> attrs;
|
|
161
|
+
v8::Local<v8::Object> store;
|
|
162
|
+
if (a.This()->Get(c, v8util::Str(iso, "__attrs")).ToLocal(&attrs) && attrs->IsObject()) {
|
|
163
|
+
store = attrs.As<v8::Object>();
|
|
164
|
+
} else {
|
|
165
|
+
store = v8::Object::New(iso);
|
|
166
|
+
v8util::SetValue(iso, a.This(), "__attrs", store);
|
|
167
|
+
}
|
|
168
|
+
store->Set(c, a[0], a[1]).Check();
|
|
169
|
+
});
|
|
170
|
+
SetMethod(isolate, canvas, "getAttribute",
|
|
171
|
+
[](const v8::FunctionCallbackInfo<v8::Value>& a) {
|
|
172
|
+
a.GetReturnValue().SetNull();
|
|
173
|
+
if (a.Length() < 1) return;
|
|
174
|
+
v8::Isolate* iso = a.GetIsolate();
|
|
175
|
+
v8::Local<v8::Context> c = iso->GetCurrentContext();
|
|
176
|
+
v8::Local<v8::Value> attrs;
|
|
177
|
+
if (a.This()->Get(c, v8util::Str(iso, "__attrs")).ToLocal(&attrs) && attrs->IsObject()) {
|
|
178
|
+
v8::Local<v8::Value> v;
|
|
179
|
+
if (attrs.As<v8::Object>()->Get(c, a[0]).ToLocal(&v) && !v->IsUndefined()) {
|
|
180
|
+
a.GetReturnValue().Set(v);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
SetMethod(isolate, canvas, "removeAttribute",
|
|
185
|
+
[](const v8::FunctionCallbackInfo<v8::Value>& a) {
|
|
186
|
+
if (a.Length() < 1) return;
|
|
187
|
+
v8::Isolate* iso = a.GetIsolate();
|
|
188
|
+
v8::Local<v8::Context> c = iso->GetCurrentContext();
|
|
189
|
+
v8::Local<v8::Value> attrs;
|
|
190
|
+
if (a.This()->Get(c, v8util::Str(iso, "__attrs")).ToLocal(&attrs) && attrs->IsObject()) {
|
|
191
|
+
(void) attrs.As<v8::Object>()->Delete(c, a[0]);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
149
194
|
// 主要 canvas を HostContext から参照できるよう記録 (WndProc の入力配送先)
|
|
150
195
|
HostContext::From(isolate)->main_canvas.Reset(isolate, canvas);
|
|
151
196
|
return canvas;
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
#include "RasterCore.h"
|
|
12
12
|
#include "platform/TextRasterizer.h"
|
|
13
13
|
#include "v8/V8Util.h"
|
|
14
|
+
#include "v8/WeakHandle.h"
|
|
14
15
|
|
|
15
16
|
#include <Windows.h>
|
|
16
17
|
|
|
@@ -352,9 +353,6 @@ void GetImageData(const v8::FunctionCallbackInfo<v8::Value>& a)
|
|
|
352
353
|
a.GetReturnValue().Set(image_data);
|
|
353
354
|
}
|
|
354
355
|
|
|
355
|
-
// GC 時に Canvas2D 実体を解放
|
|
356
|
-
void ReleaseCanvas2D(const v8::WeakCallbackInfo<Canvas2D>& info) { delete info.GetParameter(); }
|
|
357
|
-
|
|
358
356
|
// fillStyle / strokeStyle / lineWidth / globalAlpha / font アクセサ
|
|
359
357
|
void InstallAccessors(v8::Isolate* isolate, v8::Local<v8::Object> obj)
|
|
360
358
|
{
|
|
@@ -424,8 +422,7 @@ v8::Local<v8::Object> CreateCanvas2DContext(v8::Isolate* isolate, HostContext* /
|
|
|
424
422
|
|
|
425
423
|
auto* impl = new Canvas2D(width > 0 ? width : 1, height > 0 ? height : 1);
|
|
426
424
|
obj->SetInternalField(0, v8::External::New(isolate, impl));
|
|
427
|
-
|
|
428
|
-
handle->SetWeak(impl, ReleaseCanvas2D, v8::WeakCallbackType::kParameter);
|
|
425
|
+
v8util::AttachWeak(isolate, obj, impl);
|
|
429
426
|
|
|
430
427
|
SetMethod(isolate, obj, "beginPath", BeginPath);
|
|
431
428
|
SetMethod(isolate, obj, "moveTo", MoveTo);
|
|
@@ -5,6 +5,10 @@
|
|
|
5
5
|
#include "v8/V8Util.h"
|
|
6
6
|
|
|
7
7
|
#include <Windows.h>
|
|
8
|
+
#include <bcrypt.h>
|
|
9
|
+
#include <cstdio>
|
|
10
|
+
|
|
11
|
+
#pragma comment(lib, "bcrypt.lib")
|
|
8
12
|
|
|
9
13
|
namespace next2d {
|
|
10
14
|
|
|
@@ -107,6 +111,109 @@ void ClipboardWriteText(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
|
107
111
|
resolver->Resolve(ctx, v8::Undefined(isolate)).Check();
|
|
108
112
|
}
|
|
109
113
|
|
|
114
|
+
// crypto.getRandomValues(typedArray): OS 乱数で埋めて同じ view を返す
|
|
115
|
+
void CryptoGetRandomValues(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
116
|
+
{
|
|
117
|
+
v8::Isolate* isolate = args.GetIsolate();
|
|
118
|
+
if (args.Length() < 1 || !args[0]->IsArrayBufferView()) {
|
|
119
|
+
v8util::ThrowTypeError(isolate, "getRandomValues requires a TypedArray");
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
auto view = args[0].As<v8::ArrayBufferView>();
|
|
123
|
+
std::vector<uint8_t> bytes(view->ByteLength());
|
|
124
|
+
if (!bytes.empty()) {
|
|
125
|
+
BCryptGenRandom(nullptr, bytes.data(), static_cast<ULONG>(bytes.size()),
|
|
126
|
+
BCRYPT_USE_SYSTEM_PREFERRED_RNG);
|
|
127
|
+
// view のバッキングへ書き戻す
|
|
128
|
+
auto ab = view->Buffer();
|
|
129
|
+
auto* dst = static_cast<uint8_t*>(ab->GetBackingStore()->Data()) + view->ByteOffset();
|
|
130
|
+
memcpy(dst, bytes.data(), bytes.size());
|
|
131
|
+
}
|
|
132
|
+
args.GetReturnValue().Set(args[0]);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// crypto.randomUUID(): RFC 4122 v4
|
|
136
|
+
void CryptoRandomUUID(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
137
|
+
{
|
|
138
|
+
v8::Isolate* isolate = args.GetIsolate();
|
|
139
|
+
uint8_t b[16] = {};
|
|
140
|
+
BCryptGenRandom(nullptr, b, sizeof(b), BCRYPT_USE_SYSTEM_PREFERRED_RNG);
|
|
141
|
+
b[6] = static_cast<uint8_t>((b[6] & 0x0f) | 0x40); // version 4
|
|
142
|
+
b[8] = static_cast<uint8_t>((b[8] & 0x3f) | 0x80); // variant
|
|
143
|
+
char buf[37];
|
|
144
|
+
std::snprintf(buf, sizeof(buf),
|
|
145
|
+
"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
|
146
|
+
b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7],
|
|
147
|
+
b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15]);
|
|
148
|
+
args.GetReturnValue().Set(Str(isolate, buf));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// --- 名前付きストレージの永続化バックエンド ---------------------------------
|
|
152
|
+
// localStorage / indexedDB のセマンティクスは Polyfills.cpp の JS 側が実装し、
|
|
153
|
+
// ここは名前ごとのファイル I/O のみを担う。
|
|
154
|
+
// 保存先: %LOCALAPPDATA%\Next2D\<name>.json
|
|
155
|
+
// «EXTEND» コンソール実機では XGameSave への置き換えが必要 (devkit 後の作業)。
|
|
156
|
+
std::wstring StorageFilePath(const std::string& name)
|
|
157
|
+
{
|
|
158
|
+
// ファイル名として安全な文字だけを残す
|
|
159
|
+
std::wstring safe;
|
|
160
|
+
for (char c : name) {
|
|
161
|
+
const bool ok = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
|
|
162
|
+
(c >= '0' && c <= '9') || c == '_' || c == '-';
|
|
163
|
+
safe.push_back(ok ? static_cast<wchar_t>(c) : L'_');
|
|
164
|
+
}
|
|
165
|
+
if (safe.empty()) {
|
|
166
|
+
safe = L"storage";
|
|
167
|
+
}
|
|
168
|
+
wchar_t base[MAX_PATH] = {};
|
|
169
|
+
if (!GetEnvironmentVariableW(L"LOCALAPPDATA", base, MAX_PATH)) {
|
|
170
|
+
return safe + L".json"; // フォールバック: カレント
|
|
171
|
+
}
|
|
172
|
+
std::wstring dir = std::wstring(base) + L"\\Next2D";
|
|
173
|
+
CreateDirectoryW(dir.c_str(), nullptr);
|
|
174
|
+
return dir + L"\\" + safe + L".json";
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// __next2d_storage_load(name) -> string
|
|
178
|
+
void StorageLoad(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
179
|
+
{
|
|
180
|
+
v8::Isolate* isolate = args.GetIsolate();
|
|
181
|
+
const std::string name = args.Length() > 0
|
|
182
|
+
? v8util::ToStdString(isolate, args[0]) : "localStorage";
|
|
183
|
+
HANDLE h = CreateFileW(StorageFilePath(name).c_str(), GENERIC_READ, FILE_SHARE_READ,
|
|
184
|
+
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
|
|
185
|
+
if (h == INVALID_HANDLE_VALUE) {
|
|
186
|
+
args.GetReturnValue().Set(Str(isolate, ""));
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
DWORD size = GetFileSize(h, nullptr);
|
|
190
|
+
std::string data(size, '\0');
|
|
191
|
+
DWORD read = 0;
|
|
192
|
+
ReadFile(h, data.data(), size, &read, nullptr);
|
|
193
|
+
CloseHandle(h);
|
|
194
|
+
data.resize(read);
|
|
195
|
+
args.GetReturnValue().Set(Str(isolate, data));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// __next2d_storage_save(name, data)
|
|
199
|
+
void StorageSave(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
200
|
+
{
|
|
201
|
+
if (args.Length() < 2) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
v8::Isolate* isolate = args.GetIsolate();
|
|
205
|
+
const std::string name = v8util::ToStdString(isolate, args[0]);
|
|
206
|
+
const std::string data = v8util::ToStdString(isolate, args[1]);
|
|
207
|
+
HANDLE h = CreateFileW(StorageFilePath(name).c_str(), GENERIC_WRITE, 0,
|
|
208
|
+
nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
|
209
|
+
if (h == INVALID_HANDLE_VALUE) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
DWORD written = 0;
|
|
213
|
+
WriteFile(h, data.data(), static_cast<DWORD>(data.size()), &written, nullptr);
|
|
214
|
+
CloseHandle(h);
|
|
215
|
+
}
|
|
216
|
+
|
|
110
217
|
// new OffscreenCanvas(width, height)
|
|
111
218
|
void OffscreenCanvasConstructor(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
112
219
|
{
|
|
@@ -144,6 +251,19 @@ void InstallDomShims(v8::Isolate* isolate, v8::Local<v8::Object> global, HostCon
|
|
|
144
251
|
SetValue(isolate, global, "self", global);
|
|
145
252
|
NoopEventTarget(isolate, global);
|
|
146
253
|
|
|
254
|
+
// crypto (getRandomValues / randomUUID — OS 乱数)。
|
|
255
|
+
// player の WebGLUtil が ID 生成に crypto.randomUUID を使う。
|
|
256
|
+
{
|
|
257
|
+
v8::Local<v8::Object> crypto = v8::Object::New(isolate);
|
|
258
|
+
SetMethod(isolate, crypto, "getRandomValues", CryptoGetRandomValues);
|
|
259
|
+
SetMethod(isolate, crypto, "randomUUID", CryptoRandomUUID);
|
|
260
|
+
SetValue(isolate, global, "crypto", crypto);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// localStorage のファイル I/O バックエンド (Polyfills.cpp の JS が利用)
|
|
264
|
+
SetMethod(isolate, global, "__next2d_storage_load", StorageLoad);
|
|
265
|
+
SetMethod(isolate, global, "__next2d_storage_save", StorageSave);
|
|
266
|
+
|
|
147
267
|
// devicePixelRatio
|
|
148
268
|
SetValue(isolate, global, "devicePixelRatio",
|
|
149
269
|
v8::Number::New(isolate, host->device_pixel_ratio));
|
|
@@ -176,6 +296,8 @@ void InstallDomShims(v8::Isolate* isolate, v8::Local<v8::Object> global, HostCon
|
|
|
176
296
|
// document
|
|
177
297
|
v8::Local<v8::Object> document = v8::Object::New(isolate);
|
|
178
298
|
SetMethod(isolate, document, "createElement", CreateElement);
|
|
299
|
+
// 起動時点で読み込み完了扱い (ゲームの boot 分岐が readyState === "loading" で待機しないように)
|
|
300
|
+
SetValue(isolate, document, "readyState", Str(isolate, "complete"));
|
|
179
301
|
SetMethod(isolate, document, "getElementById",
|
|
180
302
|
[](const v8::FunctionCallbackInfo<v8::Value>& a) { a.GetReturnValue().SetNull(); });
|
|
181
303
|
SetMethod(isolate, document, "querySelector",
|
|
@@ -216,6 +338,11 @@ void InstallGlobalBindings(v8::Isolate* isolate, v8::Local<v8::Object> global, H
|
|
|
216
338
|
InstallAudio(isolate, global, host);
|
|
217
339
|
InstallWebGPU(isolate, global, host); // navigator.gpu
|
|
218
340
|
InstallGamepad(isolate, global, host); // navigator.getGamepads
|
|
341
|
+
|
|
342
|
+
// TextEncoder/TextDecoder/queueMicrotask。bootstrap.js はメインコンテキスト
|
|
343
|
+
// でのみ実行されるため、worker を含む全コンテキストにはここで注入する
|
|
344
|
+
// (Vite バンドルの worker は評価時に TextDecoder を要求する)。
|
|
345
|
+
InstallPolyfills(isolate, isolate->GetCurrentContext());
|
|
219
346
|
}
|
|
220
347
|
|
|
221
348
|
} // namespace next2d
|
|
@@ -104,6 +104,7 @@ void DispatchEvent(v8::Isolate* isolate, v8::Local<v8::Object> target,
|
|
|
104
104
|
if (target->Get(ctx, Str(isolate, onname.c_str())).ToLocal(&on) && on->IsFunction()) {
|
|
105
105
|
v8::TryCatch tc(isolate);
|
|
106
106
|
(void) on.As<v8::Function>()->Call(ctx, target, 1, args1);
|
|
107
|
+
v8util::ReportCaught(isolate, &tc, onname.c_str());
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
// __listeners[type]
|
|
@@ -118,6 +119,7 @@ void DispatchEvent(v8::Isolate* isolate, v8::Local<v8::Object> target,
|
|
|
118
119
|
if (arr->Get(ctx, i).ToLocal(&fn) && fn->IsFunction()) {
|
|
119
120
|
v8::TryCatch tc(isolate);
|
|
120
121
|
(void) fn.As<v8::Function>()->Call(ctx, target, 1, args1);
|
|
122
|
+
v8util::ReportCaught(isolate, &tc, ("listener:" + type).c_str());
|
|
121
123
|
}
|
|
122
124
|
}
|
|
123
125
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#include "HostContext.h"
|
|
4
4
|
#include "AssetLoader.h"
|
|
5
5
|
#include "v8/V8Util.h"
|
|
6
|
+
#include "v8/WeakHandle.h"
|
|
6
7
|
|
|
7
8
|
#include <cstring>
|
|
8
9
|
#include <vector>
|
|
@@ -84,12 +85,6 @@ void ResponseJson(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
|
84
85
|
args.GetReturnValue().Set(resolver->GetPromise());
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
// External に持たせた vector を解放するための弱参照コールバック
|
|
88
|
-
void ReleaseResponseData(const v8::WeakCallbackInfo<std::vector<uint8_t>>& info)
|
|
89
|
-
{
|
|
90
|
-
delete info.GetParameter();
|
|
91
|
-
}
|
|
92
|
-
|
|
93
88
|
v8::Local<v8::Object> MakeResponse(v8::Isolate* isolate, v8::Local<v8::Context> ctx,
|
|
94
89
|
std::vector<uint8_t>&& bytes, bool ok, int status)
|
|
95
90
|
{
|
|
@@ -101,8 +96,7 @@ v8::Local<v8::Object> MakeResponse(v8::Isolate* isolate, v8::Local<v8::Context>
|
|
|
101
96
|
response->SetInternalField(0, v8::External::New(isolate, data));
|
|
102
97
|
|
|
103
98
|
// GC 時に解放
|
|
104
|
-
|
|
105
|
-
handle->SetWeak(data, ReleaseResponseData, v8::WeakCallbackType::kParameter);
|
|
99
|
+
v8util::AttachWeak(isolate, response, data);
|
|
106
100
|
|
|
107
101
|
v8util::SetValue(isolate, response, "ok", v8::Boolean::New(isolate, ok));
|
|
108
102
|
v8util::SetValue(isolate, response, "status", v8::Integer::New(isolate, status));
|