This commit is contained in:
33333-33333 2026-08-22 20:55:27 +09:00
commit caa90775f9
89 changed files with 7126 additions and 0 deletions

43
scripts/build-hosted.ps1 Normal file
View file

@ -0,0 +1,43 @@
param([string]$OutputDirectory = (Join-Path $PSScriptRoot '..\dist\hosted'))
$ErrorActionPreference = 'Stop'
$workspace = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..')).Path
$output = [System.IO.Path]::GetFullPath($OutputDirectory)
if (-not $output.StartsWith($workspace, [System.StringComparison]::OrdinalIgnoreCase)) {
throw "OutputDirectory must stay inside the workspace: $output"
}
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot 'extract-wasm.ps1') | Out-Null
if ($LASTEXITCODE -ne 0) { throw "WASM extraction failed with exit code $LASTEXITCODE." }
New-Item -ItemType Directory -Path $output -Force | Out-Null
$manifestPath = Join-Path $workspace 'dist\wasm\manifest.json'
$manifest = Get-Content -LiteralPath $manifestPath -Raw -Encoding UTF8 | ConvertFrom-Json
$html = Get-Content -LiteralPath (Join-Path $workspace 'index.html') -Raw -Encoding UTF8
$html = [regex]::Replace($html, '<script src="kernels\.js"></script>\s*<script src="script\.js"></script>', '<script type="module" src="./hosted-loader.js"></script>')
Set-Content -LiteralPath (Join-Path $output 'index.html') -Value $html -Encoding UTF8
Copy-Item -LiteralPath (Join-Path $workspace 'script.js') -Destination (Join-Path $output 'script.js') -Force
$loader = Get-Content -LiteralPath (Join-Path $workspace 'hosted-loader.js') -Raw -Encoding UTF8
$kernelMeta = [ordered]@{}
foreach ($payload in $manifest.payloads) { $kernelMeta[$payload.symbol] = $payload.sha256 }
$loader = $loader.Replace('/*__KERNEL_META__*/{}', ($kernelMeta | ConvertTo-Json -Compress))
$expectedHashed = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
foreach ($payload in $manifest.payloads) {
$extension = [System.IO.Path]::GetExtension($payload.file)
$stem = [System.IO.Path]::GetFileNameWithoutExtension($payload.file)
$hashed = "$stem.$($payload.sha256.Substring(0,16))$extension"
[void]$expectedHashed.Add($hashed)
Copy-Item -LiteralPath (Join-Path (Split-Path $manifestPath) $payload.file) -Destination (Join-Path (Split-Path $manifestPath) $hashed) -Force
$loader = $loader.Replace($payload.file, $hashed)
}
$wasmDirectory = (Resolve-Path -LiteralPath (Split-Path $manifestPath)).Path
if (-not $wasmDirectory.StartsWith($workspace + [System.IO.Path]::DirectorySeparatorChar, [System.StringComparison]::OrdinalIgnoreCase)) {
throw "Refusing to prune hashed assets outside the workspace: $wasmDirectory"
}
foreach ($asset in Get-ChildItem -LiteralPath $wasmDirectory -File) {
if ($asset.Name -match '^[a-z0-9-]+\.[0-9a-f]{16}\.wasm$' -and -not $expectedHashed.Contains($asset.Name)) {
Remove-Item -LiteralPath $asset.FullName -Force
}
}
[System.IO.File]::WriteAllText((Join-Path $output 'hosted-loader.js'), $loader, [System.Text.UTF8Encoding]::new($false))
Copy-Item -LiteralPath (Join-Path $workspace 'hosted-headers.txt') -Destination (Join-Path (Split-Path $output) '_headers') -Force
Write-Output $output

50
scripts/build-kernels.ps1 Normal file
View file

