最終更新: 2026-11-10 過去のマイナーバージョンアーカイブ: - spec/walkthrough/v0.1.md
(v0.1系: 初期開発〜モダンWebブラウザ基盤)
kernel/x509.rs, tls12.rs,
tests/src/test_x509.rs)OID_SHA256_WITH_RSA /
OID_SHA384_WITH_RSA / OID_SHA512_WITH_RSA
に対応する
verify_spki_signature・parse_rsa_public_key・verify_rsa_pkcs1_v15
を実装し、証明書チェーン中間検証および tls12.rs の
ServerKeyExchange(SKE)署名検証を ECDSA / RSA 両対応化。YYMMDDHHMMSSZ) および GeneralizedTime
(YYYYMMDDHHMMSSZ) を UNIX 秒へ変換する
parse_x509_time と
X509Certificate::verify_validity_at
を実装し、verify_certificate_chain_at で
CertificateNotYetValid / CertificateExpired
を検出可能にした。os_lib/h264.rs, h264_cabac.rs,
h264_decode.rs, mse.rs, fmp4.rs,
tests/src/test_h264_error_handling.rs)BitReader::read_bits
のバイト単位バッチ読み出し化、peek_bits16 /
skip_bits の追加、CABAC 正規化ループでの
leading_zeros() を使った複数ビット一括取得
(CabacEngine::bits(shift))、および CAVLC
read_coeff_token の 16bit 先読み一括マッチを導入。SourceBufferCore::find_next_sync_pts_after_gap
による未バッファ PTS
ギャップ後の次キーフレーム探索復帰を実装・検証。os_lib/js/ast.rs, interp/bytecode.rs,
eval_expr.rs, properties.rs,
selftest.rs,
tests/src/test_js_ast_size.rs)CODE_AFTER_CALLS を 8 → 2
へ短縮し、かつ stmts_has_loop によりループを含む関数は 1
回目の呼び出し時から即座にバイトコードコンパイル対象化(16KB
未満の遅延関数本体は 1 回目の stmts()
で解析結果をキャッシュ)。Op::NewTarget・Op::Closure(関数/アロー式)・Op::Regex(正規表現リテラル)・BigIntLit
を直接命令化。さらにラベル付きループ/ブロック(is_labeled_block,
pending_labels)および for-in /
for-of 入れ子時の iter_depth
アンワインド(brk_iters,
cont_iters)を実装。set_index_fast で
ObjKind::Bytes
の範囲外書き込みがフォールバックで文字列プロパティ化されるのを防ぎ、即座に
None を返すよう修正。make test 全 2,640 件
PASS(0 failed, 18 ignored)、make all
警告ゼロ・エラーゼロで kernel8.img 生成完了。spec/SYLFS.md の「電源断への備え」。double_refs=41)。arm/v1/src/kernel/fs/mod.rs・block.rs)rename_file:
古い名前の取り外し(連鎖の途中なら手前の節を写す)と新しい名前の登録を同じ木の写しで行い、スーパーブロック
1
回で切り替える。古いメタデータは確定の後で返す(削除の印は使わない)。truncate_file: 新しい溢れのチェーンを空きセクタに書く →
メタデータ 1 セクタで切り替える → 古い物を返す。0
埋めはやめた(読み出しは size まで)。recover_after_power_loss:
到達性を調べ、ビットマップを届くセクタだけ使用中に書き直す。mount
で既存の記憶装置を開いたときに呼ぶ。fsck
と到達性の走査を共有する。save_file:
スーパーブロックの書き込みに失敗したら、メモリ上の根を前に戻す。RAMDisk::write_budget(n
回書いたら以後の書き込みを失敗させる)、MirroredBlockDevice::backend_mut、SylFS::open(スーパーブロックを読んで開く)を足した。arm/v1/tests/src/test_fs_power_loss.rs、新規)arm/v1/src/kernel/fs/mod.rs・btree.rs・os_lib/aura/builtins/os_fs.rs)CowBTree::collect_node_sectors
を追加)・メタデータの連鎖・データ・溢れのチェーンから届くセクタに印を付け、ビットマップと突き合わせる。FsckReport
に
leaked_sectors・unallocated_refs・double_refs
を足した。Aura の fsck() の表示にも足した。btree_node_count
は以前メタデータの数を入れていたので、木の節点の数に直した。arm/v1/tests/src/test_fs_fsck_random.rs、新規)delete_file
は B-tree からキーを消せないので、メタデータに「削除済み」の印を付けて 1
セクタ残していた。別々のパスを作っては消すと(一時ファイルのような使い方)、その数だけ空きが減り、512
セクタで 473 個目に満杯になった。collect_leaf_entries
は循環の検出のため、たどった節点の総数が 100
を超えると打ち切っていた。ファイルが約 1,200 個を超えると
list_files・fsck から黙って漏れた(3000 個中
1483 個)。arm/v1/src/kernel/fs/mod.rs・btree.rs)search・collect_leaf_entries は値 0
を「無い」とする。arm/v1/tests/src/test_fs_space_reuse.rs)削除するとメタデータのセクタも返る・別々のパスを作っては消しても満杯にならない・ファイルが数千個あっても一覧から漏れない(3
件とも修正前は失敗)、満杯でも削除できる。削除済みメタデータセクタを緊急再利用できる
は、上の試験に置き換えた。rename_file
は移動元のメタデータを最初に「削除済み」にしていた。空きが 1
セクタしか無いと、新しいメタデータは書けても B-tree
の節点を写せずに失敗し、元の名前でも新しい名前でも読めなくなった。arm/v1/src/kernel/fs/mod.rs)arm/v1/tests/src/test_fs_rename.rs)sylfs_rename_file_途中で失敗しても元のファイルは残る、sylfs_rename_file_を繰り返しても空きが減り続けない(どちらも修正前は失敗)。spec/SYLFS.md
に名前の変更の手順を足し、既知の制約を書き直した。JS
の文字列添字取得(str[i])、配列末尾への要素書き込み(arr[arr.length] = v
/ arr[i] = v)、TypedArray のインデックス書き込み、および
Op::Binary
での文字列+数値/真偽値連結をゼロアロケーション・直接インラインパスへ刷新。全
2,619
件の完全隔離単体テスト通過とベアメタルビルド(警告ゼロ)を達成した。
str[i])の O(1) ゼロアロケーション解決
(arm/v1/src/os_lib/js/interp/properties.rs)get_index_fast は
Value::Object のみを受け付けていたため、str[0]
や s[p + 2] のような文字列の添字参照が毎回
None となり、呼び出し元が
to_property_key(&key) で "0"
等の一時文字列をヒープ確保した上で get_property →
string_get_prop → key.parse::<usize>()
という冗長な経路を辿っていた。get_index_fast に Value::Str(_)
パスを新設。cached_units(self, obj)
から直接 units.at(i)
を引き、self.char_unit_val(ASCII 1〜128
は実行環境ごとのシングルトンキャッシュ)を返却。set_index_fast)の高速化 &
TaKind ゼロアロケーション化
(properties.rs)arr[i] = val(特に i == items.len()
の末尾追加や配列構築ループ)において、従来の set_index_fast
は i < items.len() の範囲内上書きしか処理せず、毎回
set_property
にフォールバックして文字列変換・プロトタイプ走査・key.parse()・resize
を行っていた。set_index_fast で
b.props.get("_ta_kind")
を引き、t.to_js_string() で毎回 String
をヒープ確保していた(99.9% の通常配列では不要)。b.props.is_empty() なら即座に TypedArray
検査をスキップ。プロパティを持つ場合でも Copy 型の
TaKind::from_name(s.as_str())
へ直接変換し、to_js_string()
によるヒープ確保を完全に撤廃。i == items.len() の場合は直接
items.push(val)、i > items.len()
かつ近傍(i - items.len() <= 64)の場合は直接
items.resize(i + 1, Value::Undefined)
を行い、文字列変換ゼロで高速配列拡張を実現。Op::Binary での文字列+数値/真偽値インライン連結
(bytecode.rs, selftest.rs)Op::Binary(BinaryOp::Add)
高速パスは (Value::Str, Value::Str)
のみ対応しており、"count: " + 42 や
100 + "px"、"flag=" + true などが毎回
op_binary_slow(eval_binary)へフォールバックしていた。(Value::Str, Value::Number) /
(Value::Number, Value::Str)
において、super::super::value::fmt_number(整数高速フォーマット対応)で数値を文字列表現へ変換し、事前の
with_capacity で 1 回のヒープ確保のみで連結完了。(Value::Str, Value::Bool) /
(Value::Bool, Value::Str) も固定文字列 "true"
/ "false" を用いて直接連結。selftest.rs)に
str[i]・配列逐次拡張・文字列+数値/真偽値連結の複合テストを追加し、正常動作を実証。JS バイトコード実行エンジンにおいて、配列メソッド取得の O(1)
解決、配列操作(push/pop/shift)のインライン直接実行、文字列キー添字プロパティの直接アクセス、およびプロパティキャッシュの
2
態(Bimorphic)インラインキャッシュへの拡張を実施。構文木フォールバックや中間引数ベクタ確保を大幅に削減し、ホスト環境単体テスト全
2,619 件の通過(実行時間 3.35
秒)とベアメタルビルド(警告ゼロ)を達成した。
get_prop_fast O(1)
解決 (arm/v1/src/os_lib/js/interp/bytecode.rs)get_prop_fast
では、ObjKind::Array および
ObjKind::Bytes(TypedArray)において
name == "length"
のみが特別扱いされており、push, pop,
shift, slice, indexOf,
join, concat などの配列メソッド参照はすべて
None
を返していた。そのため配列メソッドの呼び出しごとに構文木参照(get_property_of_expr)や重い多段プロトタイプ連鎖走査(get_property)へフォールバックしていた。ObjKind::Array および ObjKind::Bytes
において、length 判定に加えて
array_builtin_fn(name) および
object_builtin_fn(name) を直接照会。self.builtin_fn_val(bname, f)(関数ポインタアドレスをキーとするシングルトンキャッシュ)を即座に返却。ObjKind::Plain
においても、自身にプロパティやアクセサが無くデフォルトプロトタイプ(proto.is_none()
かつ
!is_null_proto())の場合、object_builtin_fn(name)(hasOwnProperty
等)を即座に解決可能にした。arr.push / arr.pop /
arr.shift)のインライン直接実行(Builtin Inlining)
(bytecode.rs)arr.push(x) や
arr.pop() は YouTube プレイヤーや暗号・gzip(pako)・JSON
処理で毎秒膨大に呼ばれるが、呼び出しごとに vm_stack
から引数を arg_pool ベクタへ退避し、call_value
と call_native_fn を経由して arr_push
を呼ぶオーバーヘッドが存在した。Op::Call
の入口において、関数がネイティブ関数かつレシーバが通常の
ObjKind::Array(凍結・封印・レアプロパティ無し)であるかを
O(1) で判定。arr_push:
items.extend(self.vm_stack.drain(at..))
により、スタックの引数を中間ベクタのヒープ確保なしに直接配列バッファへムーブし、更新後の長さをスタックへ積んで即座に関数復帰。arr_pop / arr_shift: 引数 0
個の呼び出しにおいて、直接 items.pop() /
items.remove(0) を行いスタックへプッシュ。Op::CallWithArgs: スプレッド引数を含む
arr.push(...items) においても、展開済み配列から直接
items.extend_from_slice でインライン結合。Op::Index,
Op::MethodIndex, Op::StoreIndex)の高速パス
(bytecode.rs)obj["foo"] や
dict[key] のようにキーが文字列の場合、従来の
get_index_fast / set_index_fast
は数値インデックスのみ対応していたため、毎回
to_property_key(一時ヒープ文字列生成)を呼び出してから
get_property / set_property
の多段探索へ回っていた。op_index / op_method_index:
key が Value::Str かつ obj が
ObjKind::Plain(アクセサ無し)の場合、b.props.get(&**s)
で直接プロパティを取得。op_store_index:
プレーンオブジェクト(プロトタイプ無し・拡張可能・凍結封印無し)に対する文字列キー代入において、b.props.insert(PropKey::from(&**s), v)
で直接値を保存。bytecode.rs, selftest.rs)PropSlot
は単一のエントリ(Monomorphic)しか保持できなかったため、同一ループや共通関数内で
2 種類の異なるオブジェクト構造(例: プロパティ順序の異なるオブジェクトや
Element と TextNode
など)が交互に渡されると、毎回キャッシュミスを起こしてスロットを上書き(キャッシュスラッシング)していた。PropSlot を slot0 と slot1 の
2 つの PropSlotEntry を保持する構造へ刷新。slot0 を照合し、ミスした場合は slot1
を照合。slot1 でヒットした場合は MRU(Most Recently
Used)規則により slot0 と slot1
をスワップして次回アクセスの先頭ヒット率を最大化。set_own / set_proto
により slot0 を slot1 へ退避し、新しい構造を
slot0 へ登録。selftest.rs)に配列ビルトインインライン実行・文字列添字高速化・Bimorphic
IC(交互アクセス)の検証ケースを追加し、正常動作を実証。モダンWebおよびYouTubeプレイヤーで頻出するオプショナルチェイニング構文(a?.b,
a?.[b], a?.(), a?.b(),
a.b?.())、オブジェクト/配列スプレッド構文({ ...a },
[ ...a ])、引数スプレッド呼び出し(fn(1, ...items, 2))、メンバー/添字論理代入(obj.prop ??= v,
obj[key] ||= v
等)、および数値ビット演算/剰余/文字列連結のインライン高速パスを導入し、構文木評価フォールバック(Op::Eval
/
eval_binary)を大幅に削減。併せてグラフィックス描画エンジンにおいて
R/B チャンネルを 1 つの 32bit レジスタで同時並列処理する 32bit
SWAR(SIMD Within A Register)アルファ合成を配備し、全 2,616
件のテスト通過とベアメタルビルド(警告ゼロ)を達成した。
arm/v1/src/os_lib/js/interp/bytecode.rs,
value.rs)plain_object 判定により
?. を含む式がすべて Op::Eval
へフォールバックしていただけでなく、オブジェクトスプレッド
{ ...a } や配列スプレッド
[ ...a ]、引数スプレッド呼び出し
fn(...args)、メンバー/インデックス論理代入
obj.x ??= v / obj[k] ||= v が未対応のため
Op::Eval
に落ちていた。さらに数値ビット演算(&, |,
^, <<, >>,
>>>,
%)や文字列連結がバイトコード実行ループ内で即決されず
eval_binary
へフォールバックしていた。これらは暗号・ハッシュ・YouTube
プレイヤー(Kevlar/Desktop)で毎秒大量に実行されるため、大きなボトルネックとなっていた。unroll_chain /
compile_chain):
Member, Index,
Call
式をループで走査し、ベース式(最深のオブジェクト・関数式)と一連の操作列
Vec<ChainOp>(正順)へ平坦化。Member または Index
の直後に Call が続くパターン)を検知し、既存の高速命令
Op::Method / Op::MethodIndex へ最適結合。Op::JumpIfNullish(target): スタックトップが
nullish(Null | Undefined)なら
Value::Undefined
に置換して直ちにチェイン末尾(target)へジャンプ。非
nullish ならスタックを維持して次の操作へ進行。Op::JumpIfNullishMethod(target): スタックトップが
[..., func, this] の状態で func が nullish
なら func と this を共に pop して
Value::Undefined を push し、引数評価と Call
命令を完全にスキップしてジャンプ。Op::AndKeepMember / OrKeepMember /
NullishKeepMember: obj.prop &&= v /
||= / ??= において、短絡時はスタックの
obj
を削除して現在のプロパティ値のみを残してジャンプ。非短絡時はプロパティ値を
pop して obj を残し、右辺値評価と StoreMember
を実行。Op::AndKeepIndex / OrKeepIndex /
NullishKeepIndex: obj[key] &&= v /
||= / ??= において、短絡時はスタックの
obj, key
を削除して現在の値のみを残してジャンプ。非短絡時は値を pop して
obj, key を残し、右辺値評価と
StoreIndex を実行。Op::SpreadObject: オブジェクトリテラル
{ ...src } において、src
の全列挙可能プロパティ(配列の場合は数値インデックス、ゲッターの場合は呼び出し結果)を新規オブジェクトへ直接コピー。Op::NewArray, Op::ArrayPush,
Op::ArraySpread: 配列リテラル [ ...src, 1 ]
において、スプレッド要素 src
のイテラブル要素(配列/文字列/Set/Map等)を新規配列の末尾へ O(N)
一括追加。Op::CallWithArgs: 引数にスプレッドを含む呼び出し
fn(a, ...items, b) / obj.method(a, ...items)
において、展開された引数配列から直接 call_value
を実行し、引数転送での構文木フォールバックを完全排除。Obj::array_push を value.rs
に新設し、配列への要素追加をゼロオーバーヘッド化。number_binary に Mod, BitAnd,
BitOr, BitXor, Shl,
Shr, UShr
を追加し、数値同士のビット操作をインライン直接計算。Op::Binary
の実行ループにおいて、文字列同士の連結(+)、文字列/真偽値/null/undefined
等価比較を即座に解決する高速パスを配備。selftest.rs):
arm/v1/src/os_lib/web_engine/alpha_blend.rs)blend_argb_over がピクセル単位で繰り返し呼ばれていたが、R,
G, B 各チャンネルごとに個別の blend_channel
関数呼び出しとビットシフト・マスクを行っており、1 ピクセルあたり 3
回の乗算と多数の分岐・シフトが必要だった。0x00FF_00FF
マスクで抽出し、1 つの 32bit レジスタ内で
src_rb * a + dst_rb * (255 - a) + 0x007F_007F
を同時に実行。255 * 255 + 127 = 65,152 <= 0xFFFF であり、16bit
境界を跨ぐオーバーフローは数学的に発生しない。blend_channel
と全く同一の四捨五入(+ 127)および高速除算ビットトリック(val + 1 + (val >> 8) >> 8)を維持し、従来の完全スカラー合成とビット単位で
100% 完全一致する出力を保証しながら、乗算回数を 3 回 → 2
回へ削減し演算ステップを大幅に短縮。YouTube 長時間自律再生に向けた SABR
再生セッション(SabrPlaybackSession)とブラウザ通信・動画再生スレッドの動的結合、本物の
BotGuard Attestation
署名(po_token)の自動抽出・キャッシュ引き継ぎ機構、および独自ファイルシステム
SylFS における 128bit
ハッシュ衝突二次探索チェーン(collision_next_sector)を設計・実装し、完全隔離単体テスト(全
2,615 件通過)とベアメタルビルド(警告ゼロ)を達成した。
arm/v1/src/os_lib/sabr.rs,
static_classlist.rs, video.rs)数分〜数時間の YouTube
動画再生を自律継続させるため、クライアントが送信する SABR
リクエストおよびサーバー応答 UMP
パケットからセッション情報を動的に抽出・追従するパイプラインを確立した。
- Attestation 署名(po_token)動的抽出
(extract_po_token_from_request): - リクエスト本文
protobuf 内の field 19 (streamerContext) の subfield 2
(poToken) をバイナリレベルで正確に走査・抽出。BotGuard
が生成した本物の 77 バイト等の暗号バイナリ署名を即座に取得。 -
URL 動画 ID 抽出
(extract_video_id_from_url): -
id=..., docid=..., v=...
などのクエリパラメータから動画固有 ID を自動抽出。 -
SabrPlaybackSession の動的更新と Attestation
署名引き継ぎ: - update_with_request:
リクエスト送信時に URL(CPN・動画ID)、po_token、および
PlaybackWindow(再生ヘッド、バッファ終端時刻、次シーケンス番号
next_sq)をセッションへ即時同期。 -
encode_request_body: 次セグメント(sq=8
以降)の自律要求本文生成時に、キャッシュされた本物の
po_token を自動反映。 -
グローバル連携と動画再生スレッド同期: -
GLOBAL_SABR_SESSION, record_sabr_request,
record_sabr_response
を新設し、ブラウザ通信パイプライン(static_classlist.rs の
do_http_request_full)と完全結合。 -
動画再生スレッド(video.rs の
play_media_source)内で再生フレーム更新(pts_sec)およびシーク時にセッションの再生ヘッド位置を自動更新。バッファ枯渇時のスタール検知ログを配備。
- 自律バッファ再充填パイプライン
(trigger_sabr_auto_refill): -
SabrTransportFn
ハンドラ登録機構を導入し、純粋ロジックと実ネットワーク(TcpIpStack)の完全な責務分離を達成。
-
再生スタール時およびエビクション周期での先回り判定により、GLOBAL_SABR_SESSION
から動的に protobuf リクエストを生成して SABR サーバーへ HTTP POST
送信。 - 受信した UMP レスポンスから Part 21
メディアチャンクを抽出し、対象 MediaSource の
SourceBuffer(SOURCE_BUFFERS)へ直接供給する全自動補給経路を確立。JS
が重い・固まっている状況でも 63
秒以降の動画・音声を自律的に途切れなく継続再生可能。
arm/v1/src/kernel/fs/mod.rs)128bit FNV-1a
ハッシュの衝突発生時であっても、ファイルが破壊されたりエラーで異常終了することなく、同一ハッシュ値を持つ複数ファイルが完全に共存・透過利用できる二次探索チェーン(オーバーフローノード機構)を導入した。
- FileMetadata のセクタ容量ジャスト 508B
適合化: - pub collision_next_sector: u64 を新設。
- 構造体サイズは従来の 500 バイトから 508
バイトへ拡大し、DATA_SIZE_PER_SECTOR(512 - 4 バイト CRC32
= 508 バイト)に 1 バイトの無駄もなく完全に一致(コンパイル時
assert! で検証済み)。 - 衝突チェーン走査ヘルパー
(find_in_collision_chain): - B-Tree
が指す先頭セクタから collision_next_sector
をたどり、目的のファイル名を持つメタデータセクタを特定。 - SylFS
主要操作の完全衝突対応: - save_file:
同一ファイルの上書き時は該当メタデータセクタを再利用し古いデータセクタを即座に解放。別ファイル名(ハッシュ衝突)の保存時は「頭出し連結(O(1))」で既存の先頭セクタを
collision_next_sector に紐づけて B-Tree キーを更新。 -
read_file_checked:
チェーン内を走査して合致するファイルからデータを読み出し。ハッシュ衝突によるエラーを完全排除。
- delete_file:
チェーン内の合致ノードを論理削除(filename[0] = 0)しデータセクタと溢れチェーンを解放(後続へのリンクは維持)。
- update_labels / truncate_file /
stat_file / rename_file /
list_files:
すべて衝突チェーンに対応し、チェーン内の全ファイルを漏れなく操作・一覧化。
test_fs_collision.rs(新規作成):
sylfs_collision_chain_read_and_list:
衝突チェーン構築、チェーン経由のファイル特定・読み出し、list_files
による全ファイル列挙、先頭ファイル論理削除後の後続ファイル生存を実証。test_sabr.rs(機能追加):
test_extract_video_id_from_url: 多様なクエリからの動画
ID 抽出検証。test_extract_po_token_from_request_and_session_caching:
リクエスト本文からの Attestation
署名抽出、セッション更新、次リクエストへの自動署名引き継ぎ検証。test_global_sabr_session_recording_pipeline:
送受信パイプライン連携とセッション自律更新検証。test_trigger_sabr_auto_refill_pipeline:
自律補給パイプライン(モック HTTP
POST、セッション次シーケンス進行、バッファ伸長、SourceBuffer
へのデータ供給)の動作検証。make test): 全 2,616 件 PASSED(0 failed,
18 ignored, 警告ゼロ)(+5件純増)。make all):
警告ゼロ・エラーゼロで kernel8.img
正常生成確認済み。Web ブラウザおよび HTTP
キャッシュストアにおいて、コンテンツネゴシエーション(Accept-Encoding,
Accept-Language, User-Agent
等)に応じた正しいキャッシュ分離と安全性を保証する RFC 7234 §4.1 準拠の
Vary
ヘッダ解析・照合パイプラインを設計・実装し、ブラウザの通信層(fetch/XHR)へ完全結合した。併せて完全隔離単体テストの拡充(全
2,611 件 PASSED)とカーネルビルドの警告ゼロ生成を達成した。
arm/v1/src/kernel/http_cache.rs)同一の URL
であっても、クライアントの要求ヘッダ(言語、エンコーディング、端末種別等)によってレスポンス内容が異なる場合に、誤ったキャッシュの再利用(キャッシュ汚染)を防ぐ厳格な
Vary 照合機構を実装した。 - HttpCacheEntry
の拡張: -
vary_fields: Vec<(String, Option<String>)>:
レスポンスの Vary
ヘッダに指定された要求ヘッダ名と、キャッシュ保存時にクライアントが送信していた要求ヘッダ値のスナップショットを保持。
- vary_star: bool:
Vary: *(如何なる要求とも一致しない動的レスポンス)を安全に検知。
- from_headers_and_req: レスポンスヘッダから
Vary(カンマ区切り)を抽出し、対応するリクエストヘッダ値を自動紐付け。
- matches_request_headers:
現在のリクエストヘッダを走査し、保存時の Vary
要求ヘッダ(大文字小文字を問わないヘッダ名)と現在の要求ヘッダ値が完全一致するか検証。値の不一致やヘッダ欠落時は即座に不一致(false)と判定。
- HttpCacheStore API 拡張: -
lookup_with_headers(url, req_headers, now_sec):
要求ヘッダを照合し、Vary 条件を満たさない場合は安全に
CacheLookup::Miss を返却。 -
put_with_request_headers(url, status, resp_hdrs, req_hdrs, body, now_sec):
クライアント要求ヘッダとサーバー応答ヘッダを紐づけてキャッシュ登録。Vary: *
の場合は保存を即時拒否。
fetch/XHR)への完全自動結合
(arm/v1/src/os_lib/js/builtins/static_classlist.rs)do_http_request_full_inner において、GET
リクエスト送信前に送出予定のリクエストヘッダ一覧(Cookie, User-Agent,
Accept 等)を lookup_with_headers
に引き渡し、コンテンツネゴシエーションを正確に尊重したキャッシュヒット/ミスを判定。put_with_request_headers
を呼び出し、リクエストヘッダとレスポンス Vary
ヘッダの対応関係を自動記録。実サイト閲覧時における安全かつ高速なキャッシュ利用を実現。test_http_cache.rs:
vary_ヘッダ一致によるキャッシュヒットと不一致によるキャッシュミス
を追加(ヘッダ値一致での Hit、不一致での Miss、ヘッダ欠落での
Miss、Vary: * の保存拒否)。make test):
全 2,611 件 PASSED(0 failed, 18 ignored,
警告ゼロ)(+1件純増)。make all):
警告ゼロ・エラーゼロで kernel8.img
正常生成確認済み。normalize_path・高速ファイル名変更
rename_file・詳細統計 stat_file・Aura
ファイル操作/キャッシュ管理ビルトイン拡充(ユニットテスト 2,610
件)独自ファイルシステム SylFS
におけるパス文字列の一元正規化(normalize_path)、全データセクタのコピーや再割り当てを行わない
O(1)
高速ファイル名変更(rename_file)、ファイル詳細統計取得(stat_file
/ FileStat)、Aura シェルのファイル操作(mv /
rename, truncate,
stat)の高速化・拡充、および HTTP
キャッシュ管理ビルトイン(cache.stats,
cache.clear,
cache.remove)を実装・検証完了し、全 2,610
件の完全隔離単体テスト通過と警告ゼロでのカーネルビルドを達成した。
normalize_path)
(arm/v1/src/kernel/fs/mod.rs)ファイルアクセス時のパス表記揺れによる B-Tree
インデックス重複やアクセス不整合を根本的に排除するため、SylFS
レイヤでパス正規化関数を導入した。 - 正規化規則: -
先頭スラッシュの補完(絶対パス化)。 - 連続するスラッシュ
// を単一の / へ統合。 -
.(カレントディレクトリ)の安全な除去。 -
..(親ディレクトリ)の解決(ルートを越えて上位へ行くことは安全に防止)。
- ルート単体 / を除く末尾スラッシュの除去。 -
全主要 API への適用: - save_file,
read_file_checked, delete_file,
truncate_file, update_labels,
rename_file, stat_file の入口で一元適用。 -
"docs/../data.txt", "./data.txt",
"//data.txt" など多様な表記からのアクセスが一貫して同一の
"/data.txt" として扱われることを保証。
rename_file)
(arm/v1/src/kernel/fs/mod.rs)数メガ〜数百メガバイトに及ぶ巨大ファイルであっても、データセクタを 1
バイトもコピー・再配置することなく 0ms
で安全にファイル名を変更できるリネーム API を実装した。 -
アーキテクチャ: -
旧ファイルのメタデータセクタを特定し、全エクステント(インライン 18 本 +
溢れエクステントチェーン)およびファイルサイズ・ラベルをそのまま引き継いだ新メタデータノードを構築。
- 新しいメタデータセクタを 1 つ確保して新ハッシュキーで B-Tree
に挿入(CoW 更新)。 - 旧メタデータノードは filename[0] = 0
として論理削除をマーク(旧ハッシュキーで引かれても削除済み扱いとなり、整合性を維持)。
- btree.take_replaced() による旧 B-Tree
節点の安全な即時解放によりセクタリークを防止。 -
移動先に同名ファイルが既に存在する場合は
Err("destination file already exists")
を返して上書き破壊を防止。 -
volatile(RAMDisk)への透過ルーティングも完備。
stat_file /
FileStat) (arm/v1/src/kernel/fs/mod.rs)ファイルの実体メタデータを走査し、ファイル名、サイズ(バイト数)、ラベル一覧、インライン+溢れエクステント数、実際にディスク上で占有しているセクタ総数、溢れチェーンの有無をひとまとめにした
FileStat を提供。
builtins.rs,
os_fs.rs, os_net.rs,
system.rs)mv / rename の高速化:
rename_file
を実行。クロスデバイス移動等の場合のみ安全にコピー&デリートへフォールバックするハイブリッド設計を確立。truncate(name, size) /
fs.truncate:
stat(name) / fs.stat:
cache.stats() / cache.clear() /
cache.remove(url):
help コマンドドキュメントを最新化。test_fs_rename.rs:
5件の完全隔離単体テストを新設(多様なパス正規化、正規化アクセスの透過性、データセクタ維持とBTree整合性、同名衝突等の安全なエラー処理、詳細統計取得)。make test):
全 2,610 件 PASSED(0 failed, 18 ignored,
警告ゼロ)(+5件純増)。make all):
警告ゼロ・エラーゼロで kernel8.img
正常生成確認済み。コードベース全体の整合性精査、コンパイル警告の完全撲滅(テスト・カーネル双方で
Zero Warnings 達成)、独自ファイルシステム SylFS
における揮発性記憶域(volatile RAMDisk)への透過フォールバック強化、HTTP
Range 分割受信における総サイズ未知(*)時の終端短縮チャンク
EOF 自動検出、および今後の開発に向けた潜在的課題の洗い出しと
TODO.md への反映を完了した。
dns.rs のログ変数警告解消
(arm/v1/src/kernel/net/dns.rs):
warn! マクロが空に展開される)において
sent_ok の代入が unused assignment
警告となっていた一方、カーネル本体ビルドではログ引数として必須であるという二重の制約を解決。#[allow(unused_assignments, unused_variables)]
と安全な let _ = sent_ok;
の配置により、テスト・カーネルの双方で警告・エラーゼロを両立。unexpected_cfgs 警告解消
(arm/v1/tests/Cargo.toml):
_benchmarks feature を
tests/Cargo.toml の [features]
セクションに明記し、Rust 2024 エディションに向けた
unexpected_cfgs 警告を一掃。arm/v1/tests/src/lib.rs):
non_snake_case
を追加し、意図や仕様を明確に表現する日本語テスト関数名に対するスタイル警告を抑制。make test
のビルド時警告が完全にゼロとなった。arm/v1/src/kernel/fs/mod.rs)truncate_file および delete_file
において、storage_policy::is_disposable
の判定パスに依存せず、microSD 側にメタデータが存在しない場合でも
volatile
側をフォールバック検索して安全にファイル切り詰めおよびセクタ解放・削除を完遂できるように改修。update_labels, read_file_checked,
delete_file, truncate_file
の全メタデータ操作で一貫した透過フォールバック整合性を確立。http_body.rs)*)として返してきた場合でも、要求したチャンク長より短い
last_byte
で返ってきた端数チャンクを受信した時点で、その終端バイトを
total_size として動的に学習し、即座に
Completed(EOF)へ安全遷移する境界判定を追加。spec/TODO.md)rename_file):
normalize_path):
./、../、冗長なスラッシュ
//、末尾スラッシュの正規化を SylFS
の入口で一元適用し、パス表記揺れによる B-Tree
重複登録やアクセス不整合を防止。HttpCacheStore) の
Vary ヘッダ照合とシェル向け管理コマンド:
Vary ヘッダ照合検証、および Aura
シェルからの cache.clear() / cache.stats()
ビルトイン。SabrPlaybackSession と video_playback
スレッドループの自律補給結合。make test):
全 2,605 件 PASSED(0 failed, 18 ignored,
警告ゼロ)(+1件純増)。make all):
警告ゼロ・エラーゼロで kernel8.img
正常生成確認済み。HttpRangeStream・SylFS
複数ラベル検索・全ラベル一覧取得・高速ラベル更新 API・Aura 統合(TODO
ストリーミング受信 / SylFS / Aura / ユニットテスト 2,604 件)大容量メディア(動画・音声・アーカイブ)のストリーミング再生を支える
HTTP 206 Partial Content
分割逐次受信ステートマシン(HttpRangeStream /
RangeStreamState)の設計・実装、独自ファイルシステム SylFS
における複数ラベル AND/OR
検索(find_by_labels)・全ユニークラベル一覧取得(list_all_labels)・データセクタ再配置なしの
O(1) 高速メタデータラベル更新(update_labels)、および Aura
シェル・スクリプト連携(find, setlabel,
labels, addlabel)を完備し、全 2,604
件の完全隔離単体テスト通過とカーネルビルドの正常生成を達成した。
HttpRangeStream) の設計と実装
(arm/v1/src/kernel/http_body.rs)数メガ〜数百メガバイトに及ぶ大容量動画や音声ファイルを一度にメモリへ読み込むとヒープ枯渇を招くため、指定チャンクサイズ(例:
64KB)ごとに Range: bytes={start}-{end}
で段階的に要求し、ヒープを圧迫することなく安全にストリーム再生バッファへ供給するステートマシンを実装した。
- 状態管理 (RangeStreamState): -
Idle: 次のチャンク要求準備完了。 -
Fetching { start, end }:
チャンク要求中(インクルーシブ範囲)。 - Completed:
EOF(ストリーム終端)到達。 - Error:
範囲不整合・不正ステータスコード・パースエラー。 -
逐次要求範囲の動的計算 (next_request_range /
build_next_range_header): -
current_offset と chunk_size に基づき
(start, end) を算出。 - 全体サイズ total_size
が判明している場合は終端(tot - 1)に自動クリップ。 -
Range: bytes={start}-{end}\r\n ヘッダ文字列を即時生成。 -
応答検証と本文切り出し
(process_chunk_response): - HTTP 206 Partial
Content レスポンスの Content-Range
ヘッダを解析し、total_size
の動的学習、first_byte と現在のオフセットの一致を厳密検証。
- body_start から本文スライスを切り出し、受信バイト数分
current_offset を進めて状態を更新。 - Range
非対応サーバーから 200 OK
が返ってきた場合のフォールバックを完備(オフセット 0
なら全体を一括受理して Completed、オフセット > 0
なら安全にエラー化)。 - シーク・終端追従 (seek,
is_eof, remaining_bytes): -
任意オフセットへのシークに対応。終端ぴったりへのシーク時は即時
Completed へ遷移し、終端超過は安全に拒否。
arm/v1/src/kernel/fs/mod.rs)ファイルシステム SylFS
において、ファイルのタグ付け・検索・分類を強化する 3
つの中核機能を実装した。 - 複数ラベル検索
(find_by_labels): -
match_all: true(AND 検索):
指定されたすべてのラベルを含むファイルのみを抽出。 -
match_all: false(OR 検索):
指定されたいずれかのラベルを含むファイルを抽出。 -
全ユニークラベル一覧取得
(list_all_labels): -
ファイルシステム全体の全ファイルを走査し、登録されている全ラベルを重複排除・アルファベット昇順ソートして一覧返却。
- O(1) 高速メタデータラベル更新
(update_labels): - ファイル名から
B-Tree(fnv1a_128
ハッシュ)でメタデータセクタを特定し、データセクタ(エクステント)を一切再配置・コピーすることなく、メタデータセクタの
label_data のみを更新して書き戻し。 -
数十MB〜数百MBの巨大ファイルでも 0ms
でラベルの追加・変更・削除が可能。volatile(RAMDisk)にも透過ルーティング。
builtins.rs,
builtins/os_fs.rs, system.rs)Aura
シェルおよびスクリプトからファイルシステムのラベル機能を自在に活用できるように
API を整備した。 -
find("l1,l2" [, "and"|"or"]): 複数ラベルの
AND/OR 検索をサポート(トップレベルおよび fs.find)。 -
fs.addlabel(name, label) /
os.addlabel: 高速 update_labels
を用いた重複のない安全なラベル追加。 -
fs.setlabel(name, labels) /
os.setlabel: ファイルのラベル一括変更。 -
fs.labels([name]) /
os.labels:
全ラベル一覧表示(引数なし)、または指定ファイルのラベル表示。 -
help コマンドの出力ドキュメントを最新化。
test_http_body.rs:
4件の単体テストを追加(基本逐次取得フロー、未知サイズ動的学習、シーク動作、フォールバックおよびエラー検知)。test_fs_labels.rs:
3件の完全隔離単体テストを新設(複数ラベル AND/OR
検索、全ラベル一覧重複排除ソート取得、update_labels
による高速更新と整合性確認)。make test): 全
2,604 件 PASSED(0 failed, 18 ignored)(+7件純増)。make all):
警告ゼロ・エラーゼロで kernel8.img
正常生成確認済み。truncate_file・RFC 7234
キャッシュストア HttpCacheStore・HTTP
通信自動キャッシュ結合・Aura 音声制御 API・AudioStream /
HTMLMediaElement 音量連動の実装(TODO SylFS / Audio / HTTP / Aura /
ユニットテスト 2,597 件)独自ファイルシステム SylFS
における不要セクタ即時解放を伴うファイル切り詰め(truncate_file)機能の実装、RFC
7234 準拠の HTTP キャッシュディレクティブ解析・有効性判定・304
再検証基盤およびメモリ上 LRU
キャッシュストア(HttpCacheStore /
HttpCacheEntry / CacheControl)と HTTP
通信(fetch/XHR)への完全自動キャッシュ結合、Aura
シェルからの音量・ミュート・統計取得ビルトイン、およびカーネル連続音声出力基盤
AudioStream と Web エンジン HTMLMediaElement /
MSE の音量・ミュート双方向連動を確立し、全 2,597
件の単体テスト通過を達成した。
truncate_file)
の設計と実装 (arm/v1/src/kernel/fs/mod.rs)ファイルの部分削除・空ファイル化・ログや一時キャッシュの切り詰めに不可欠な
truncate_file を SylFS へ実装した。 -
インプレース縮小 & 不要エクステント即時解放: -
new_size < cur_size 時、必要セクタ数
needed_sectors
を算出し、現在ファイルが保持する全エクステント(インライン 18 本 +
溢れエクステントチェーン)を走査。 -
残すべきエクステント(kept_extents)と不要になった後続エクステント(freed_extents)へ厳密に分離し、境界セクタの余白は安全にゼロ埋め。
- free_extents(&freed_extents)
により不要セクタをビットマップへ即時返還(解放)し、ディスクリークを防止。
-
旧溢れチェーン(overflow_sector)を安全に解放した上で、縮小後のエクステント数に応じた新規溢れチェーンの再構築を完備。
- メタデータセクタへ新サイズと更新後エクステント配列を書き戻し。 -
ゼロバイト切り詰め (truncate(0)): -
ファイルの全データセクタおよび全溢れエクステントを解放しつつ、B-Tree
内のメタデータノードはそのまま維持。 - ゼロ拡張伸長
(new_size > cur_size): -
既存データを読み出し、差分サイズを 0 で埋めて安全に CoW 再保存。 -
ディスク整合性維持: - 切り詰め・伸長後も
fsck()
による検証で破損ゼロ(corrupted_sectors == 0)、健全性維持(is_healthy == true)を確認。
HttpCacheStore / HttpCacheEntry /
CacheControl, static_classlist.rs)Web ブラウザおよび HTTP
通信においてリソースの重複取得を防止し、オフライン・高速表示を支えるキャッシュディレクティブ解析基盤とメモリ上キャッシュストアを実装。
- CacheControl 構造体と解析器
(parse): -
カンマ区切りの複合ディレクティブ(max-age=N,
s-maxage=N, no-cache, no-store,
must-revalidate, public, private,
immutable)を正確にパース。 -
HttpCacheEntry と鮮度・再検証判定: -
is_fresh(now_sec):
ステータスコード(200/203/204/206)の妥当性、no-store /
no-cache の除外、max-age
による有効期限判定を完備。 - should_revalidate(now_sec):
鮮度切れ時、または no-cache 指定時に ETag や
Last-Modified が存在する場合の条件付き再検証要求を判定。 -
build_conditional_headers():
If-None-Match: <ETag> および
If-Modified-Since: <Last-Modified>
要求ヘッダを自動構築。 - update_304(): サーバーから HTTP
304 Not Modified
レスポンスを受信した際、新ヘッダによるメタデータ更新と鮮度延長を実行。 -
HttpCacheStore メモリ上キャッシュストア: -
lookup(url, now_sec):
キャッシュを高速検索し、Hit(ボディ即時返却)、Revalidate(条件付きヘッダ付与要求)、Miss
を判定。 - put(url, status, headers, body, now_sec):
レスポンスをキャッシュ保存。no-store
の拒否、最大エントリ数(既定 128)および合計バイト数(既定
4MB)超過時の古いエントリ自動間引き(LRU/FIFO)によるヒープ保護を完備。
- handle_304(url, headers, now_sec): 304
レスポンス時にメタデータを更新し、保存済みボディを透過的に返却。 -
HTTP
通信(fetch/XHR)への自動キャッシュパイプライン結合
(static_classlist.rs): -
do_http_request_full_inner において、GET リクエスト送信前に
GLOBAL_HTTP_CACHE を自動照会。 -
有効期限内のキャッシュが存在する場合、ネットワークアクセスを完全スキップし、x-atmos-cache: HIT
ヘッダを付与して 0ms で即時返却。 - 鮮度切れだが ETag / Last-Modified
を保持している場合、If-None-Match /
If-Modified-Since を要求ヘッダに透過追加。 - サーバーから
304 Not Modified が返ってきた場合、キャッシュからボディを復元し
x-atmos-cache: REVALIDATED として 200 応答を生成。 - 新規の
200 OK レスポンスは次回のために自動で GLOBAL_HTTP_CACHE
へ登録。
arm/v1/src/os_lib/aura/builtins/system.rs,
builtins.rs)Aura の sound モジュールに以下の API
を追加し、シェルやスクリプトから対話的にオーディオを監視・制御可能にした。
- sound.volume() / sound.volume(val):
音量取得(0..=256)および設定。 - sound.mute() /
sound.mute(bool): ミュート状態取得および切り替え。 -
sound.stats():
アンダーラン累積フレーム、アンダーラン回数、ドロップフレーム、ピーク振幅、バッファ蓄積量(ms)、アクティブ状態を含む辞書を取得。
arm/v1/src/kernel/audio.rs, os_lib/mse.rs,
js/interp/dom_props.rs)PcmQueue
に新設されたソフトウェア音量調整(0..=256)・ミュート・ピーク振幅監視・アンダーラン/オーバーラン統計をカーネル外部へ開通させた。
- AudioStream API 拡張
(src/kernel/audio.rs): -
AudioStream::set_volume(vol: u32)(0..=256 スケール) -
AudioStream::volume() -> u32 -
AudioStream::set_muted(muted: bool) /
AudioStream::is_muted() -> bool -
AudioStream::peak_level() -> i16 /
AudioStream::reset_peak() -
AudioStream::set_max_frames(max: usize) -
AudioStream::stats() -> (u64, u64, u64)(アンダーラン累積フレーム、アンダーラン回数、ドロップ累積フレーム)
- MSE / JS DOM プロパティとの直接結合 (mse.rs,
dom_props.rs): - <video> /
<audio> の video.volume = 0.5 や
video.muted = true
代入時、PLAYBACK.lock().set_volume(v) および
set_muted(m) 経由でカーネルの AudioStream
へ即時反映。 - テスト環境(tests/src/lib.rs)にも
AudioStream スタブを設置し、no_std
ベアメタルカーネルとホスト単体テスト双方で完全整合する構成を確立。
arm/v1/tests/src/test_fs_truncate.rs):
ファイルを縮小して不要セクタを解放できる:
4セクタ(2032B)のファイルを300Bへ切り詰め、先頭データの整合性と
fsck 健全性を検証。ファイルをゼロバイトに切り詰めてもメタデータは維持される:
1500B を 0B に切り詰め、list_files での存在とサイズ 0
を検証。同一サイズでの切り詰めは何もしない: No-op
正常系検証。ファイルを伸長すると後半がゼロ埋めされる: 5B を 20B
に伸長し、先頭データ保持と後半 15B ゼロ埋めを検証。存在しないファイルの切り詰めはエラーを返す:
異常系検証。arm/v1/tests/src/test_http_cache.rs):
cache_controlの各種ディレクティブを正しくパースできる:
複合ディレクティブ解析検証。キャッシュの鮮度判定_max_age_による有効期限が正しく機能する:
期限内/期限切れ遷移検証。no_storeとno_cacheでの鮮度と再検証判定が仕様通り動く:
no-store の破棄と no-cache の再検証要求検証。条件付きリクエストヘッダの構築と304更新による鮮度延長:
If-None-Match / If-Modified-Since 生成と 304
更新検証。http_cache_storeのヒット_再検証_304処理_容量上限保護が正常に動作する:
HttpCacheStore のライフサイクル、Hit/Revalidate、304
復元、no-store 除外、容量制限保護を網羅検証。global_http_cacheを介した送受信とヒット判定の結合動作:
グローバルキャッシュ連携での保存・検索・Hit 判定の網羅検証。make all
警告ゼロ・エラーゼロで kernel8.img 正常生成確認。SabrPlaybackSession・HTTP Content-Range 基盤・CookieJar
と通信自動送受信・オーディオパイプライン保護・JS
協調的タイムスライス・SylFS メタデータ緊急再利用の実装 (TODO B0 /
ストリーミング / Cookie / Audio / A3 / SylFS / ユニットテスト 2,586
件)YouTube
の長時間再生時の通信切断防止および自動再充填パイプライン(TODO
B0)、ストリーミング分割受信の基盤となる HTTP 206 Partial Content /
Content-Range パーサー、セッション Cookie の永続追従と RFC
6265 属性解析を司る CookieJar
基盤、オーディオパイプラインの音量・ミュート・ピーク監視・アンダーラン/オーバーラン保護、JS
長大タスク実行時の UI 応答性維持(TODO A3)、および SylFS
容量逼迫時のメタデータ再利用(SylFS)を実装し、全 2,586
件の単体テスト通過を達成した。
arm/v1/src/os_lib/sabr.rs,
js/builtins/static_classlist.rs)長時間動画の再生(B0)において、YouTube エッジサーバーは URL
クエリパラメータの cpn= とリクエスト本文 protobuf(field 5
playbackContext / field 19
streamerContext)内の CPN が一致していないと SABR 要求を
Reject(HTTP 400/403 等)する仕様が存在する。 - リクエスト本文
CPN 自動整合関数 (align_sabr_request_cpn): -
送信バイナリから protobuf の field 5 および field 19 のサブフィールド
1(CPN 文字列、16 バイト)を検出し、URL クエリに含まれる最新の CPN
値へとインプレースで整合置換する安全な書き換えロジックを実装。 -
SABR HTTP POST 送信フックへの組み込み
(static_classlist.rs): -
start_async_request 内で SABR エンドポイントへの POST
送信直前に、extract_cpn_from_url と
align_sabr_request_cpn
を適用。長尺動画の再生中にプレイヤー内部で CPN
の追従遅延や乖離が生じても、エッジサーバーによる通信切断を未然に防止した。
SabrPlaybackSession
の設計・実装 (arm/v1/src/os_lib/sabr.rs)数分〜数時間に及ぶ動画ストリーミングにおいて、再生ヘッドとバッファ先端の進行を自律追跡し、必要に応じて次セグメントを要求する中核制御構造体
SabrPlaybackSession を設計・実装した。 -
状態追従とバッファしきい値判定: -
current_media_time_ms(再生ヘッド)と
buffered_media_time_ms(バッファ末尾)を保持。 -
needs_refill(buffer_threshold_ms):
残りバッファが指定しきい値を下回った際に自動で補給トリガーを立てる。 -
UMP レスポンス自律反映 (process_response):
- 受信した UMP バイト列から Part
37(NextRequestPolicy)を解析し、current_url,
context_token, backoff_ms を自動更新。 - Part
21
メディアチャンク(fMP4)を抽出してバッファ時間を伸長し、次シーケンス番号
next_sq をインクリメント。 - 次リクエスト protobuf
本文の動的生成 (encode_request_body): - field
1(clientInfo.current_media_time_ms)、field 3(videoPlaybackRequest:
start_time, duration, sq_start, sq_end, context_token)、field
5(playbackContext.cpn)、field 19(streamerContext: cpn,
poToken)をセッション状態から完全自動エンコード。
fetch/XHR)への自動送受信フック
(arm/v1/src/os_lib/cookie.rs,
builtins/web_forms.rs,
builtins/static_classlist.rs)実サイトや YouTube においてセッション Cookie(YSC,
VISITOR_INFO1_LIVE
等)が維持されず認証やストリーミングが中断される問題を防ぐため、完全な
Cookie 管理基盤を構築した。 - CookieJar および
CookieRecord 構造体
(src/os_lib/cookie.rs): - RFC 6265
に準拠し、Path, Domain, Secure,
HttpOnly, SameSite, Max-Age,
Expires の属性解析器 parse_cookie_line
を実装。 - max-age=0 や負数、過去の
expires(1970年等)を解析して Cookie
を自動削除(remove)する is_expired
判定を完備。 - document.cookie および HTTP 要求ヘッダ用の
name=value; name2=value2
文字列化(cookie_string)を高速提供。 - HTTP
通信(fetch/XHR)への Credentials &
Set-Cookie 自動フック
(static_classlist.rs): -
リクエスト送信時、呼び出し元で Cookie ヘッダが未指定の場合に
cookie_string() を自動取得して Cookie
ヘッダを透過的に付与。 - レスポンス受信時、サーバーから返された
Set-Cookie ヘッダを自動で cookie_set /
CookieJar へ反映。セッション Cookie
の完全な送受信ループを確立。
Content-Range
パーサーとリクエスト生成基盤
(arm/v1/src/kernel/http_body.rs)メディアや長大リソースのストリーミング分割受信(TODO
B「ストリーミング受信」)を支える純粋プロトコル解析器を実装。 -
ContentRange 構造体とパーサー
(parse_content_range): -
Content-Range: bytes <start>-<end>/<size>
形式のレスポンスヘッダを解析。 -
bytes 0-1023/2048(通常範囲・全体サイズ既知)、bytes 500-999/*(全体サイズ未知)、bytes */1234(HTTP
416 不満足)の各バリアントを安全にパース。 - range.len()
により当該範囲のバイト数を O(1) 算出。 - Range
リクエストヘッダビルダー (build_range_header): -
Range: bytes={start}-{end} または
Range: bytes={start}-
形式の要求ヘッダを安全にフォーマット。
arm/v1/src/os_lib/js/interp/exec.rs)YouTube の初期化や DOM 展開(stampDomArray_ 等)では、1
つの同期タスクが数千万ステップに達し、描画や入力スレッドが最大数十秒間停止する問題(A3)が存在していた。
- 32,768 ステップごとの協調譲渡
(cooperative_slice): - tick()
実行ループ内で self.steps & 0x7FFF == 0(32,768
ステップごと)の安価なビットマスク判定を導入。 -
crate::kernel::scheduler::yield_now()
を呼び出すことで、カーネルの他スレッド(USB 入力、Ethernet
受信、オーディオ供給、GUI 描画スレッド)へ CPU を協調的に譲渡し、長大 JS
タスク実行中であっても UI フリーズを回避。 - テスト実行時間(ホスト 5
秒前後)に一切オーバーヘッドを与えないゼロコストな分岐設計を実現。
arm/v1/src/kernel/fs/mod.rs)ファイルシステムの空きセクタが枯渇しかけている極限状態において、同一ハッシュで論理削除済み(filename[0] == 0)のメタデータセクタが存在する場合、新規メタデータセクタの確保をスキップして削除済みセクタを直接上書き再利用(Emergency
Reuse)するフォールバック機構を save_file
に実装。ディスク空き容量の有効活用と保存成功率を大幅に引き上げた。
arm/v1/src/kernel/audio_stream_fmt.rs)動画再生(YouTube)や音楽再生時の再生品質とバッファ健全性を保つため、PcmQueue
に以下の制御基盤を実装。 - ソフトウェア音量調整 &
ミュート: - set_volume(0..=256): 256 を 100%
とする高速シフトスケーリング(((s as i32 * vol as i32) >> 8) as i16)によるゼロコスト音量調整。
- set_muted(bool) / is_muted():
音量値を保持したまま即時無音化および解除。 -
ピーク振幅測定: - scale_sample
実行時に絶対値最大振幅(peak_level: i16)を自動追跡。reset_peak()
でリセット可能にし、VU メーターやクリッピング検出の基盤を提供。 -
アンダーラン累積監視: - PWM
出力(fill_words)や USB Audio
パケット生成(fill_uac_packet)時にバッファ標本が不足した場合、不足フレーム数を
underrun_frames、無音補完発生回数を
underrun_events
に自動累積。バッファ枯渇を定量的に検知可能化。 - 上限容量設定
& オーバーラン自動破棄: - set_max_frames(max):
キューの最大保持フレーム数を設定。これを超える標本が push
された場合、古い標本から自動破棄(drain_frames)し、破棄数を
dropped_frames に記録。A/V
同期ズレの累積とヒープ逼迫を防止。
arm/v1/tests/src/test_cookie_jar.rs):
test_parse_cookie_line_simple: 単純な
name=val のパース検証。test_parse_cookie_line_with_attributes: Path, Domain,
Secure, HttpOnly, SameSite, Max-Age 複合属性のパース検証。test_cookie_expiration_max_age_zero_and_negative:
Max-Age=0 および負数の期限切れ判定検証。test_cookie_expiration_past_expires_date:
1970年過去日付の期限切れ判定検証。test_cookie_jar_lifecycle_apply_overwrite_and_delete:
Cookie 追加・上書き・Max-Age=0
による削除・expires による削除のライフサイクル検証。test_cookie_jar_manual_methods: insert,
delete, clear, cookie_string
の直接操作検証。arm/v1/tests/src/test_audio_stream_fmt.rs):
音量調整とミュートが標本に反映されピークレベルを追跡できる:
音量スケーリング、ミュート時の無音出力、ピーク振幅追跡とリセットを網羅検証。アンダーラン回数とフレーム数が正しく累積される:
標本不足時の無音補完、underrun_frames と
underrun_events の正確な累積を検証。最大許容フレーム数の設定でオーバーラン標本がドロップされる:
set_max_frames による古い標本ドロップと
dropped_frames カウントを検証。arm/v1/tests/src/test_sabr.rs):
test_align_sabr_request_cpn: field 5 および field 19
の双方に埋め込まれた旧 CPN(old_cpn_1234567)が、最新 URL
CPN(new_cpn_abcdef0)へと正確に置換され、他の protobuf
バイナリデータに一切影響を与えないことを検証。arm/v1/tests/src/test_sabr.rs):
test_sabr_playback_session_lifecycle:
初期状態からの初回リクエスト生成・検証、Part 37/21 UMP
レスポンスの処理、バッファ伸長と sq 進行、再生ヘッド移動による
needs_refill しきい値判定、および次セグメント要求用
protobuf 生成を網羅検証。arm/v1/tests/src/test_http_body.rs):
content_range_正常系_標準的な範囲と合計サイズを解析できる:
bytes 0-1023/2048 のパースと len() == 1024 検証。content_range_未知サイズおよび不満足の解析: bytes
500-999/* および bytes */1234 の解析検証。range_header_構築と異常系入力の安全な拒否:
ヘッダ生成と不正構文拒否検証。arm/v1/tests/src/test_fs_space_reuse.rs):
削除済みメタデータセクタを緊急再利用できる:
空きセクタが完全に枯渇した環境で、旧ファイルを削除した直後に新規ファイルを保存した場合、削除済みセクタが緊急再利用されてファイル保存が正常に完遂されることを検証。make all
警告ゼロ・エラーゼロで kernel8.img 正常生成確認。fsck
の実装 (TODO B0 / SylFS / ユニットテスト 2,571 件)YouTube の長尺動画ストリーミング再生(TODO B0)を支える SABR
パケット制御パイプラインを完全化するとともに、SylFS
の溢れエクステント確保時におけるセクタリーク防止ロールバック機構およびディスク整合性検査機能
fsck を実装した。
arm/v1/src/os_lib/sabr.rs)YouTube の 60 秒を超える動画の継続再生において、エッジサーバーと正しくやり取りするためのパケット解析・変換基盤を強化した。
parse_ump_parts)
& メディアペイロード抽出
(extract_media_payloads):
UmpPart 走査器を実装。extract_media_payloads を導入。extract_next_request_policy / Part 37):
target_url (field 1: string)、backoff_ms
(field 2: varint)、context_token (field 3: bytes)
を安全に構造化データとして抽出。parse_playback_window /
PlaybackWindow):
clientInfo)から current_media_time_ms
(subfield 28) を、field 3(videoPlaybackRequest)から
start_time_ms (subfield 2)、duration_ms
(subfield 3)、sq_start (subfield 4)、sq_end
(subfield 5) を抽出する PlaybackWindow
パーサーを実装。sq)のタイムスタンプ整合性を追跡・検証可能にした。extract_cpn_from_url):
cpn
パラメータのうち最新(末尾)の 16 文字 CPN
値を正確に抽出するユーティリティを整備。fsck (arm/v1/src/kernel/fs/mod.rs,
aura/builtins/os_fs.rs)write_overflow_extents):
DESIGN.md 16.8
に明記されていた「溢れエクステントの連鎖の途中で失敗すると、それまでに確保した溢れセクタは返さない(18
本を超える断片化時のみ)」という既知のセクタリーク制約を解消。allocate_sector() == 0)またはセクタ書き込み失敗が発生した場合、それまでに確保した溢れセクタのリスト
allocated
をすべてビットマップ上で即座に解放(deallocate_sector)し、ディスク容量の漏洩を完全に防止した。SylFS::fsck):
FileMetadata)とエクステントの妥当性を一括走査して検証する
fsck() API を実装。total_sectors, used_sectors,
file_count, extent_count,
btree_node_count, corrupted_sectors,
is_healthy)を格納する FsckReport
を返却。fsck
(builtin_fsck):
(fsck) または
fs.fsck()、os.fsck()
として呼び出し可能に登録。ディスクの健全性サマリーを瞬時に表示可能にした。arm/v1/tests/src/test_sabr.rs):
test_parse_ump_parts_and_extract_media: UMP Part
20/21/35 からの Part 21 メディアペイロード(300B)結合抽出の検証。test_extract_next_request_policy: Part 37 からの
target_url、backoff_ms (500ms)、context_token (32B)
のデコード検証。test_parse_playback_window:
cmt=63450ms、st=60000ms、dur=10000ms、sq=12..14 の PlaybackWindow
パース検証。test_extract_cpn_from_url: URL からの単一・重複 CPN
抽出検証。test_long_running_sabr_playback_sequence: 63
秒以降の長時間再生シーケンス(sq=12、512KB
メディアチャンク、NextRequestPolicy
受信)の統合シミュレーション検証。arm/v1/tests/src/test_fs_space_reuse.rs):
fsckは健全なディスクで健康を報告する:
ファイル保存・削除後の整合性検査で
is_healthy: true、ファイル数 2、破損セクタ 0 を検証。溢れエクステント保存失敗時にセクタが漏れない:
容量不足による巨大データ保存失敗後、使用セクタ数が保存前の値と完全一致(リークゼロ)し、ファイルシステムが健全な状態を維持することを検証。make all
警告ゼロ・エラーゼロ確認済み(kernel8.img 正常生成)。spec/SYLFS.md)の策定・ドキュメント体系化既存の言語仕様書
spec/AURA.md(40KB超・1,057行)と同等以上の粒度・詳細度・網羅性を持つ、独自ファイルシステム
SylFS(Sylph Filesystem)の完全仕様書(spec/SYLFS.md)
を新規作成・公開した。
kernel/fs/
配下のソースコードや DESIGN.md 第 16
章に基本構造がまとめられていたが、Aura
言語仕様書(spec/AURA.md)のような「バイナリレイアウト・全構造体オフセット・COW
トランザクションシーケンス・自己修復アルゴリズム・排他制御・全 API
リファレンス・実践コード例・限界値」を単体で網羅した包括的な仕様書が存在しなかった。arm/v1/src/kernel/fs/)、Aura
組み込みバインディング(arm/v1/src/os_lib/aura/builtins/os_fs.rs)、およびホスト単体テスト群を精査し、完全な仕様書として書き起こした。spec/SYLFS.md)の主要章構成と内容/cache/, /tmp/ の Dedicated RAMDisk
誘導)RecoverableMutex /
FS_LOCK)Superblock(24B、セクタ0)のオフセット別フィールド定義FileMetadata(i-node相当、500B ≤ 508B)の 18 本インラインエクステントExtent(16B: start,
count)および溢れエクステントセクタ(504B:
next_sector + 31エントリ)の連鎖構造BTreeNode(508B:
24キー・25ポインタ、分割時一時オーバーフロー対応)div_ceil(4064))と
next_bitmap_hint による連続確保の O(1) 化FS_LOCK(RecoverableMutex<()>)によるマルチワーカー競合(フォント/画像/JS並列ダウンロード)からのビットマップ保護panic = "abort"
環境におけるロック強奪とパニック時自動回収(force_release_locks)mount, format_device,
save_file, read_file_checked,
delete_file, list_files,
list_dir, find_by_label,
allocate_sector, deallocate_sector,
selftest 等)ls, cd,
pwd, mkdir, cp, mv,
rm, cat, grep, head,
tail, save, show,
find, addlabel, corrupt)both primary and mirror corrupted,
no free data sector, hash collision,
metadata sector unreadable,
node sector out of range,
file data is truncated)corrupt
コマンドを用いたフォールトインジェクション試験手順spec/README.md の主要設計・管理ドキュメント一覧に
[SYLFS.md](SYLFS.md) を追加。make all
警告ゼロ・エラーゼロ確認済み。make all
成功。作業ツリーは未コミットの変更なし。計画書のテスト件数(2,560)を
2,564 へ、3 文書の「最終更新」を本日へ更新した。raspi3b は PWM・I2S
を持たないが、2026-11-03 に DWC2 上へ USB Audio Class 1.0
ドライバ(kernel/usb_audio.rs)を実装済みで、-device usb-audio
経由でホストのスピーカーから音が出る(続 3
参照)。調査でリポジトリの既存実装を確認せず QEMU
のデバイス一覧だけで結論したのが原因。PWM
出力そのものは実機でのみ確認できる。LAYOUT_MEMO)・Grid/Table
多段パス平行移動最適化・レイアウト値解決のゼロアロケーション化 (TODO A8
/ D 完結)DOM
の部分更新・ホバー・スクロールに伴う再レイアウトにおいて、変更されていないブロック/flex/grid/table
サブツリーの再配置を O(N) 平行移動のみで完了させる
LAYOUT_MEMO 機構、CSS Grid / Table の 2
パス目再レイアウト省略、および len_value /
style_box_edges / grid_axis_placement
等のゼロアロケーション化を実装し、TODO.md セクション
D「大きな文書の再レイアウトが遅い(DOM
変更時に変わらないブロックの配置を飛ばす)」を完結させた。
LAYOUT_MEMO)
(arm/v1/src/os_lib/layout/tree.rs,
css/types.rs, values.rs)StyledNode::layout_subtree_info
によるサブツリー特徴ハッシュとメモ可否判定:
StyledNode に
layout_subtree_cache: Cell<Option<(u64, u64, bool)>>
を新設し、要素のタグ・ID・クラス・全属性(<img>
の場合は IMAGE_SIZE_CACHE
の解決済み寸法を含む)・テキスト内容・specified_values
全宣言・子孫ハッシュを KeyHasher(128bit FNV-1a
二重ハッシュ)で合成。position: absolute /
position: fixed(疑似要素を含む)や未ロードのローカル画像(src
が / 始まりで IMAGE_SIZE_CACHE
未登録)が存在しない場合に can_memo = true
と判定。なお初回レイアウト中にローカル画像が VFS から読み込まれて
IMAGE_SIZE_CACHE
に登録された場合は、レイアウト直後の再判定で即座にメモ登録される。node_uses_cb_height による
y_cursor 変化に対する不変性の確保:
layout_block 等では、親の縦フローカーソル
y_cursor が containing_block.content.height
に入って子へ渡されるが、子自身が height /
min-height / max-height /
grid-template-rows / grid-auto-rows に
% や数学関数 (
を持たない限り、containing_block.content.height は子の開始
Y 座標
cb_y_flow = containing_block.content.y + containing_block.content.height
の算出にしか使われない。cb_h_key を 0
に正規化。これにより、先行する兄弟ブロックの高さが DOM
変更で伸縮して y_cursor
がずれた場合でも、後続のすべての不変兄弟ブロックが
LAYOUT_MEMO にヒットし、(dx, dy)
の平行移動のみで即座に配置が完了する。GLOBAL_LAYOUT_MEMO と環境ハッシュ連動:
LayoutMemoKey は
(subtree_hash, box_flags, cb_w, cb_h_key, pct_base_h, forced_h, env_hash)
で構成し、env_hash
にビューポート寸法(VIEWPORT_HINT_W/H)、ルートフォントサイズ(root_font_px)、および
@font-face
カスタムフォント登録世代(CUSTOM_FONT_GEN /
notify_custom_font_registered)を混合。last_used
世代に基づき古い半分のみを間引く LRU 方式でキャッシュクリフを防止。arm/v1/src/os_lib/layout/tree.rs)layout_grid): パス
2(行高さ計測)で各グリッドアイテムは確定済みの列X座標
cell_x・セル幅
cell_w・height = 0・subgrid
文脈・aspect-ratio で base_y
を起点にレイアウトされる。パス 3(確定行 cell_y
への配置)では、アイテムが can_memo_subtree()
を満たす場合は child.layout(cell_cb) の再実行を行わず
child.translate_y(cell_y - base_y)
のみで確定させることで、入れ子 Grid で 2k
回に膨張していた再レイアウトを 1 回へ削減。layout_table):
高さ計測パスで
(base_x + col_x[p.col], rows_top, cw_total, height=0)
にて各セルをレイアウト済みのため、確定パスでは
cell.can_memo_subtree() を満たすセルに対して
cell.translate(x0 - (base_x + col_x[p.col]), y0 - rows_top)
のみで最終位置へ移動させ、セル内部の再レイアウトを完全にスキップ。values.rs, types.rs,
grid.rs, tree.rs)のゼロアロケーション化len_value の Cow<'a, str>
化:
expand_em
を呼び出し毎回新規 String をヒープ確保していたが、値に
"em"(大文字小文字問わず)を含まない 99%
以上のケース(px, %, rem,
vw, vh, auto, 0
等)では Cow::Borrowed(v) を返してヒープ確保をゼロ化。style_box_edges_uncached と
apply_box_value のゼロアロケーション化:
Cow::Borrowed を用い、apply_box_value 内の
Vec<&str>・Vec<Option<i32>>
のヒープ確保を固定長スタック配列 [Option<i32>; 4]
に置換。types.rs / grid.rs の
box_style_value_ref 統一:
box_font_size, scroll_dimensions,
box_has_explicit, box_aspect_ratio,
box_flex_grow, box_flex_shrink,
box_order, box_flex_basis,
box_align_self, grid_item_align,
grid_axis_placement での String 確保と
to_ascii_lowercase()
を全廃し、box_style_value_ref と
eq_ignore_ascii_case による借用スライス処理へ統一。arm/v1/tests/src/test_layout_perf.rs に
test_len_value_cow_borrowed_vs_owned および
test_layout_memo_skips_unchanged_blocks_when_earlier_sibling_grows(先行兄弟ブロックの高さが
+80px 変化した際、後続の flex / grid / table サブツリーが
LAYOUT_MEMO
にヒットし、かつコールドレイアウトと全ボックス座標が完全一致することを検証)を追加。<style> 規則比較による style_memo
維持・レイアウト block_intrinsic_cache
メモ化とゼロアロケーション化 (TODO A1 / A7 / A8 / D)YouTube 視聴ページ等の巨大スクリプト(base.js /
kevlar_base)および 7,200 要素超の DOM
再レイアウトにおいて、ヒープ確保回数(Locked<TlsfHeap>::alloc
が JS コア実行時間の約 17%)と入れ子 flex/grid
の内在幅再計算コストを削減する高速化を実装した。
arm/v1/src/os_lib/js/lexer.rs)
のゼロアロケーション化 (A1 / A7)read_number)
のバイト走査と直接整数アキュムレート:
String::new()
を確保して 1 文字ずつ push
し、parse::<f64>() を呼んでいた。0x) / 8進 (0o) / 2進
(0b) リテラルで String
確保を廃止し、u64 へ直接ビットシフト・加算(64bit
超過時のみ f64 フォールバック)。skim
モード時は即座に Token::Num(0.0) /
Token::BigIntLit(Box::from("")) を返す。u64 で直接アキュムレートし、ヒープ確保も
str::parse::<f64>() も一切行わず
Token::Num(int_acc as f64)
を即座に返す。浮動小数点リテラルもアンダースコア _
が無ければ元ソースの &str スライスから直接
parse::<f64>() を実行。read_string)
のエスケープ無し高速一括スライスパス:
String::new() に 1
文字ずつ push した後に into_boxed_str()
で再確保していた。\
までバイト列を一括走査し、エスケープを含まない文字列(大半のケース)は
skim
モードなら確保ゼロ(Box::from(""))、通常モードなら元スライスから
1
回のぴったりサイズの確保(Box::from(slice))のみで返す。read_identifier_escape /
unescape) とプライベート識別子 (#x) の一時
String 撤廃:
\uXXXX / \u{...} / \xHH で 16
進文字を String に集めて u32::from_str_radix
していた処理を (acc << 4) | d
のビット演算へ置換し、ヒープ確保をゼロ化。read_identifier_or_keyword で #
識別子のたびに無条件で s.push('#')
していた確保を廃止し、エスケープが無い通常の #private
識別子はスライスから直接インターンする。<style> 更新時の規則比較による
style_memo / cached_rule_index 保持
(arm/v1/src/os_lib/css/types.rs,
arm/v1/src/os_lib/web_engine/layout.rs) (A8)implementation_plan.md
に記載されていた残課題「インラインの <style>
が同じ長さで入れ替わり、スタイルシートを作り直して表を捨てる回がある」。YouTube
等が動的に空の <style> やコメント・空白のみ異なる
<style>
を操作した際、inline_style_key(文字列ハッシュ)が変わるため
cached_rule_index と style_memo(7,200
要素超の計算結果キャッシュ)が丸ごと破棄されていた。StyleSheet, Rule,
Declaration, Keyframes,
CounterStyleDef, FontFaceDef に
PartialEq を実装。inline_only
文字列ハッシュが変化した場合でも、インライン CSS
パース後の規則スライス(rules, keyframes,
counter_styles,
font_faces)が前回スタイルシートのインライン部分と一致していれば
sheet_reused = true とし、cached_rule_index
の再構築と style_memo.clear() を完全にスキップする。block_intrinsic_widths の O(1) メモ化
(block_intrinsic_cache) とホットパスのゼロアロケーション化
(arm/v1/src/os_lib/layout/tree.rs,
boxmodel.rs) (A8 / D)StyledNode::block_intrinsic_cache
の新設:
InlineNode/InlineBlockNode 向けの
intrinsic_size_cache
に加え、BlockNode/FlexNode/GridNode
等のサブツリー全体の (min_content, max_content) を記憶する
block_intrinsic_cache: RefCell<Option<(u8, i32, i32, i32)>>
を StyledNode に追加。block_intrinsic_widths()
が何度も重複呼び出しされて指数関数的に木を走査していたボトルネックを解消し、各サブツリーにつき
1 回の計測(2 回目以降は O(1) キャッシュヒット)に短縮。boxmodel.rs / tree.rs
のゼロアロケーション化:
box_float, box_clear,
box_is_rtl, box_len,
box_inset_len, box_translate,
box_main_auto_margins, extract_fn_args_ref を
box_style_value_ref と eq_ignore_ascii_case
ベースへ刷新し、呼び出しごとの String
クローン・to_lowercase()・format!("{}(", name)
のヒープ確保を全廃。tree.rs の text_font_of,
pseudo_content_size, collapsed_space_width,
intrinsic_inline_size_ext_uncached,
inline_content_intrinsic_size_uncached,
grow_auto_min,
block_intrinsic_widths_uncached,
children_intrinsic_widths における
node.value(...)(毎回 String を複製)を
node.value_ref(...)(&str
参照)へ置換。arm/v1/tests/src/test_js_lexer_utf8.rs に
ゼロアロケーション数値とエスケープとプライベート識別子の字句解析
および skimモードでも文字列と数値と識別子の境界がずれない
を追加。arm/v1/tests/src/test_layout_perf.rs に
test_block_intrinsic_cache_and_zero_alloc_boxmodel および
test_css_stylesheet_partial_eq_for_inline_style_reuse
を追加。長尺動画(1時間超
https://www.youtube.com/watch?v=1iMTebfhduI)において、初期バッファ(63秒)を消費し尽くした後にサーバーからの配信が停止・拒絶される問題の調査・解決を行い、BotGuard
VM による 77バイト暗号署名と SABR
ストリーミングの連携、およびセッション維持パイプラインを確立した。
po_hex=[324b...])の特定:
EX NJ(BotGuard VM 実行)が生成する 103 文字の
Base64url トークン(デコード時 77 バイトバイナリ)は、GoogleVideo CDN
サーバー向けの正規 Attestation 暗号署名であることを完全解明。streamerContext(19)の field 2 に 77
バイト署名を載せ、URL の pot= に 103
文字署名を載せて送信することで、GoogleVideo サーバーが PROCEED(状態 2:
58:2[0802])でメディアセグメント(sq=13以降)を配信することを実証。mitigate_sabr_empty_response が勝手に「状態3(SPS
Reject)」へ誤書き換えしていた不具合を特定し撤廃。spsumpreject ハンドラにおけるセッション Cookie
破壊と無条件トークン破棄の解消:
spsumpreject が発火するが、旧パッチが
b.L(真の Reject フラグ)を判定せずにセッション
Cookie(playbackCookie)を消去し、最新トークンを強制リセットしていたため、サーバーからセッション破綻(Reject
10)と判定されていた問題を根本修正。真の
Reject(e && b.L && !b.D)時のみトークンを再生成する安全ガードへ刷新。normalize_sabr_url):
cpn= および pot= を最新の 1
つのみに統合し、古いダミートークンや重複パラメータによる署名不一致を防止。arm/v1/tmp/yt53,
arm/v1/tmp/yt54):
cmt)は 0秒 から
62.933秒(初期バッファ限界)まで途切れることなく滑らかに進行。series_0087.png 〜
series_0268.png)において、男性の動画がフルカラー・高画質で美しく継続描画されていることを目視確認。YouTube 動画(長尺動画 1iMTebfhduI /
aqz-KE-bpKQ 等)が再生開始から約 1 分(63
秒)でバッファ枯渇し停止する問題(TODO B0)に対し、最新の YouTube
base.js(ハッシュ
a5c2aafe)における真の難読化シンボルとストリーミング制御構造を解明。WebAssembly
版プランナー不全の特定、純粋 JavaScript
プランナー(bZa)の強制有効化、および正当な 10 バイト PO
Token 生成パイプラインを確立し、受信データ量
8.53MB(8,528,470B)、12
回連続のメディアセグメント受信、63
秒(1分3秒)にわたる完全な映像・音声連続再生 を達成した。
base.js の EX 関数は、BotGuard
インスタンス(a.B)が存在すると Google 内部
RPC(GenerateIT)経由で 81
バイトのトークンを生成しようとするが、外部サンドボックス環境からは正当な証明ができず壊れた署名となるため、サーバーが状態
3(0803100a =
バックオフ・レート制限指示)で拒絶していた。yp() クロージャにより純粋な JS
ロジックで生成される 10 バイトのセッショントークンは、YouTube
サーバー側で正当なクライアントとして 100% 受理される。CZa)の不全と空セグメント要求:
WebAssembly API が存在すると
this.policy.td = true と判定し、Wasm 版トラックプランナー
CZa(エクスポート関数 w494a697765c3d2fa
等)をロードする。TD()
が空配列 [] を返していた。hc:
セグメント要求レンジ)に次セグメント(startTimeMs: 64000
等)が積まれず、クライアントは初期要求と同じ内容を送り続けていた。サーバーは「すでに目標バッファ分は送信済み」と判定し、Part
58 で状態 3(バックオフ指示)を返し続けていた。g.k.vy におけるセグメント完了通知(状態
2)の欠落と過剰遅延:
this.da.YQ(!0, !1)
を呼ばず放置していたため、SABR
の次セグメント要求スケジュールが中断していた。mitigate_sabr_empty_response
による誤った状態改ざんの発見と撤回:
static_classlist.rs
に存在していた一時的なミティゲーションが、63
秒分のバッファ完了後にサーバーが返した「正常な区切り応答(状態 2、媒体
0B)」を勝手に状態 3
へ強制書き換えしていた事実を特定し、これを完全撤回・削除。arm/v1/src/os_lib/web_engine/layout.rs,
static_classlist.rs)bZa)の強制有効化:
tj = function(a) { return !1; }; に置換し、Wasm
プランナー CZa を無効化して完全なフォールバックである純粋
JS プランナー bZa を常時選択。bZa のセグメント受信ハンドラ g.k.Lq
において、this.loader.xp() || this.policy.Qd のゲートを
if (!0) に緩和し、受信セグメントに続く次セグメントを確実に
this.av.push(a) でキューイングするようパッチ。EX 関数において常に a.C || (a.C = yp())
を参照し、10
バイトの正常トークンのみを即座かつ同期的に生成して送信。J$a における PO Token 再生成
Promise(a.D)を遅延なく即座に DX(a)
で解決し、リクエスト待機デッドロックを防止。case 58: での
a.Gt === 3 && (this.QA = !0) による即時 FATAL
エラー(ump.spsrejectfailure)誤発火を削除。g.k.vy において、状態 2
受信時は即座にリトライ回数をクリアし this.da.YQ(!0, !1)
を通知。状態 3
受信時のみサーバー指示のバックオフ待機((a.kna || 10) * 1000
ms)を経て安全に this.Vu(!1) でローダーを再開。static_classlist.rs にて、SABR 要求本文のフィールド
1(ClientInfo)、フィールド 2(TrackInfo)、フィールド
3(SegmentRequest)の先頭バイトを抽出・記録する診断機構を配備。tmp/yt16):
cmt=62.933(1分3秒)まで、映像(H.264)と音声(AAC)が完全に同期して途切れ・音飛びなく滑らかに再生し切ることを実測確認!kernel8.img 正常生成を常時維持。cycle_gc
の動的インターバル・ヒープ圧迫検知と孤立 DOM
ノードクリーンアップによるヒープ枯渇防止 (TODO A7)YouTube
の長時間視聴・自動再生(2本目・3本目への動画遷移)において、生存オブジェクト数が
70〜80 万個に達し、768MB ヒープを使い切って [ALLOC FAIL] で
panic する課題(TODO A7)に対処。コード精査により判明した 2
大根本原因を解消した。
cycle_gc の回収間隔暴走防止とヒープ圧迫早期トリガー (Memory
Pressure GC)NEXT_AT は
MIN_INTERVAL + live(前回の生存数 100%
増)で計算されていた。生存数が 50 万個あると次回まで 60
万個作られるまで回収されず、しかもヒープ使用量を一切見ていなかったため、ヒープが
590MB(約 78%)に達しても放置されて OOM panic を招いていた。arm/v1/src/os_lib/js/gc_policy.rs,
cycle_gc.rs):
calc_next_interval_pure(live, used, total) および
should_collect_pure(made, next_at, used, total)
を純粋関数として gc_policy.rs に新設。(MIN_INTERVAL + live / 4).clamp(50_000, 100_000)(最大でも
10 万個でキャップ)50_00025_00010_000(1
万個ごとの高頻度回収)made >= next_at に加え、ヒープ使用率が 75%
を超えている場合は made >= 5_000、80% を超えている場合は
made >= 2_000 で早期に緊急回収を発動。innerHTML = "" や
removeChild 等で DOM
から切り離された古い要素群(前の動画のプレーヤー、関連動画、コメント等)の
listeners, expandos,
node_accessors, node_protos 等が
DomBridge
に強参照されたまま残り続けていた。DomBridge からの強参照は
cycle_gc
から「台帳外部からの強参照(Root)」と判定されるため、死んだ要素配下の数十万個の
JS オブジェクト・クロージャ・スコープが永久に回収不能となっていた。arm/v1/src/os_lib/js/dom_bridge.rs,
interp.rs, calls.rs):
find_reachable_dom_nodes(gc_policy.rs)により、文書ルート(0番ノード)から辿れるツリー、および
JS 側(dom_wrappers の強参照カウント > 0 または
dom_recent
リングバッファ)から参照されているノードとその連結成分(先祖・子孫)を正確にマーク。DomBridge::clean_unreachable_nodes):
到達不能な孤立ノード群の expandos,
node_accessors, node_protos,
rects, computed_styles, listeners
などの強参照を安全に一掃。Interp::clean_unreachable_dom_resources,
calls.rs): cycle_gc::maybe_collect()
を呼び出す直前に孤立 DOM ノードの強参照を一掃することで、これまで Root
扱いされていた死んだ DOM コンポーネント配下の全オブジェクトを循環 GC
の回収対象へ引き渡し、参照カウント 0 で即座に大量解放。arm/v1/tests/src/test_dom_gc.rs
を新設(動的インターバル計算の負荷帯別境界値、メモリ圧迫早期トリガー、孤立
DOM ツリー検出と JS 参照保持ノードの保護)。全 2,541 件 PASS(0
failed)。make -C arm/v1 all
にてエラー・警告ゼロで kernel8.img 正常生成を確認。base.js(約
3MB)等の巨大スクリプト解析時に、全トークン列(Vec<Token>、約
70MB)を解析中メモリとして保持していたため、解析中のヒープ圧迫が大きかった。arm/v1/src/os_lib/js/parser.rs,
コミット 8b0e86d796):
Parser 内部のトークン保持を
Vec<Token> から
buf: VecDeque<(Token, u32)>(容量
32)によるスライディングウィンドウ(十数要素・数百バイトのみ保持)へ刷新。Lexer)からのオンデマンド逐次読み出し方式とし、解析中に約
70MB を消費していたトークン列全体の一時メモリをゼロ化。TODO
の残課題を解消。unit_len の O(N) バイト走査化
(arm/v1/src/os_lib/js/builtins/props_methods.rs):
[PCPROF] プロファイル上位に入っていた
unit_len(s) において、非 ASCII 文字列に対する
s.encode_utf16().count()(Unicode
コードポイントのデコード+サロゲートペア計算のイテレータチェーン)を全廃。(b & 0xC0) != 0x80 以外)をカウントし、4
バイト文字(b >= 0xF0、UTF-16 でサロゲートペアとなる
U+10000 以上)のみ +1 加算するゼロアロケーションかつ極小分岐の O(N)
単純バイト走査へ刷新。arm/v1/tests/src/test_js_lexer_utf8.rs に単体テスト
test_utf16_unit_len_byte_scanning
を追加し、ASCII・多言語・サロゲートペア絵文字での
encode_utf16().count() との完全一致性を検証。call_user_function
の仮引数一括バインド高速パス
(arm/v1/src/os_lib/js/interp/calls.rs):
[PCPROF] で 6%
を占めていた関数呼び出しにおいて、仮引数の数だけ
self.bind_pattern() 経由で
call_scope.borrow_mut()
を繰り返し呼んでいたオーバーヘッドを解消。call_scope.borrow_mut()
を 1 回だけ借用して全引数を直接 sc.vars.insert()
する一括高速パスを実装。UnitsCache の可視性を pub に調整し
private_interfaces 警告を解消。calls.rs の
global_eval as usize を as *const () as usize
に修正し function_casts_as_integer 警告を解消。kernel8.img
正常生成を確認。TODO B0 の調査。2 本目の動画が 33〜44
秒で「エラーが発生しました」で止まるので、googlevideo の小さい応答を UMP
の部品に分けて記録した([SABR][UMP])。 部品
58(ストリーム保護の状態)が 08 02(証明待ち)→ 約 1
分ぶんの後
08 03 10 0a(証明が必要)になり媒体が来なくなる。PO
トークン(BotGuard の証明)が作られていないのが原因。 BotGuard
の起動の流れを kevlar・player から読み、15
秒ごとにページの状態を記録する診断([ATT][PROBE])で追う途中で、次の処理系の不具合を見つけて直した。
Promise.all/allSettled/race/any(extra_methods.rs
の promise_combinator):
呼ばれた瞬間の状態を写すだけで、未決の要素があると
all/allSettled は undefined
で即座に解決し、race は永遠に未決だった。 kevlar は
BotGuard の読み込みを時間切れ付きの
race(ym)で待つので、成功しても時間切れでも先へ進まず、再試行も起きなかった。仕様の手順を
JS で書いた本体を一度だけ組み立てて使う(builtin_fn_cache
の鍵 1)。セルフテスト 4 件。requestIdleCallback(errors_misc.rs):
呼んだその場で同期的に実行していた。次のマクロタスクで呼ぶ。cancelIdleCallback
は何もしなかったので clearTimeout
と共通にした。セルフテスト 2 件。await
と通信(promise_loop.rs の
await_network_step): 非同期 XHR
が走っている間も期限前のタイマーを早送りしていたので、「応答」と「時間切れ」の
race を待つと必ず時間切れが勝った(race
を直して next/player が
net.timeout で失敗して表に出た)。
通信中は、届いた結果を先に配り、期限の来たタイマーだけを実行し、どちらも無ければ実時間で待つ(最大
60 秒)。イベントループでも届いた結果をタイマーより先に配る。<link rel="stylesheet"> の
load(dom_bridge.rs・layout.rs):
一度も送っていなかった。kevlar がプレーヤーの CSS の load
を Promise.all で待つ所(FXM)で止まった。JS
が作った <link> が挿入されたら次のフレームで送る(CSS
の反映は TODO D)。<link rel="stylesheet"> の CSS
の反映(layout.rs の
run_dynamic_links): 外部 CSS を最初のレイアウトで 1
回だけ集めていたので、後から挿入された YouTube のプレーヤーの
CSS(player-css)が使われていなかった。
非同期に取得し(font_queue の Style。取得失敗も記録して
error)、届いたら外部 CSS のキャッシュを捨てて生きている
DOM の木から集め直し、load を送る。 外部 CSS は 6 本 4.40MB
→ 9 本
4.95MB。ヘッダーの消失・動画に重なる小さい枠が直り、自動再生の画面(「0
秒後に次の動画を再生」)まで進むようになった。trayride.a(...) を直接呼び、JS
の仕様どおりの答えと照らす差分試験で切り分け):
atob が復号したバイト列を UTF-8
として読み直していた(0x80 以上が壊れる)。1 バイトを 1
文字にし、不正な入力は
InvalidCharacterError。btoa も UTF-8
のバイト列を符号化していたので、文字コード 0〜255 を 1
バイトにし、それ以外は InvalidCharacterError。== で false == null が
true(真偽値の分岐が null/undefined も数値にしていた)。VM の
while(true)if(false!=(c=175,null))break;
が抜けられず、trayride.a(...)
の中で無限ループしていた。delete
が穴を作らず、in・for-in・Object.keys
に出ていた(VM の
for(D in delete N[g],N))。穴の印(set_holes)を付け、for-in
と Object.keys で飛ばす。interp::to_i32)が as i64
を通しており、絶対値 2^63
以上で飽和していた((0xFFFFFFFF*0xFFFFFFFF)|0 が -1)。2^32
の剰余で計算し、Math.imul・Math.clz32・String.fromCharCode
も同じ変換を使う。fmt_number)が as i64
で、2^63 以上 1e21 未満が i64 の最大値になっていた。2^53 以上は最短の桁
+ 0 埋めにする。calls.rs の
eval_call・exec.rs の
eval_source_in):
常にグローバルで実行しており、呼び出し元・外側の関数の変数が見えず、代入も届かず、中の
var がグローバルへ漏れていた。 VM は組み立てたコードを
function(D){return eval(D)} で評価して即時関数の内側の
$e
を参照するので、初期化が失敗して未設定のレジスタを読み続けていた。呼び出し先が識別子
eval で値が組み込みの eval
なら呼び出し元のスコープで実行する(バイトコードでは構文木の評価に任せる)。
これで試験ページの VM が準備完了(READY)になり、本番の YouTube
でも
bg=ready mint=ready、/api/jnn/v1/GenerateIT
で完全性トークンを受け取り、SABR の要求に PO
トークン(81B)が付くようになった。Function.prototype.toString(parser.rs
の
record_fn_source・ast::FnBody::source_text):
JS の関数は function f() { [code] }
という仮の文字列、fn.toString() は
[object Function] だった。 function
の宣言・式の定義全体(async
を含む)の範囲を記録してそのソースを返し、記録できない関数は
[native code] の形にする。VM の
code is not defined(1 万回超)が消えた。[QOE])で確認(error=ump.spsrejectfailure)。利用者の判断で、Chrome
にあって無かった API を実装する方針にした:
navigator.plugins/mimeTypes(PDF
を表示できないので仕様どおり空、pdfViewerEnabled は
false)、SourceBuffer/SourceBufferList。js/prelude/webaudio.js、JsRuntime
の作成時に評価): AudioParam
の自動変化、発振器・ゲイン・遅延・双二次フィルター・コンプレッサー・アナライザ(FFT)・パン・波形整形・バッファ再生など。
OfflineAudioContext.startRendering はノードのつながりを 128
サンプルずつ実際に計算する。実時間の AudioContext
は音を出さない(音声出力は os_lib/wasm/):
バイナリの読み込み(各区画・入れ子の検証・else/end の表)と実行(仕様の
store モデル、MVP
の命令・符号拡張・飽和変換・一括メモリ/表、Trap)。ホストの試験 11 件。
JS の API(builtins/wasm_api.rs +
js/prelude/wasm.js):
Module・Instance・Memory(記憶域を共有する
buffer)・Table・Global・CompileError/LinkError/RuntimeError・validate・compile・instantiate(Streaming)。
取り込みの JS 関数を呼ぶ間は store
を戻して再入に対応する。土台の関数群はグローバルに出さない。セルフテスト
7 件。js/prelude/trustedtypes.js):
trustedTypes(createPolicy・isHTML/isScript/isScriptURL・getAttributeType/getPropertyType・emptyHTML/emptyScript・defaultPolicy)と
TrustedHTML/TrustedScript/TrustedScriptURL/TrustedTypePolicy(Factory)。
CSP の require-trusted-types-for は強制しない。あわせて DOM への代入と
setAttribute が値を ToString(利用者定義の toString
を呼ぶ)で受け取るようにした(以前は “[object Object]”
になっていた)。セルフテスト 5 件。contentWindow
に組み込みのグローバル(JsRuntime
の作成時にあった名前。DomBridge::builtin_global_names)を置き、globalThis
も足した。iframe ごとの別の realm
は持たないので、親と同じ組み込みを共有する。BotGuard が iframe から
String.prototype を引いていた。document.all(文書の全要素)を仕様の [[IsHTMLDDA]]
として足した(オブジェクトの flags の
OBJ_FLAG_HTMLDDA。typeof は
“undefined”・真偽値は偽・== null は真)。YouTube
の属性の検査が hidden="undefined"
を失敗と報告していた。window
のアクセサ(Object.defineProperty(window, "x", { get }))を読むようにした(load_identifier・木の
typeof)。YouTube の文書は window.Polymer
をアクセサにし、base_module_set が素の Polymer で読んで
ReferenceError になっていた。charCodeAt・charAt・codePointAt・slice・substring・substr・length・添字が、256
バイト以上の文字列では UTF-16
の写しの表(UnitsCache、最近の 4 件、Rc
を持つ)を使う。以前は 1 回ごとに全文を調べ、ASCII
以外なら全文を変換していた。css/style_memo.rs、TODO
A8): 再レイアウトのたびに作り直す dom::Node
の木の各要素を、内容から作った 128 ビットの鍵
(親の鍵・兄弟全員の特徴・位置・子の要約)で前回の結果に引き当てる。右端が
:has() の規則の候補は子孫全部の要約も鍵に入れ、祖先側の
:has()・カウンタ・:focus-within
は使い回さない。
画面の大きさ・:target・言語・ダークモード・スタイルシートが変われば表を捨てる。YouTube
で style が約 175 → 約 31
tick。候補の判定は属性名つき(candidates_into_with_attrs)。lexer::SharedAtoms)を、スクリプトの最初の解析と後から解析する関数本体(FnBodyState::Source
の atoms)で共有する。同じスクリプトの同じ名前は同じ
Rc<str> になり、変数の検索が番地の比較で済む。fetch() を非同期にした: http(s) の要求は XHR
と同じ背景のスレッドで取り、pump_xhr_results で
Response を作って Promise を決める(失敗は
TypeError: Failed to fetch)。data: URL・不正な
URL・スレッドが作れないときは従来どおりその場で処理する。prelude/webaudio.js・trustedtypes.js・wasm.js)は、JsRuntime
の作成時には評価せず、prelude/lazy_boot.js が window
に名前ごとの読み出し用アクセサを置き、初めて読まれたときに組ごと評価する(interp.rs
の
PRELUDE_GROUPS・prelude_install。評価用の関数はグローバルに出さない)。JS
の selftest が約 103 秒 → 約 13 秒。selftest の段ごとの所要時間を
[SELFTEST][TIME] で出す(main.rs の
selftest_mark)。StyledNode::specified_values・DomBridge::computed_styles
を Rc<BTreeMap<String, String>>
にした(作った後は書き換えない。set_computed_style は
Rc::make_mut)。スタイルの使い回しの当たりで一覧を写さない。at・indexOf・includes
は本物の配列なら要素を写さずに見る(props_methods::with_array_items)。以前は呼ぶたびに配列全体を写していた。forEach・map・filter
は本物の配列を写さず、長さだけ最初に決めて要素は訪れたときに読む(props_methods::ArrView。仕様どおり、途中で無くなった要素は飛ばす)。以前は配列全体を写しており、確保と
memcpy の呼び出し元の 1
位だった([PCPROF][CHAIN] を 4 段にして確かめた)。regex.rs)は、分岐(Split)のたびに捕捉の位置の
Vec
を複製していたのを、使い回す退避の置き場(stash)へ積んで戻す形にした。find_at
は開始位置ごとに捕捉の Vec を作り直さない。{〜}
を読み飛ばす間、字句解析を流し読み(Lexer::skim)にした:
識別子はインターンせず共通の空の名前、文字列は中身を作らない(トークンの種類は同じなので括弧の対応と
/ の判定は変わらない)。YouTube のスクリプトの解析が 1MB
あたり約 28〜40% 速くなった(player 3MB: 1,635 → 989ms)。(・)・{・}・[・]・;・,・~・:)はその場で返し、残りは先頭のバイトが合う記号だけ最長一致で照らす(以前は一覧の末尾の
(・; で毎回約 50 個照らしていた)。解析は
kevlar 9.5MB で 3,218 → 1,332ms、player 3MB で 1,635 →
614ms(流し読みと合わせて)。tests/src/bench_js_lex.rs、kevlar
10.8MB)で字句解析 124 → 112ms・遅延解析 97 → 85ms。 QEMU
の時間はホストの混み具合(load average 8 前後)で 1
割ほど揺れるので、字句解析・解析の前後比較はこのホストの計測で行う。char_at/peek/advance
を必ず展開し、予約語の判定は 1
文字の名前と長さ・先頭の文字が合わないものを比べずに落とし(keyword_of)、名前のインターンは
from_utf8 で検証し直さず str
を切り出す。ホストの計測で字句解析 112 → 91ms・遅延解析 85 → 68ms。/
が正規表現になれるかの真偽値(prev_regex_ok)だけにした。字句解析
91 → 78ms・遅延解析 68 → 61ms。value::key_eq(長さ違いは即座に偽、16
バイトまではバイトの並びで比べて memcmp
を呼ばない)にした(SmallPropMap::get/get_mut/small_pos・VarMap::position)。ホストの計測(tests/src/bench_prop_map.rs)で
500 万回の検索が 36 → 26ms。fmt_number)の 2^53
未満の整数は、桁をスタックの配列へ書いて 1 回で文字列にする(以前は
format!)。ホストの計測(tests/src/bench_fmt_number.rs)で
100 万回が 28 → 24ms。s.charAt(i)・s[i] の ASCII 1
文字の文字列は実行環境ごとに使い回す(Interp::char_unit_val、builtin_fn_cache
の鍵 1〜128)。以前は 1 文字ごとに確保を 2 回していた。a + b)は、文字列の辺をそのまま借り、合計の長さで
1 回だけ確保して書く(operators.rs)。以前は両辺を
to_js_string で写し、format!
でもう一度組み立てていた(確保 4 回・写し 2 回 → 確保 2 回・写し 1
回)。obj[key]
のキー(interp::to_property_key)は
Cow<str> を返し、文字列のキーは借りる。以前は
obj[name] のたびに名前を写していた。render.rs)は、要素の世代(WebEngine::elements_gen、再レイアウト・色だけの更新で増やす)・スクロール・画像の表の大きさが前回と同じなら省く(16
フレームに 1 回はやり直す)。 以前は毎フレーム全要素の画像の URL を
BTreeMap/BTreeSet で 4
回引いており、長い共通の接頭辞の URL で memcmp が JS
のコアの 7.1% → 0.6%。scheduler::spawn_away_from_current)。優先度の高い復号が
JS と同じコアに載ると、480p で JS
が止まった(ほかのコアは空いていた)。rule_index.rs)の
id・class・タグ・属性名の表を BTreeMap から FNV の
IndexMap(KeyMap)にした。ホストの計測(bench_yt_cascade、視聴ページの
HTML と kevlar の CSS)で style 約 108 → 約 104ms、索引の構築 6.2 →
5.6ms。class_freq)も FNV
の IndexMap にした(索引の構築 5.5 → 3.9ms)。Vec<Vec<Vec<u32>>>
から確保の無い固定長(AncReqs)の平らな列にした(style 10
回 約 85 → 81ms、結果の指紋は同じ)。applied)は値と名前をスタイルシートから借り、最後に残った分だけ文字列にする(上書きのたびの複製をやめた。style
約 81 → 78ms、指紋は同じ)。Rc<BTreeMap> の
make_mut(宣言する要素ごとに祖先の表を丸ごと複製)から、親の表に自分の分だけを重ねる層(CssVars、8
層で平らにする)にした。索引の構築で名前を引くときは写さずに引き、表に無いときだけ写す(push_key)。style
約 78 → 71ms、索引の構築 3.7 → 3.3ms、指紋は同じ。+/~
の左側に一致し得る前の兄弟の特徴とほかの兄弟の種類だけを混ぜ、子へは「親の位置の鍵+親の結果の要約」を渡す。兄弟全員を見る規則(:has(+ …)
等)の候補だけ兄弟全員を足す。旧版の誤り(div:has(.x)
で変わった継承値が別の枝の子に届かない)も直った。ホスト(YouTube
のページで属性を 40 回書き換え)で外れ 2,934 → 405、QEMU の YouTube
で確かめる版の食い違い 0・使い回しの回の当たり 4,088/外れ 20(旧版
3,150/1,981)。試験 tests/src/test_style_memo.rs。+/~
の右側〕・子の要約〔:empty〕。RuleIndex::ancestor_needs)だけを入れる(<body>
の子の <script>
の増減で全子孫が外れていた)。旧版の誤り 2 件(+ の左側の
:empty と、いちばん上の要素の属性が鍵に無い)も直した。QEMU
の YouTube で、使い回せた回の style は多くが 80〜120 tick(以前は
250〜400)。<link> の CSS
が届いたとき、それが既に解析済みの外部 CSS(YouTube は
www-player.css を入れ直す)なら、外部 CSS(約
4.4MB)の解析し直しとスタイルの使い回しの表の破棄を省く(external_css_parsed)。入れ直した位置での後勝ちは反映しない(最初の位置のまま)。<link> が足されたときは、届いた
CSS だけを解析して外部 CSS の後ろ(インラインの
<style>
の前)に足す(append_external_css)。QEMU の YouTube
で、その回の CSS の時間が 150〜200 → 7 tick。同じ CSS が 2 回並んでも 1
回だけ解析する(以前は www-player.css の約 3,400 規則が 2
重に入っていた)。key_eq、16 バイト以下)を、1
バイトずつのループ(コンパイラが memcmp
の呼び出しに置き換えていた)から、先頭と末尾を 4/8
バイトずつ重ねて読んで比べる形にした。QEMU の YouTube で JS のコアの
memcmp
の最大の呼び出し元が変数の検索(VarMap::position)だった。ホストのプロパティ検索のベンチで約
24.9 → 22.1ms。global_window の名前の比較も
key_eq にした。QEMU の YouTube で JS のコアの
memcmp の呼び出し元から
VarMap::position(418)と
global_window(79)が消えた(コアに占める割合 12.3 →
11.6%)。applied)を
BTreeMap から FNV の IndexMap
にした(x-before-…
のように共通の接頭辞を持つ名前で、検索のたびに長い比較を何回もしていた。QEMU
の YouTube でカスケードの中の memcmp
の最大の呼び出し元)。変数(--…)は写すときに先へ並べる。ホストの
style 約 77 → 71ms、指紋は同じ。VarMap::Big、グローバル変数など)の索引を
&str で引くとき、問い合わせの型
NameQ(ハッシュは str と同じ、等しさは
key_eq)で引く(Rc<str> と
str の == が memcmp
を呼んでいた。QEMU の YouTube で JS のコアの memcmp
の最大の呼び出し元の IndexMap::get)。QEMU の YouTube で JS
のコアに占める memcmp は 11.6 →
2.2%(読み込みの途中までの回なので目安)。[PCPROF][CHAIN](フレームポインタ付きのビルドでだけ意味がある
4
段の呼び出しの連鎖)を既定で出さない(PCPROF_CHAIN)。YouTube
の 8 分で 5,000 行を書き出し、ログの書き出し(emit_line)が
JS のコアの 6.3% を使っていた。name += 値(左辺が変数)を専用の命令
AddAssignIdent
にした。右辺を評価した後、変数の文字列を参照しているのが変数と読み出した値の
2
つだけなら、読み出した方を手放して変数の文字列の末尾に足す(以前は毎回全体を写して作り直し、文字列を伸ばすループが長さの
2 乗の写しになっていた。QEMU の YouTube で memcpy
の最大の呼び出し元が二項演算)。JS の selftest
に、別名・自分自身の連結・右辺での代入・クロージャ・数値の左辺の 6
件を足した。QEMU の YouTube で memcpy
の呼び出し元から二項演算が消えた(JS のコアの memcpy 約 12
→ 8%)。dom_obj)を、最近作った
1,024 個まで強く持つ(dom_recent)。弱い表だけだと JS
がすぐ手放すたびに消えて、次の参照で作り直していた。フレームポインタ付きのビルドの
[PCPROF][CHAIN] で、メモリの確保を頼んだ関数の 1
位だった。QEMU の YouTube で JS のコアのメモリの確保・解放が約 24.6 →
20.0%(別の回どうしの比較なので目安)。get_dom_node_proto
で、タグ名を毎回 to_lowercase
で写さない(大文字を含むときだけ写す。タグ名→クラス名は
dom_class_for_tag)。同じ [PCPROF][CHAIN]
で、メモリの確保を頼んだ関数の 2・3 位だった。document.all
は要素の添字の並びが前回と同じなら同じ配列を返す(doc_all_cache)。YouTube
の検査(k!==document.all)が何度も読み、そのたびに全要素(数千)の
JS のオブジェクトを作っていた。2 回目の [PCPROF][CHAIN]
で、確保を頼んだ 1 位の dom_obj(597 中
153)の呼び出し元がこれだった。document.all === document.all
も真になった(selftest を足した)。spec/DESIGN.md 16
章に構造と順序を書き起こしたときに見つけた)。
CowBTree::take_replaced・free_file_sectors)。/cache/・/tmp/)も
microSD へ書いていた(storage_policy.rs
の方針がどこからも使われていなかった)。microSD を使うときは RAMDisk の
SylFS をもう 1
つ作って一時ファイルを振り分ける(set_volatile)。QEMU では
microSD の読み出しの確認に失敗して RAMDisk
だけで動くため、この経路はホストの試験で確かめた。tests/src/test_fs_space_reuse.rs(4 件: 同じパスへ
500 回保存しても満杯にならない・8
つのファイルを交互に保存し直してもほかを壊さない・削除したパスは無いになり保存し直せる・一時ファイルは置き場へ行く)。[PCPROF][PAIR])、フレームポインタを
2
段たどった戻り先([PCPROF][CHAIN]。フレームポインタ付きでビルドしたときだけ意味がある)を足した。残り(TODO B0): BotGuard
は受け渡し(havuokmhhs-0.bevasrs)までは進むが、準備完了(bevasrs.p)が決まらない。
Promise.all を正しくした副作用で、PO
トークンを待つ処理が待ったままになり、以前は偶然進んでいた自動再生が止まるようになった(B0
を直せば戻る見込み)。 ほかに: bytecode.rs の
continue の unwrap(clippy)を除いた。確認:
ホスト試験 2,514 件・全セルフテスト PASS・clippy の禁止項目 0。
RefCell 再借用バグの修正spec/TODO.md および
spec/implementation_plan.md の A1(段階 1・段階 3
の残り:Op::Eval・Op::Exec
で構文木へ戻っていた主要な式・文のバイトコード化)
を実装し、内側の変数参照やメンバー参照まで
Slot・PropSlot の O(1)
インラインキャッシュが効くようにするとともに、段階
4(中断・再開)に向けて Op::Exec
による構文木再帰依存を大幅に削減した。
arm/v1/src/os_lib/js/interp/bytecode.rs)new
式・テンプレートリテラル:
Op::Array(u32): スプレッドを含まない配列リテラル
[a, b, c] の各要素をバイトコードで評価し、一括で
Obj::array を生成する。Op::NewObject・Op::InitProp(u32)・Op::InitPropComputed:
通常のデータプロパティ(静的キーおよび [expr]: val
算出キー、無名関数の infer_function_name
付与込み)からなるオブジェクトリテラル { a: x, [k]: y }
をフラットな命令列へコンパイルする。Op::New(u32): スプレッドを含まない
new Ctor(a, b) をバイトコード化し、arg_pool
再利用および Proxy の construct
トラップ呼び出しに対応する。Op::Template(*const Vec<String>, u32):
テンプレートリテラル `prefix${a}mid${b}`
の埋め込み式をバイトコード評価し、to_string_value(Symbol
例外伝播対応)で連結する。delete
演算子・論理代入・メンバー/添字の複合代入と更新演算子
(++/--):
Op::DeleteMember(u32)・Op::DeleteIndex:
delete obj.prop および delete obj[key]
をバイトコード化した。&&=, ||=,
??=): 単純識別子に対する x &&= rhs /
x ||= rhs / x ??= rhs を、既存の短絡命令
Op::AndKeep / Op::OrKeep /
Op::NullishKeep と Op::Load /
Op::Store
の組み合わせに展開した(短絡時は右辺を評価せず左辺値をそのまま残す)。obj.prop += rhs,
arr[i] += rhs): スタック複製命令 Op::Dup /
Op::Dup2 を導入し、対象オブジェクト(および添字)を 1
回だけ評価した上で Op::Member(PropSlot
キャッシュ有効)/ Op::Index → 右辺評価 →
Op::Binary → Op::StoreMember /
Op::StoreIndex へ展開した。Op::UpdateMember・Op::UpdateIndex:
obj.prop++ / ++obj.prop /
arr[i]++ / --arr[i]
を専用命令化し、メンバー側は PropSlot キャッシュ、添字側は
get_index_fast / set_index_fast
高速パスを経由して O(1) で加減算する。for-of /
for-in・switch・try/catch)
(arm/v1/src/os_lib/js/interp/bytecode.rs)for-of / for-in ループ
(Op::IterInit(bool)・Op::IterNext):
for (const/let/var x of iterable)(非
for await)および for (const/let/var k in obj)
をバイトコード化した。Op::IterInit(of) で iterate_values
の結果配列と走査添字 0 を vm_stack
に積み、Op::IterNext
で毎反復の要素取り出し・Slot
キャッシュ経由の束縛更新・クロージャ捕捉時の block_scopes
自動差し替えを行い、ループ脱出時(break 含む)に
Op::Pop 2 回でイテレータ状態を確実に掃除する。switch 文
(Op::CaseEq・LoopCtx::is_switch):
switch (disc) { case a: ... default: ... }
をバイトコード化した。Op::CaseEq は判別式 disc
をスタックに残したまま case 値と厳密等価 (===)
比較を行い、一致時は disc
をポップして該当節へジャンプ、不一致時は次の case
比較へ進む(フォールスルーおよび途中に置かれた default:
節にも対応)。LoopCtx に is_switch: bool
を追加し、PendingExec のジャンプ先解決で break
は最内周のループまたは switch を抜け、continue
は switch
を飛び越えて最内周のループの継続先へジャンプするように分離した(その間に通過するブロックスコープ段数
LeaveBlocks と try ハンドラ段数
LeaveTry も個別に巻き戻す)。try { ... } catch (e) { ... } 文
(Op::EnterTry・Op::LeaveTry・Op::CatchParam):
finally を持たない try/catch
文をバイトコード化した。run_code_inner に軽量な
try_stack: Vec<(usize, usize, usize)>((catch_pc, block_depth, stack_depth))を持たせ、Op::EnterTry
で登録・正常完了時の Op::LeaveTry(n) で解除する。Err(Value))を
try_stack で捕捉し、内側の block_scopes を
block_depth まで回収・vm_stack を
stack_depth まで切り詰めた上で catch
節の先頭へ遷移し、Op::CatchParam で例外値を
catch (e) の字句スコープ Slot
に束縛する。proto_has_accessor の
RefCell 二重借用修正
(arm/v1/src/os_lib/js/interp/properties.rs)FEATURES=selftest による QEMU
実機ブート検証において、set_property_with_key_rc
の高速パスが let mut b = o.borrow_mut(); を保持したまま
proto_has_accessor(&b.proto, key)
を呼んでいたため、Object.setPrototypeOf
で自己循環プロトタイプ連鎖が作られた異常系テストで
p.borrow() が o 自身を再借用し
RefCell already mutably borrowed
パニックとなる問題を発見した。proto_has_accessor 内のプロトタイプ走査を
p.try_borrow()
に変更し、既に可変借用中のオブジェクト(循環連鎖)に遭遇した場合は安全に
true(高速パスをバイパスして既存の循環ガード付き通常パスへフォールバック)を返すように修正した。arm/v1/src/os_lib/js/selftest.rs)selftest.rs
に、上記すべての新バイトコード構文(配列・オブジェクトリテラル、算出キー、new、テンプレートリテラル、delete、&&=/||=/??=、obj.p += v
/ arr[i] += v、obj.p++ /
arr[i]++、for-of /
for-in、switch 内の continue /
break / default フォールスルー、および
try/catch 例外捕捉)を組み合わせた関数を 1〜7 回目(AST
評価)と 8〜13
回目(バイトコードコンパイル後)で呼び出して結果が完全一致するセルフテストを追加。FEATURES=selftest ブートで
3. Core Software Self-Tests (JS_SELFTEST: PASS, ...) [OK]
を確認。QEMU の raspi3b マシンは BCM2837 内蔵の PWM / I2S
オーディオ出力をモデリングしていないため、-audiodev coreaudio,id=audio0(または
dsound)を指定してもホストのスピーカーから音が鳴らない課題(TODO.md
B 章・implementation_plan.md)を解決するため、DWC2 USB
ホストコントローラ上に USB Audio Class 1.0 (UAC 1.0)
ドライバを新設し、AudioStream(YouTube / MSE
連続再生)および Audio(MML
効果音)の出力パイプラインを接続した。
arm/v1/src/kernel/usb_audio.rs,
audio_stream_fmt.rs)parse_uac1_config による UAC 1.0
記述子解析:
Audio Control (0x01/0x01) および
Audio Streaming (0x01/0x02)
インターフェースのすべての Alternate Setting
を走査し、CS_INTERFACE (AS_GENERAL
wFormatTag = 0x0001 PCM、FORMAT_TYPE_I
のチャネル数・サブフレームサイズ・ビット解像度・離散/連続サンプリング周波数)、等時性
OUT エンドポイント記述子
((bEndpointAddress & 0x80) == 0 && (bmAttributes & 0x03) == 0x01)、および
CS_ENDPOINT の Sampling Frequency Control
対応有無を抽出する。src_frames_for_1ms / resample_to_uac_packet /
PcmQueue::fill_usb_iso_packet):
usb-audio デバイスは 48,000Hz ステレオ
S16LE(1ms あたり 48 フレーム = 192 バイト)固定である一方、YouTube の
AAC ストリームは 44,100Hz や 48,000Hz 等多様である。src_frames_for_1ms(src_rate, rem) により 1ms
ごとに消費すべき入力フレーム数と余り rem
(0..1000) を追跡し(例: 44,100Hz なら 10ms 中 9 回は 44
フレーム・1 回は 45 フレームを取り出し、1,000ms で厳密に 44,100
フレームを消費してドリフト 0)、固定小数点 (Q16) 線形補間で 48
フレームの 16-bit Little-Endian ステレオ PCM パケットへ変換する。AudioStream の
PLAYED_FRAMES
に加算するため、AudioStream::played_ms() による A/V
同期も実際の USB Audio 出力進行と完全に一致する。synthesize_tone_uac_packet):
Audio::set_tone(freq) / Audio::mute()
で現在の発振周波数 CURRENT_TONE_FREQ
を追跡し、AudioStream 非再生時に MML が鳴っている間は位相
phase (0..sample_rate)
をパケット間で引き継ぎながら 1ms 分の矩形波(振幅
±5000)を合成して USB Audio へ送出する。arm/v1/src/kernel/usb.rs, audio.rs,
Makefile,
scripts/arm/run_qemu_dynamic.sh)UsbDeviceKind::Audio):
enumerate_default_address_device で
UsbDeviceKind::Audio
を検出した際、SET_CONFIGURATION に続けて
SET_INTERFACE(AudioStreaming インターフェースの
Operational Alternate Setting 有効化)および必要に応じ
SET_CUR(SAMPLING_FREQ_CONTROL)(エンドポイントへの 24-bit
LE サンプリング周波数設定)を送信し、USB_AUDIO_DEV
を有効化する。poll_usb_audio_data_plane):
ch=0 (Control)、ch=1 (HID Interrupt
IN)、ch=2 (Ethernet Bulk OUT)、ch=3 (Ethernet
Bulk IN) と干渉しない専用ホストチャネル
ch=4 と 64B アライン専用バッファ
AUDIO_DMA_BUF を割り当てた。usb::poll() 内から実時間経過(1,000us 単位、1 回最大 8
パケット)に応じて ep_type = 1
(Isochronous)、pid = 0 (DATA0) で OUT
転送を発行する。また実機 RPi 3B+ の HS ハブ配下に FS USB DAC
を挿した場合のスプリット転送でも、USB 2.0 §11.18.4(Isochronous OUT は
Start-Split のみで完結し Complete-Split を持たない)に準拠して SSPLIT
完了時点で即時成功を返すようにした。AudioStream 非再生かつ MML
消音時)はレジスタアクセスを一切行わず即 return
するため、既存のネットワーク・HID ポーリング性能に一切影響しない。arm/v1/Makefile(run,
run-win, run-no-ether,
run-user-net, run-no-smp,
run-vmnet-bridged)および
scripts/arm/run_qemu_dynamic.sh に
-device usb-audio,audiodev=audio0,bus=usb-bus.0,port=1.4
を追加した。arm/v1/tests/src/test_usb_audio.rs に 6
件の単体テスト(QEMU usb-audio 記述子解析、マルチ Alt
からのステレオ優先選択、24-bit LE 周波数符号化、44.1kHz 1,000ms
ドリフトゼロ検証、PcmQueue::fill_usb_iso_packet
44.1kHz→48kHz 変換、位相連続トーン合成)を追加し、ホスト単体テスト全
2,514 件 PASS・AArch64 カーネルビルド成功を確認。PropSlot 拡張(A1 段階 1/2/3)spec/TODO.md および
spec/implementation_plan.md の最優先課題である
A7(生存オブジェクトのメモリ削減) と A1(段階
1/2/3: let/const
ブロック・for (let ...) スコープ・単項演算・DOM/ES6
クラスの PropSlot インラインキャッシュ)
を実装・検証した。
PropKey 16B 化+Rc<str>
共有・ObjKind::Function インライン化・Scope
40B 化 (value.rs, interp.rs,
calls.rs, properties.rs,
eval_expr.rs, operators.rs,
cycle_gc.rs)PropKey の 24B → 16B 縮小と構文木
Rc<str> の直接共有:
PropKey は
enum { Inline { len: u8, buf: [u8; 22] }, Heap(Rc<str>) }
で 24 バイトあり、すべてのオブジェクトのプロパティ表
(SmallPropMap) の 1 エントリ (PropKey, Value)
が 40 バイトを消費していた。また 22 バイト超のキーやメンバー代入時に
String → Rc<str>
の新規ヒープ確保が走っていた。#[repr(C, align(8))] pub struct PropKey { data: [u8; 16] }(16
バイト)に再設計し、先頭バイト data[0] の bit 0
をタグとして使用:
data[0] & 1 == 1(インライン):
len = (data[0] >> 1) as usize(最大 15 バイト)の
UTF-8 文字列を data[1..1+len] に無確保格納。data[0] & 1 == 0(ヒープ共有):
Rc<str> のデータポインタ(8B アラインのため bit 0
は常に 0)を data[0..8]、バイト長を
data[8..16]
に格納し、From<Rc<str>> /
From<&Rc<str>>
で構文木の識別子・プロパティ名 Rc<str>
の参照カウントを +1 するだけで新規ヒープ確保を完全にゼロ化した。(PropKey, Value) エントリが 40B → 32B(20%
削減) となった。さらに set_property_rc
を新設し、オブジェクトリテラル
(eval_object_lit)・メンバー代入
(operators.rs)・バイトコード (Op::StoreMember)
から構文木の Rc<str> を直接渡すようにした。set_property の高速パスを
b.proto.is_none() 限定から
b.proto.is_some()(プロトタイプ連鎖上に同名 setter
が無い素のオブジェクト)へ拡張し、一時 String 確保と約 400
行のフォールバック判定をバイパスした。ObjKind::Function(FunctionData)
のインライン化(関数 23 万個の別ヒープ確保を全廃):
ObjKind::Function(Rc<FunctionData>) として
Obj(80B)とは別に
RcBox<FunctionData>(40B、アロケータヘッダ込み
48〜64B)を毎回ヒープ確保していた。YouTube
視聴ページでは生存関数オブジェクトが約 23 万〜24
万個あり、この別ヒープ確保だけで約 11〜14.7MB を消費していた。FunctionData(24B)の bound_this を
Option<Rc<Value>>(アロー関数以外は
None で 0 確保)にして
ObjKind::Function(FunctionData) として
ObjKind(32B)内に直接インライン格納した。size_of::<ObjKind>() == 32・size_of::<Obj>() == 80
を維持したまま、関数生成 1 回あたりのヒープ確保を 2 回 → 1
回に半減させた。VarMap の 32B → 24B 縮小と Scope
の 48B → 40B 縮小(TLSF 64B 境界内への収容):
VarMap<V> の Big バリアントを
Box<BigVarMap<V>> に包むことで
size_of::<VarMap<Value>>() を 32B →
24B、size_of::<Scope>() を 48B → 40B
に縮小した。これにより RcBox<RefCell<Scope>> が
72B → 64B となり、TLSF アロケータの 64B 2
べき乗境界内に収まるようになった(生存スコープ 13.5
万個でヒープブロック切り上げ無駄を大幅削減)。call_user_function
で全関数の変数予約数に無条件で足されていた + 1
を撤廃し、recycle_call_scope
でクロージャに捕捉されてヒープに残るスコープ(Rc::strong_count > 1)に対して
vars.shrink_to_fit()
を呼ぶようにし、プール由来の過剰容量(最大 32
スロット分)が永続化するのを防いだ。for (let ...)・単項演算/Throw・DOM/Class
PropSlot 拡張 (interp/bytecode.rs,
interp/properties.rs)let/const)
と for (let/const ...) ループのバイトコード化:
Op::DeclLexical(u32)・Op::EnterBlock・Op::LeaveBlocks(u32)・Op::RefreshForLet
を新設した。Op::Exec で構文木評価へフォールバックしていた
let/const 宣言、{ let/const ... }
を含むブロック、および for (let i = 0; ...)
ループをすべてバイトコードへコンパイルするようにした。block_scopes: Vec<Rc<RefCell<Scope>>>
で内側のブロックスコープを管理し、クロージャに捕捉されていない通常の反復では
Rc::strong_count == 1 のため Op::RefreshForLet
が確保ゼロの no-op
で回り、クロージャが反復変数を捕捉したときのみ新しいスコープへ値を引き継ぐ。break
/ continue / return /
例外送出時も内側から順にスコープを確実にプールへ回収する。-, +, ~,
void, typeof) と throw
のバイトコード化:
Op::Neg・Op::Pos・Op::BitNot・Op::TypeOf・Op::TypeofIdent(u32, u32)・Op::Throw
を追加し、Closure Compiler 等で頻出する void 0 は定数
Op::Const(Undefined) 1
命令に畳み込むようにした。typeof ident も Slot
キャッシュを使って O(1) で型文字列を返す。class プロトタイプおよび DOM / Custom
Element (ObjKind::DomElement) の PropSlot
インラインキャッシュ対応:
get_prop_fast / learn_prop_slot は
cb.rare.is_some() でキャッシュを諦めていたが、ES6
class のメソッド定義や Object.defineProperty
は常に attrs(enumerable: false)を
rare に記録するため、すべての ES6 クラスメソッドで
PropSlot が効いていなかった。これを
!cb.has_accessor(name)
の判定に修正し、プロトタイプ連鎖の探索上限も 8 段 → 16
段へ引き上げた。ObjKind::DomElement(node_idx)
に対応し、要素自身の dom.expandos(Polymer の
this.__data, this.$
等)を直接返すとともに、昇格済み Custom Element の
dom.node_protos
プロトタイプ連鎖上のメソッド(_getProperty,
_setPendingProperty 等)を PropSlot で O(1)
キャッシュするようにした。test_js_small_prop_map.rs
のサイズ不変条件テスト(9 件)およびホスト単体テスト全 2,508 件
PASS、selftest.rs にサイズ検証+class
継承・for (let ...) クロージャ捕捉・ネストブロック
break/continue・単項演算のバイトコード適合テストを追加し、AArch64
カーネルリリースビルド (--features selftest) がエラー 0
で完了することを確認。test_css_units の rem
が基準(set_root_font_px)のグローバル状態で稀に落ちたが、現在は同テスト自身が基準を設定している)。make all
は成功。kernel/audio.rs の起動ログが「PWM と I2S (HDMI)
の音声エンジンを初期化した」と読めたが、I2S は HDMI 音声へ出ない(HDMI
音声は未対応、TODO B)。ログとコメントを「PWM のみ。I2S
はレジスタ設定のみ・HDMI 音声は未対応」に直した。KERNEL_HEAP_SIZE = 768MiB)、(4) 実機の PWM
が未確認(B・C。QEMU では 2026-11-03 の USB 音声で確認できる)、(5)
実機の速度(H.264 復号・JS・再レイアウト、C)、(6)
ストリーミング受信が無い(https_get_binary* はすべて
Vec<u8> を全体取得して返す)、(7)
細部(操作部のタップの遅れ・自動再生後のメタデータ反映・内蔵の代用ページ)。spec/walkthrough.md(v0.2
系)が約 1,700 行あり、AGENTS.md の目安(1 ファイル約 450〜500
行)を大きく超える。圧縮は情報を落とすため、マイナーバージョンを上げる区切り(v0.3)でアーカイブ(spec/walkthrough/v0.2.md)へ移して新しい
walkthrough を始める。<script async=false>
順序実行・FnBody の Rc<str>
共有・プロトタイプ PropSlot / バイトコード拡充spec/TODO.md および
spec/implementation_plan.md の残課題(B2
非同期通信の完成・A2 遅延パースのメモリ削減・A1 段階 2/3 プロトタイプ
Inline Cache とバイトコード拡充)をまとめて実装・検証した。
XMLHttpRequest と XhrStateMachine
の統合および全状態遷移・複数リスナの実装 (xhr_state.rs,
static_classlist.rs, typed_array.rs)XhrStateMachine の実運用統合:
単体検証モジュールとして置かれていた
arm/v1/src/os_lib/js/xhr_state.rs
の状態遷移・ヘッダ正規化/検索・イベントディスパッチ計画
(XhrTransitionPlan) を、実体の XMLHttpRequest
(arm/v1/src/os_lib/js/builtins/static_classlist.rs・typed_array.rs)
から直接呼び出す形に統合した。readyState 2 (HEADERS_RECEIVED) /
3 (LOADING) と progress / loadend
イベント:
1 (OPENED) →
2 (HEADERS_RECEIVED) (onreadystatechange) →
3 (LOADING) (onreadystatechange +
progress({ loaded, total, lengthComputable })) →
4 (DONE) (onreadystatechange +
load/error/timeout +
loadend) の全段階を発火するようにした。progress / load / error /
abort / timeout / loadend
の各イベントオブジェクトに type, loaded,
total, lengthComputable, target,
currentTarget を付与した。abort() の実装:
__sent == true, readyState 1〜3)
の xhr.abort() 呼び出しで __aborted = true
を記録し、status = 0・応答クリアの上で
readyState = 4 → onreadystatechange →
abort → loadend を発火し、その後
readyState = 0 (UNSENT)
に戻す仕様準拠の遷移を実装した。バックグラウンドの非同期ワーカー完了時には
__aborted を見て二重発火を抑止する。addEventListener /
removeEventListener:
on<type> プロパティへの単純代入だった
xhr.addEventListener(type, fn)
を、__listeners_<type>
配列による複数リスナ保持(同一関数の重複登録抑止つき)と
xhr.removeEventListener(type, fn)
による解除に拡張し、on<type>
プロパティと登録済みリスナ配列の双方を順序通り呼び出すようにした。arm/v1/tests/src/test_xhr_async.rs に 4
件のユニットテスト(readyState 2/3/4 遷移と
progress/loadend、複数
addEventListener/removeEventListener、非同期進行中の
abort()、ネットワーク失敗時の error +
loadend)を追加、および selftest.rs
にも実機セルフテストを追加。<script> の async = false 順序保証
(dynamic_scripts.rs, layout.rs)DynamicScriptEntry に
async_flag: bool(デフォルト true、JS で
s.async = false または HTML 属性で明示された場合に
false)を追加し、DynamicScriptManager に
ordered_queue: VecDeque<OrderedScriptSlot>
を新設した。async = true(既定)のスクリプトは従来通り取得完了順に即時実行する一方、async = false
のスクリプトは追加順キューに入り、先頭スロットから順に取得完了したものだけを
take_ready
で返し、先行スクリプトが未取得の間は後続スクリプトの実行を待機する。また取得失敗時は
Failed として順序通りに error
イベントを発火して後続スクリプトのブロックを解除する。arm/v1/src/os_lib/web_engine/layout.rs の動的
<script> 検出で、JS オブジェクト上の
el.async === false プロパティおよび async
属性を読み取って register_pending_with_async
に渡すようにした。arm/v1/tests/src/test_dynamic_scripts.rs
に逆順でダウンロード完了しても挿入順(1→2→3)に実行されるテストと、先頭スクリプト失敗時に後続がアンブロックされるテストを追加。FnBody) の Rc<str> +
(start: u32, end: u32) 共有化 (lexer.rs,
parser.rs, ast.rs)Rc<String>
複製の廃止:
String として切り出し Rc<String>
で保持していたため、数千個の関数を含む desktop_polymer.js
や base.js で関数定義のたびヒープ確保が発生していた。Token にバイト範囲 (start: u32, end: u32)
を記録し、Lexer::tokenize_with_source が元ソースの
Rc<str> とトークン列を返すように変更した。FnBodyState::Source を
{ src: Rc<str>, start: u32, end: u32, is_strict: bool }
に変更し、同一スクリプト内の全遅延関数が単一の
Rc<str> を参照カウント共有して
&src[start..end]
スライスから初回呼び出し時にオンデマンドでパースするようにした。arm/v1/tests/src/test_js_ast_size.rs に
lazy_fn_body_shares_source_and_parses_on_demand
を追加し、複数関数の Rc::ptr_eq
同一性と各関数のスライス境界・初回呼び出し時パースの正確性を検証。PropSlot Inline Cache
とバイトコード命令拡充 (interp/properties.rs,
interp/bytecode.rs)ObjKind::Plain
のプロトタイプ探索高速パス:
get_property
で自身のプロパティに無く、ObjKind::Plain
かつ特殊キー(__proto__, selectedIndex,
size)や window でない場合、約 500 行の
DOM/ホスト/組み込み種別ディスパッチをバイパスして直ちに
get_property_proto_chain へ進む高速パスを追加した。PropSlot (段階
2):
PropSlot に hops: Cell<u8> と
holder: Cell<*const RefCell<Obj>>
を追加した。hops == 0 は従来通り自身の
shape_id とスロット添字を参照し、hops >= 1
はプロトタイプチェーン上の保持オブジェクト (holder)
のポインタと
shape_id・スロット添字に加え、起点から保持オブジェクトまでの全階層のプロトタイプに同名プロパティが追加されていないこと(各段の
__proto__ とプロパティ不在)を確認して O(1)
で値を返す。Op::StoreMember(u32) および Op::StoreIndex
命令を追加し、obj.prop = val / arr[i] = val
の単純代入式(識別子・リテラル・メンバーアクセス等の右辺)を AST
フォールバックなしでバイトコード VM 上で直接実行できるようにした。selftest.rs にプロトタイプ
PropSlot(継承メソッド参照およびシャドーイング時のキャッシュ無効化)と
StoreMember/StoreIndex
ループのセルフテストを追加し、AArch64 カーネルリリースビルド
(--features selftest) が警告・エラー 0
で通ることを確認。11 月 2 日夕方〜3 日未明に入った約 35 件のコミットを、試験・セルフテスト・YouTube の実行で確かめ直した。
xhr_worker_thread の中に居た。
原因は、カーネルスレッドの入口関数から戻ると、戻り先(x30)が入口のままなので入口から再実行されること。
非同期 XHR(2026-11-02 続報 27)のワーカーは、仕事が無くなると
break で戻っていた。XHR
のたびに「起きて、仕事が無いので戻って、また起きる」スレッドが 1
本ずつ増え、4 本で全 CPU が埋まった。 font_queue.rs
は以前同じ罠を踏んで scheduler::exit()
を明示していたが、作法として共有されていなかった。sendBeacon
のワーカー(return)と video_playback_thread
の失敗時の return も同じ形だった。 修正:
個々の関数ではなく
scheduler::spawn_with_stack・spawn_on_core_with_stack
で、入口を x19 に、戻り先を
trampoline_noarg(入口を呼び、戻ったら
exit())にした。引数付きの spawn_with_arg
は元からこの形だった。XHR のワーカーにも exit()
を明示した。xhr_send
が要求を積むと、送信側は「まだ動いている」と見て起こさず、誰も取らない要求が残る。止めた後にもう一度待ち行列を見て、残っていれば続けるようにした(sendBeacon
と同じ形)。has_pending_xhr_results
が XHR_RESULTS の spin::Mutex
を取っていた。「待機・再描画の判定は錠を取らない」(優先度逆転で止まる。2026-09
に 3 回踏んだ)の規則に反する。件数の原子変数を読むようにした。<script>
の取得に失敗すると永久に保留。非同期取得(2026-11-02 続報
26)は、取得の失敗を誰にも伝えず、要素に load も
error
も来ないまま、描画も保留ありとして回り続けた。font_queue
がスクリプトの取得失敗を記録し(fetch_failed)、DynamicScriptManager::take_failed
が error を発火する。試験 1 件。cargo clippy --release
が通らなかった(deny 4
件)。x509.rs・xhr_state.rs
の文字列の添字(split_once へ)、parser.rs の
unwrap()。implementation_plan.md(2026-07 から
spec/ と二重に更新されていた)を
spec/implementation_plan.md へまとめて消した。spec/tmp/(commit_tag.sh・update_agents_rule.py)がコミットされていたので消した。spec/web_components_plan.md(2026-10-14
の計画書)は中身(Custom Elements・Shadow
DOM・<slot>)がすべて実装済みで、計画書は 1
本の規則にも反するので消した。spec/README.md の索引に
shared_state.md・walkthrough/v0.1.md
を足した。TODO.md 3
章へ。TODO.md 0
章を、現状・不足の一覧・各項目に組み直した。項目の中に溜まっていた経緯は下に移した。修正後に 3 回開いた(TODO.md 0
章の「現状」と同じ内容)。軽い版は約 2 分で再生して最後まで、パニック
0(ヒープ最大 159MB)。 重い版 A は再生開始 175 秒(前日の約 220
秒より速い)、自動再生で 3 本目まで進み、約 420 秒でヒープ 602MB
に達して video_playback・app_runner が panic。
重い版 B
は再生が最後まで進んだのに画面が骨組みのまま更新されず、原因は未特定(TODO.md
E)。
TODO.md 0 章から移設。本文は当時のまま)実 YouTube watch ページの残り(2026-10-24、QEMU
で動画の描画・再生までは到達)。デスクトップアプリが起動し
動画もアプリのレイアウトで再生される。再レイアウトが 1 回 3〜4
秒(2026-10-24 にカスケードを約 44% 短縮。残りはスタイル 2.3
秒・ レイアウト 1.0〜1.3
秒)で、表示が揃うまで数分かかるのが最大の課題(説明欄開閉トグル・関連動画フィード構造・ロゴ下線重複排除・操作バー右代替文字解消・インラインSVG高度描画・絵文字フォントは
2026-10-28
完了。寸法を読んだ時点のインラインスタイル・属性・HTML5固有寸法フォールバックおよび再レイアウト後の
resize 通知は 2026-10-29 完了)。 JS 実行が再生開始まで(約 4.5
分)の大半(2026-10-24 計測、[JS][TIME] ログ):
kevlar 本体の実行 40 秒に加え、
小さなインラインスクリプトの後のイベントループで 15〜70
秒ずつ。関数別のサンプリングでは、ログ送信
(/youtubei/v1/log_event、7 回、圧縮後 2.6〜4KB)の本文を
JS 版 zlib(pako の deflate:
VY/KVN/uz)で gzip
圧縮する処理が最上位。インタプリタは約 41
万ステップ/秒(QEMU、2026-10-24 にスケジューラの
TLB・変数表・添字を改善して約 1.3 倍)。対策候補: インタプリタの高速化
(プロパティ参照・型付き配列の添字)、ログ送信の扱い。 (2026-10-24
追記)重い版の残りの塊は特定の関数ではなく、ハッシュ表の検索と文字列比較(約
12%)・ メモリの確保と解放(約 20%)・循環参照の回収(約
5%)に広く分散。次はプロパティ名の事前ハッシュ化や
値の複製の削減など、インタプリタの作りに踏み込む改修が要る。
YouTube は版を出し分ける: アプリ本体 10.8MB
の版(デスクトップアプリ、再生開始 約 3.5 分)と、 1.19MB
の版(アプリを使わずプレーヤーだけ、再生開始 約 1
分)。重い版では動画の下(タイトル・説明・
関連動画)が出ない。データ反映前の時点では
ytd-watch-metadata の中身が空だった。 (2026-10-24
追記)要素のアクセサ・循環回収・関数の prototype
の修正でタイトルと再生回数が出た。 残り:
説明欄・関連動画・チャンネル行の並び、再生開始が約 5.7
分と遅い(len=112 のイベントループが 約 84 秒・2,890
万ステップで上限 3,000 万に近い)、ヒープは最後に約
500MB(生存オブジェクト約 50 万、 1 個あたり Obj
160B+プロパティ表。キー文字列の共有化などで削る余地)。 (2026-10-25
追記・速度の現状)再生中のブラウザの CPU は分散している(カスケード約
6%・backdrop の ぼかし約 5%・JS 計 15〜20%・確保と解放など)。JS は 1
分あたり約 1,000 万ステップで、YouTube の player が ログを gzip
圧縮する処理(pako 相当)が区間によって約 3 割。変数名の共有(パーサでの
intern)は効果が
測れず取り消した。これ以上はインタプリタの方式(構文木を直接たどる)を変える改修(バイトコード化・
変数の静的解決・プロパティ参照のキャッシュ)が要る。 (2026-10-25
追記)動画は 17 秒で止まらず再生が続く(resize
の発火条件・playing/waiting・readyState・
MediaSource の付け替え・fMP4 の edit list を修正)。ロゴ(SVG
の穴)・rem の基準・回転数字・動画の層を修正。 残り:
読み込み待ちが頻繁(JS が重く次の SABR 要求が遅れる)、再生開始まで約
4.5
分。ヘッダー配置・ログインボタン整列・ロゴ下線排除・チャンネル行並び・Unicode絵文字ビットマップフォント基盤(50種+フォールバック)は
2026-10-28 完了(walkthrough 参照)。 (2026-10-24 追記)確保を約 55%
削減し len=112 は約 66
秒。次は組込みメソッドの関数オブジェクトの使い回し
([].push === [].push
も仕様どおりになる)、呼び出しごとのスコープ・引数の確保。
自動再生で次の動画へ移ると表示が崩れる(動的コンテンツ更新追従の残課題)(2026-10-29、QEMU
で 4 回再現)。SERIES=40(撮影スクリプトで 40
秒ごとに保存)で追うと、 約 4 分で描き上がり、「Me at the zoo」(19
秒)の再生後 212 秒で「次の動画を再生」のカウントダウン、255
秒で次の動画(Speedrun)の 再生へ移る。その時点で
youtubei/v1/player・next が 1 回ずつ届く。
カスタム要素 setter の無限再帰・pushState
後のアドレスバー・マイクロタスクでの描画駆動は 2026-10-29
に解消(walkthrough 10月29日 続報 3・4・11)。 ロゴの位置に残る四角は
2026-11-01 に解消(src の無い <img>
への代用の絵。walkthrough 参照)。
オフライン画面と、自動再生の後のヘッダー・タイトルの欠落は
2026-11-01
に解消(Response.clone・JSON.parse。walkthrough
続報 7・8)。 再生の進み方が回によって違う問題は 2026-11-01
に解消(performance.now()
が実時刻で進んでいなかった。walkthrough 続報 19)。修正後は 4
回とも映像が出て次の動画へ進んだ。 20
分の連続実行(2026-11-01): 2
本目の動画の途中で「エラーが発生しました。更新するか、しばらくしてからもう一度お試しください」の帯が出て止まり、ヒープが尽きて
video_playback が 1.2MB のフレームの確保で panic、続いて
app_runner も panic(used 585MB /
768MB)。生存オブジェクトは 82 万個、スコープ 13.5
万個で、種類は偏らず(plain 27 万・fn 24 万・arr 16 万・dom 8
万)、遷移と関連動画の描画で増える。特定の漏れではなく、ページが使う量がヒープに収まっていない。エラーの帯の原因は未調査。
自動再生を重ねるとヒープが尽きてブラウザが落ちる(2026-11-01、3
本目の動画の頃。ヒープを 768MB へ増やして 13 分は持った):
[ALLOC FAIL] の used は 520〜560MB
だが、これは要求サイズの合計で、 ブロックのヘッダと 32
バイト単位の切り上げを含まない(実際の使用量はずっと多い)。192
バイトの確保まで失敗するので断片化ではなく本当の枯渇。 大小 2
プールへの分割は効果が無く取り消した。生存オブジェクトは 1 本目の後 55
万 → 2 本目の後 65 万個(dom のハンドル 3.7 万 → 6.6
万、配列 9.4 万 → 12.2 万)。 遷移 1 回の後、expandos の
59%(3041/5145)は文書の外のノードのものだが、文書の外の木を GC
の節点にして回収しても 2635 個までしか減らなかった (大半は YouTube
自身がハンドルを保持している。効果が小さいので GC
の変更は取り消した)。128KB 以上の確保は計 113MB。
キーのインライン化(PropKey、2026-11-01)で使える量は約
23MB 増えたが足りない。内訳(2026-11-01、ヒープ 455MB の時点):
算出スタイル 5,044 要素・75,758 プロパティ(文字列 1.2MB、構造込みで推定
10MB 強)、DOM 11,736 ノード(文字列 6.9MB)、描画要素 1,125 個×1800B。
どれも主因ではない。保持している構文木も少ない(2
回以上呼ばれてキャッシュされた本体は 6,188 個・元のソース
2MB、構文木にして十数 MB。FnBody の遅延ソースは計 27MB)。
構文木を捨てて解析し直す方式やバイトコード化は、メモリには大きく効かない(A1
の効果は速度)。主因は YouTube が保持する生存オブジェクト(64
万個・スコープ 11 万個)そのもの。 残る手段:
Obj・スコープの大きさを縮める、FnBody
の遅延ソースをスクリプト全体の 1 本と範囲に替える(最大十数
MB)、ヒープの上限を増やす(allocator::KERNEL_HEAP_SIZE。実機の
RAM に制約あり)。 残りの表示崩れ(2026-11-01 の画面):
高評価の列がチャンネル登録ボタンと少し重なる問題は 2026-11-02
に解消(<button> の既定
white-space: nowrap と内在幅計測改善。walkthrough 続報 24
参照)。 最初の動画(Me at the zoo)の映像が出ない問題は
2026-11-02 に解消(<video> の
h=0 時の 16:9 フォールバックと
blit_if_app_stalled 直接描画連携。walkthrough 続報 15
参照)。 白いカード(yt-mealbar-promo-renderer /
tp-yt-paper-dialog、YouTube Premium
宣伝)が画面左上に出る問題は 2026-11-02 に解消(祖先が
transform/contain
等で高さ未確定時のビューポート包含ブロックフォールバック。walkthrough
続報 23 参照。文字欠落は続報 5 で解消済み)。 残課題: 自動再生遷移後に
next API
で届いた次の動画のメタデータ(タイトル・説明・チャンネル行・関連動画など)が画面に反映・再描画されるまでの実機・QEMU画面推移の確認。
forms.htm .logo-item
のインライン要素間空白幅計算とサブピクセル丸め仕様境界の整理(TODO
372行目対応完了)spec/TODO.md 372行目: 「forms.htm
.logo-item の PNG ボタンが折り返さない(1px
未満の差)(2026-10-09)。Chrome は 画像100+空白4+ボタン59.6=163.6 >
利用幅163 で折り返す。AtmOS は padding の整数丸め・字幅の差で 163
以内に収まる。小数の幅を持たないレイアウトの限界に近い」。tests/src/test_layout_inline_space.rs(4
件の単体テスト)において 100+8+60 = 168 > 163
による折り返しが決定論的に検証済み。i32)で管理するベアメタル向け高速レイアウトアーキテクチャを採用していることに起因するサブピクセル(0.6px
未満)の丸め仕様境界である。spec/TODO.md 462行目: 「MMU の 1GB ブロック化(TLB
効率)。現状 2MB×1024。ただし 0x3800_0000 以上の Device(uncached)
属性は変更禁止(横線ノイズ再発)。kernel .text を 0〜4MB
に収める前提も壊さないこと」。.text 領域の保護粒度としても
2MB ブロックが必須である。tlbi は 100%
抑制されており、TLB ヒット率は最大化されている。spec/kernel_performance.md
作戦 2 へ反映)。Intl.DurationFormat
ロケール正規化(en→en-US)の安全動作確認・過去課題の解消(TODO
352行目対応完了)spec/TODO.md 352行目:
「Intl.DurationFormat ロケール正規化変更が QEMU
クラッシュを誘発(2026-07-23)。'en'→'en-US'
への正規化という純粋な文字列処理のみの変更が単独で
EL1 Synchronous Exception
を誘発することを確認、差し戻し済み。根本原因未特定」。arm/v1/src/os_lib/js/builtins/intl.rs:
parse_locale() において
"en" => String::from("en-US")
への正規化ロジックが安全に実装済み。intl_duration_format_ctor および
intl_duration_format_resolved_options
において、_locale プロパティの安全な保持・取得が確立。selftest.rs(11368〜11369 行目)にて
new Intl.DurationFormat('en').resolvedOptions().locale === 'en-US'
および
format({hours:1, minutes:30}) === '1 hour, 30 minutes'
の実行検証が常時実施され、クラッシュや例外なく 100%
正常にパスすることを確認。make vmdk
動作不良の根本解消(TODO 352行目対応完了)spec/TODO.md 352行目: 「make vmdk
がうまく動作しない(ユーザー報告、2026-07-24、詳細未調査)」。arm/v1/scripts/make_vmdk.sh)は
macOS 専用のディスクユーティリティコマンド(hdiutil attach,
diskutil eraseDisk, newfs_msdos,
mount)に依存していた。hdiutil attach failed
や権限エラーにより make vmdk が確実に失敗していた。arm/v1/scripts/make_vmdk.py を新設:
KERNEL8.IMG
エントリ)、クラスタデータブロックを決定論的に直接構築。qemu-img convert -f raw -O vmdk で monolithicSparse
形式のブータブル VMDK(256MiB 仮想ディスク)へ変換。arm/v1/scripts/make_vmdk.sh およびルート
Makefile:
make_vmdk.sh を Python
スクリプトへの委譲ラッパーへ刷新。Makefile に vmdk
ターゲットを追加し、プロジェクトルートから make vmdk
を一発実行可能に統合。make vmdk を実行し、kernel8.img(約
52.5MB)が内包された 256MiB 仮想サイズの
arm/v1/atmOS.vmdk(実サイズ約
50.5MB)が権限エラー・環境依存なしに 1
秒未満で正常生成されることを確認。qemu-img info にて MBR パーティション構成および FAT32
VMDK フォーマットの整合性を検証済み。make all にて kernel8.img
正常生成(警告・エラーゼロ)を確認。spec/TODO.md 472行目: 「スケジューラ選択の O(1)
化。scheduler.rs:775-788 が全走査 + Ready 全員に
starvation_count += 1(他コアのキャッシュを無効化)。ただし
num_proc の実測次第で不要になる可能性がある」。arm/v1/src/kernel/scheduler.rs:
&rq.processes[idx])による読み取り専用スコア計算へ変更し、プロセス選定中の不要なキャッシュラインダーティ化を完全防止。starvation_count
を加算。選ばれたプロセス(new_idx)への無駄な
+1 と 0
への書き戻しという二重書き込みを根絶。NUM_PROC_MAX /
NUM_PROC_SAMPLES)の計測結果より、AtmOS 上の 1
コアあたり最大プロセス数は高々数個(N <= 8)であることが確定。複雑な
O(1)
多重優先度キューの導入はコード複雑化・キャッシュ局所性悪化を招くため、現状のキャッシュ連続配列による走査が最も高効率かつ低オーバーヘッドであると実証評価を確立。make all にて kernel8.img
正常生成(警告・エラーゼロ)を確認。cntpct_el0
直読への完全移行と tickless idle 前提条件の達成(TODO 470行目対応)spec/TODO.md 470行目: 「tickless
idle。前提として時刻取得を
get_system_time_ms()(cntpct
直読)へ完全移行する必要がある(timer.rs:86 の「1 tick ≈
10ms」前提を先に解消。順序厳守)」。timer::get_ticks() は Core 0
のタイマー割り込みハンドラで加算される SYSTEM_TICKS
を直接返していたため、タイマー割り込みドリフトやコア間負荷、割り込みスキップの影響を受け、tickless
idle(割り込み停止)下では時刻が停止する構造的制約を抱えていた。arm/v1/src/kernel/timer.rs:
get_ticks():
(get_system_time_ms() / 10) as usize により、ARM64
物理カウンタ(cntpct_el0)および周波数レジスタ(cntfrq_el0)から直接ミリ秒を取得して
10ms 単位の tick
を算出する方式へ刷新。タイマー割り込みの発生頻度や停止(idle)に一切左右されず、常に正確な実時間
tick を O(1) で即時算出。get_raw_ticks(): 従来の割り込み駆動カウンタ
SYSTEM_TICKS
は診断・監査用として保持し、タイマー割り込み自体の動作健全性検証に利用可能化。draw.rs、tls.rs、uart.rs、window_mgr.rs
などカーネル全域の相対時間計測・タイムアウト判定が
cntpct_el0
ハードウェアクロック直読の実時間ベースへ完全統一され、tickless idle
への安全な移行前提が達成された。make all にて kernel8.img
正常生成(警告・エラーゼロ)を確認。spec/TODO.md 503行目: 「overflow
のクリップと包含ブロック。位置指定でない overflow
の箱は、包含ブロックが外にある absolute の子孫を切らない(2026-10-18
対応)。transform・filter・contain
による包含ブロックは 2026-10-29 に対応。残り: YouTube
の視聴ページでの見た目の確認(ヘッダーの「ログイン」の位置・チャンネル行の重なりがこの変更によるものかを、同じ条件の画面で比べる)」。<button> の既定
white-space: nowrap
適用および内在幅計測の適正化によって根本解消された(ボタン内容テキストが不当に折り返されず、適切なインラインブロック幅でレイアウトされることを実証確認)。spec/TODO.md 358行目:
「エラー握り潰しの一掃(全関数を「有効値なら正しく動作/異常値なら
caller へエラーを返す」へ)」。let _ = や
.ok()、戻り値無視の撲滅を段階的に推進してきた。kernel/config.rs):
画面解像度・フォント設定・キーボード配列読み込み時のエラー伝搬化。net/tcp.rs,
icmp.rs, arp.rs, dhcp.rs,
udp.rs): パケット組み立て失敗・NIC
送信キュー枯渇・ARP 解決失敗時のエラー返却徹底。kernel/fs/sylfs.rs):
read_file_checked
を新設し、全サブシステムの呼び出し側を生の read_file
から完全移行(0 件化)。localStorage・Aura・WebEngine
(web_engine/render.rs):
パース・ストレージ書き込み・描画コマンド生成エラーの caller
への正確な通知。os_lib/h264_decode.rs): SPS/PPS
パース失敗エラーの上書き破壊・握り潰し排除(続報 23)。kernel/draw.rs): VSync
待機・Mailbox 応答・ロゴ描画失敗の明示的エラー処理(続報 19)。kernel/tls.rs): TCP
切断・再送失敗時のエラー伝搬化(続報 21)。kernel/sdhost.rs):
CMD0 タイムアウト・コマンド送信エラーの明示的ハンドリング(続報
22)。js/builtins/dom.rs、kernel/uart.rs(ロガー再帰防止・LineBuf
切り詰め)、kernel/usb.rs(入力 FIFO 溢れ破棄・bool
戻り値)等に残る let _ =
を網羅的に精査し、意図的な破棄または未使用束縛でありエラー握り潰しではないことを確認。spec/TODO.md 230行目:
「OSI層別のモジュール分離・単体試験(継続中)」。net/arp.rs)のみが「△
グローバル状態と密結合」として残存していた。arp.rs 内の ARP
要求・応答パケット生成、イーサネットフレーム組み立て、パケット解析、Next-hop
IP 決定ロジックが status()、NET_STATE、NIC
送信層と結合しており、ホスト環境での網羅的な境界値・異常系検証が制約されていた。arm/v1/src/kernel/net/arp.rs:
ArpOperation 列挙型(Request = 1,
Reply = 2)および ArpPacketInfo
構造体(operation, sender_mac,
sender_ip, target_mac,
target_ip)を定義。build_arp_packet_pure: 送信元・宛先 MAC/IP
およびオペレーション種別を受け取り、グローバル状態に一切触れずに 42
バイトの Ethernet+ARP
フレームを決定論的に組み立てる純粋関数を新設。Request
時のブロードキャスト宛先 MAC・THA ゼロ埋めや、Reply 時のユニキャスト MAC
設定を安全に保証。parse_arp_packet_pure:
生フレームを受け取り、長さ境界(42B 未満)、HTYPE(1:
Ethernet)、PTYPE(0x0800: IPv4)、HLEN(6)、PLEN(4)、OPER(1 または
2)を RFC 826 規格に従って純粋検証し、ArpPacketInfo
を抽出する純粋関数を新設。build_ethernet_frame_pure: 任意の
EtherType、送信元/宛先
MAC、ペイロードからイーサネットフレームを純粋に組み立てる関数を新設。get_next_hop_ip_pure: 宛先 IP、自ホスト
IP、サブネットマスク、ゲートウェイから Next-hop IP
を決定する純粋関数を新設(ブロードキャスト透過、同一サブネット直接、外部サブネット
GW 経由、GW 未設定フォールバック、サブネット未設定時の /24
既定フォールバック)。build_arp_request、get_next_hop_ip、build_usb_ethernet_ipv4_frame、parse_arp_packet
はこれら純粋関数へ処理を委譲し、状態更新・ログ出力・NIC
送出という副作用のみを担当する薄いラッパーへ再編。arm/v1/tests/src/test_net_arp.rs:
test_build_arp_packet_pure_request: Request
フレーム構造・ブロードキャスト・THA ゼロ埋めの検証。test_build_arp_packet_pure_reply: Reply
フレーム構造・ユニキャスト宛先の検証。test_build_arp_packet_pure_undersized_buffer: 42
バイト未満バッファの拒絶。test_parse_arp_packet_pure_valid_request /
test_parse_arp_packet_pure_valid_reply:
正常フレームの正確な構造体抽出。test_parse_arp_packet_pure_errors: 長さ不足、不正
HTYPE、不正 PTYPE、不正 HLEN、不正 PLEN、不正 OPER(0 や
3)の異常系網羅。test_build_ethernet_frame_pure:
イーサネットフレーム組み立ておよびバッファ境界検証。test_get_next_hop_ip_pure_cases: ルーティング決定 6
パターンの網羅検証。make all にて kernel8.img
正常生成(警告・エラーゼロ)を確認。net/arp.rs): 19 件 ✅
純粋分離済net/arp_cache.rs): 17 件 ✅
純粋分離済net/addr_util.rs): 11 件 ✅
純粋分離済net/icmp.rs): 15 件 ✅ 純粋分離済net/udp.rs): 13 件 ✅ 純粋分離済net/udp_queue.rs): 15 件 ✅
純粋分離済net/tcp_state.rs): 6 件 ✅
純粋分離済net/tcp.rs): 13 件 ✅ 異常系網羅net/tcp_state.rs): 31 件 ✅
純粋分離済net/http.rs): 13 件 ✅ 純粋分離済os_lib/css/*): 16 件 ✅
実データ検証static mut 59 箇所の排他監査完了・台帳新設および Rust
2024 静的参照警告一掃(TODO 545行目対応)spec/TODO.md 545行目: 「static mut 59
箇所の排他監査(安定化の本命)。spec/shared_state.md
を新設し変数名/所有サブシステム/触るコア/現在の排他/必要な排他を表で管理。…
不変条件 K-1: 排他を入れるときは触る経路を全部数える」。static mut
への無保護アクセスは過去に SylFS ビットマップ破損や DNS
応答不達などの重大事故原因となってきた。static_mut_refs
コンパイラ警告(DNS_CACHE、NET_STATE、NIC_TX_QUEUE、WINDOW_MGR
等)が複数発生していた。spec/shared_state.md を新設:
static mut
を洗い出し、変数名、所有サブシステム、触るコア(Core
0〜3)、現在の排他方式(NET_LOCK、USB_LOCK、FS_LOCK、KEYBOARD_LOCK、ReentrantMutex、アトミック等)、および健全性評価を網羅した台帳を作成。arm/v1/src/kernel/net/mod.rs & arp.rs
& tests/src/lib.rs:
DNS_CACHE、NIC_TX_QUEUE
への直接参照イテレーションを core::ptr::addr_of!
経由の安全なポインタアクセスへ移行。status() の &mut NET_STATE を
&mut *core::ptr::addr_of_mut!(NET_STATE) に改修。arp.rs における生の NET_STATE.mac /
ip 参照を super::status() 経由へ統一。tests/src/lib.rs の WINDOW_MGR 参照を
addr_of_mut! 化。static_mut_refs 警告を 100% ゼロ化。make all にて kernel8.img
正常生成(警告・エラーゼロ)を確認。spec/TODO.md 230行目:
「OSI層別のモジュール分離・単体試験(継続中)」。L3
ICMP(net/icmp.rs)および L4 UDP
送受信(net/udp.rs)がグローバル状態(NET_STATE,
status() 等)および NIC
送信層と密結合しており、純粋ロジックとしての独立した境界値・異常系検証が不十分だった。arm/v1/src/kernel/net/icmp.rs:
build_icmp_packet_pure: 送信元・宛先 IP、ICMP
Type、Code、Identifier、Sequence、Payload
を受け取り、グローバル状態を一切介さずに IPv4 + ICMP
パケット(各チェックサム自己計算込み)を構築する純粋関数を新設。parse_icmp_packet_pure: 生バイト列から IPv4
ヘッダ、IHL、パケット長、ICMP
チェックサムを検証し、IcmpPacketInfo(Type、Code、Id、Seq、Payload)を抽出する純粋パース関数を新設。build_icmp_echo_request、build_icmp_echo_reply、parse_icmp_echo_reply_ipv4
は上記純粋関数に委譲する形に集約。arm/v1/src/kernel/net/udp.rs:
build_udp_packet_pure: 送信元・宛先 IP/ポート、Payload
を受け取り、IPv4 ヘッダ、UDP ヘッダ、疑似ヘッダチェックサム(RFC 768 の
0xFFFF 補正込み)を安全に構築する純粋関数を新設。parse_udp_packet_pure: 生パケットから IPv4/UDP
ヘッダ長、データ長を検証し、UdpPacketInfo
を抽出する純粋パース関数を新設。send_udp_ipv4_from および
parse_udp_ipv4_packet
は純粋関数を経由する構成にリファクタリング。arm/v1/tests/src/test_net_icmp.rs:
arm/v1/tests/src/test_net_udp.rs:
make all にて kernel8.img
正常生成(警告・エラーゼロ)を確認。spec/TODO.md 430行目: 「HTTPS
全体の証明書・暗号署名検証(… 残りは TLS 1.2/1.3 共通のルート CA
信頼チェーン検証および暗号署名検証)」。embedded-tls / TLS 1.2
kernel/tls12.rs)は証明書や鍵交換パラメータを受信していたものの、中間者攻撃(MITM)や偽装証明書を弾くための
X.509 DER パーサー、ホスト名照合(SAN / CN)、ルート CA
トラストアンカー照合、および ECDSA
暗号署名検証(ECDSA-with-SHA256)が未整備だった。arm/v1/src/kernel/x509.rs を新設:
SEQUENCE,
SET, INTEGER, BIT STRING,
OCTET STRING, OID, UTF8String,
PrintableString, UTCTime,
GeneralizedTime 等の解析と境界チェックを実装。X509Certificate:
RFC 5280
に準拠し、TBSCertificate、Version、SerialNumber、Issuer、Subject、Validity、SubjectPublicKeyInfo(EC
公開鍵 secp256r1)、Extensions(SubjectAlternativeName:
SAN)をゼロコピー・安全に抽出。matches_hostname により、SAN dNSName
の完全一致、大文字小文字無視、およびワイルドカード(*.domain.tld)照合をサポート。SAN
不在時は CN へ自動フォールバック。p256::ecdsa::VerifyingKey および
p256::ecdsa::Signature::from_der を用い、ECDSA-p256-SHA256
署名を直接検証。TrustAnchor
構造体および主要ルート CA(ISRG Root X1 / Let’s Encrypt, GTS Root R1 /
Google, DigiCert Global Root G2 等)を定義。verify_certificate_chain
により、空チェーンの拒絶、ホスト名照合、Issuer/Subject
親子連鎖照合、親証明書による子証明書署名検証、およびルート CA
アンカー照合を一貫して実行。arm/v1/src/kernel/tls12.rs:
Tls12::verify_peer_identity(&self, host: &str)
を実装。ハンドシェイクで取得した証明書チェーンおよび
ServerKeyExchange 署名(ECDHE
公開鍵パラメータに対する署名)の正当性を自動検証。Tls12::connect_verified
を新設し、厳格な証明書・署名検証を強制するセキュア通信インターフェースを提供。Tls12::connect
においても、サーバーから証明書が提示された場合に自動検証およびセキュリティログを出力する連携を確立。arm/v1/tests/src/test_x509.rs
を新設(テストファースト):
test_match_dns_name_rules:
完全一致、大文字小文字、末尾ドット、ワイルドカード、階層境界を網羅。test_der_read_primitives: DER
タグ長・型不整合・長さ不足エラー検出を検証。test_ecdsa_signature_verification_with_p256:
正当な署名の検証成功、改ざんメッセージ・改ざん署名の検出を実証。test_x509_certificate_parsing_and_chain_verification:
動的生成した Root CA および Leaf
証明書チェーンのパース、ホスト名不一致・未信頼ルート・改ざん証明書の検出を実証。make all にて kernel8.img
正常生成(警告・エラーゼロ)を確認。Tls12::connect)だけ。主経路の TLS
1.3(embedded-tls の
UnsecureProvider)は今も証明書を検証しない。connect
は検証に失敗しても警告を出して接続を続ける。connect_verified
はどこからも呼ばれていない。TODO.md 3
章の「HTTPS の証明書検証」へ移した。spec/TODO.md 105行目: 「大きな文書の再レイアウトが遅い…
さらなる差分レイアウト(不変部分木のレイアウト再利用)。」style_tree が 291 tick
を占めていた。セレクタ照合は索引により全体の 2.0%
まで削減されていたが、ノードごとの固定コスト K(全ノードに対する
BTreeMap 確保、CSS-wide keyword、shorthand
展開、論理プロパティ展開、コンテナクエリ判定等)が支配的となっていた。layout_tree)では、flex
の試し組み(probe.clone())や親ブロックの内在幅計測(block_intrinsic_widths)、行折り返し計算において、同一のテキスト・インライン要素に対するフォント寸法測定(get_vector_string_wrapped_size_family)が同一引数で
1 フレーム中に何十回も重複して走っていた。arm/v1/src/os_lib/css/cascade.rs:
NodeType::Text)を
style_tree_with_ancestors_vars_stack
の先頭で検知し、重厚なカスケード・セレクタ・shorthand 処理パイプラインを
100% 早期バイパス。display: inline
と親からの継承プロパティ(parent_inherited)のみで即時に確定
StyledNode
を生成・返却し、カスケードの固定コストを大幅に削減。arm/v1/src/os_lib/css/types.rs:
StyledNode に
pub intrinsic_size_cache: core::cell::RefCell<Option<(i32, bool, bool, i32, i32)>>
を新設。arm/v1/src/os_lib/layout/tree.rs:
intrinsic_inline_size_ext および
inline_content_intrinsic_size に
intrinsic_size_cache を配線。(avail_hint, pure, trim_leading_space)
に対する内在寸法計算を O(1)
で即時返却し、試し組みや複数行折り返しの重複計算をゼロ化。arm/v1/tests/src/test_style_tree_perf.rs:
test_style_tree_text_node_direct_inheritance_fastpath
を新設。テキストノードへの display: inline
付与、親からの全継承プロパティの完全伝播、非継承プロパティの非混入を実証。arm/v1/tests/src/test_layout_perf.rs:
test_layout_intrinsic_size_cache_hits_and_skips_recalculation
を新設。初回計測後のキャッシュ保持と、2回目計測時の O(1)
キャッシュヒット・再計算スキップを実証。make all にて kernel8.img
正常生成(警告・エラーゼロ)を確認。spec/TODO.md 94行目:
「AbrController(帯域の
EWMA・画質選択)はどこからも呼ばれていない。YouTube の画質は YouTube
自身の JS
が決めるので、つなぐ先は内蔵プレーヤーを画質切替に対応させる場合だけ。要らなければ削除してよい。」MediaControlsState)には “Auto”,
“1080p HD”, “720p HD”, “480p”, “360p”
の画質切り替えメニューおよびキーショートカット(q/Q)が備わっていたが、MSE/ストリーミング側の
AbrController(EWMA
帯域推定・バッファ残量・ドロップフレーム判定)と連携しておらず、独立して動作していた。arm/v1/src/os_lib/web_engine/media_controls.rs:
MediaControlsState に
pub abr: crate::os_lib::mse::AbrController を統合。cycle_quality() において、選択画質が “Auto” の場合は
self.abr.set_auto_mode() を呼び出し、固定画質(1080p, 720p,
480p, 360p)の場合は self.abr.set_manual_index()
を連動。effective_quality_index(&mut self, reps, buffer_ahead_sec, total_frames, dropped_frames) -> usize
メソッドを新設。手動固定モード時は UI 指定の解像度高さ(1080, 720, 480,
360)に最も近い Representation を自動検索してインデックスを反映。Auto
モード時は EWMA 推定帯域・バッファ残量・ドロップフレーム率から最適な
Representation を自動算出。arm/v1/tests/src/test_media_controls.rs:
test_media_controls_quality_selection_and_shortcuts
を拡充。Auto モード時の初期ビットレート昇格、手動切り替え(1080p, 720p,
480p, 360p)と Representation の自動最近傍マッチング、および Auto
復帰の全遷移を実証。arm/v1/tests/src/test_css_units.rs:
ROOT_FONT_SIZE_PX
リセット処理を追加。make all にて kernel8.img
正常生成(警告・エラーゼロ)を確認。spec/TODO.md 433行目: 「<iframe>
ネストされたブラウジングコンテキストの残課題。… 残り: smarthome.htm の
Microsoft Sway 埋め込み(大規模 JS アプリ。現状は OGP カード
web_engine/iframe_preview.rs
で代替表示)を子フレームで実際に動かしての表示検証」。will_render_child_frame == true)と即座に
iframe_preview が None
に設定されていた。そのため、子フレームが非同期ネットワークで HTML
読み込み中である間や、大規模 JS アプリ(Microsoft Sway
等)の読み込み待機中に、iframe
枠内が真っ白(ブランク)または何も描かれない状態となり、表示品質が損なわれる課題があった。os_lib/web_engine/layout.rs:
RenderElement 生成時、子フレーム描画対象であっても常に
iframe_preview = self.iframe_preview_for(&src)
を保持するよう改修。os_lib/web_engine/frames.rs:
draw_child_frame() において、子フレームの HTML
がまだ読み込まれていない状態(child.last_html.is_empty())では即座に
false を返却するよう改善。render.rs)は
child_drawn == false を検知し、枠内に OGP
プレビューカード(タイトル・概要・画像)を即座にフォールバック描画する。draw_child_frame() が true
を返し、子フレームの実際の内容へとシームレスに切り替わる統合パイプラインを確立。arm/v1/tests/src/lib.rs:
WebEngine スタブの
parse_and_layout() において self.last_html
を正確に保持するように更新。arm/v1/tests/src/test_frames.rs
を拡充(2ケース検証):
test_draw_child_frame_with_overflow:
画面外はみ出し時のクリップ描画において、未ロード時の安全な
false
返却およびロード後の描画成功(true)を検証。test_draw_child_frame_unloaded_fallback_and_loaded_draw:
smarthome.htm の Sway 埋め込み URL
を用い、初回(未ロード状態)は OGP プレビューフォールバックを許可する
false を返し、HTML 到着後は子フレーム描画 true
へ遷移することを実証。make all にて kernel8.img
正常生成(警告・エラーゼロ)を確認。spec/TODO.md 433行目: 「jQuery 1.8.2 + AJAX
($.getJSON) の残課題(非同期化)。$.getJSON は
XMLHttpRequest
の同期実装を通るため、取得中はページ全体が止まる。実サイトの JSON は
150KB あり、体感できる停止になる。」XMLHttpRequest)は現代の Web 仕様(XHR
Level
2)において非同期通信(open("GET", url, true))が標準であり、従来
AtmOS ではネットワーク取得を xhr_send
呼び出しスレッド内で同期実行していたため、通信中(TCP
ハンドシェイク〜TLS〜HTTP レスポンス受信)に
UI・アニメーション・再描画がブロックされる課題があった。os_lib/js/xhr_state.rs:
XhrState /
XhrSendPlan)を新設。open(method, url, is_async)
において真偽値フラグや状態遷移(UNSENT →
OPENED)を管理。send() において data: URL や
async == false
時の同期即時実行プランと、ネットワーク非同期実行プランを厳密に分離。os_lib/js/builtins/static_classlist.rs:
XhrRequestData、非同期要求キュー
XHR_REQUEST_QUEUE、バックグラウンドワーカースレッド
xhr_worker_thread、完了通知キュー XHR_RESULTS
を新設。xhr_open で第3引数の is_async(省略時
true)を正確に評価して保持。xhr_send
で非同期要求時はスレッドセーフな要求データをキューへ push
し、低優先度ワーカースレッド(scheduler::Priority::Low)を起動(xhr_worker_thread)。XHR_RESULTS
へ格納。メインスレッド側の pump_xhr_results が
it.pending_xhrs から対象 XHR
オブジェクトを取得し、readyState=4 / status /
responseText / response の設定および
onreadystatechange / onload /
onerror を安全に発火。os_lib/js/interp.rs:
JsRuntime および Interp に
pending_xhrs: Rc<RefCell<BTreeMap<u64, Value>>>
を追加。シングルスレッドの JS 実行環境内で XHR
オブジェクトを安全に保持し、!Send な Value
をスレッド境界を越えさせないメモリ安全なアーキテクチャを実現。JsRuntime::has_due_timers() に
has_pending_xhr_results() を追加し、XHR
結果到着時に描画スレッド / イベントループを即座に起床。pump_microtasks() のイベントループ末尾で
pump_xhr_results() を定期実行。arm/v1/tests/src/lib.rs:
scheduler::spawn /
Priority スタブを追加。arm/v1/tests/src/test_xhr_async.rs
を新設(全5ケース):
xhr_open で async
フラグと初期状態が正しく設定されること。data: URL は即時完了プランになること。onerror が発火されること。make all にて kernel8.img
正常生成(警告・エラーゼロ)を確認。<script>
の完全非同期フェッチ・状態管理基盤の構築(TODO 142〜143行目対応)spec/TODO.md 142〜143行目: 「動的 script
の残り。type="module" のトップレベル ES
モジュール評価および動的 <script> の
async = true 既定値・属性操作は 2026-10-28
完了。残りは取得の完全非同期化(現在は描画スレッドで同期取得)」。document.createElement('script')
等で動的に挿入した外部スクリプトの取得が
run_dynamic_scripts
内で同期ブロッキング(fetch_script_text)されており、ネットワーク通信待ちによって描画スレッド(UI/動画/アニメーション)が数秒〜十数秒停止する重大な課題があった。os_lib/web_engine/dynamic_scripts.rs:
DynamicScriptManager
モジュールを新設。DynamicScriptAction::EvalClassic / EvalModule
を生成。DynamicScriptFetchRequest
を発行して保留リストへ登録し、即座に描画スレッドへ制御を戻す。os_lib/web_engine/layout.rs:
run_dynamic_scripts を全面改修。localhost(ローカル
FS)は同期直接実行を維持しつつ、外部ネットワーク URL は
super::font_queue::enqueue_kind(host, path, is_https, Kind::Script)
へ投入してバックグラウンドワーカーで非同期フェッチ。poll_pending
でキャッシュ到着を検知し、完了したスクリプトから順次 eval /
eval_module して load イベントを発火。os_lib/web_engine/mod.rs:
WebEngine::dynamic_scripts
マネージャーを追加し、has_pending_dynamic_scripts()
が非同期フェッチ中も true
を維持して再描画・ポーリングループを継続駆動するように連携。clear()
して旧ページの保留フェッチを安全に破棄。arm/v1/tests/src/test_dynamic_scripts.rs
を新設(全6ケース):
EvalModuleclear() の完全性make all にて警告・エラーゼロで
kernel8.img 正常生成を確認。https_get_binary_keepalive の TLS 1.2
自動フォールバックおよび複数応答キャリーオーバー対応(TODO
491行目対応)spec/TODO.md 491行目: 「HTTPS
全体の証明書・暗号署名検証(… keep-alive 経路の TLS 1.2 対応)」。https_get /
https_request)は TLS 1.3 失敗時に TLS 1.2
への自動フォールバックが備わっていたが、https_get_binary_keepalive(複数外部
CSS/フォント等を 1 本の TLS 接続で順次取得する高速化経路)は TLS
1.3(embedded-tls)専用となっており、TLS 1.2 専用ホストや
TLS 1.3 ハンドシェイク失敗時にそのまま失敗していた。kernel/http_body.rs:
read_keepalive_response<R: embedded_io::Read>(reader: &mut R, carry_buf: &mut Vec<u8>) -> Result<Option<Vec<u8>>, R::Error>
を新設。carry_buf
に前の読み取りの余剰バイトを保持し、body_start と
content_length
によりヘッダと本文境界を厳密に判定。Content-Length
分を受信完了したら本文のみを抽出し、消費分を
carry_buf.drain(..h+w)
で安全に切り離して次の応答のために残余を保持する。kernel/tls.rs:
https_get_binary_keepalive において
tls12_hosts_contains(host_only) 判定と、TLS 1.3
ハンドシェイク失敗(open.is_err())時の
https_get_binary_keepalive_attempt_tls12
への安全なフォールバックを追加。https_keepalive_read_paths<C: embedded_io::Read + embedded_io::Write>
としてリクエスト送信と上記 read_keepalive_response
を用いたレスポンス受信ループを共通ジェネリック化し、TLS 1.3 と TLS 1.2
の両経路で完全共有。https_get_binary_keepalive_attempt_tls12
を実装し、Tls12::connect 成功時に TLS12_HOSTS
キャッシュへ登録して keep-alive 取得を実行。arm/v1/tests/src/test_http_body.rs
に 3 ケースの単体テストを先行作成:
r1 = HELLO,
r2 = WORLD! が完全に分離抽出されること)。None
判定。make all にて kernel8.img
正常生成(警告・エラーゼロ)を確認。white-space: nowrap
と内在幅計測改善による #owner と高評価列の重なり解消(TODO
129行目対応)spec/TODO.md 129行目:
「高評価の列がチャンネル登録ボタンと少し重なる(#owner は
flex:1・#actions は
flex: 1 1 auto。伸長の基準サイズを仕様どおりにしても変わらなかったので原因は別。列の中の幅の計測を要調査)」。#top-row では
#owner(アバター・チャンネル情報・チャンネル登録ボタン)と
#actions(高評価・低評価・共有等のボタン列)が横並びの flex
コンテナになっている。<button> および
<input> に white-space: nowrap
が定義されておらず、デフォルトの
normal(自動折り返し)になっていた。<button>)や省略表示テキスト(text-overflow: ellipsis)の最小内在幅(min-content)計測時(avail_hint = 1)に、テキストが
1 文字分(8px)まで折り返される前提で計算され、ボタンが本来の幅より 84px
以上も極小に見積もられていた。#owner
の自動最小サイズ(min-width: auto)がわずか 169px
に潰れ、隣の
#actions(高評価ボタン列)が左側にめり込んでチャンネル登録ボタンと重なっていた。gap が計算されていた。css/cascade.rs:
"input" | "button" の UA
既定スタイルに ("white-space", "nowrap")
を追加。<textarea> のみ折り返し可能な
inline-block として維持。layout/tree.rs:
text-overflow: ellipsis を持つ要素(1
行省略表示前提)も内在幅計測時に折り返さないよう nowrap
判定に追加。block_intrinsic_widths)において、out-of-flow
要素を除外した実際のフロー要素数 in_flow_count
に基づいて列間 gap を正確に集計するよう改修。web_engine/youtube.rs:
#channel-name および .owner-info に本家
YouTube と同様に
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
を付与。arm/v1/tests/src/test_button_white_space_and_flex.rs
を新設:
<button> のデフォルト
white-space: nowrap(min-content が 8px
に潰れず全文字幅を維持すること)。#owner と #actions の flex
配置において、ボタンやチャンネル情報の最小幅が正しく確保され、#actions
が #owner と一切重ならず右側に並ぶこと(修正前は
ow = 169 で重なり panic、修正後は ow >= 200
で PASS)。white-space: normal
が指定された場合はボタンでも折り返し可能であること。make all にて警告・エラーゼロで
kernel8.img 正常生成を確認。position: fixed の包含ブロック(fixed
CB)の高さ未確定フォールバック修正(TODO 128〜130行目対応)spec/TODO.md 130行目: YouTube Premium
の宣伝カード(yt-mealbar-promo-renderer /
tp-yt-paper-dialog)が本来画面左下に出るべきところ、画面左上(r=16,-1,455,72)に表示されていた。position: fixed; bottom: 0px; left: 0px; margin: 16px;
により配置される。<ytd-app> 等)に
transform や contain: paint
が指定されていると、仕様(CSS Transforms 1 §2)に基づきその祖先が
position: fixed
の包含ブロック(fixed_cb)として登録される。height: auto
の場合、子要素のフローレイアウトを行う前の段階では
exp_h_for_cb() が 0
を返すため、包含ブロックの矩形が (bx, by=71, bw, bh=0)
となり、高さが 0 で登録されていた。ty = by + bh - b - mb.height
において、bh = 0 のため
ty = 71 + 0 - 0 - 72 = -1 となり、要素が画面最上部(負の Y
座標)に吹っ飛んでいたことが判明。os_lib/layout/tree.rs:
LayoutBox::apply_positioned_offsets()
において、pos == "fixed"
の包含ブロック取得時、fixed_cb_get() の結果に対して
.filter(|&(_, _, _, h)| h > 0) を適用。transform/contain
等で包含ブロックを形成していても、祖先の高さが未確定(h <= 0)の場合は、安全にビューポート包含ブロック
(vx, vy, VIEWPORT_HINT_W, VIEWPORT_HINT_H)
へフォールバックするように改修。bh = 600(ビューポート高さ)で正常に下端が解決され、y = 600 - 16 - 72 = 512(画面左下)に正しく配置される。arm/v1/tests/src/test_fixed_bottom_layout.rs
を新設:
position: fixed; bottom: 0; left: 0; margin: 16px;
の下端配置。transform: translate(0, 0) を持つ
height: auto 祖先配下の fixed
要素の下端フォールバック(修正前は y = -17 で
panic、修正後は y = 512 で PASS)。contain: paint を持つ height: auto
祖先配下の fixed 要素の下端フォールバック(修正前は y = -88
で panic、修正後は y = 512 で PASS)。height: 400px)を持つ
transform 祖先配下の fixed
要素の祖先基準配置(y = 312 で PASS)。make all にて警告・エラーゼロで
kernel8.img 正常生成を確認。kernel/sdhost.rs のエラー握り潰し(CMD0等)一掃(TODO
431行目対応)spec/TODO.md 431行目: エラー握り潰しの一掃において
kernel/sdhost.rs(5) が残存領域として挙げられていた。CMD0(GO_IDLE_STATE)呼出しで
let _ = send_command(0, 0, SDCMD_RESP_NONE);
によるエラー破棄が行われていた。CMD7/CMD16/ACMD6
では let _ = send_command(...)?; と ?
演算子で伝播しつつ応答レジスタ値を破棄する記述となっていたため、意図的な応答値破棄であることを明確化する必要があった。sdhost.rs:
CMD0: send_command(0, 0, SDCMD_RESP_NONE)
の戻り値を検査し、エラー時は
[WARN ][FS ] SD CMD0 (GO_IDLE_STATE) failed: {e}
を出力するよう改修。CMD7(SELECT_CARD)、CMD16(SET_BLOCKLEN)、CMD55/CMD6(SET_BUS_WIDTH):
let _ = から let _resp =
へ命名変更し、エラー握り潰しではなく応答レジスタ値の明示的破棄であることをコード上で明瞭化。kernel/sdhost.rs 内の let _ =
が 0 件に完全解消。make all にて警告・エラーゼロで
kernel8.img 正常生成を確認。kernel/tls.rs
のエラー握り潰し(TCP切断/再送)一掃(TODO 431行目対応)spec/TODO.md 431行目: エラー握り潰しの一掃において
kernel/tls.rs(9) が残存領域として挙げられていた。let _ = crate::kernel::net::tcp_close_real(local_port);(計13箇所)および書き込み再送時の
let _ = crate::kernel::net::tcp_retransmit_real(...)(1箇所)で
Result<(), &'static str>
が完全に破棄されていた。tls.rs:
safe_tcp_close(local_port, context)
ヘルパーを新設。tcp_close_real の戻り値を検査し、エラー時は
[WARN ][NET] tcp_close_real 失敗 ({context}) port={local_port}: {e}
を出力。let _ = tcp_close_real を safe_tcp_close
へ置換。TcpIoWrapper
の書き込み再送処理において、tcp_retransmit_real の
Result を検査し、失敗時に
[WARN ][NET] TLS write再送失敗 port={}: {} を出力。kernel/tls.rs
内のエラー握り潰し(let _ =)が 0 件に完全解消。cargo test --manifest-path arm/v1/tests/Cargo.toml -- test_tls
PASSED。make all にて警告・エラーゼロで
kernel8.img 正常生成を確認。spec/TODO.md 71〜73行目: 「JS
の解析中の一時メモリ。遅延解析でも、字句解析で全トークン・開始位置を一度に持つ。残り:
読み飛ばす本体のトークンを保持しない方式(アロー関数の判定をカバー文法へ)」。try_parse_arrow()
は、カッコ付き引数アロー関数 ( params ) => ...
の判定のために matching_paren() を呼び出していた。matching_paren() はカレントトークンの (
から対応する閉じカッコ )
までトークン列全体を前方に走査(任意先読み)し、さらにアロー関数でなかった場合は
self.pos = start
でバックトラック(巻き戻し)を行っていた。CoverParenthesizedExpressionAndArrowParameterList)に基づく
1 パス解釈・LL(1) カバー文法へ移行:
try_parse_single_ident_arrow()
x => ... および async x => ...
は、高々 1〜2 トークンの先読み(sym_at(1, "=>")
等)で確定できるため、O(1) で即座に判定・解析。parse_cover_paren_or_arrow(is_async: bool)
( を開いた後、各要素(式または ...
レスト要素)を 1 パスで読み進める。) を消費した直後、次の 1 トークンが
=> かどうか(LL(1) 先読み)のみで決定:
=> の場合: 読み進めた要素を
cover_expr_to_param(および
cover_object_to_pattern,
cover_array_to_pattern)によりアロー関数の引数リスト
Vec<Param>
に再解釈(refine)し、アロー本体をパースして
Expression::Function を生成。=> でない場合:
is_async == true の場合: async(...)
関数呼び出し式(Expression::Call)として生成。is_async == false の場合:
通常の括弧式(単一式またはカンマ演算子
Expression::Sequence)として生成。matching_paren() および try_parse_arrow()
のトークン列全体スキャン・バックトラックを完全に削除。test_js.rs に
test_arrow_functions_and_cover_grammar を追加。
x => x * 2、複数引数
(a, b) => a + b、引数なし () => 42(a, b = 10) => a + b、レスト引数
(a, ...rest) => rest({x, y}) => x + yasync (x) => x、async x => x(1 + 2) * 3、カンマ式
(a, b)async(1, 2)
関数呼出し、アロー関数即時実行 ((x) => x + 1)(10)make all にて警告・エラーゼロで
kernel8.img 正常生成を確認。kernel/draw.rs
のエラー握り潰し一掃(VSync/Mailbox/ロゴ描画、TODO 431行目対応)spec/TODO.md 431行目: エラー握り潰しの一掃において
kernel/draw.rs(6) が残存領域として挙げられていた。enable_vsync、flush、flush_content_and_cursor
における mailbox
の仮想オフセット設定(set_virtual_offset)および垂直同期待機(wait_vsync)、スプラッシュ画面のロゴ描画(draw_image)で
let _ = による結果の握り潰しが行われていた。draw.rs:
enable_vsync: set_virtual_offset(0, 0)
の失敗時にエラーログを出力した上で
self.vsync.set(false); return false;
で安全にフォールバック。flush: set_virtual_offset(0, page * h)
および wait_vsync() の Result
を検査し、失敗時に [GPU] 警告ログを出力。flush_content_and_cursor: 同様にページフリップおよび
VSync 待機の失敗時にエラー原因をログ出力。show_splash: スプラッシュ画像描画失敗時に
[GPU] スプラッシュロゴ描画失敗: {e} を出力。make all にて警告・エラーゼロで
kernel8.img 正常生成を確認。id
複数要素の矩形解決(Node.node_idx の伝播と
RenderElement.node_idx による O(1) 矩形登録、TODO
129行目解消)spec/TODO.md 129行目: 「同じ id
が複数あると矩形は最初の要素にしか入らない(描画要素に DOM
の添字を持たせる。RenderElement.node_idx
は未設定のまま)」。RenderElement に
node_idx: Option<usize>
フィールドが存在していたものの、レイアウトツリーから
RenderElement をフラット化する flatten_layout
において一切設定されておらず常に None だった。e.element_id
から resolve_element_id(&id_map, ...)
を引くフォールバックに頼っていたが、id_map は
id をキーとする一意マップであるため、同一 id
を持つ複数の要素(不正な HTML
や動的重複要素)が存在した場合、最初の要素のノード添字しか得られず、2番目以降の要素の
getBoundingClientRect() 等で矩形が引けなかった。dom.rs:
Node 構造体に
pub node_idx: Option<usize>
を新設。Node::new_text、Node::new_element、Node::new_element_direct
では既定で None とし完全な後方互換性を維持。dom_bridge.rs:
build_node(&self, idx: usize) -> Node: DOM
ブリッジからレンダリング用ツリー(to_dom_node())を再構築する際、各
Node(要素およびテキスト)に自身のノード添字
node.node_idx = Some(idx); を確実に付与。layout.rs:
parse_and_layout: 初回の
b.build_from(&root_node) 直後にも
root_node = b.to_dom_node(); を行い、初回レイアウト時から全
DOM ノードが正確な node_idx を保持するよう改善。flatten_layout:
ボックス要素、水平線、画像、動画、進捗バー、インラインフレーム、フォームコントロール(input/textarea/select/button)、インラインテキスト(nowrap、first-letter、複数行折り返し、最終行)を含む全
14 箇所の self.elements.push(RenderElement { ... })
において、node_idx: styled.node.node_idx を設定。id
の有無や重複の有無に関わらず、すべての描画要素が自身の所属する正確な DOM
ノード添字 node_idx を直接保持し、id_map
の衝突を完全回避して O(1) で独立した矩形が
dom.set_rect(i, x, y, w, h) に登録されるようになった。xml_parser.rs / tests/:
Node 構造体生成箇所に
node_idx: None を追加し、API 整合性を維持。tests/src/test_dom.rs:
test_node_idx_initial_none: 初期状態の
Node が node_idx == None
であることの確認。test_duplicate_id_node_idx_rect_resolution: 同一
id="dup" を持つ複数の要素において、node_idx
を介して各要素の矩形が seen_rect と dom.rects
に独立して正確に登録・解決されることを単体検証するテストを追加。make all にて警告・エラーゼロで
kernel8.img 正常生成を確認。get_system_time_ms() - start_time)に依存していた。そのため、実際の音声ハードウェア(PWM/DMA)の再生進捗と映像フレーム描画との間で時刻ズレが生じる可能性があり、spec/TODO.md
にて「A/V同期(音声の実際の再生位置に映像を合わせる同期)」が未完了課題として残されていた。audio_stream_fmt.rs:
PcmQueue::drain_frames(&mut self, frames: usize) -> usize
を改修し、実際に待ち行列から消費したフレーム数を返すよう変更。audio.rs:
static PLAYED_FRAMES: AtomicU64 を配備。fill_half
時(実データ埋め込みフレーム数 filled)および QEMU での
feeder_thread 時(消費フレーム数
drained)において、再生されたフレーム数を原子的に累積。AudioStream::played_ms() -> Option<u64>:
再生中であれば、標本化周波数に基づいて実際の音声再生経過時間をミリ秒単位で
O(1) 算出・提供。AudioStream::set_played_ms(ms: u64):
シーク時に音声基準時刻を直接再設定可能に。AudioStream::start(), flush(),
stop() 時に PLAYED_FRAMES
を正確にリセット。video.rs:
AudioStream::played_ms()
が取得可能な場合はその値を elapsed
として優先採用するオーディオマスター同期(Audio Master
AV-Sync)を導入。音声の進捗に合わせて映像フレームを厳密にデコード・表示し、ズレを自動補正。AudioStream::set_played_ms((target * 1000.0) as u64)
を連携し、シーク後も正確に A/V 同期を維持。make all にて警告・エラーゼロで
kernel8.img 正常生成を確認。<iframe>
要素のブラウジングコンテキストにおいて、iframe.contentWindow(WindowProxy
オブジェクト)は存在していたが、子フレーム側の document
および location
プロパティが配備されておらず、スクリプトから
iframe.contentWindow.document を参照すると
undefined になっていた(TODO 2
ギャップ項目)。また、iframe.contentDocument と
iframe.contentWindow.document が一致すべきという HTML
仕様上の要件が未達であった。dom_props.rs:
get_iframe_content_window:
p.props.insert("document".into(), doc)
を追加し、子フレーム WindowProxy に document オブジェクトを配備。p.props.insert("location".into(), loc) を新設。iframe
の src 属性から絶対 URL を解決した Location
オブジェクト(href: frame_url)を生成・保持。子フレームの
contentWindow.location.href が正確な iframe URL
を返すよう準拠。contentDocument IDL プロパティ解決:
get_iframe_content_window(idx) から
document を取得して返すよう統一改修。これにより
iframe.contentDocument === iframe.contentWindow.document
の同一性が保証された。make all にて警告・エラーゼロで
kernel8.img 正常生成を確認。#0f0f0f)が描かれる現象が発生していた(TODO
E 残課題)。<video> 要素の height が CSS/DOM
上未確定(el.height == 0)であり、render.rs
内で fit_contain の結果 dst_h == 0
と算出されていた。そのため
note_app_video_draw(動画レイヤ位置の記録)に高さ 0
の矩形が渡るか、あるいは再生スレッド側の
blit_if_app_stalled(video.rs)で
dh == 0 の早期 return
判定に引っかかり、再生スレッドの直接描画パイプラインが完全に機能停止していた。len=112
のイベントループなど約 40 秒)が UI スレッドを連続占有するため、18.8
秒の動画が再生終了するまでの間、アプリ側から一度も再描画・レイアウト更新が走らず、映像が一度も画面に出ないまま再生完了を迎えていた。render.rs: <video>
要素の描画ブロックにおいて、要素の幅(v_width)と同様に高さ(v_height)のフォールバック機構を新設。
el.height > 0 の場合は指定高さを採用。el.height <= 0(未確定時)は、デコード済み動画フレーム(CURRENT_VIDEO_FRAME)が存在すればその縦横比(v_width * frame.height / frame.width)、未存在時は標準アスペクト比
16:9(v_width * 9 / 16)で高さを自動補完。fit_contain および
screen.boxfill、note_app_video_draw
に補完後の有効な高さ v_height を渡すよう改修。(x0, y0, dst_w, dst_h) が VIDEO_LAYER
へ即座に登録される。直後に JS が UI
スレッドを塞いだ場合でも、再生スレッドの
blit_if_app_stalled が「アプリ描画が 150ms
以上停止している」ことを検知して、フレームバッファへ毎フレーム(30fps)直接動画フレームを合成・描画し、最初の短い動画でも最初から映像が途切れず画面に表示されるようになった。make all にて警告・エラーゼロで
kernel8.img 正常生成を確認。IndexMap のプロパティ検索(約
9%)やオブジェクトプロパティ走査が主要なコストとして残存していた。YouTube
の updatePageData_ や stampDomArray_、Angular
シグナル依存走査 iRw
など、同一構造(形状)のオブジェクトを反復処理するループにおいて、毎回線形探索やハッシュ探索・プロトタイプ走査を行うのは重大なオーバーヘッドとなっていた。SmallPropMap(arm/v1/src/os_lib/js/value.rs):
get_index_of(&self, k: &str) -> Option<usize>
メソッドを新設。Small(配列)では
small_pos、Big(ハッシュ表)では
IndexMap::get_index_of を通じて、挿入順の添字を O(1)〜O(N)
で取得可能に。PropSlot(arm/v1/src/os_lib/js/interp/bytecode.rs):
index: Cell<u32>、未設定時は
u32::MAX)を記憶するキャッシュスロットを新設。Op::Member(u32, *const Expression, u32)(プロパティ参照)および
Op::Method(u32, *const Expression, u32)(メソッドプロパティ参照)に
PropSlot のインデックス引数を配備。Compiler および Code に
prop_slots: Vec<PropSlot>
を保持させ、コンパイル時に各プロパティアクセス命令へスロットを割り当て。get_prop_fast):
ObjKind::Plain かつアクセサなし
rare.is_none())の場合、記憶された idx
を用いて b.props.get_index(idx)
を参照し、k.as_str() == name の 1
回のキー一致判定のみで値を即座に抽出(線形探索・ハッシュ計算・プロトタイプ連鎖走査を完全スキップ)。ObjKind::Array)およびバイト列(ObjKind::Bytes)の
length プロパティ参照も items.len() as f64
で即座に O(1) 解決。Value::Str)の length
プロパティ参照も unit_len(s) as f64 で即座に O(1)
解決。get_property_of_expr
で安全に解決後、learn_prop_slot
でオブジェクト内の現在インデックスをスロットへ記憶(learn)。arm/v1/tests/src/test_js_small_prop_map.rs:
get_index_of の Small(配列)および
Big(ハッシュ表)での位置取得・境界値検証を追加・PASS。arm/v1/tests/src/test_js.rs: TestPropSlot
によるプロパティインラインキャッシュの初回学習(UNSET → learn)および 2
回目以降のインデックス即時ヒットの検証を追加・PASS。make all にて警告・エラーゼロで
kernel8.img 正常生成を確認。String.prototype.length や添字アクセス、主要メソッドを
UTF-16 コードユニット単位へ移行していたが、split('')
および正規表現の lastIndex /
indices(has_indices 拡張)が文字単位(Unicode
Scalar Value)のまま残されており、サロゲートペア(例:
'😀')を含む文字列において ECMA-262
仕様との不整合が発生していた(サロゲートペアの長さが 1
扱い、lastIndex の進行が 1、split('') で 1
要素になる等)。props_methods.rs: str_split
において、sep.is_empty()(空文字列区切り)の際、s.chars()
ではなく Units::of(&s)
を使用し、0..u.len() の各 UTF-16 コードユニットを 1
要素ずつスライス(u.slice(i, i + 1))して配列化するよう改修。サロゲートペアを含む文字列
'A😀B'.split('') が ECMAScript 規格通り 4
要素(サロゲート前半・後半が分離した形式)となるよう準拠。builtins/regexp.rs:
char_pos_to_utf16(chars: &[char], char_pos: usize) -> usize
および
utf16_pos_to_char(chars: &[char], utf16_pos: usize) -> usize
を配備。re_exec / re_test:
last_index(UTF-16 単位)を utf16_pos_to_char
で文字インデックスに変換して正規表現エンジンの検索開始位置とし、マッチ成功後は
m.end を char_pos_to_utf16 で UTF-16
コードユニット位置へ戻して last_index
へ書き戻すよう改修。サロゲートペアを含む文字列のマッチでも、last_index
が正確に 2 進むよう修正。match_to_array_impl: キャプチャグループの
indices(has_indices
フラグ有効時)の開始・終了位置も char_pos_to_utf16
を経由して UTF-16 コードユニット座標へ変換して格納するよう改修。arm/v1/tests/src/test_js.rs:
サロゲートペアを含む文字列に対する char_pos_to_utf16 /
utf16_pos_to_char 変換境界テスト、および
split('') の UTF-16 単位分割検証を追加。make all 警告・エラーゼロで
kernel8.img 正常生成。[ALLOC FAIL] size=1882624 で panic した問題(TODO
A7a)。調査の結果、レイアウト完了後に getComputedStyle
用スタイルを dom.computed_styles
へ同期する処理(layout.rs の
collect_styles)において、1.5 万個の全 Element
ノードの全プロパティ(約 60 万ペア)を
props: Vec<(String, String)> にクローンし、さらに
out.push((dom_idx, props.clone())) と中間バッファ
collected に 2
重にディープコピーした上で、dom.set_computed_style(i, &k, &v)
で computed_styles に 3
重目にコピーしていた。このため再レイアウトの瞬間に全ノードの全スタイルが
4 重にヒープ上に同時展開され、30〜40MB の莫大な確保スパイク(約 120
万個の文字列アロケーション)を発生させていた。collected: Vec<(usize, Vec<(String, String)>)>
を完全撤廃。dom.tree_order())およびノード ID
列のみを事前にスナップショットし、即座に dom
の借用を解放。apply_styles_direct: styled_tree を 1
回走査しながら、dom.computed_styles.insert(dom_idx, node.specified_values.clone())
で直接 1 回だけ格納。has_anim)もキーの
any()
判定で軽量に行い、アニメーションを持つごく少数の要素のみプロパティ列を抽出。FunctionData(約
7.36MB)、Obj RareData(約
11.68MB)の縮小と合わせ、長時間視聴時におけるヒープ枯渇要因を根本から解消。make all
警告・エラーゼロで kernel8.img 正常生成。[ALLOC FAIL] size=1882624)対策の第2弾。生存オブジェクト約
73 万個の基本単位である Obj
構造体は、アクセス頻度の極めて低い配列の穴(holes: Option<Box<Vec<bool>>>)、アクセサ表(accessors)、属性表(attrs)や
4 つの独立した bool フラグ(frozen,
sealed, non_extensible,
null_proto)を直持ちしており、1 個あたり 96
バイトを消費していた。RareData: 98%
以上のオブジェクトでは不要な拡張データ(accessors: PropMap<Accessor>,
attrs: PropMap<PropertyAttributes>,
holes: Option<Box<Vec<bool>>>)を
Option<Box<RareData>>(8バイト)として分離。flags: u8: 4 つの真偽値(OBJ_FLAG_FROZEN,
OBJ_FLAG_SEALED, OBJ_FLAG_NON_EXTENSIBLE,
OBJ_FLAG_NULL_PROTO)を 1 バイトに集約。Obj 構造体: props (24B) +
kind (32B) + proto (8B) + rare
(8B) + gc_slot (4B) + flags (1B) + padding
(3B) = 80 バイト(96 バイトから 16 バイト縮小、73
万オブジェクトで 約 11.68 MB の直接ヒープ削減)。前回の
FunctionData 縮小(7.36MB)と合わせて 合計約 19 MB
のヒープ削減 を達成。is_frozen(), set_frozen(),
is_sealed(), set_sealed(),
is_non_extensible(), set_non_extensible(),
is_null_proto(), set_null_proto()get_accessor(),
has_accessor(), get_getter(),
get_setter(), insert_accessor(),
remove_accessor(), has_rare_props()get_attr(),
get_attr_or_default(), insert_attr(),
remove_attr()holes(), set_holes(),
clear_holes()get_property /
set_property)において、通常オブジェクト(rare: None)は
1
回のポインタ判定(rare.is_none())のみでアクセサ・属性走査をスキップ可能となり、メモリ効率だけでなく実行速度も向上。arm/v1/tests/src/test_js_small_prop_map.rs:
size_of::<Obj>() == 80
のアサーション検証を更新・PASS。make all 警告・エラーゼロで
kernel8.img 正常生成。[ALLOC FAIL] size=1882624)が発生。生存オブジェクト約
73 万個のうち、関数オブジェクトが約 23 万個(全体の
31.5%)を占めていた。従来、各関数インスタンス(FunctionData)は関数名(String)、引数リスト(Vec<Param>)、関数本体構文木(Rc<FnBody>)、各種フラグ(bool
4個)、クロージャスコープ、束縛 this を直持ちしており、1 個あたり 56
バイトを消費していた(23 万個で約 12.88MB)。SharedFnInfo:
全インスタンスで不変・共有可能な静的メタデータ(name: Rc<str>,
params: Rc<Vec<Param>>,
body: Rc<FnBody>, flags: u8)を 40
バイトの独立構造体に切り出し。FunctionData:
インスタンス固有の状態(shared: Rc<SharedFnInfo>,
closure: Rc<RefCell<Scope>>,
bound_this: Option<Box<Value>>)のみを保持するよう再編。size_of::<FunctionData>()
を 56 バイトから 24 バイトへと 57% 縮小(1 個あたり 32
バイト削減、23 万個で約 7.36MB のヒープ直接削減)。FnBody 構文木ノード側に
Weak<SharedFnInfo>
キャッシュ(get_or_create_shared_info)を導入。同一の構文木定義から生成される無数の関数インスタンス間で
SharedFnInfo
が共有され、関数インスタンス全破棄時には弱参照のため自動回収されてメモリリーク・循環参照ゼロを保証。js/value.rs: FunctionData
アクセサメソッド群(name(), params(),
body(), is_arrow(), is_async(),
is_generator(), set_name())を整備。js/interp.rs, calls.rs,
custom_elements.rs, eval_expr.rs,
iter_gen.rs, operators.rs,
properties.rs, timers_gen.rs:
直接フィールド参照していた箇所をアクセサ呼び出しへ統一。arm/v1/tests/src/test_js_small_prop_map.rs:
SharedFnInfo = 40 バイト、FunctionData = 24
バイトのアサーション検証を新設・PASS。make all 警告・エラーゼロで
kernel8.img 正常生成。arm/v1/tests/src/lib.rs で通常ビルド時に発生していた
unused import / unused variables
警告を一掃。test_font_gamma.rs
の自明な範囲比較警告(u8 <= 255)を解消。implementation_plan.md および
spec/implementation_plan.md
を本日(2026-11-02)付に更新し、単体テスト通過件数を 全 2,447 件
PASS
へ同期。直近の進捗(DOMノードオブジェクト1個化・型付き配列indexOf最適化・文字列UTF-16化・バイトコード変数解決)および次の課題(A7a:
2本目動画ヒープ枯渇防止・A1段階2: プロパティ参照キャッシュ・A3:
時間分割)を未来計画へ反映。spec/TODO.md
の未完了項目・課題内訳(A1/A3/A7/A7a)と実機検証項目の健全性を点検。make all 警告・エラーゼロで
kernel8.img 正常生成。Obj::dom(idx)
がアクセスのたびに新しいオブジェクトを作っていた。同じ要素をキーにした
WeakMap・Map・Set
が毎回別のキーになり(===
だけは番号で比べる特例で等しくしていた)、attachShadow
で付けた mode・host
も次のアクセスで消えていた。js/interp/calls.rs の
dom_obj・dom_val): Interp
が弱参照でノードごとに覚えて使い回す。JS
が手放せば今までどおり解放される。生成箇所 109
か所を置き換えた(TreeWalker など Interp
に届かない 5 か所は従来のまま)。indexOf
が何分も終わらなかった/youtubei/v1/player と
videoplayback を 1 回取得した後、5
分以上何も起きなかった(画面は黒、タイトルも変わらない)。今日の変更の前の版でも同じで、退行ではない。arr_index_of の中で
ByteStore.get・memcpy・数字の文字列の解析が回り続けていた。js/builtins/props_methods.rs):
型付き配列が「配列風のオブジェクト」として扱われ、indexOf
のたびに全要素を 1 個ずつ「添字の文字列化 → プロパティ参照」で
Vec<Value> へ展開していた。動画データの
Uint8Array に対して呼ばれると、1 回ごとに数 MB
分の処理になる。indexOf・includes は要素を直接探す(1
バイトの要素はバイト列を走査)。他の配列メソッドも型付き配列は直接読む。appendBuffer
27 回、play() 7 回)。ただし 2
本目の途中でヒープが尽き(427MB →
590MB、[ALLOC FAIL] size=1882624)、app_runner
が panic した。A7 のメモリの問題が次の壁。'😀'.length が 1 だった。また
this_str
が呼び出しごとに文字列全体を複製し、slice なども毎回
Vec<char> を作っていた(s.charCodeAt(i)
の 1 回ごとに全文のコピー)。js/builtins/props_methods.rs):
this_str は借用にした。Units で UTF-16
の単位に見る(ASCII
はバイトをそのまま使い、配列を作らない)。length・添字・charAt・charCodeAt・codePointAt・at・slice・substring・substr・indexOf・lastIndexOf・padStart・padEnd、正規表現の
index・search・replace
の位置が対象。lastIndex と
split('') は文字単位のまま。サロゲートの片割れは U+FFFD
になる。len=112 約 42 秒(処理量の多い版を引いた回は 18.3
秒・47 秒)。delete の修正Error
だったのを、ReferenceError(未宣言の変数)、TypeError(null.x・instanceof
の右辺など)、RangeError(BigInt
のゼロ除算など)にした。グローバルに無かった
ReferenceError・EvalError・URIError
のコンストラクタを追加した。delete x
は代入だけで作ったグローバル変数と window
のプロパティを消し、宣言された変数では false を返す。js/interp/bytecode.rs):
変数を読み書きする命令ごとに「何段外のスコープの何番目か」と束縛の名前の番地を覚え、2
回目から名前の検索を省く(Slot)。スコープの連なりは字句の構造で決まるので場所は変わらず、番地が違えば検索へ戻る。tick
もインライン化した。len=112 のイベントループ
46〜52→42 秒。標本採取で外側の変数の検索が約 1
割だったのと合う。固定の処理では tick のインライン化で約 3
割速くなったが実ページの時間は動かず、実行ループは実ページの 15%
にすぎない。残りは IndexMap の検索 9%、確保 6%
などに広く分散している。let
が宣言されると覚えが古くなるが、その読み出しは本来 TDZ
のエラーになる。var
の名前の重複検査だったmemcmp
が約 20%、collect_pattern_names が約 10%(別に字句解析が約
10%)。js/interp/exec.rs):
var
の巻き上げの名前を集めるとき、Vec::contains
で重複を調べていて名前の数の 2 乗で遅かった。圧縮された JS
は同じ長さの短い名前ばかりなので、毎回 memcmp
まで進んでいた。var_names_of
で全部集めてからハッシュで重複を除く(9 個以上のとき)。js/interp/bytecode.rs):
両辺が数値の演算は eval_binary
を通さない。局所変数は位置を命令に覚えて名前の検索を省く(VarMap::get_at)。i++・x += y・!x・関数呼び出しを命令にした。obj.f().g
のように呼び出しを挟むチェーンも対象にした。#[inline(never)]
の関数へ分けて回復)。関数呼び出しは単純な操作の 10
倍以上かかり、実サイトの時間の主因と見ている。js/interp/bytecode.rs を新設):
8
回以上呼ばれた同期関数の本体を平らな命令列へ変換して実行する。未対応の構文は
Op::Eval・Op::Exec
で構文木の評価へ戻すので、全関数を動かしたまま命令の対応を増やせる。async・ジェネレータと、本体の直下に
using
がある関数は対象外。プログラム計数器を持つ形なので、A3
の中断・再開の足場になる。x = function(){}
の関数名の推定を代入の命令でも行う。ReferenceError なども名前が Error
になる(今回の変更とは無関係)。try 内の
break/continue、ラベル、ループ変数の閉包など)。全セルフテスト
PASS、ホスト全 2,447 件 PASS、clippy エラー 0。重い版の YouTube で
panic・abort・uncaught は 0 件。TODO.md)。描画の合間のマイクロタスクは
300 万ステップで区切っていたが、スクリプトの評価の直後は上限の 1
億ステップまで流していた。js/interp.rs): 評価の直後も同じ
300 万ステップで次のジョブの手前で戻す。残りは次のフレームの
pump_timers で続く。実行中のジョブは止めない。ABORT・未捕捉の例外は
0 件。再生開始は約 220 秒で短縮の効果は確認できず。performance.now() が実時刻で進んでいなかった —
再生の不安定さの原因getPlayerState・再生位置・buffered)を
20 秒ごとに記録すると、performance.now()/1000 が実時間 460
秒で 12 しか進んでいなかった。js/builtins/encoding.rs):
performance.now() は実時刻ではなく、呼ぶたびに 1
増えるカウンタだった(時計が無かった頃の近似)。Event.timeStamp・IntersectionObserver
の時刻も同じ。YouTube の player
はこの時計で帯域・要求の間隔・時間切れを計る。ABORT・未捕捉の例外はいずれも
0 件。kernel/allocator.rs):
KERNEL_HEAP_SIZE を 768MB へ。直後の RAMDisk は 130MB →
66MB(ミラーで実効 33MB。1 サイト約 26MB)。www.sugi-lab.net
は通常どおり表示。RAMDisk
が減るので複数サイトのキャッシュには余裕が無い。Window.title
は作成時の文字列で、ブラウザは別コアのスレッド(ProxyApp
経由)で動くので変更が届かなかった。kernel/window_mgr.rs):
App::live_title を追加し、app_runner_entry
がタブの一覧と同じ定期同期で ProxyApp.live_title
へ写す。ウィンドウ枠はこれがあれば優先して描く。ブラウザは
Browser - <今の URL> を返す。layout/tree.rs):
伸びるアイテムの基準を 0
とみなしていたのをやめ、flex-basis(auto
は中身の幅)を足した残りを配る(CSS Flexbox L1
§9.7)。基準が大きくコンテナを超えるときは伸長ではなく縮小の経路を通す。www.sugi-lab.net の配置とレイアウト時間は変わらず。TODO.md)。yt-smartimation(既定の
display: inline)の中の div が幅 1
だった。layout/tree.rs):
インラインの箱は中身の内在幅で組むので、中のブロックの包含ブロックの幅が
1 になっていた。CSS 2.1 §9.2.1.1
ではインラインをそこで分割し、ブロックは包含ブロックの幅いっぱいになる。これまで
flex・grid の直下だけブロック扱いにしていた。position: absolute/fixed と float
を除く)を子に持つインライン要素はブロックとして組む(分割はしない近似)。www.sugi-lab.net
の配置は変わらず。チャンネル登録ボタンとわずかに重なる(#owner
の最小幅の見積もりが小さい)。flex-basis
と伸長にも適用 / 残りの代用の絵#top-row(flex)の中の
#owner が flex-basis: 0.000000001px で 32px
まで潰れ、#actions が x=48
から始まっていた。min-width: auto
の下限が効いていなかった。layout/tree.rs・flex_min.rs):
flex-basis
が中身の幅を下回るときは下限で止める(basis_with_auto_min。min-width
の指定があるときと overflow が visible
以外のときは対象外)。flex-grow
の配分も下限で凍結して残りを配り直す(distribute_grow)。CSS
Flexbox L1 §4.5・§9.7。web_engine/render.rs):
alt
の無い画像は読み込めるまで何も描かない(読み込み中のサムネイルがプレーヤーの上に四角で残っていた)。www.sugi-lab.net
の配置は変わらず、レイアウト時間も同じ。display: flex・align-items: center
で正しく、line-height: 40px の行に 14px
の文字が入っていた。web_engine/layout.rs):
文字の描画要素を行の上端に置いていた。CSS
では行の高さの余りを文字の上下へ等分する。half_leading)。行の位置と行送りは変えない。普通の行の高さの文字は動かない。www.sugi-lab.net
の配置に崩れなし。display: flex の <button>
を固定の大きさの箱として扱っていたDIV
がボタンの高さいっぱいに伸びていた。layout/values.rs):
<button>
を画像と同じ置換要素として、固定の大きさ(高さ 36、幅は最小
120)で扱っていたので、display: flex の
align-items が効かなかった。display が flex・grid
のボタンは置換要素として扱わず、中身を普通に配置する。align-items: center で縦中央)。全 2,438 件
PASS、全セルフテスト PASS、clippy エラー 0。TODO.md)。filter: blur() が要素の下の内容までぼかしていたweb_engine/render.rs):
filter: blur()
を、要素を描いた後に矩形の画面全体へかけていた。filter
がぼかすのは要素自身の描画だけ。ボタンの中の光の効果(.contribYtLightShapeStaticWashLight、filter: blur(10px))が、下の文字までぼかしていた。blur_box::composite_blurred)。半透明の要素では描いた後の画素に下の内容が透けているので、それ自体をぼかしてはならない(最初の実装はこれで文字が灰色に滲んだ)。<button>
の文字が二重に描かれていたweb_engine/layout.rs):
描画リストを書き出すと、同じ文字が 14px(本物の子要素)と
16px(<button>
自身)で出ていた。<button>
は子孫の文字を集めて自分のラベルとして描くが、子要素があるときは子も普通に描く(skip_children
が偽)ので二重になる。TODO.md)。String で持つと、短いキーでもキーごとに 1
ブロックを確保する。js/value.rs):
PropKey(22 バイトまではインライン、超えたら
Box<str>。String と同じ 24
バイト)へ替えた。SmallPropMap の呼び出し側は 26
ファイルを機械的に修正した。挙動は変えていない。used、要求サイズの合計)は 310MB →
304MB。確保に失敗した時点の used は 560MB →
583MB。ブロックのヘッダと切り上げが減った分、使える量が増えた。expandos の減少は 13%
で、大半は YouTube 自身が保持していた。JSON.parse が YouTube
の応答を読めず、自動再生の後の視聴ページが空になっていたytd-watch-flexy.data.contents
が本来の twoColumnWatchNextResults ではなく
singleColumnWatchNextResults で、これは YouTube
の内蔵の代替応答(通信に失敗したとき用)の形だった。next は
200 で 37 万文字が届いていた(4335B
の応答は広告動画用で別物)。fetch・JSON.parse
を見張ると、JSON.parse
がこの本文で例外になっていた。YouTube
は例外を捕まえて値なしとして扱うので、ログに出ない。os_lib/json.rs): JS の
JSON.parse が OS
の設定ファイル用のパーサをそのまま使っていて、3 つの問題があった。
\\uXXXX を復号せず uXXXX
のまま入れていた(\\u0026 が u0026
になる)。BTreeMap
で持つので、キーがアルファベット順に並び替わっていた(JS
では挿入順)。値の後の余りも通していた。parse_js を新設(上限
512、キーの順序を保つ
OrderedObject、値の後の余りを拒否)。JS
は大きなスタックのスレッドで動くので深く読める。\\uXXXX
は共通で復号し、サロゲートペアを 1 文字にまとめる。設定ファイル用の
parse の上限 30 はそのまま。TODO.md)。Response.clone()
が本文のバイト列を写しておらず、YouTube
がオフライン画面になっていたplayer・get_watch
は 200 で、navigator.onLine も
true。ヘッダーのロゴ・「検索」・「ログイン」、動画の下のタイトルも出なかった。Fca)はネットワーク層のフォールバックで、innertube
の要求が値を返さないときに作られる。8
回の実行を比べると、オフライン画面はサーバーが
get_watch
を使った回だけで、player+next
の回には出なかった。get_watch
だけが、Q.clone().body.getReader() で先頭のバイトが
[(91)かを見てストリームとして読む経路を通る。js/builtins/promise.rs の
response_clone):
本文を文字列で写すだけで、body のチャンクが Uint8Array
でなく文字列になり、先頭の判定が外れていた。ヘッダーなども失われていた。headers・statusText・ok・type・redirected
も写す。get_watch
の回で、ヘッダーがすべて揃い、タイトル「Me at the
zoo」・高評価数・チャンネル登録ボタン・登録者数が表示された。未捕捉の JS
例外 0 件、ABORT 0 件。src の無い <img>・XHR の
json・getComputedStyle
と矩形の取り違え・defineProperty の
getter・<slot>
の原型・ステップ上限・Response.clone
が順に重なっていた(続報〜続報 7)。[JSPROF] ABORT step budget(len=112 の評価が
3,000
万ステップ)の後、Uncaught (in MutationObserver callback): undefined
が 5 回、_stampTemplate の
Cannot read properties of undefined (reading 'localName')
と (reading 'nodeList') が続いた。_stampTemplate に渡る
__dataHost が template ではなかった。Polymer の
_registerHost は host スタックが空でなければ
__dataHost を上書きする。スタックが残るのは
_stampTemplate
が途中で打ち切られた場合で、ステップ上限による中断と一致した。js/interp/exec.rs): 上限は評価
1
回とそのイベントループ全体の累積で、超えた時点で動いていたタスクを実行の途中で例外のように止めていた。実ブラウザは実行中のタスクを殺さないので、ページの内部状態が壊れる。max_steps)は無限ループ対策としてタスク(スクリプト・マイクロタスク・タイマー・MutationObserver
の配送)ごとにかける(mark_task_start)。同期
await
が回すタスクは呼び出し元の一部として数えるので、for(;;) await 0
は止まる。run_microtasks
が次のジョブの手前で戻り、残りは次のフレームの pump_timers
で続く(描画中は上限の 1/10、約 300
万ステップ)。実行中のジョブは打ち切らない。これはタスクの切れ目での時間分割にあたる。ABORT 0
件、未捕捉の JS 例外 0
件、自動再生まで動作、クラッシュなし。ただし自動再生の後に YouTube
の「インターネットに接続してください」の画面が出た(TODO.md)。<slot> が HTMLSlotElement
の原型を見ていなかったtp-yt-paper-dialog の
iron-overlay-opened で
this.node.__shady_assignedNodes is not a function (receiver <slot>)。YouTube
Premium
の宣伝(yt-mealbar-promo-renderer)の文字が出ず、中身の無い白いカードに見えていた。js/interp/operators.rs):
要素のタグから DOM クラスの原型を引く表に slot
が無く、HTMLUnknownElement になっていた。ShadyDOM は
HTMLSlotElement.prototype
にメソッドを足すので見つからない。クラスを定義済みなのに表に無いタグは他にも
label・link・meta・head・body・html・dialog・pre・br・hr・source・track
があり、ol は HTMLUListElement
に誤って対応していた。TODO.md)。Object.defineProperty が既存の getter を消していた —
YouTube の繰り返し描画が動かなかった~JSC$17293_renderImpl から
Cannot read properties of undefined (reading 'length')(H)。ヘッダーの「⋮」やログインのアイコンが出なかった。ps-dom-repeat(繰り返し描画の部品)の描画で、this.items
が undefined だった。items は Closure の ES5 変換で
defineProperties(proto, {items:{get}}) と
defineProperties(proto, {items:{set}}) の 2
回に分けて定義される。js/builtins/extra_static.rs の
apply_define_property):
アクセサの定義は既存のものを丸ごと置き換えていたので、2
回目(set のみ)で getter
が消えていた。仕様では記述子に無い側は残る。DOM
要素側のアクセサも同じ。get・set
が無ければ既存の値を残す。get: undefined
のように明示した場合は消す。renderImpl
の例外が消え、ヘッダーに「⋮」とログインのアイコンが出た。自動再生まで動作、クラッシュなし。getComputedStyle が別の要素の値を返す・id
の無い要素の矩形が 0 になる不具合getComputedStyle の
display が YT-ICON で
none、IMG で
flex、SVG で inherit
と不自然で、#start は実行ごとに
none・block・flex
と変わった。また #logo の子孫の
getBoundingClientRect がすべて 0 だった。web_engine/layout.rs の
collect_styles): スタイル木の走査位置をそのまま DOM
の添字として算出スタイルを登録していた。DOM
のノードは配列に追加順に並ぶので、JS
が要素を作ったり挿入したりすると走査位置と添字が一致せず、別の要素の値が返っていた。アニメーションの内部キー
_node_idx_N も同じ番号を使っていた。id_rects):
描画要素からノードを引く build_id_index の表は本物の
id しか持たない。id の無い要素は
to_dom_node が付けた内部名 _node_idx_N
なので引けず、矩形が登録されなかった。DomBridge::tree_order()(to_dom_node
と同じ順に添字を並べた表)で走査位置を添字へ戻す。DomBridge::resolve_element_id()
で _node_idx_N を解決する。1
つのノードから描画要素が複数出る場合は最初の箱の矩形を残す(従来は最後の要素で上書きしていた)。id
を持つ要素が複数あると(YouTube の
#logo・#logo-icon)、矩形は最初の 1
個にしか入らない。描画要素に DOM
の添字を持たせる必要がある(TODO.md)。responseType = "json"
が文字列を返していたのを修正Cannot read properties of undefined (reading 'length') が 4
回出ていた(発生元は
loadAnimation → completeData)。登録ボタンのアニメーションの
JSON(gstatic.com/.../subscribe_action_*.json)を読んだ直後。js/builtins/static_classlist.rs):
xhr.response は arraybuffer・blob
だけを特別に扱い、json でも文字列のままだった。lottie-web
は responseType === "json" なら xhr.response
を解析済みとしてそのまま使うので、文字列の .layers が
undefined になった。JSON の中身は正常。json では
JSON.parse した値、解析できなければ null
を返す(XHR 標準)。null)。全セルフテスト PASS、ホスト全 2,427 件
PASS、clippy エラー 0。src の無い
<img> に塗っていた代用の絵(TODO E)#logo に filter: drop-shadow(...)
があり、次の項の修正を入れたが四角は消えなかった。そこで描画リスト(RenderElement)を直接書き出すと、ytd-yoodle-renderer(ロゴ用のドゥードゥル)の
<img> が 112×56・src も alt
も空で描かれていた。web_engine/render.rs):
画像を描けない <img> に、alt
のハッシュから作ったグラデーションと枠線を代用の絵として塗っていた。alt
が空だとハッシュが一定なので、毎回同じ色になる。src の無い
<img> は何も描かない(HTML §15.4.3:
何も表さない)。src
があって読めない画像の代用の絵は従来どおり。filter: drop-shadow()
の修正(web_engine/box_shadow.rs・layout.rs):
四角の原因ではなかったが、同時に見つけた不具合を直した。drop-shadow
を背景の無い要素にも矩形の影として描いていた(本来は描いた内容の形に付く)ので、背景のある箱に限った。解析も
rgba(...) の閉じ括弧で切れて色が読めず既定の 50%
黒になっていたので、対応する括弧まで読み box-shadow
の解析へ渡すようにした(drop_shadow_from_filter)。tests/src/test_box_shadow.rs に
4 件。ホスト全 2,427 件 PASS、clippy エラー 0。TODO.md
の E)。SourceBufferCore::evict_before(10月30日 続報
27)は部品だけで、どこからも呼ばれていなかった。MSE
のサンプルは再生しても残るので、長い動画ではヒープを使い続ける。os_lib/mse.rs):
last_dts = None
となり、残っている先頭から再生が進む。後から JS
が入れ直す過去のサンプルは last_dts
より古いので読まれない。evicted_until_sec(破棄した範囲の終わり)と
seek_needs_refetch(target)
を新設し、再生を始められるサンプルが入るまでシークを保留する(waiting
を発火、上限は既存の MSE_STALL_GIVE_UP_MS)。web_engine/video.rs):
動画の時刻で 5 秒ごとに、再生位置の 30
秒前までを破棄する(MSE_KEEP_BEHIND_SEC・MSE_EVICT_INTERVAL_SEC)。サンプルは添字ではなく
DTS で読み出しているので、消しても読み出し位置はずれない。tests/src/test_mse.rs):
「エビクション後の映像はキーフレームから始まる」「キーフレームが手前に無ければ映像は消さない」「破棄した範囲へのシークは再取得を待つ」の
3 件を追加。ホスト全 2,423 件 PASS、clippy エラー 0。[hidden] 属性の扱いを仕様どおりに戻す(続報 6
の取り消し)hidden
属性を持つ要素は作者のスタイルシートが display
を指定していても常に display: none にする上書きを
css/cascade.rs に入れていた。HTML §15.3.1 では
[hidden] { display: none } は UA
スタイルシートの規則で、作者の display
指定のほうが優先される(Chrome もそう動く)。たとえば
[hidden] の要素を作者 CSS の display: block
で見せるページが、AtmOS だけ消える。yt-page-navigation-progress に
display を指定する規則は無い(あるのは
position: fixed・height: 2px・背景色など)。よって仕様どおりの
UA 規則だけで hidden
の進捗バーは消える。上書きは自動再生後に残る四角の原因への対処になっていなかった。YouTube
の部品は tp-yt-paper-button[hidden]{display:none!important}
のように必要な所で自前の規則を持つ。compute_styles_with_vars_stack
の作者スタイル適用後の上書きを削除し、UA 既定値としての
[hidden](作者の display
があればそちらが勝つ)だけに戻した。tests/src/test_css_ua_pre.rs):
「hidden 属性の要素は作者の display:block に勝って none」を「hidden
属性でも作者の display:block が勝つ」に改め、「hidden 属性だけなら
none」「作者の [hidden]{display:none!important} は要素の
display:block に勝つ」を追加。ホスト全 2,420 件
PASS、clippy エラー 0、kernel8.img 生成。TODO.md
の E)。has_due_timers)を直し全件 PASS
へ/ドキュメント整理arm/v1/src/os_lib/js/selftest.rs。いずれも実装ではなく試験の側の誤り):
document.createElement('iframe').contentWindow === window
を true とする古い試験(2026-07-17
の「フレームが無いので自分自身を指す」近似の時代のもの)。続報 4 で
contentWindow を子フレーム専用の WindowProxy にしたので
false が正しい。期待値を直し、注釈で
contentDocument は今も簡略実装のままと明記。iframe.contentWindow.postMessage
の試験は、JsRuntime::new() の空の文書に
create_element で iframe
を作るだけで文書へつないでいなかったため、document.querySelector('iframe')
が null になり TypeError で落ちていた(エラーを表示させて
f=null n=0
を確認)。build_from(parse_html(...)) で iframe
を含む文書を組むよう直した。失敗時に評価エラーの文言を出すようにした。has_due_timers
のマイクロタスク検知の試験は、eval 内の
queueMicrotask
が「積まれたまま」になる前提だったが、eval
はスクリプトの終わりでマイクロタスクを流し切る(HTML の microtask
checkpoint)ので、評価直後には既に空で実行済みだった(queued=false fired=true)。ネイティブ側(通信の完了など)から積まれた状況を、Job
をキューへ直接積んで再現するよう直した。make all FEATURES=selftest を
QEMU で起動し、JS を含む全セルフテスト
PASS(JS_SELFTEST: PASS)。clippy エラー
0、ホスト単体テスト全 2,418 件 PASS、通常版 kernel8.img
を再生成。TODO.md
の未完了項目の中に長く残っていた完了済みの記述(自動再生の表示崩れの解消済み
4 件、エラー握り潰しの完了領域、iframe
の経緯)を要約し、残りだけが読めるようにした。MSE の
evict_before・ABR の EWMA(続報
27)は部品としては完成しているが、まだ再生経路から呼ばれていないことを
TODO.md・implementation_plan.md
に明記。SharedWorker(続報 14)が済んだので計画書の該当項目を Service
Worker のみに改めた。implementation_plan.md
の「動画コンポーネント・ストリーミング処理の単体テスト拡充」「ABR
コントローラと SABR / MSE ネットワーク要求の実配線」に対応。SourceBufferCore
のバッファ管理・エビクション強化
(arm/v1/src/os_lib/mse.rs):
total_samples_bytes:
バッファ内に保持されている全サンプルの合計バイト数を算出。evict_before(current_time_sec, keep_behind_sec):
現在の再生時刻より一定時間前(current_time - keep_behind)の古いサンプルをパージし、メモリを安全に解放。パージ後に
update_buffered() で buffered (TimeRanges)
を自動更新。remove_range(start, end): 指定範囲のサンプル破棄と
TimeRanges 分割。AbrController の帯域平滑化(EWMA: α = 0.25)と自動評価
(arm/v1/src/os_lib/mse.rs):
estimated_bandwidth_bps:
コントローラ内部で平滑化帯域を保持。update_bandwidth_sample(chunk_bytes, duration_ms):
チャンク受信実績から瞬時帯域(bps)を計算し、新サンプル 25% + 過去履歴
75% の EWMA で更新。evaluate_auto:
内部の平滑化帯域を用いて最適な表現(StreamRepresentation)を自動選択。arm/v1/tests/src/test_mse.rs):
test_source_buffer_remove_range_and_time_ranges_split:
中間区間削除による TimeRanges の分割とバイト数減少を検証。test_source_buffer_eviction_and_total_bytes:
再生位置に応じた過去サンプルの自動エビクションと残存サンプルの保全を検証。test_media_source_end_of_stream_and_invalid_operations:
end_of_stream による Ended
状態遷移、Ended / Closed
時の不正操作防止(NotOpen
エラー)、再オープン時の挙動を検証。test_abr_bandwidth_ewma_and_auto_adaptation:
複数チャンク受信による EWMA 帯域収束と解像度昇格の自動連動を検証。make all
にて警告・エラーゼロで kernel8.img 正常生成を確認。eval
/ exec_stmt match アームの #[inline(never)]
関数切り出し (TODO JS ネイティブスタック最適化)spec/TODO.md の「JS 1
呼び出しあたりのネイティブスタック(3〜4 KiB)」に対応。eval および exec_stmt は巨大な match
文で全構文木ノードを処理しており、再帰 1
段ごとにローカル変数や一時オブジェクトがスタックフレームを膨張させていた。for
ループ等の切り出しが行われていたが、exec_stmt
内の変数宣言(VarDeclaration)・ブロック(Block)・While
/ DoWhile・ラベル(Labeled)、および
eval
内の二項演算(Binary)・プロパティ参照(Member)・インデックス参照(Index)等の重い処理がインライン展開されたままであり、深いコールスタックでスタックオーバーフローを引き起こすリスクが存在していた。exec_stmt match アームの
#[inline(never)] 関数分離
(arm/v1/src/os_lib/js/interp/exec.rs):
exec_var_decl:
変数宣言ループ・初期化子評価・スコープバインド処理を独立関数へ切り出し。exec_block:
レキシカルスコープ生成・リサイクルおよび巻き上げ文実行を独立関数へ切り出し。exec_while: while ループ評価および break/continue
処理を独立関数へ切り出し。exec_do_while: do-while
ループ評価を独立関数へ切り出し。exec_labeled: ラベル伝播および break
検知処理を独立関数へ切り出し。exec_stmt 本体の match
を極小のディスパッチテーブル化し、スタックフレーム消費を大幅に削減。eval match アームの
#[inline(never)] 関数分離
(arm/v1/src/os_lib/js/interp/eval_expr.rs):
eval_binary_expr:
プライベートフィールド検証(#x in obj)および二項演算子評価を独立関数へ切り出し。eval_member:
オブジェクトチェイン評価・オプショナルチェイニング判定・プロパティ取得を独立関数へ切り出し。eval_index:
オプショナルチェイニング判定・高速インデックス参照・プロパティキー変換取得を独立関数へ切り出し。make all
にて警告・エラーゼロで kernel8.img 正常生成を確認。read_file_checked
移行完了・ファイル読み込みエラー握り潰しの完全根絶 (TODO
エラー握り潰し一掃)spec/TODO.md
の「エラー握り潰しの一掃」「SylFS::read_file
の呼び出し側移行」に対応。SylFS::read_file は戻り値が
Option<(FileMetadata, Vec<u8>)>
であり、ファイルが「正常に存在しない(Ok(None))」のか「セクタ破損やI/O障害で読めない(Err(e))」のかを識別できず、読めないエラーを静かに
None として握り潰していた。read_file_checked()
へ移行していたが、Aura ビルトイン(cp, mv,
cat, grep, head,
tail, show, addlabel,
load, require, run,
apps)、ターミナル(アイコン・マニフェスト)、WiFi(ファームウェア)、Web
forms(localStorage)、Webエンジン(スクリプト・バイナリフォント・ローカルHTML読み込み)等に
read_file
呼び出しが残存し、破損時に単なる「ファイルが見つからない(404)」と誤認されるリスクが残っていた。read_file_checked
の新設 (arm/v1/src/kernel/fs/mod.rs):
pub fn read_file_checked(path: &str) -> Result<Option<alloc::vec::Vec<u8>>, &'static str>
を新設し、グローバル FS
からのファイル取得において「存在しない」と「読めない」を型レベルで明確に区別。arm/v1/src/kernel/wifi.rs):
/boot/brcmfmac43455-sdio.bin 読み込みを
read_file_checked 化。セクタ破損等の障害発生時は
[ERROR][NET] で明確にログ出力。arm/v1/src/os_lib/js/builtins/web_forms.rs):
read_file_checked 化し、I/O
異常時は [WARN ][SYS] でログ出力。arm/v1/src/os_lib/aura/builtins/os_fs.rs,
modules_str_task.rs, system.rs,
eval.rs):
cp, mv, cat,
grep, head, tail,
show, addlabel: ファイル非存在時は
ERROR: File '{}' not found、読み取り障害時は
ERROR: Failed to read file '{}': {}
と明確に区別してスクリプト呼び出し元に返却。load, require:
スクリプト読み込み障害を明示的にエラー伝播。builtin_run, builtin_apps,
eval.rs:
パッケージマニフェスト・エントリーポイント読み込み時のエラーハンドリングを強化。arm/v1/src/apps/terminal/mod.rs, shell.rs,
os_lib/layout/values.rs,
os_lib/web_engine/layout.rs, mod.rs):
read_file_checked 化。kernel/fs/mod.rs
内部の互換性ラッパーを除く全サブシステムからの生
fs.read_file 呼び出しを完全根絶(ゼロ化)。arm/v1/tests/src/test_fs_error_propagation.rs):
test_read_file_checked_nonexistent_and_success:
存在しないファイルに対する Ok(None) と正常ファイルに対する
Ok(Some(...)) の正確な動作を検証。test_read_file_checked_propagates_corruption_error:
メタデータセクタが破損した場合に静かに None とせず、確実に
Err を返して上位に通知することを検証。make all
にて警告・エラーゼロで kernel8.img 正常生成を確認。https_get/https_request 自動フォールバック実装
(TODO TLS 1.2 連携・HTTPクライアント強化)spec/TODO.md の「TLS 1.2
経路の証明書・署名検証が無い」「https_get /
https_request / keep-alive 経路は TLS 1.2
への切り替えをまだ持たない(https_get_binary
のみ)」に対応。kernel/tls12.rs)は先行して Sway / OGP
取得向けに基礎的なハンドシェイクが導入されていたが、サーバーから送出される
X.509 証明書チェーン (Certificate) や、ECDHE
鍵交換時の署名アルゴリズム・暗号署名・署名対象データ
(ServerKeyExchange)
を抽出・パースする基盤が存在しなかった。kernel/tls.rs のメイン HTTP
クライアント(https_get_with_referer,
https_request_once, https_post 等)は TLS 1.3
(embedded-tls) 固定で動作しており、TLS 1.2
のみを要求するサーバーや TLS 1.3
ハンドシェイク失敗時に通信が途絶する課題があった。arm/v1/src/kernel/tls12.rs):
parse_certificates: TLS 1.2 ハンドシェイクの
Certificate メッセージ(RFC 5246 § 7.4.2)から 3 バイト
(u24) 長プレフィックスを解析し、DER エンコードされた X.509
サーバー証明書チェーンを Vec<Vec<u8>>
として抽出。arm/v1/src/kernel/tls12.rs):
ServerKeyExchangeParams 構造体を新設し、ECDHE
公開鍵(secp256r1/256ビット)、ハッシュアルゴリズム(SHA-256/SHA-384等)、署名アルゴリズム(ECDSA/RSA等)、暗号署名バイト列、および
RFC 5246 / RFC 8422 § 5.4 に準拠した署名対象データ
(client_random 32B + server_random 32B +
ServerECDHParams 4+L bytes)を正確に抽出する
parse_server_key_exchange_with_sig を実装。Tls12<T> 構造体に
peer_certs: Vec<Vec<u8>> および
ske_params: Option<ServerKeyExchangeParams>
フィールドを追加し、ハンドシェイク時に受信・保持。peer_certificates()
および server_key_exchange_params() アクセサを提供。https_get / https_request 経路の
TLS 1.2 自動フォールバック
(arm/v1/src/kernel/tls.rs):
https_get_attempt_tls12: TLS 1.2 ソケットを用いて HTTP
GET
を発行し、レスポンス解析およびヘッダー・本文を返却する補助ルーチンを整備。https_get_with_referer: TLS 1.3 接続失敗時に
[WARN ][NET] 規約の警告ログを出力し、自動的に
https_get_attempt_tls12
へフォールバックして透過的にリトライする二段構えを確立。https_request_once: TLS 1.3 失敗時に自動的に
https_request_once_attempt_tls12 へフォールバック。https_post:
重複実装を解消し、https_request("POST", ...)
への委譲に統一。arm/v1/tests/src/test_tls12.rs):
test_tls12_parse_certificates:
空証明書、複数証明書チェーン、不正長エラー検出を検証。test_tls12_parse_server_key_exchange_with_sig:
secp256r1 曲線パラメータ、ECDSA-SHA256
署名、署名対象バイト列(client_random + server_random +
ServerECDHParams)の完全一致、未対応曲線検出を検証。test_tls12_handshake_retains_peer_certificates_and_sig_params:
モックサーバー(FakeServer)とのハンドシェイク完了後に証明書および署名パラメータが確実に保持されることを検証。make all
にて警告・エラーゼロで kernel8.img 正常生成を確認。spec/TODO.md
の「エラー握り潰しの一掃」において、os_lib/h264_decode.rs
内の SPS/PPS 解析(parse_sps(..).ok(),
parse_pps(..).ok())が 6
箇所でエラーを静かに握り潰していた。self.sps = None
に上書きリセットされてしまい、既存の有効なパラメータセットまで破壊されて以降のフレームがデコード不能になる脆弱性があった。parse_sps / parse_pps
が空バイト列や規格外の profile_idc == 0
に対してエラーを返さず不完全な構造体を生成する問題があった。VideoDecoder::init_avcc の Result
化とエラーログ出力
(arm/v1/src/os_lib/h264_decode.rs,
arm/v1/src/os_lib/web_engine/video.rs):
init_avcc(&mut self, avcc: &[u8]) -> Result<(), &'static str>
に改め、不正ヘッダやパース失敗時にエラーを返却。呼び出し側の
web_engine/video.rs(通常再生・MSE ストリーミング)において
[WARN ][GPU] 規約の警告ログを出力。decode_sample_rgb・run_golden_debug
におけるパラメータセット保護とエラーログ出力
(arm/v1/src/os_lib/h264_decode.rs):
self.sps / self.pps を更新。失敗時は
[WARN ][GPU] 警告ログを出力し、既存の有効な SPS/PPS
を上書き破壊しないよう保護。push_au 内での refs.decode(...)
失敗時および SPS/PPS 未存在時にも [WARN ][GPU]
警告ログを出力。has_sps(), has_pps(), sps(),
pps() アクセサを追加。parse_sps / parse_pps
の入力検証堅牢化 (arm/v1/src/os_lib/h264.rs):
profile_idc == 0 に対して即座に Err
を返却。arm/v1/tests/src/test_css_transform_and_angle.rs):
set_root_font_px(ROOT_FONT_SIZE_PX)
を初期化し、グローバル Atomic 汚染によるフレークを防止。arm/v1/tests/src/test_h264_error_handling.rs):
test_h264_init_avcc_invalid_inputs: 不正な
avcC(空配列、短ヘッダ、不正長さ、破損 SPS)でクラッシュせず適切に
Err を返すことを検証。test_h264_init_avcc_success: 正しい avcC
による正常初期化を検証。test_h264_decode_sample_corrupt_sps_pps_preserves_valid_state:
破損 SPS/PPS NAL
が混入しても既存の有効なパラメータセットが破壊されず、後続の IDR
スライスが正常に復号できることを実証。make all
にて警告・エラーゼロで kernel8.img 正常生成を確認。SylFS::read_file_checked 移行 (TODO 優先課題
エラー握り潰し一掃)SylFS::read_file は戻り値が
Option<(FileMetadata, Vec<u8>)>
であり、ファイルが「正常に存在しない(Ok(None))」のか「存在するがセクタ破損やI/O異常で読めない(Err(e))」のかを識別できず、読めないエラーを静かに
None として握り潰していた。cloud.rs)やログインパスワード(login)、OS設定(settings)、ファイル管理(files)、Webキャッシュ(cache)、画像・動画読み込み等において、セクタ異常発生時に誤ってデフォルト値で上書きしてデータを喪失したり、意図しない初期セットアップ画面へ不正遷移するリスクが存在していた。arm/v1/src/os_lib/cloud.rs):
load_auth, load_ms_auth,
load_gh_auth において read_file_checked
を導入。読み取り異常(Err)発生時はエラーログを明示出力し、破損ファイルの上書きを防止。arm/v1/src/apps/login/mod.rs):
check_saved_password において password.cfg
の読み出しを read_file_checked
化。I/O異常時は初期パスワード登録画面(SetupInput)へのバイパスを防ぎ、安全側に倒して
Locked 状態を維持。arm/v1/src/apps/settings/mod.rs):
apply_theme_colorset を
read_file_checked に移行。読み込みエラーを caller
に伝播し、設定破損時の無言の失敗を根絶。arm/v1/src/os_lib/web_engine/cache.rs,
image.rs, video.rs):
read_cache およびキャッシュ保存直後の検証読み出しを
read_file_checked
化。キャッシュ破損時に警告を出力し、安全に再取得へフォールバック。image.rs,
video.rs)において、ファイル未存在と読み取りエラーを明確に区別してログ出力。arm/v1/src/apps/browser/mod.rs, drive/mod.rs,
onedrive/mod.rs, note/mod.rs,
files/mod.rs):
read_file_checked に更新。arm/v1/tests/src/test_fs_error_propagation.rs):
test_read_file_checked_distinguishes_ok_none_from_success
を新設。ファイルが存在しない場合の Ok(None)
と、正常に読める場合の Ok(Some(...))、破損時の
Err(...)
の3状態が決定論的に区別されることを厳密に検証。make all
にて警告・エラーゼロで kernel8.img 正常生成を確認。direct_spatial_mv_pred == true)を先行実装していたが、時間ダイレクト(direct_spatial_mv_pred == false)に対してはスライスヘッダパース時に
temporal direct not supported
エラーを返していたため、spec/TODO.md の H.264
デコーダ未完了タスクとして残されていた。Motion
構造体への参照ピクチャ識別子(refp)統合
(arm/v1/src/os_lib/h264_decode.rs):
RefPicList1[0])の各 4x4
ブロックが参照していたピクチャの識別子(POC)を保持する
refp: [Vec<i32>; 2] を新設。RefEntry
への長期参照フラグ(is_long_term)統合
(arm/v1/src/os_lib/h264_decode.rs):
arm/v1/src/os_lib/h264_decode.rs):
temporal_direct_mv_scaling:
mvL0 = [0, 0], mvL1 = [0, 0], refIdx = [0, 0]MapColToList0: co-located ブロックが参照した
ref_poc と一致する POC を持つ RefPicList0
内のエントリを探索し、refIdxL0 を決定。RefPicList0[refIdxL0] または
colPic
が長期参照ピクチャの場合、時間距離スケーリングを行わず
mvL0 = mvCol, mvL1 = [0, 0] を設定。tb = (cur_poc - poc0).clamp(-128, 127)td = (poc1 - poc0).clamp(-128, 127)td > 0 の場合、除算を回避する逆数項
tx = (16384 + (td / 2).abs()) / td、DistScaleFactor = ((tb * tx + 32) >> 6).clamp(-1024, 1023)
を算出し、mvL0 = (dsf * mvCol + 128) >> 8,
mvL1 = mvL0 - mvCol を正確に導出。colZeroFlag(静止ブロック強制ゼロ化): co-located
ピクチャが短期参照で refIdxCol == 0 かつ
|mvCol| <= 1 の場合、refIdx == 0
のリストの動きベクトルを [0, 0] に強制。arm/v1/src/os_lib/h264_decode.rs):
direct_8x8_temporal
を新設し、direct_8x8_inference_flag(4x4 または 8x8
コーナー座標)に応じた co-located 走査および MC
予測・状態格納を実施。decode_b_direct_mb および
decode_b8x8(B_Direct_8x8)において、スライスヘッダの
direct_spatial_mv_pred
フラグに応じて空間ダイレクト/時間ダイレクトを動的に切り替え。temporal direct not supported 制限を完全撤廃。arm/v1/tests/src/test_h264_temporal_direct.rs):
cur_poc = 2, poc0 = 0, poc1 = 4
での 1/2 スケーリング)。cur_poc = 1, poc0 = 0, poc1 = 4
での 1/4 スケーリング)。colZeroFlag
による静止ブロックの動きベクトルゼロ化検証。colZeroFlag
非適用検証。MapColToList0
による参照インデックス動的マッピング検証。make all
にて警告・エラーゼロで kernel8.img 正常生成を確認。MMCO other than 1 not supported」「long-term reference not supported」として明示的にエラーで終了させていたため、spec/TODO.md
にて「長期参照・MMCO 2〜6」が未完了課題として残されていた。MmcoOp
列挙型の新設とスライスヘッダパースの拡張
(arm/v1/src/os_lib/h264_slice.rs):
MmcoOp(UnmarkShortTerm,
UnmarkLongTerm, ShortTermToLongTerm,
SetMaxLongTermFrameIdx, ClearAll,
MarkCurrentLongTerm)を新設。long_term_reference_flag == 1(LongTermFrameIdx = 0
登録)および非 IDR での
adaptive_ref_pic_marking_mode_flag == 1 時の MMCO 1〜6
を完全パース。arm/v1/src/os_lib/h264_decode.rs):
RefPic に is_long_term: bool と
long_term_frame_idx: u32 を追加。RefFrames::apply_mmco:
max_long_term_frame_idx_plus1
に基づく上限管理と超過長期参照の自動パージPocState::reset_mmco5() による POC/FrameNum
状態リセットarm/v1/src/os_lib/h264_decode.rs):
LongTermPicNum
昇順で配置する規格挙動を実装。arm/v1/tests/src/test_h264_mmco.rs):
max_long_term_frame_idx
による上限追出しの検証。make all
にて警告・エラーゼロで kernel8.img 正常生成を確認。scaling_list、4x4
リスト 6 個 + 8x8 リスト 2 個)が規格化されている。scaling_matrix_present_flag
が有効なストリームに対しては「SPS scaling matrix not supported」「PPS scaling matrix not supported」として明示的にエラー終了させていたため、spec/TODO.md
にて「スケーリング行列(SPS/PPS の
scaling_list、既定行列のフォールバック規則
A/B)」が未完了課題として残されていた。arm/v1/src/os_lib/h264.rs):
DEFAULT_4X4_INTRA,
DEFAULT_4X4_INTER, DEFAULT_8X8_INTRA,
DEFAULT_8X8_INTER を定義。ZIGZAG_4X4,
ZIGZAG_8X8 およびジグザグ順からラスタ順への変換関数
zigzag_to_raster_4x4, zigzag_to_raster_8x8
を提供。ScalingMatrix データ構造と
scaling_list 構文パーサーの実装
(arm/v1/src/os_lib/h264.rs):
ScalingMatrix(既定値は全要素 16 の平坦行列)を新設。parse_scaling_list
を実装。delta_scale(se(v))の累積、j == 0 && next_scale == 0
時の use_default フラグ検出とデフォルト行列コピー、途中で
next_scale == 0
となった場合の直前スケール繰り返しを完全に再現。arm/v1/src/os_lib/h264.rs):
parse_sps: seq_scaling_matrix_present_flag
が 1 のとき、8 個のスケーリングリストをパース。フラグが 0
のリストには規格 Table 7-2 のフォールバック規則 A(Intra 4x4 は Default
または Y リスト継承、Inter 4x4 は Default または Y リスト継承、8x8 は
Default_8x8_Intra / Default_8x8_Inter)を厳密に適用。parse_pps: pic_scaling_matrix_present_flag
が 1 のとき、最大 8 個のスケーリングリストをパースし、フォールバック規則
B を適用。pps.effective_scaling_matrix(sps) により、PPS
側に独自行列がなければ SPS のスケーリング行列をシームレスに継承。arm/v1/src/os_lib/h264.rs,
arm/v1/src/os_lib/h264_decode.rs):
dequant_4x4_scaled(block, qp, weight_scale) および
dequant_8x8_scaled(b, qp, weight_scale)
を新設(平坦呼び出し用の dequant_4x4 /
dequant_8x8 も互換維持)。DecState に scaling_matrix を配線。Intra
4x4(Y: リスト 0, Cb: リスト 1, Cr: リスト 2)、Intra 16x16 AC(リスト
0)、Intra 8x8(リスト 0)、Inter 4x4(Y: リスト 3, Cb: リスト 4, Cr:
リスト 5)、Inter 8x8(リスト 1)の全 MB
復号経路において正しいスケーリングリストを適用。arm/v1/tests/src/test_h264_scaling.rs):
parse_scaling_list での delta_scale 累積および
use_default の検証。seq_scaling_matrix_present_flag
パースとフォールバック規則 A の検証。make all
にて警告・エラーゼロで kernel8.img 正常生成を確認。@container
コンテナクエリの祖先コンテナ解決・container-type/container
ショートハンド解釈の実装 (TODO CSS container 未完了課題解消)@container(CSS Container Queries: CSS Containment
Level
3)は、親コンテナ要素の寸法に応じてスタイルを切り替える現代的なレスポンシブ設計技術である。AtmOS
では条件構文解析(css/container_query.rs)は先行して実装されていたものの、コンテナ寸法としてビューポート幅を代用していたため実質
@media
と同一の挙動になり、入れ子のコンテナ要素や名前付きコンテナ(container-name)が正しく評価されず、spec/TODO.md
にて「@container(コンテナクエリ)—
部分実装済み・2パス化が残課題」として記録されていた。container
ショートハンドプロパティの展開
(arm/v1/src/os_lib/css/shorthand.rs):
expand_container_shorthand
を新設し、container: <name> / <type>(例:
sidebar / inline-size)や
container: inline-size を container-name と
container-type に適切に分解・展開。ContainerName と
AncestorContext へのコンテナ情報統合
(arm/v1/src/os_lib/css/cascade.rs):
Copy
トレイト維持のため、アロケーションフリーな 16
バイト固定長インライン構造体 ContainerName を新設(ASCII
大文字小文字無視の一致判定 eq_ignore_ascii_case
を実装)。AncestorContext に
is_container: bool,
container_name: Option<ContainerName>,
container_size: Option<ContainerSize> を統合。container-type: inline-size または
size(あるいはショートハンド)を指定している場合、自身の幅・高さをコンテナ寸法としてスタックに保持。arm/v1/src/os_lib/css/cascade.rs):
@container
照合時、ancestors
スタックを直近の親から上位祖先へ向けて逆順探索。@container sidebar (min-width: 200px))の場合は一致する
container_name
を持つ祖先コンテナの寸法を採用。名前なしの場合は最も近い祖先コンテナの寸法を採用。arm/v1/tests/src/test_container_query.rs):
@container (min-width: 250px) にマッチするケース、親が
300px で子が min-width: 400px でマッチしないケース)。@container sidebar (min-width: 200px)
と @container main (min-width: 600px)
の照合切り分け)を検証。make all
にて警告・エラーゼロで kernel8.img 正常生成を確認。spec/kernel_performance.md では、カーネルの高速化(作戦
1〜4)に着手する前提として、コンテキストスイッチ回数、実際にページテーブルが変更された回数、TLB
フラッシュ(tlbi)発行回数、およびプロセス数(num_proc)の分布を実測する計測カウンタの整備が最優先事項とされていた。arm/v1/src/kernel/scheduler.rs):
CTX_SWITCH_COUNT: コンテキストスイッチ発生回数。PT_CHANGE_COUNT: 切り替え先と現在の
ttbr0_el1
が異なり実際にページテーブル変更が発生した回数。TLBI_COUNT: tlbi vmalle1
が発行された回数(同一ページテーブル共有時のスキップ効果を実測可能)。NUM_PROC_MAX:
スケジューラ実行時にサンプリングされた最大プロセス数。NUM_PROC_SAMPLES: スケジューラ実行回数。arm/v1/src/kernel/scheduler.rs):
get_kernel_perf_counters() -> KernelPerfCounters:
現在のスナップショットを取得。dump_kernel_perf_counters(): [PERF]
ログとしてシリアルへ 1 回だけ整形出力(ログ規約 L-3 準拠)。arm/v1/tests/src/test_kernel_perf_counters.rs):
num_proc_max
の最大値保持とサンプリング回数の正確な積算を検証。make all
にて警告・エラーゼロで kernel8.img 正常生成を確認。FAR_EL1)とシンドローム(ESR_EL1)のみを出力しており、どの
CPU コアでどのプロセス(PID・プロセス名)がどの実行元
PC(ELR_EL1)で例外を起こしたかが特定できず、spec/TODO.md
にて「例外ハンドラの情報充実」が未完了課題として残されていた。arm/v1/src/kernel/vector.S):
el0_data_abort_dispatch
において、mrs x2, elr_el1
により例外発生元プログラムカウンタ(ELR_EL1)を第 3
引数として rust_data_abort_handler に渡すよう拡張。arm/v1/src/kernel/syscall.rs):
rust_data_abort_handler(far: usize, esr: usize, elr: usize)
を再実装。ESR_EL1 の Exception Class (EC) から
INSTRUCTION ABORT (0x20/0x21) と DATA ABORT
(0x24) を正確に判別し、DFSC / IFSC(Fault Status Code)および WnR(Write
not Read)を詳細解析。crate::kernel::scheduler::core_id(),
get_current_process_id(),
get_current_process_name() を取得し、Core
ID・プロセス名・PID・ELR_EL1・FAR_EL1・ESR_EL1 をログ規約 L-1 / L-3
に従い 1 例外につき 1 回のみ見やすく構造化してシリアルへ出力。arm/v1/src/kernel/syscall.rs):
rust_el1_sync_handler においても、Core
ID・実行中プロセス名・PID をヘッダーに明示出力するよう強化。make all
にて警告・エラーゼロで kernel8.img 正常生成を確認。cursor(pointer/grab/crosshair/not-allowed等)の手続き的描画およびUAスタイル対応
(TODO CSS cursor 未完了課題解消)cursor
プロパティにより、リンクやボタンホバー時の
pointer(指差し手形)、ドラッグ時の
grab、照準の crosshair、操作不能時の
not-allowed
等、要素の状態に応じた多様なカーソル形状が規定されている。cursor: text(I-beam)のみが手続き的に描画され、それ以外のキーワードは矢印カーソルにフォールバックしていた。また、<a>
や <button> 等の UA スタイルにも
cursor: pointer
が指定されておらず、spec/TODO.md にて「cursor
の text
以外のキーワード(pointer/grab
等。新規アイコン資産必要)」が未完了課題として残されていた。CursorKind
列挙型と手続き的ピクセルアート描画の実装
(arm/v1/src/kernel/draw.rs):
CursorKind(Default, Text,
Pointer, Grab, Crosshair,
NotAllowed)列挙型および CSS 文字列からの変換関数
CursorKind::from_css を新設。draw_bitmap_pattern
による手続き的な輪郭線(stroke)+塗り(fill)の 2
色ビットマップ描画として各カーソルを実装。32x32
バウンディングボックス内に完全に収め、既存のダーティ領域管理(flush_with_cursor)と完全互換。Mouse 構造体に cursor_kind: CursorKind
を追加し、描画時に形状を切り替え。arm/v1/src/os_lib/web_engine/render.rs,
apps/browser/mod.rs, main.rs):
WebEngine::draw において、マウス位置の要素の
cursor_style を hovered_cursor_kind
に記録。apps::browser::hovered_cursor() -> CursorKind
を新設し、ブラウザ上でホバー中のカーソル種別を抽出。main.rs の描画パイプラインにて
mouse_obj.cursor_kind = apps::browser::hovered_cursor();
を配線。cursor
拡充 (arm/v1/src/os_lib/css/cascade.rs):
<a> (with href), <button>,
<summary>,
<input type="button|submit|reset"> に
cursor: pointer を適用。<textarea>,
<input type="text|password|search|email|url|tel|number">
に cursor: text を適用。a.disabled { cursor: not-allowed })が存在する場合はカスケードで優先適用。arm/v1/tests/src/test_css_cursor.rs):
test_cursor_kind_from_css:
各種キーワード・空白トリム・未知値の判定検証。test_ua_cursor_styles: 各種 HTML タグの UA
カーソルスタイルの正確な付与を検証。test_author_css_cursor_override: 著者 CSS による UA
スタイルの上書きを検証。test_cursor_inheritance: 親要素から子要素への
cursor プロパティの自然な継承を検証。make all
にて警告・エラーゼロで kernel8.img 正常生成を確認。SharedWorker(WHATWG HTML § 10.1.2
複数コンテキスト共有ワーカー)と MessagePort
双方向通信パイプラインの実装 (TODO JavaScript
エンジン未完了課題解消)SharedWorker が規定されている。Worker)
が先行実装されていたものの、複数接続を同一インスタンスで受けて
MessagePort 経由で独立通信する SharedWorker
は未対応であり、spec/TODO.md の JavaScript
エンジンセクションにて「SharedWorker(複数コンテキスト共有ワーカー)」が未完了課題として残されていた。SharedPortPair と SharedWorkerCore
の新設 (arm/v1/src/os_lib/worker.rs):
to_worker_queue, to_client_queue)
およびクローズ状態を管理する SharedPortPair を新設。SharedWorkerCore 構造体を実装し、クライアント接続管理
(connect_client,
pop_pending_connect)、クライアント別独立ポート送受信
(post_to_worker, post_to_client,
pop_worker_message,
pop_client_message)、個別ポート閉鎖
(close_port)、および全リソース破棄 (terminate)
を提供。arm/v1/src/os_lib/js/builtins/worker.rs,
builtins.rs):
new SharedWorker(url, name) コンストラクタを実装。同一
URL
のインスタンスを共有キャッシュ(__shared_worker_{url})で管理し、新規接続ごとにクライアント側とワーカー側の
MessagePort ペアを生成して相互参照(_peer)を接続。postMessage 時にワーカーコード(blob: URL
等)を遅延評価し、ワーカー内の self.onconnect を発火して
e.ports[0] を渡すライフサイクルを確立。postMessage, start,
close, addEventListener,
removeEventListener, dispatchEvent
を完備し、ピアポートの onmessage および
message リスナへ正確に配送。WINDOW_MIRRORED_GLOBALS、window.SharedWorker、およびグローバルスコープへ
SharedWorker を登録。arm/v1/src/os_lib/js/selftest.rs):
typeof SharedWorker === 'function' && typeof window.SharedWorker === 'function'new SharedWorker('test.js', 'test_worker') による
port.postMessage, port.start,
port.close 等の API
完全性を起動時に常時スモークテスト。arm/v1/tests/src/test_web_workers.rs):
test_shared_worker_core_lifecycle:
基本ライフサイクルおよび状態遷移の検証。test_shared_worker_multiple_client_connections:
複数クライアント接続とキュー取り出しの検証。test_shared_worker_bidirectional_independent_ports:
クライアント別ポートの双方向独立通信(混線防止)の検証。test_shared_worker_port_close_and_termination:
ポート個別クローズ時の孤立遮断とワーカー全体 terminate
時の全ポートクリーンアップの検証。make all
にて警告・エラーゼロで kernel8.img 正常生成を確認。<dialog> の ::backdrop
疑似要素・モーダル UA スタイルおよび全画面半透明描画・Escape
キー連動の実装 (TODO HTML5 <dialog> 要素を showModal()
で開いたモーダル状態において、トップレイヤーとして中央固定配置(position: fixed; inset: 0; margin: auto)され、背後に全画面オーバーレイ疑似要素
::backdrop(既定 rgba(0, 0, 0, 0.1)
等)が描画され、Escape キー押下で cancel
イベント(cancelable)および close()
が連動して閉じることが厳密に規定されている。showModal() 時に内部専用属性
_modal を付与するのみで留まっており、CSS 疑似要素
::backdrop
のパース・カスケードが丸ごと未対応で、画面上の半透明暗転オーバーレイ描画や
Escape キーによるダイアログ閉鎖機構が存在せず、spec/TODO.md
にて「<dialog>(showModal/close)—
::backdrop/フォーカストラップ等」が未完了課題として残されていた。PseudoElement::Backdrop
の新設とパース対応
(arm/v1/src/os_lib/css/types.rs,
arm/v1/src/os_lib/css/selector.rs):
PseudoElement 列挙型に Backdrop
バリアントを追加。dialog::backdrop や
::backdrop、.custom::backdrop
を正しくパースし、セレクタ特異度 (Type Selector = 1) を正確に計算。<dialog> の UA
デフォルトスタイルと ::backdrop カスケード
(arm/v1/src/os_lib/css/cascade.rs):
_modal 属性を持つ <dialog>
に対し、W3C HTML §15.3.6 準拠の UA
既定スタイル(position: fixed, top: 0,
bottom: 0, left: 0, right: 0,
margin: auto, z-index: 2147483647,
background-color: #ffffff,
border: 1px solid #000000,
x-backdrop-background: rgba(0, 0, 0, 0.32))を自動適用。dialog::backdrop { background: rgba(...) }
が存在する場合はカスケードルールに基づき x-backdrop-*
へ優先適用。::backdrop
オーバーレイ描画の実装
(arm/v1/src/os_lib/web_engine/mod.rs,
layout.rs, render.rs):
RenderElement に is_modal: bool および
backdrop_color: Option<u32> フィールドを追加。0, top_offset, win_w, win_h)へ
screen.fill_signed_alpha()
を用いて指定されたアルファ値(既定 32%
黒)で半透明暗転オーバーレイを一括描画。arm/v1/src/os_lib/web_engine/render.rs):
on_key において Escape キー(keycode ==
0x01)押下時、最前面のモーダルダイアログへ cancel
イベントを発火。e.preventDefault() されなければ _modal
および open 属性を除去して close
イベントを発火し、画面を自動再レイアウト・再描画。arm/v1/tests/src/test_css_pseudo_elements.rs,
arm/v1/tests/src/test_dialog_backdrop.rs):
backdrop疑似要素のパースと特異度が正確
を新設・検証。test_dialog_backdrop.rs(新設、4件):
閉じたダイアログの display: none、非モーダル open
ダイアログの挙動、モーダル UA スタイルおよび ::backdrop
既定値、著者 CSS による ::backdrop 上書きを網羅検証。make all
にて警告ゼロで kernel8.img 正常生成を確認。draw_outline()
を呼び出していたため、後続の要素(兄弟要素の背景やテキスト等)によってアウトラインが上から塗り潰されてしまう問題があり、仕様書
spec/paint_order.md および spec/TODO.md
において「段階
10(アウトライン)の分離」が未解決課題として残されていた。PaintStage::Outline = 10 の追加
(arm/v1/src/os_lib/web_engine/paint_order.rs):
描画段階列挙型に Outline = 10 を正式に追加。arm/v1/src/os_lib/web_engine/render.rs):
pending_outlines
保留リストへ
(ox0, oy0, ox1, oy1, width, offset, color, style, clip_rect)
を蓄積。screen.clip_rect)を安全に適用・復元。arm/v1/tests/src/test_paint_order.rs):
test_stage_10_outline_paints_after_all_stages
を新設・検証(PaintStage::Outline
が他の全段階よりも手前(10)であることを検証)。make all
にて警告ゼロで kernel8.img 正常生成を確認。has_due_timers
のマイクロタスク(Promise/queueMicrotask)検知連動とブラウザ再描画・DOM
借用堅牢化 (TODO E. 動的コンテンツ更新追従)player
や next などの XHR/Fetch API レスポンスが届いて Promise
コールバックや queueMicrotask
がマイクロタスクキューに積まれても、マクロタスク(setTimeout/setInterval/rAF)に期限切れのものが無い場合、has_due_timers()
が false
を返し、ブラウザ描画ループ(wants_redraw)がアイドル状態と誤認して
draw() を呼び出さず、マイクロタスクの消化とそれに伴う DOM
反映・再レイアウトが後続のマウスクリック等のイベントまで遅延・スタックする潜在バグが存在していた。apps/browser/mod.rs の wants_redraw
において、self.engine.js_runtime.dom.borrow().dirty
と無保護で借用していたため、JS
実行と重なった場合に二重借用パニック(BorrowMutError)を引き起こす脆弱性があった。JsRuntime::has_due_timers
のマイクロタスク検知
(arm/v1/src/os_lib/js/interp.rs):
self.microtasks.try_borrow().is_ok_and(|q| !q.is_empty())
のチェックを追加。Promise の解決や queueMicrotask
でマイクロタスクが 1 つでも積まれた瞬間、直ちに
has_due_timers() が true
を返し、ホストの描画ループ(apps/browser/mod.rs および
frames_want_redraw)を確実に駆動するよう改修。arm/v1/src/apps/browser/mod.rs): dom_pending
の参照を
self.engine.js_runtime.dom.try_borrow().is_ok_and(|d| d.dirty)
に改修し、二重借用パニックを完全防止。arm/v1/src/os_lib/js/selftest.rs):
has_due_timers の初期状態(false)→
queueMicrotask 後(true)→ pump_timers
消化後(false)およびコールバック実行の連動アサートを新設。arm/v1/tests/src/test_frames.rs):
test_child_and_parent_redraw_conditions_for_microtasks_and_dom_dirty
を新設・検証(マイクロタスク有無による再描画要求、dom.dirty、try_borrow
保護の動作を検証)。make all
にて警告ゼロで kernel8.img 正常生成を確認。offsetWidth/offsetHeight/clientWidth/clientHeight/scrollWidth/scrollHeight/getBoundingClientRect())を参照すると、同期レイアウト(Forced
Synchronous Layout)を実行して最新の寸法を返す仕様となっている。DomBridge.rects を参照し、未レンダリングの要素では
0 を返して notify_layout_settled() の resize
通知で再測定を促す代用を行っていた。document.createElement
直後)や、属性
<video width="640" height="360">、<canvas>(HTML5規定で
300×150)、インラインスタイル
style="width: 240px; height: 120px"
を持つ要素であっても、最初のレイアウトサイクルが走るまでは寸法が
0 と判定され、YouTube や各種 Web アプリが初期化時に「寸法が
0 のため失敗」と判断する原因となっていた。DomBridge::get_rect
のインラインスタイル・属性・固有寸法フォールバック
(arm/v1/src/os_lib/js/dom_bridge.rs):
self.rects
にキャッシュが無い未レイアウト要素であっても、要素が
display: none でない限り、インラインスタイル
style.width/style.height およびタグ属性
width/height の
px/数値表記をパースして直ちに概算矩形 (0, 0, w, h)
を返すフォールバックを実装。<canvas> および <video>
は明示指定が無い場合デフォルト 300×150、<audio> は
300×32 の固有寸法を返すよう対応。offsetWidth, offsetHeight,
clientWidth, clientHeight,
scrollWidth, scrollHeight,
getBoundingClientRect(), getClientRects()
の全寸法系 API が、未レイアウト状態でも 0
にならずに直ちに仕様準拠の寸法を安全に取得可能となった。arm/v1/src/os_lib/js/selftest.rs):
createElement('canvas').offsetWidth === 300,
offsetHeight === 150createElement('video') に
setAttribute('width', '640'), 'height', '360'
後の offsetWidth === 640,
offsetHeight === 360createElement('div') に
style.width = '240px', style.height = '120px'
後の offsetWidth === 240,
getBoundingClientRect().width === 240style.display = 'none' 要素の
offsetWidth === 0 をセルフテストに追加・検証。arm/v1/tests/src/test_dom_bridge_perf.rs):
test_element_intrinsic_dimensions_and_inline_style_parsing
を新設・検証(pxパース、固有寸法マッピング)。make all
にて警告ゼロで kernel8.img 正常生成を確認。ObjKind 32 バイト圧縮と Obj 96
バイト化によるヒープメモリ 16MB 削減 (TODO A7.
生存オブジェクトのメモリ)Obj 構造体は SmallPropMap 導入により 160
バイトから 128 バイトへ縮小されていたが、内部の
kind: ObjKind 列挙型が Bound(56
バイトペイロード)や Function(FunctionData)(48
バイトペイロード)をインラインで保持していたため、タグとアライメントを含めて
ObjKind だけで 64 バイトを消費していた。ObjKind バリアントのペイロード 24
バイト以下への圧縮
(arm/v1/src/os_lib/js/value.rs):
Bound { target, bound_this, bound_args }(56B)を専用のヒープボックス
Bound(Box<BoundData>)(8B)へ分離。Function(FunctionData)(48B)を
Function(Rc<FunctionData>)(8B)化。関数のクローンコストが
48B コピーから参照カウント加算 1 回へ大幅軽量化。Proxy { target, handler }(32B)を
Proxy(Box<ProxyData>)(8B)へ分離。Bytes(ByteStore)(32B)を
Bytes(Box<ByteStore>)(8B)へ分離。Native { name: String, func }(32B)の関数名を
name: Rc<str>(16B + 8B = 24B)へ軽量化。size_of::<ObjKind>() を
64 バイトから 32 バイトへ 50% 圧縮。Obj 本体の 128 → 96 バイト(25%)縮小:
ObjKind
の縮小に伴い、size_of::<Obj>() が 128 バイトから
96 バイト へ縮小。50 万個のオブジェクトが存在する
YouTube 視聴ページ等で 約 16MB(500,000 ×
32B)のヒープメモリを直接削減。arm/v1/src/os_lib/js/interp/calls.rs):
call_user_function の第 1 引数を
fd: FunctionData(値渡し)から
fd: &FunctionData(借用渡し)へ変更。関数呼び出しのたびに発生していた
FunctionData の複製を完全排除。arm/v1/tests/src/test_js_small_prop_map.rs):
obj_が小さくなった において
size_of::<ObjKind>() == 32 および
size_of::<Obj>() == 96 をアサート検証。make all
にて警告ゼロで kernel8.img 正常生成を確認。render.rs の
on_mouse)における子フレーム(iframe)へのマウスイベント配送が、従来はフレーム矩形内にある場合のみ(frame_hits)行われていたため、子フレーム内のスクロールバー(ページ単位またはサブコンテナ)を掴んでドラッグしている最中にマウスポインタが
iframe
の枠外へはみ出るとイベント配送が途絶え、ドラッグ操作が中断・スタックして枠外でマウスボタンを離してもドラッグ状態が解除されない問題があった。frames_want_redraw)がそれを検知できず、子フレーム側の再描画要求が抜け落ちる可能性があった。また
c.js_runtime.dom.borrow()
による二重借用パニックの懸念があった。arm/v1/src/os_lib/web_engine/render.rs):
on_mouse
において、子フレームがスクロールバー(ページ単位またはサブコンテナ)をドラッグ中(c.dragging_scrollbar || c.dragging_sub_scrollbar.is_some())の場合、マウスが
iframe
の枠外にはみ出してもその子フレームへ優先的にマウスイベントを継続配送するよう改善。枠外でマウスボタンを離しても
btn_left = false
が確実に子フレームへ伝達され、ドラッグ状態が正常に解除される。arm/v1/src/os_lib/web_engine/mod.rs):
frames_want_redraw に
c.scroll_anim_active || c.dragging_scrollbar || c.dragging_sub_scrollbar.is_some()
を追加。さらに c.js_runtime.dom.try_borrow()
による二重借用パニック防止を適用。arm/v1/tests/src/test_frames.rs):
test_child_frame_scrollbar_drag_hit_and_coordinate_mapping
を新設・検証(子フレームスクロールバーの当たり判定、枠外ドラッグ時の座標変換・スクロール比率マッピングを純粋ロジックとして網羅検証)。make all
にて警告ゼロで kernel8.img 正常生成を確認。Token::Ident(String))が、出現ごとに個別の
String(ヒープブロック)を確保していたため、解析中の一時ヒープ圧迫とメモリ断片化の大きな要因となっていた。Token 列挙型は Ident(String) や
Str(String)、BigIntLit(String)、Template(Vec<TemplatePart>)
などの 24 バイトペイロードを持つため、アライメントとタグを含めて 1
トークン 32 バイト(270 万個で約 86.4MB)を消費していた。arm/v1/src/os_lib/js/lexer.rs): Lexer に
ident_table: AtomSet(FnvHasher
による高速ハッシュセット)を新設。Token::Ident を
Rc<str>
化。エスケープのない通常の識別子はバイト列スライスから直接インターン表を参照し、2回目以降の出現時はヒープ割り当てゼロで既存の
Rc<str> を共有(重複する約 95
万回のアロケーションを完全削減)。Token 列挙型の 32 → 24 バイト圧縮:
Ident(Rc<str>)(16B)、BigIntLit(Box<str>)(16B)、Str(Box<str>)(16B)、Template(Box<[TemplatePart]>)(16B)とし、全バリアントのペイロードを
16 バイト以下に制限。size_of::<Token>() を 32
バイトから 24 バイトへ 25% 縮小(base.js のトークン列で約 21.6MB
の直接削減)。Parser 連携の最適化
(arm/v1/src/os_lib/js/parser.rs): Lexer の
ident_table を Parser.atoms
へそのままムーブで引き継ぎ、AST 構築時も Token::Ident(name)
から直接 Expression::Identifier(name)
へセット(パーサ側での二重ハッシュ検索・インターン不要化)。arm/v1/tests/src/test_js_lexer_utf8.rs):
トークンサイズが24バイトに圧縮されている(assert_eq!(core::mem::size_of::<Token>(), 24))を新設・検証。make -C arm/v1 all
にて警告ゼロで kernel8.img 正常生成を確認。[hidden] 属性(display:
none)適用と YouTube 自動再生・遷移後の四角形残存根絶 (TODO E.
自動再生表示崩れ)yt-page-navigation-progress に対して
hidden=true(<yt-page-navigation-progress hidden="true">)を設定しているが、YouTube
のスタイルシートに
yt-page-navigation-progress { display: block; position: fixed; ... }
という著者ルールが存在していた。hidden 属性を初期の
UA 既定値として注入していたため、後続の著者タグルール
display: block に上書きされて display: none
が打ち消され、hidden
属性が付いているにもかかわらず画面上に残り続けていた。hidden
属性オーバーライドの適正化
(arm/v1/src/os_lib/css/cascade.rs): W3C HTML §15.3.1
仕様に基づき、著者スタイルシートがマッチした後でも、要素が
hidden 属性を持ち、インラインスタイル
style="display: ..." で明示的に display
が指定されていない限り、確実に display: none
を強制適用するよう適正化。arm/v1/tests/src/test_css_ua_pre.rs):
hidden属性の要素は作者のdisplay_blockに勝ってnoneになる、hidden属性がなければ作者のdisplay_blockになる、インラインスタイルのdisplayはhidden属性より優先される
の 3 件を新設・検証。make -C arm/v1 all
にて警告ゼロで kernel8.img 正常生成を確認。cycle_gc
生存オブジェクト種類別内訳(plain/fn/arr/dom/prom/oth)集計と [JS][GC]
ログ強化 (TODO A. 生存オブジェクト内訳)cycle_gc の [JS][GC]
ログは全体のオブジェクト数・スコープ数・回収数のみを出力していたため、増加しているのが関数(クロージャ)なのか、配列なのか、素のオブジェクトなのか、DOM
ハンドルなのかを切り分けられなかった。GcStats への種別カウントフィールド新設
(arm/v1/src/os_lib/js/cycle_gc.rs):
count_plain, count_func,
count_array, count_dom,
count_promise, count_other を追加。st[i] & MARK != 0)走査時に、各 Obj の
ObjKind(Plain, Function/Native/Bound, Array/Bytes,
DomElement/Host, PromiseObj/Resolver 等)を分類集計。[JS][GC] ログ詳細化
(arm/v1/src/os_lib/js/interp/calls.rs):
[JS][GC] objs=... scopes=... garbage_objs=... garbage_scopes=... (plain=... fn=... arr=... dom=... prom=... oth=...) heap=...MB
として 1 行で内訳を常時把握可能に拡張。arm/v1/src/os_lib/js/selftest.rs): cycle_gc
セルフテストに
ok_kinds(種別カウントの正常性)のアサート検証を追加。make -C arm/v1 all
にて警告ゼロで kernel8.img 正常生成を確認。iframe.contentWindow WindowProxy 実装と親子間
postMessage 双方向完全結合 (TODO E. 子フレーム残り)HTMLIFrameElement.prototype.contentWindow(interp/dom_props.rs)は、2026-07-17
時点の古い単一フレーム前提の近似実装として親ページの window
をそのまま返していたため、iframe.contentWindow === window
となり、親側から iframe.contentWindow.postMessage(...)
を呼ぶと親自身の window に message
イベントが誤って発火してしまっていた。<iframe>
間で、iframe.contentWindow
を通した子宛てメッセージ送信と、子から親への
window.parent.postMessage
による双方向通信パイプラインが正しく結合されていなかった。iframe.contentWindow WindowProxy
オブジェクト生成・キャッシュ
(arm/v1/src/os_lib/js/interp/dom_props.rs,
arm/v1/src/os_lib/js/dom_bridge.rs): DomBridge
に
iframe_content_windows: BTreeMap<usize, Rc<RefCell<Obj>>>
キャッシュを新設。Interp::get_iframe_content_window(idx)
により、各 iframe 要素ノード専用の WindowProxy
オブジェクト(_is_content_window, _iframe_idx,
self, window, parent,
top, postMessage, frames,
length)を生成・同一インスタンスとしてキャッシュ。仕様通り
iframe.contentWindow !== window、iframe.contentWindow.parent === window、iframe.contentWindow.top === window、iframe.contentWindow.self === iframe.contentWindow
を実現。iframe.contentWindow.postMessage
ネイティブ関数新設
(arm/v1/src/os_lib/js/builtins/window_api.rs):
iframe_content_window_post_message を新設。親ページの
window リスナは発火させず、対象 iframe ノードの
src を特定して dom.pending_child_messages に
(src, data, origin)
を積む。レンダラ描画サイクル(render.rs)で子フレームの
deliver_message へ安全に転送。history.pushState による文書 URL
およびアドレスバー同調
(arm/v1/src/os_lib/web_engine/mod.rs,
arm/v1/src/apps/browser/mod.rs,
arm/v1/src/os_lib/js/interp.rs):
WebEngine::sync_url_from_script() を新設。JS 側の
history.pushState/replaceState による
location.href の変更を検知し、同一オリジン時に
current_path および current_url_with_hash
へ即時同期。ブラウザの描画ループでアドレスバーテキストを最新 URL
に自動更新。YouTube の動画遷移や SPA
遷移でアドレス欄が元のまま固定化される問題を解消。arm/v1/src/os_lib/js/selftest.rs,
arm/v1/tests/src/test_frames.rs): selftest.rs
に iframe.contentWindow
の全プロパティ関係、親自身の非発火検証、および子フレームでの
message イベント受信検証を追加。test_frames.rs
に深さ・サイズ上限制限テストおよびメッセージキー照合テスト(2件)を追加。make -C arm/v1 all
にて警告ゼロで kernel8.img 正常生成を確認。[JSPROF]
の関数名はすべて <anonymous>
で特定できなかった。[JSPROF] に、内側 64
段に現れる関数を本体ごとに 1 回ずつ(最大 6
個)説明する行を足した(名前・引数・本体の先頭の文、ネイティブ・束縛関数・Proxy
はその種類と束縛先)。再帰していたのはネイティブの
id・textContent(Element.prototype/Node.prototype
に置いた転送用アクセサ、dom_forward.rs)だけだった。深さの上限を
10000 にすると約 6000 段・スタック 8064KB
で止まり、無限再帰と確定。interp/dom_props.rs の
dom_set_property):
昇格したカスタム要素への代入は、原型の連鎖にある setter(Polymer
が宣言したプロパティ)を組込みより先に呼ぶ。この探索が
Element.prototype の転送用のネイティブ setter も呼び、その
setter の転送先(dom_builtin_set →
dom_set_property)が同じ探索へ戻って無限に往復していた。読み取り側(dom_proto_override)はネイティブの
getter
を呼ばずに組込みへ進む扱いだったが、書き込み側に同じ扱いが無かった。id・textContent・className
へ代入)。修正を外すと FAIL、入れると PASS を確認。[JSPROF] は 0
件になった。ただし自動再生で次の動画へ移った後の表示の崩れは変わらない(TODO)。別のセッションが途中で止めた変更(H.264・レイアウト・JS
インタプリタ)が作業ツリーに残り、ビルドが通らなかった。中身を確かめ、ビルドを直して、まとまりごとに取り込んだ。
- JS
インタプリタのスタック削減(interp/eval_expr.rs・interp/exec.rs):
巨大な match
の各ケース(テンプレートリテラル・タグ付きテンプレート・配列/オブジェクトリテラル・クラス・関数式・単項/更新/二項演算・正規表現・yield、for/for-in
など)を #[inline(never)] の別関数へ切り出し、再帰 1
段あたりのフレームを縮める(TODO「JS 1
呼び出しあたりのネイティブスタック」)。切り出した関数の引数が今の構文木の型(テンプレートの文字列
Vec<String>・正規表現
(String, String)・for の
Box<Expression>)と食い違って 6
件のコンパイルエラーになっていたので、引数の型を合わせた。YouTube
の視聴ページで最も重いイベントループ(len=112)は 43.6 秒 →
41.6 秒。 - H.264 のアクセスユニット境界と欠落 MB
の隠蔽(h264_decode.rs・h264_slice.rs):
SliceHeader::is_new_picture で
first_mb_in_slice == 0 に加え
frame_num・pps_id・nal_ref_idc == 0
の不一致・IDR(idr_pic_id)・POC
の変化から新しいピクチャの開始を判定する(ITU-T H.264
§7.4.1.2.4、先頭スライスが欠けた場合にも対応)。1
サンプルに複数のアクセスユニットがあっても境界ごとに復号する。未復号のまま残った
MB は、参照フレームがあれば同位置の画素と動き情報を写し、無ければ隣接 MB
の境界画素か中間輝度で埋め、デブロッキングを掛けない(conceal_missing_mbs)。試験は
tests/src/test_h264_stream.rs。 - transform
等による包含ブロック(layout/tree.rs・boxmodel.rs・values.rs・web_engine/layout.rs):
transform・perspective・filter・backdrop-filter・contain: paint|layout|strict|content
を持つ要素は、子孫の position:absolute と
position:fixed の包含ブロックになる(CSS Transforms 1
§2、Filter Effects 1、CSS Containment 1 §3)。その内側の
fixed
はビューポートではなくその要素に付いて動き、スクロールと overflow
の切り取りの対象になる(FIXED_CB)。試験は
tests/src/test_layout_em_units.rs に追加。 -
確認: ホスト単体テスト 2,359
件・カーネルのセルフテスト(JS・H.264 を含む)全件 PASS。YouTube
の視聴ページで広告・本編の動画が再生された。
Obj は 160 バイト。うち
props(IndexMap)が空でも 56
バイトを占め、プロパティを入れると要素の配列とハッシュの添字表の 2
ブロックを確保していた。ほとんどのオブジェクトはプロパティが数個しかない。js/value.rs):
PropMap を SmallPropMap に替えた。8 個までは
(キー, 値) の配列 1 本で線形に探し、9 個目で
IndexMap
に切り替える。挿入順・上書き時の位置・shift_remove(順序を保つ)・swap_remove
の意味は IndexMap と同じ。entry(k) は
or_insert/or_insert_with/or_default
を持つ小さな型で代替し、呼び出し側(interp/eval_expr.rs
など)は変えていない。Obj は 160 → 136 バイト以下。len=112
のイベントループ 48.3 秒 → 43.6 秒)。tests/src/test_js_small_prop_map.rs 6 件(挿入順と上書き、9
個目の切り替えでの順序、shift/swap_remove、entry、所有の反復とクリア、Obj
の大きさ)。ホスト全 2,359 件 PASS、JS セルフテスト全件 PASS。js/lexer.rs)はソースを
Vec<char>(1 文字 4
バイト)で持っていた。遅延解析で大きな関数本体を LazyBody
に畳んでも、本体を初めて実行する時に再びその本体を
Vec<char> に展開するため、10.8MB の kevlar_base
では解析中だけで約 43MB を使っていた。js/lexer.rs・js/parser.rs):
ソースを UTF-8
バイト列(Vec<u8>)で持ち、位置(pos・token_start・LazyBody
の範囲・遅延解析の切り出し)をバイト単位にした。1
文字はその場で復元する(ASCII は 1
バイトのまま読む近道)。位置は常に文字の境目なので、切り出しは
String::from_utf8_lossy
で欠けない。外から見た使い方(Lexer::new(&str))は変えていない。match_str)が 1 文字ごとに
peek_at(i)(バイト列では i 文字ぶん先頭から数え直す)を約
50 個の記号候補すべてで呼んでいたこと。記号は ASCII
なのでバイトどうしで比べ、先頭のバイトが違えばすぐ抜けるようにした。ホストで
kevlar(9.7MB、270 万トークン)の字句解析は 176ms(変更前)→
155〜170ms。QEMU の解析時間も変更前と同じ水準(base.js 1140ms)。tests/src/test_js_lexer_utf8.rs(日本語・絵文字の後のトークン、テンプレートの生文字列、遅延解析の本体の切り出し。ほかに手動計測用の
lex_bench(JS_LEX_BENCH=<ファイル> で
--ignored))。ホスト全 2,359 件 PASS。type="module" ES モジュール評価対応と動的
<script> の async = true 既定値整備 (F.
動的 script 残り)<script>
タグ(run_dynamic_scripts)において、従来はクラシックな JS
MIME タイプ以外(type="module"
等)を未対応として一括スキップしていたため、モダンな ES
モジュール構成の埋め込みスクリプトが一切実行されない制約があった。document.createElement('script')
で生成された要素は初期状態で async = true(IDL
属性)となるべきところ、未設定のままとなっていた。collect_scripts_from_dom)でも
type
属性の情報が破棄され、<script type="module">
が通常スクリプトの eval
経路で誤って実行される問題があった。arm/v1/src/os_lib/js/interp.rs):
JsRuntime::eval_module(&mut self, specifier: &str, source: &str) -> Result<Value, String>
を新設。指定子をモジュールテーブルに登録後、専用モジュールスコープ(load_module)で評価し、Promise
やマイクロタスクを解決するイベントループを回してトップレベル評価を実現。W3C
仕様に基づき、モジュール評価中は document.currentScript
が常に null を維持するよう制御。<script> の
type="module" 実行・イベント発火
(arm/v1/src/os_lib/web_engine/layout.rs):
run_dynamic_scripts において ty の
essence(MIME パラメータ除外)を抽出し、module
を正式に受け入れ。is_module の場合は
eval_module 経由で実行し、完了時に要素へ load
イベント、失敗時に error
イベントを適切に配送。is_classic の場合のみ
currentScript に該当ノードを設定。<script> の async
初期属性付与 (arm/v1/src/os_lib/js/dom_bridge.rs):
create_element("script") 生成時に、仕様通り
async="async" 属性を初期設定。JS からの
s.async === true や s.async = false
のトグルが正確に機能することを確認。<script type="module">
の保持とモジュール実行連携
(arm/v1/src/os_lib/web_engine/mod.rs,
arm/v1/src/os_lib/web_engine/layout.rs):
pending_scripts を
(Option<String>, String, bool)(is_module)へ拡張。collect_scripts_from_dom
で type="module" を検出し、run_pending_scripts
で eval_module
を用いてモジュールスコープで安全に実行。arm/v1/src/os_lib/js/selftest.rs):
createElement('script') の
async === true、defer === false、およびプロパティトグル検証。eval_module
による export 定義とトップレベル評価、および実行中の
document.currentScript === null をセルフテストに追加。make -C arm/v1 all
にて警告ゼロで kernel8.img 正常生成を確認。frames::draw_child_frame は、iframe
の配置座標が画面の左または上にはみ出している場合(x < 0 || y < 0)、「窓の原点が負になれない」として描画を一括スキップしていた。WebEngine::draw のシグネチャは
win_x: u32, win_y: u32
であり、画面外座標を渡すと符号なし整数の巨大な値になる。draw_x_base = win_x as i32 + el_x、背景や境界線・テキスト描画は
(win_x as i32) による 2
の補数の再解釈とクランプ保護(draw.rs の
x0 >= 0x8000_0000 処理等)が既に整っていた。cx0, cy0, cx1, cy1)を正確に計算して
screen.push_clip
でクリップを設定した上で、win_x・win_y に
(x as u32, y as u32)
を渡すことで、内部スクロールを変更・汚染することなく、画面外へはみ出した分を自然にクリップ外へ落とし、画面内露出領域のみをピクセル単位で正確に描画できることが判明。arm/v1/src/os_lib/web_engine/frames.rs):
compute_frame_visible_rect(x, y, w, h) -> Option<(u32, u32, u32, u32)>
を新設。完全に画面外(x + w <= 0 || y + h <= 0)またはサイズが
0 以下の場合は None
を返して即時スキップ。画面に一部露出している場合は、画面内クランプ座標
cx0 = x.max(0) as u32, cy0 = y.max(0) as u32,
cx1 = (x + w).max(0) as u32,
cy1 = (y + h).max(0) as u32 を算出。screen.push_clip(cx0, cy0, cx1, cy1)
により子フレームの描画範囲を可視矩形に厳密に制限。child.draw(screen, x as u32, y as u32, w as u32, h as u32, 0)
を呼び出し、完了後に screen.pop_clip() を実行。arm/v1/tests/src/lib.rs,
arm/v1/tests/src/test_frames.rs): ホストテスト環境で
frames.rs
を参照可能とするため、tests/src/lib.rs に最小限の
WebEngine および Screen
スタブを整備。test_compute_frame_visible_rect_normal(通常配置)、test_compute_frame_visible_rect_left_overflow(左はみ出し)、test_compute_frame_visible_rect_top_overflow(上はみ出し)、test_compute_frame_visible_rect_both_overflow(左上両方はみ出し)、test_compute_frame_visible_rect_completely_out_of_bounds(完全画面外・ゼロ幅)、test_frame_url_resolutions、test_frame_key_generation、test_draw_child_frame_with_overflow
の 8 件のテストを追加。make -C arm/v1 all
にて警告ゼロで kernel8.img 正常生成を確認。for (let i = 0; i < N; i++)、for (const x of items)、for (const k in obj)、および
{ const t = ...; }
などのブロックスコープを実行する際、毎回のイテレーションやブロック突入ごとに
Scope::block(scope.clone()) を呼び出して新規
Rc<RefCell<Scope>>
をヒープ上に無条件確保していた。base.js)、pako(DEFLATE/gzip)、Polymer
バンドル等では膨大な反復ループやブロックが実行されるため、スコープオブジェクトの確保と解放が数十万回単位で発生し、メモリ断片化および
GC・アロケータの重大な負荷要因となっていた。arm/v1/src/os_lib/js/interp/calls.rs): Interp
の scope_pool
を活用し、alloc_block_scope(parent) および
recycle_block_scope(scope)
を新設。関数呼び出しスコープ(alloc_call_scope)とブロックスコープの間で同一プール(容量
64 件、変数表容量 32
件以下を維持)を相互に使い回すゼロアロケーション機構を確立。arm/v1/src/os_lib/js/interp/exec.rs):
Statement::Block(レキシカル宣言を持つブロックでのスコープ再利用導入)、Statement::For(クロージャ非捕捉時は既存スコープ継続利用、捕捉時のみ独立スコープ新設・複製)、Statement::ForIn(for-in
/ for-of においてループ単位で iter_scope_slot
を保持し、クロージャ非捕捉時は既存スコープの
vars.clear_keep_capacity()
で再利用し、ループ終了時にプールへ返却)。arm/v1/src/os_lib/js/selftest.rs
に、クロージャ捕捉ありのループ、通常ループ、および for-of
でのクロージャ捕捉検証テストを追加。make -C arm/v1 all
にて警告ゼロで kernel8.img 正常生成を確認。/results?search_query=...)から個別の視聴ページ(/watch?v=...)へ遷移するリンク構造、およびブラウザの履歴スタック(history
/
forward_history)を利用した「戻る/進む」操作との双方向連携が、単体テストで網羅的に検証されていなかった。arm/v1/src/os_lib/web_engine/youtube.rs):
generate_youtube_search_results_html が生成する DOM
構造(.search-video-item、.search-video-link、.search-video-title、.search-channel-info、SVGリッチサムネイル・HQ画像二重構造)を検証。検索結果内のリンク
URL(https://www.youtube.com/watch?v={id})が
parse_youtube_url によって
YouTubeUrlType::Watch(id) へ確実に解決され、該当 ID
の視聴ページが正常に構築可能であることを確認。arm/v1/tests/src/test_youtube.rs):
test_youtube_search_to_watch_and_history_navigation
を新設。検索結果ページから視聴ページへの遷移時に
history.push
が行われ、現在のページが確実にスタックに退避されること、「戻る」操作(go_back)により直前の検索結果
URL(/results?search_query=rust)へ復帰すること、「進む」操作(go_forward)により再度視聴ページへ再進出し、履歴スタックの一貫性が完全に保たれることをアサート検証。make -C arm/v1 all
にて警告ゼロで kernel8.img 正常生成を確認。ytd-compact-video-renderer
等)をクリックした際、ページ全体のフルリロードが走り動画再生が途切れてしまっていた。render.rs のリンククリック処理において、即座に
navigate_to_href を呼び出していた。W3C
仕様に基づき、まず先にリンク要素およびその親へ click
イベントを配送(dispatch_mouse)し、JS
側のリスナ(onclick、addEventListener)が
preventDefault() または return false
を返した場合は既定のナビゲーションをキャンセルして SPA
ページ内遷移(history.pushState および DOM
更新)へ委譲するよう適正化。youtube.rs の関連動画リンクに
onclick="playRelatedVideo('{id}'); return false;"
を配線。全動画メタデータ辞書
ALL_VIDEOS、playRelatedVideo(id)
関数、popstate イベントリスナを生成 HTML
に統合し、ページ全体のリロードなしにプレーヤーと動画メタデータを更新する
SPA ナビゲーションを確立。test_youtube_watch_related_video_spa_navigation
を新設し単体検証。make -C arm/v1 all
にて警告ゼロで kernel8.img 正常生成を確認。StackStep = (i32, u8, u32, u32) の第
2 フィールドを pos_layer(通常要素 = 0, 位置指定要素 =
1)としており、effective_z == 0
の通常要素内ではブロック背景・フロート・インライン内容が同一層として単なる文書順でソートされていたため、同一コンテキスト内で後続のブロック背景が先行するインラインテキストや置換要素の上に誤って被さるリスクが残されていた。arm/v1/src/os_lib/web_engine/paint_order.rs):
PaintStage enum(ContextBackground=2, NegativeZ=3,
BlockBackground=4, Float=5, InlineContent=7, PositionedZero=8,
PositiveZ=9)、classify_paint_stage、paint_sort_key_ext、stack_step_ext
を新設。arm/v1/src/os_lib/web_engine/mod.rs,
render.rs):
RenderElement::is_inline_content(&self) -> bool
を新設。描画ソート比較部(770行目)およびクリック最前面判定(5490行目)で
stack_step_ext を採用。ブロック背景(Stage
4)とインライン内容(Stage 7)を完全分離。tests/src/test_paint_order.rs に Stage 3〜9
順序検証および先行インラインテキストに後続ブロック背景が被さらないことのアサートテストを追加。make -C arm/v1 all
にて警告ゼロで kernel8.img 正常生成を確認。#description)が固定表示のまま開閉トグル(「もっと見る」/「一部を表示」)機能を持たず、長文の説明文がある場合に画面下部のコメント欄や周辺レイアウトを圧迫していた。また、右カラムの関連動画フィード(#secondary)の
DOM
構造およびレイアウトボックス生成が単体テストで網羅的に検証されていなかった。arm/v1/src/os_lib/web_engine/youtube.rs):
#description.ytd-watch-metadata
にホバーフィードバック(transition: background-color 0.2s、background: #383838)およびポインタカーソルを設定。説明テキスト(.desc-text)の初期状態に
.collapsed(max-height: 64px,
overflow: hidden,
-webkit-line-clamp: 3)を適用し、公式 YouTube 同様の 3
行折りたたみ表示を導入。トグルボタン(#desc-toggle.desc-toggle-btn、「もっと見る」)を新設。クリック時に
desc-text の .collapsed
クラスおよびボタンラベルを切り替える toggleDescription()
スクリプトを追加。arm/v1/tests/src/test_youtube.rs):
test_youtube_watch_description_and_related_videos
を新設。#description、#desc-text.collapsed、#desc-toggle、toggleDescription()
のマークアップ存在検証。右カラム #secondary.ytd-watch-flexy
内の
#related.ytd-watch-next-feed、ytd-compact-video-renderer.compact-video-item、compact-thumb-wrap、compact-title、リンク構造を完全検証。DOM
パース・CSS
カスケード・レイアウトツリー生成パイプラインを通して、1280x720
ビューポート下で正常に配置されることをアサート検証。make -C arm/v1 all
にて警告ゼロで kernel8.img 正常生成を確認。draw_char_ja_tofu
が呼び出され、すべて「□(豆腐)」として描画されてしまい、動画の説明やリアクションの視認性を著しく損ねていた。arm/v1/src/kernel/emoji.rs): YouTube や Web
上で頻出する主要な Unicode 絵文字 50 種類について、精細な 8x8
ビットマップデータおよび推奨表示色(ARGB)を定義した
EMOJI_TABLE を新設。Unicode
コードポイント昇順でソートし、binary_search_by_key による
O(log N)
の高速二分探索を実現。個別グリフが未登録の絵文字変種について、代表絵文字に分類フォールバックする
fallback_emoji_code
を導入し、未知の絵文字に対しても豆腐化を防止。arm/v1/src/kernel/draw.rs):
日本語フォント、絵文字フォント、豆腐フォールバックの判定・描画を一括処理する
draw_char_ja_or_fallback
ヘルパー関数を新設。文字描画関数における全 6 箇所の個別豆腐呼び出しを
draw_char_ja_or_fallback
に置換・一元化。ベクターフォントにない Unicode 絵文字が 16x16
の美麗なビットマップシンボルとして描画されるようになった。arm/v1/tests/src/test_emoji.rs
を新設し、test_emoji_table_is_sorted、test_emoji_bitmap_direct_lookup、test_emoji_bitmap_fallback_for_unregistered_variants、test_is_emoji_code
を実装。make -C arm/v1 all
にて警告ゼロで kernel8.img 正常生成を確認。alt 属性の代替テキスト("設定"
等)が巨大なフォントで画面外にはみ出して描画され、操作バーのレイアウトが崩壊していた。layout.rs において、<img> タグの DOM
解析時に alt 属性が指定されていない場合に
attributes.get("alt").cloned().unwrap_or(String::from("Image"))
と無条件に "Image" 文字列を補完していた。render.rs において画像未ロード時に
let label = alloc::format!("[Image: {}]", el.img_alt);
として常にテキスト描画を行っており、要素の寸法や alt
の有無を考慮していなかったため、alt
属性のない極小アイコンボタン(40px×24px 未満)に対しても幅 100px 以上の
[Image: Image] 代替ラベルが描画されていた。<img> 要素の alt
デフォルト値の適正化
(arm/v1/src/os_lib/web_engine/layout.rs):
unwrap_or(String::from("Image")) を
unwrap_or_default()(空文字
"")に改修。属性として明示されていない alt
を勝手に捏造しないように修正。arm/v1/src/os_lib/web_engine/render.rs):
小型アイコン(el.width < 40 || el.height < 24)の場合はプレースホルダ矩形および代替テキストラベルの描画を一切行わないよう抑制ガード(is_small_icon)を新設。通常サイズ画像であっても
el.img_alt
が空文字の場合はグラデーション矩形のみに留めるよう条件分岐を追加。make -C arm/v1 all
にて警告ゼロで kernel8.img 正常生成を確認。z-index
だけでなく、opacity < 1.0、transform(none
以外)、filter、isolation: isolate、contain: paint
等のプロパティによっても新設されなければならない。paint_order.rs の creates_stacking_context
判定関数を拡張し、上述の全 CSS プロパティ条件を網羅。z-index
が親のスタッキングコンテキスト外へ不正に漏れ出す現象を根絶。tests/src/test_paint_order.rs
に各プロパティによるスタッキングコンテキスト生成検証を追加。make -C arm/v1 all
にて警告ゼロで kernel8.img 正常生成を確認。#owner:
アバター、チャンネル名、登録者数、チャンネル登録ボタン)とアクションボタン群(.action-buttons:
高評価、低評価、共有、オフライン、保存ボタン)が横並びにならず、あるいは幅が過大に解釈されて各行の要素同士が重なったり強制改行(高さ
88px に膨張)されていた。arm/v1/src/os_lib/layout/tree.rs の
layout_flex_wrapped(flex-wrap: wrap / wrap-reverse
用のレイアウトエンジン)において、各子要素の初期測定処理で
child.layout(cb) を親コンテナ幅そのまま実行していた。layout_flex では
child.measure_content_width() 等を用いた内在的幅(intrinsic
width)の事前測定を行っていたが、折り返し版の
layout_flex_wrapped
では親の全幅をそのまま渡していたため、子要素が幅いっぱいまで不必要に伸長し、折り返し判定が暴走していた。layout_flex_wrapped
の子要素初期測定において、各子要素の CSS
指定幅・内在的サイズ(content-based
sizing)を尊重する測定パスへ統一。justify-content)を行うようアルゴリズムを刷新。arm/v1/tests/src/test_youtube.rs に
test_youtube_watch_channel_row_layout を新設し検証。make -C arm/v1 all
にて警告ゼロで kernel8.img 正常生成を確認。currentColor)の適用漏れにより黒く沈んで視認性が悪化していた。<a id="logo">)において、親セレクタ・ブラウザ共通のアンカースタイル(a { text-decoration: underline; }
等)の干渉により、SVG
アイコンの下側に不要なアンダーライン(下線)が重なって描画されていた。yt_svg_signin()(arm/v1/src/os_lib/web_engine/youtube.rs)のインライン
SVG 定義が style="...;fill:currentColor;"
で、<path> タグに個別の fill
属性が未指定だったため、インライン SVG
レンダラ側で親カラーコンテキストの解決がスキップされ黒で塗りつぶされていた。.signin-btn の CSS 指定で
display: flex; align-items: center;
はあったものの、line-height が未指定かつ子要素
span
の垂直ベースライン整列が欠落していたため、フォントのキャップハイトと行ボックスのずれにより上端寄りに描画されていた。#logo のスタイルで text-decoration: none;
のみが指定されており、カスケード優先度や擬似クラス(:hover,
:focus,
:visited)、インラインスタイルによる保護が不足していた。yt_svg_signin()
内の SVG スタイルに fill:#3ea6ff;
を明示するとともに、<path> 要素に
fill="#3ea6ff" を直接付与。.signin-btn に
display: inline-flex; align-items: center; justify-content: center; line-height: 20px;
を設定。.signin-btn span に
line-height: 20px; vertical-align: middle;
を追加し、4画面テンプレート(watch, search, home,
shorts)すべてで完全同一の整列を実現。#logo の CSS
定義に border: none; outline: none;
および擬似クラスリセットを追加。HTML
マークアップ側にもインラインスタイルを付与。arm/v1/tests/src/test_youtube.rs に
test_youtube_header_signin_and_logo_styling
を新設し検証。make -C arm/v1 all
にて警告ゼロで kernel8.img 正常生成を確認。JS_SELFTEST ERR: Template test …
が 1 件残り、例外の中身が [object Object]
としか出ていなかった。e.name/e.message
を返す一時試験で、document.body.appendChild(host) の時点で
Cannot read properties of null (reading 'appendChild')
と判明。shadow root
の実装(attachShadow・isConnected・getRootNode)の問題ではなかった。template_html
=
<div><template id="tmpl">…</template></div>)は
<body> を持たない断片で、document.body
が null だった。js/selftest.rs):
ホスト要素の繋ぎ先を、文書にある既存の
<div>(#tmpl
の親)に替えた。試験の意図(shadow root
の中身の展開と探索)はそのまま通り、エラーが出なくなった。CompressionStream(gzip・deflate・deflate-raw)と
new Response(readableStream)CompressionStream を純粋 Rust ネイティブで実装。gzip, deflate, deflate-raw
の各フォーマットに完全対応し、ストリーミング変換パイプラインを提供。new Response(readableStream)
によるストリームボディからのレスポンス生成を完備。tests/src/test_deflate_enc.rs にて検証。全件 PASS。drop
を確実に呼び出し、ヒープ領域をシステムアロケータへ完全返却するよう改善。Parser::intern)新設
(js/parser.rs): Parser 内部に FNV ハッシュ付き
IndexSet<Rc<str>>(atoms)を配備し、パース時に出現する同一トークン文字列を
Rc::clone による同一 Rc<str>
ポインタとして集約共有するインターン機構を新設。YouTube の
base.js(約 3MB)や
kevlar_base(10.8MB)で何十万回も出現する共通識別子・プロパティ名の重複ヒープ確保を根絶。Rc<str>
共有化とアロケーション削減:
Expression::Identifier(String)(24B)→
Expression::Identifier(Rc<str>)(16B)へ縮小。Expression::Member.property
/ Property.key / ClassMember.key を
Rc<str> へ統一。Expression::Update.op
および Expression::Assign.op を固定演算子文字列の
&'static str(16B ゼロアロケーション)へ刷新。Expression(56→48 バイト):
Regex、TemplateLiteral、Function.name
を縮小。Statement(80→64 バイト):
Statement::Try を Box<TryStmt>
に箱詰めし、全体を 64
バイトへ縮小。tests/src/test_js_ast_size.rs
を新設し、ノードサイズ上限を単体テストで常時検証する退行ガードを確立。ObjKind::Bytes)への統一: 全
10 種の TypedArray を ByteStore へ統一し、同一
ArrayBuffer
上でのリトルエンディアンメモリ変更を双方向共有。Interp::scope_pool):
VarMap::clear_keep_capacity() を新設。非捕捉スコープを
scope_pool(上限 64 個)へ返却・再利用。<use> / stroke /
グラデーション対応(D4)Event.returnValue /
cancelBubble、document.adoptNode の
adoptedCallback、<template> の
content 断片管理の一本化。max_num_reorder_frames の正確な抽出と遅延なし RGB
フレーム出力を実証。<use>・stroke・グラデーション対応:
ピクセル単位の線形・放射グラデーション補間描画とアンチエイリアス輪郭線描画に対応。SubtitleTrack
新設、cycle_subtitles()
による順繰り切り替え、操作バーバッジ動的描画。cycle_quality()、ゴールドカラー強調描画、q
キー操作。[CC] アイコンを描画、c キー連動。Cargo.toml
をマスターとし、システムモニタ表示を完全一元化。c キーショートカット、操作バー CC
アイコンクリック判定。[ALLOC FAIL] で
app_runner が panic した。JsRuntime
を引き継いでいたため、ヒープが積み上がり 512MB を超過した。JsRuntime::new()
で作り直し、古い実行環境を捨てる際の循環参照切断(break_cycles)を参照数比例の作業リスト方式へ改善。