@ -0,0 +1,50 @@
param(
[string]$ManifestPath = (Join-Path $PSScriptRoot '..\dist\wasm\manifest.json'),
[string]$OutputPath = (Join-Path $PSScriptRoot '..\kernels.js')
)
$ErrorActionPreference = 'Stop'
$workspace = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..')).Path
$output = [System.IO.Path]::GetFullPath($OutputPath)
if (-not $output.StartsWith($workspace, [System.StringComparison]::OrdinalIgnoreCase)) { throw 'OutputPath must stay inside the workspace.' }
$manifest = Get-Content -LiteralPath $ManifestPath -Raw -Encoding UTF8 | ConvertFrom-Json
$values = @{}
foreach ($payload in $manifest.payloads) {
$path = Join-Path (Split-Path $ManifestPath) $payload.file
$values[$payload.symbol] = [Convert]::ToBase64String([System.IO.File]::ReadAllBytes($path))
}
foreach ($required in @('WASM_SIMD_B64','WASM_SCALAR_B64','DEEP_SIMD_B64','DEEP_SCALAR_B64','BLA_SIMD_B64','BLA_SCALAR_B64','COLOR_SIMD_B64','COLOR_SCALAR_B64')) {
if (-not $values.ContainsKey($required)) { throw "Missing payload: $required" }
}
$colorDeduplicated = $values.COLOR_SIMD_B64 -eq $values.COLOR_SCALAR_B64
$lines = @(
"'use strict';"
'// Generated from dist/wasm/manifest.json. Run scripts/extract-wasm.ps1 before replacing compatibility inputs.'
)
if ($colorDeduplicated) { $lines += "const COLOR_B64='$($values.COLOR_SIMD_B64)';" }
$lines += @(
'globalThis.MANDEL_KERNELS=Object.freeze({'
" WASM_SIMD_B64:'$($values.WASM_SIMD_B64)',"
" WASM_SCALAR_B64:'$($values.WASM_SCALAR_B64)',"
" DEEP_SIMD_B64:'$($values.DEEP_SIMD_B64)',"
" DEEP_SCALAR_B64:'$($values.DEEP_SCALAR_B64)',"
" BLA_SIMD_B64:'$($values.BLA_SIMD_B64)',"
" BLA_SCALAR_B64:'$($values.BLA_SCALAR_B64)',"
)
if ($colorDeduplicated) {
$lines += @(' COLOR_SIMD_B64:COLOR_B64,', ' COLOR_SCALAR_B64:COLOR_B64')
} else {
$lines += @(" COLOR_SIMD_B64:'$($values.COLOR_SIMD_B64)',", " COLOR_SCALAR_B64:'$($values.COLOR_SCALAR_B64)'")
}
$lines += @(
'});'
'globalThis.MANDEL_KERNEL_META=Object.freeze({'
)
for ($index = 0; $index -lt $manifest.payloads.Count; $index++) {
$payload = $manifest.payloads[$index]
$comma = if ($index -lt $manifest.payloads.Count - 1) { ',' } else { '' }
$lines += " '$($payload.symbol)':'$($payload.sha256)'$comma"
}
$lines += '});'
[System.IO.File]::WriteAllText($output, ($lines -join [Environment]::NewLine) + [Environment]::NewLine, [System.Text.UTF8Encoding]::new($false))
Write-Output $output

View file

@ -0,0 +1,13 @@
param([string]$OutputDirectory = (Join-Path $PSScriptRoot '..\dist\standalone'))
$ErrorActionPreference = 'Stop'
$workspace = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..')).Path
$output = [System.IO.Path]::GetFullPath($OutputDirectory)
if (-not $output.StartsWith($workspace, [System.StringComparison]::OrdinalIgnoreCase)) {
throw "OutputDirectory must stay inside the workspace: $output"
}
New-Item -ItemType Directory -Path $output -Force | Out-Null
foreach ($name in @('index.html','script.js','kernels.js')) {
Copy-Item -LiteralPath (Join-Path $workspace $name) -Destination (Join-Path $output $name) -Force
}
Write-Output $output

64
scripts/build-wasm.ps1 Normal file
View file

@ -0,0 +1,64 @@
param(
[string]$Compiler = 'clang',
[string]$OutputDirectory = (Join-Path $PSScriptRoot '..\build\wasm-v23'),
[switch]$Promote
)
$ErrorActionPreference = 'Stop'
$workspace = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..')).Path
$output = [System.IO.Path]::GetFullPath($OutputDirectory)
if (-not $output.StartsWith($workspace, [System.StringComparison]::OrdinalIgnoreCase)) { throw 'OutputDirectory must stay inside the workspace.' }
$lock = Get-Content -LiteralPath (Join-Path $workspace 'toolchain.lock.json') -Raw -Encoding UTF8 | ConvertFrom-Json
$versionLine = (& $Compiler --version | Select-Object -First 1)
if ($versionLine -notmatch [regex]::Escape($lock.version)) { throw "Expected clang $($lock.version), got: $versionLine" }
New-Item -ItemType Directory -Path $output -Force | Out-Null
$common = @('--target=wasm32-unknown-unknown') + @($lock.commonFlags)
$variants = @(
[pscustomobject]@{ name='simd'; flags=@($lock.simdFlags) },
[pscustomobject]@{ name='scalar'; flags=@($lock.scalarFlags) }
)
$modules = @(
[pscustomobject]@{ symbol='WASM'; name='wasm'; source='shallow_kernel.c'; memory=2097152 },
[pscustomobject]@{ symbol='DEEP'; name='deep'; source='deep_kernel.c'; memory=8388608 },
[pscustomobject]@{ symbol='BLA'; name='bla'; source='bla_kernel_v18.c'; memory=33554432 },
[pscustomobject]@{ symbol='COLOR'; name='color'; source='color_kernel.c'; memory=2097152 }
)
$payloads = @()
foreach ($module in $modules) {
foreach ($variant in $variants) {
$file = "$($module.name)-$($variant.name).wasm"
$path = Join-Path $output $file
$args = $common + @($variant.flags) + @("-Wl,--initial-memory=$($module.memory)", "-Wl,--max-memory=$($module.memory)", '-o', $path, (Join-Path $workspace "src\$($module.source)") )
& $Compiler @args
if ($LASTEXITCODE -ne 0) { throw "clang failed for $file" }
$payloads += [ordered]@{
symbol = "$($module.symbol)_$($variant.name.ToUpperInvariant())_B64"
file = $file
bytes = (Get-Item -LiteralPath $path).Length
sha256 = (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash.ToLowerInvariant()
source = "src/$($module.source)"
}
}
}
$manifest = [ordered]@{
format = 'mandelbrot-source-wasm-manifest-v23'
toolchain = $lock
abi = 'src/abi.json'
payloads = $payloads
}
$manifestPath = Join-Path $output 'manifest.json'
$manifest | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $manifestPath -Encoding UTF8
if ($Promote) {
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $workspace 'tests\kernel-golden.ps1') -GeneratedDirectory $output
if ($LASTEXITCODE -ne 0) { throw 'Generated-WASM golden gate failed; payloads were not promoted.' }
$generatedKernels = Join-Path $output 'kernels.js'
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $workspace 'scripts\build-kernels.ps1') -ManifestPath $manifestPath -OutputPath $generatedKernels
if ($LASTEXITCODE -ne 0) { throw 'kernels.js generation failed; payloads were not promoted.' }
$dist = Join-Path $workspace 'dist\wasm'
foreach ($payload in $payloads) { Copy-Item -LiteralPath (Join-Path $output $payload.file) -Destination (Join-Path $dist $payload.file) -Force }
Copy-Item -LiteralPath $manifestPath -Destination (Join-Path $dist 'manifest.json') -Force
Copy-Item -LiteralPath $generatedKernels -Destination (Join-Path $workspace 'kernels.js') -Force
}
Write-Output $manifestPath

56
scripts/extract-wasm.ps1 Normal file
View file

@ -0,0 +1,56 @@
param(
[string]$KernelFile = (Join-Path $PSScriptRoot '..\kernels.js'),
[string]$OutputDirectory = (Join-Path $PSScriptRoot '..\dist\wasm')
)
$ErrorActionPreference = 'Stop'
$kernelPath = (Resolve-Path -LiteralPath $KernelFile).Path
$workspacePath = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..')).Path
$resolvedOutput = [System.IO.Path]::GetFullPath($OutputDirectory)
if (-not $resolvedOutput.StartsWith($workspacePath, [System.StringComparison]::OrdinalIgnoreCase)) {
throw "OutputDirectory must stay inside the workspace: $resolvedOutput"
}
$source = Get-Content -LiteralPath $kernelPath -Raw -Encoding UTF8
$kernelObject = [regex]::Match($source, 'MANDEL_KERNELS\s*=\s*Object\.freeze\(\{(?<body>[\s\S]*?)\}\);')
if (-not $kernelObject.Success) { throw 'MANDEL_KERNELS payload object was not found.' }
$payloadSource = $kernelObject.Groups['body'].Value
$entries = [ordered]@{}
foreach ($match in [regex]::Matches($payloadSource, '([A-Z0-9_]+_B64)\s*:\s*''([^'']+)''')) {
$entries[$match.Groups[1].Value] = $match.Groups[2].Value
}
# Generated kernels.js may store byte-identical color variants once.
# Keep both public symbols in the manifest so hosted assets and compatibility
# checks remain stable when the standalone source uses the shared blob form.
$sharedColor = [regex]::Match($source, 'const\s+COLOR_B64\s*=\s*''([^'']+)''')
if ($sharedColor.Success) {
foreach ($symbol in @('COLOR_SIMD_B64', 'COLOR_SCALAR_B64')) {
if ($payloadSource -match ([regex]::Escape($symbol) + '\s*:\s*COLOR_B64')) {
$entries[$symbol] = $sharedColor.Groups[1].Value
}
}
}
if ($entries.Count -lt 8) { throw "Expected at least 8 embedded WASM payload symbols; found $($entries.Count)." }
New-Item -ItemType Directory -Path $resolvedOutput -Force | Out-Null
$manifest = [ordered]@{
format = 'mandelbrot-wasm-manifest-v1'
generatedUtc = [DateTime]::UtcNow.ToString('o')
input = $kernelPath.Substring($workspacePath.Length).TrimStart('\').Replace('\','/')
payloads = @()
}
foreach ($entry in $entries.GetEnumerator()) {
$symbol = $entry.Key
$bytes = [Convert]::FromBase64String($entry.Value)
$name = ($symbol -replace '_B64$','').ToLowerInvariant().Replace('_','-') + '.wasm'
$path = Join-Path $resolvedOutput $name
[System.IO.File]::WriteAllBytes($path, $bytes)
$hash = (Get-FileHash -LiteralPath $path -Algorithm SHA256).Hash.ToLowerInvariant()
$manifest.payloads += [ordered]@{ symbol=$symbol; file=$name; bytes=$bytes.Length; sha256=$hash }
}
$manifestPath = Join-Path $resolvedOutput 'manifest.json'
$manifest | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath $manifestPath -Encoding UTF8
Write-Output $manifestPath

View file

@ -0,0 +1,98 @@
$ErrorActionPreference = 'Stop'
$workspace = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..')).Path
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot 'build-hosted.ps1') | Out-Null
if ($LASTEXITCODE -ne 0) { throw "Hosted build failed with exit code $LASTEXITCODE." }
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot 'build-standalone.ps1') | Out-Null
if ($LASTEXITCODE -ne 0) { throw "Standalone build failed with exit code $LASTEXITCODE." }
$contract = & powershell.exe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $workspace 'tests\source-contract.ps1') | ConvertFrom-Json
if ($LASTEXITCODE -ne 0 -or $contract.status -ne 'pass') { throw 'Source contract failed; baseline was not written.' }
$wasmManifest = Get-Content -LiteralPath (Join-Path $workspace 'dist\wasm\manifest.json') -Raw -Encoding UTF8 | ConvertFrom-Json
$shallowPayload = $wasmManifest.payloads | Where-Object symbol -eq 'WASM_SIMD_B64' | Select-Object -First 1
$shallowHashed = ([System.IO.Path]::GetFileNameWithoutExtension($shallowPayload.file) + '.' + $shallowPayload.sha256.Substring(0,16) + [System.IO.Path]::GetExtension($shallowPayload.file))
function File-Info([string]$Path) {
$item = Get-Item -LiteralPath $Path
[pscustomobject][ordered]@{ file=$item.FullName.Substring($workspace.Length).TrimStart('\').Replace('\','/'); bytes=$item.Length; sha256=(Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant() }
}
$node = Get-Command node -ErrorAction SilentlyContinue
$clang = Get-Command clang -ErrorAction SilentlyContinue
$nodeVersion = if ($node) { (& $node.Source --version | Select-Object -First 1) } else { $null }
$sourceBuildVerified = $false
$sourceBuildReason = 'Pinned clang 17.0.6 and Node CLI were not available.'
if ($node -and $clang -and ((& $clang.Source --version | Select-Object -First 1) -match '17\.0\.6')) {
$generatedManifest = & powershell.exe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $workspace 'scripts\build-wasm.ps1') -Compiler $clang.Source
if ($LASTEXITCODE -ne 0) { throw 'Source-WASM build failed while recording the baseline.' }
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $workspace 'tests\kernel-golden.ps1') -GeneratedDirectory (Split-Path $generatedManifest)
if ($LASTEXITCODE -ne 0) { throw 'Generated-WASM golden failed while recording the baseline.' }
$sourceBuildVerified = $true
$sourceBuildReason = $null
} else {
$previousPath = Join-Path $workspace 'audit\v23-source-baseline.json'
$candidateManifest = Join-Path $workspace 'build\wasm-v23\manifest.json'
if ((Test-Path -LiteralPath $previousPath) -and (Test-Path -LiteralPath $candidateManifest)) {
$previous = Get-Content -LiteralPath $previousPath -Raw -Encoding UTF8 | ConvertFrom-Json
$candidateHash = (Get-FileHash -LiteralPath $candidateManifest -Algorithm SHA256).Hash.ToLowerInvariant()
$sourceInputsMatch = $true
foreach ($record in $previous.sourceBuild.files) {
$recordPath = Join-Path $workspace ($record.file.Replace('/', [System.IO.Path]::DirectorySeparatorChar))
if (-not (Test-Path -LiteralPath $recordPath) -or (Get-FileHash -LiteralPath $recordPath -Algorithm SHA256).Hash.ToLowerInvariant() -ne $record.sha256) { $sourceInputsMatch = $false; break }
}
if ($sourceInputsMatch -and $previous.sourceBuild.compiledInThisEnvironment -and $previous.sourceBuild.goldenStatus -eq 'pass' -and $previous.sourceBuild.generatedManifest.sha256 -eq $candidateHash) {
$generatedManifest = $candidateManifest
$sourceBuildVerified = $true
$nodeVersion = $previous.sourceBuild.node
$sourceBuildReason = 'Verified generated manifest reused; fixed toolchain was removed after the successful gate.'
}
}
}
$hostedFiles = @(
File-Info (Join-Path $workspace 'dist\hosted\index.html')
File-Info (Join-Path $workspace 'dist\hosted\hosted-loader.js')
File-Info (Join-Path $workspace 'dist\hosted\script.js')
File-Info (Join-Path $workspace "dist\wasm\$shallowHashed")
)
$standaloneFiles = @(
File-Info (Join-Path $workspace 'dist\standalone\index.html')
File-Info (Join-Path $workspace 'dist\standalone\script.js')
File-Info (Join-Path $workspace 'dist\standalone\kernels.js')
)
$kernelSources = @(
File-Info (Join-Path $workspace 'src\shallow_kernel.c')
File-Info (Join-Path $workspace 'src\deep_kernel.c')
File-Info (Join-Path $workspace 'src\bla_kernel_v18.c')
File-Info (Join-Path $workspace 'src\color_kernel.c')
File-Info (Join-Path $workspace 'src\abi.json')
File-Info (Join-Path $workspace 'toolchain.lock.json')
)
$baseline = [ordered]@{
format = 'mandelbrot-source-baseline-v23'
generatedUtc = [DateTime]::UtcNow.ToString('o')
rendererVersion = 23
contract = $contract
hosted = [ordered]@{
firstViewFiles = $hostedFiles
firstViewUncompressedBytes = ($hostedFiles | Measure-Object bytes -Sum).Sum
deepRequestsBeforeDeepView = 0
note = 'Transfer compression, parse, compile, and runtime timings require the fixed browser benchmark.'
}
standalone = [ordered]@{
files = $standaloneFiles
uncompressedBytes = ($standaloneFiles | Measure-Object bytes -Sum).Sum
}
sourceBuild = [ordered]@{
files=$kernelSources
compiler='clang 17.0.6'
node=$nodeVersion
compiledInThisEnvironment=$sourceBuildVerified
goldenStatus=if ($sourceBuildVerified) { 'pass' } else { 'not-run' }
generatedManifest=if ($sourceBuildVerified) { File-Info $generatedManifest } else { $null }
reason=$sourceBuildReason
}
browserBenchmark = [ordered]@{ status='not-run'; report='audit/v23-browser-baseline.json'; reason='No in-app browser binding was available in this environment.' }
}
$path = Join-Path $workspace 'audit\v23-source-baseline.json'
$baseline | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $path -Encoding UTF8
Write-Output $path

32
scripts/test-all.ps1 Normal file
View file

@ -0,0 +1,32 @@
param([switch]$RequireToolchain)
$ErrorActionPreference = 'Stop'
$workspace = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..')).Path
function Run-CheckedPowerShell([string]$Path) {
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File $Path
if ($LASTEXITCODE -ne 0) { throw "PowerShell test failed: $Path" }
}
Run-CheckedPowerShell (Join-Path $workspace 'tests\kernel-source-contract.ps1')
Run-CheckedPowerShell (Join-Path $workspace 'tests\source-contract.ps1')
$node = Get-Command node -ErrorAction SilentlyContinue
if ($node) {
foreach ($test in @('js-syntax.mjs','module-clone.mjs','precision-reference.mjs','analytic-interior.mjs','pixel-mapping.mjs','pixel-contract.mjs','runtime-budget.mjs')) {
& $node.Source (Join-Path $workspace "tests\$test")
if ($LASTEXITCODE -ne 0) { throw "Node regression test failed: $test" }
}
} elseif ($RequireToolchain) { throw 'Node.js is required by -RequireToolchain.' }
else { Write-Warning 'Node.js is unavailable; executable JavaScript/WASM tests were skipped.' }
$clang = Get-Command clang -ErrorAction SilentlyContinue
$sourceGolden = $false
if ($clang -and $node -and ((& $clang.Source --version | Select-Object -First 1) -match '17\.0\.6')) {
$generated = & powershell.exe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $workspace 'scripts\build-wasm.ps1') -Compiler $clang.Source
if ($LASTEXITCODE -ne 0) { throw 'Source-WASM build failed.' }
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $workspace 'tests\kernel-golden.ps1') -GeneratedDirectory (Split-Path $generated)
if ($LASTEXITCODE -ne 0) { throw 'Generated-WASM golden failed.' }
$sourceGolden = $true
} elseif ($RequireToolchain) { throw 'clang 17.0.6 is required by -RequireToolchain.' }
else { Write-Warning 'clang 17.0.6 is unavailable; source-WASM build/golden was skipped.' }
[ordered]@{ status='pass'; nodeTests=[bool]$node; sourceWasmGolden=$sourceGolden } | ConvertTo-Json