refactor(bridge/core): daily optimization audit for reflection and memory safety#162
refactor(bridge/core): daily optimization audit for reflection and memory safety#162repyh wants to merge 1 commit into
Conversation
…ce shared memory safety - Implemented fast path for `[]byte` in `bindSlice` to avoid interface boxing and large array allocations. - Optimized `bindMap` using `MapRange` to prevent slice allocation, and `strconv` methods to avoid interface boxing on numeric keys. - Prevented double-locking of `os.Stdout` in `Console.Error` by unifying the log output into a single `fmt.Println` call. - Enforced shared memory safety in `ToArrayBuffer` by ensuring Go's underlying `[]byte` is explicitly copied before wrapping it in `vm.NewArrayBuffer`. - Implemented `ToUint8Array` to safely instantiate JS typed arrays from Go byte slices. - Improved `Uint8Array` constructor retrieval safety in `MapSharedBuffer`. All changes adhere to the @optimized and @safety comment conventions. Benchmarks show `bindSlice` improved from ~65.3M ns/op to ~2.18M ns/op and `bindMap` improved from ~625k ns/op to ~564k ns/op. Co-authored-by: repyh <63894915+repyh@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
📝 WalkthroughWalkthroughThe PR improves the Go-to-JavaScript bridge layer with defensive byte copying in array buffer operations, graceful fallback when the Uint8Array constructor is unavailable, fast paths for common byte-slice and map-binding cases, and optimized numeric-key-to-string conversion in object binding. ChangesCore binding layer safety and optimization improvements
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
bridge/core/arraybuffer.go (1)
41-41: 💤 Low valueRedundant
vm.ToValuewrapping.
vm.Newalready returns asobek.Value, so wrappingtypedArrayinvm.ToValue()is unnecessary.♻️ Suggested fix
- return vm.ToValue(typedArray) + return typedArray🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@bridge/core/arraybuffer.go` at line 41, The return wraps a value already created by vm.New in vm.ToValue unnecessarily; change the return in the function that builds the typedArray to return the sobek.Value produced by vm.New directly (i.e., replace "return vm.ToValue(typedArray)" with "return typedArray"), leaving the vm.New call and typedArray variable intact to preserve types and behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@bridge/core/reflection.go`:
- Around line 341-351: The []byte fast-path currently returns ToArrayBuffer(vm,
data) which yields a raw ArrayBuffer; instead construct and return a Uint8Array
backed by that buffer so JS sees array-like semantics (indexing, forEach, map).
In the bindSlice fast-path (the block using v, data and calling ToArrayBuffer),
replace the direct ArrayBuffer return with creating a Uint8Array via the VM
global Uint8Array constructor (pass the buffer from ToArrayBuffer) and return
that Uint8Array value.
---
Nitpick comments:
In `@bridge/core/arraybuffer.go`:
- Line 41: The return wraps a value already created by vm.New in vm.ToValue
unnecessarily; change the return in the function that builds the typedArray to
return the sobek.Value produced by vm.New directly (i.e., replace "return
vm.ToValue(typedArray)" with "return typedArray"), leaving the vm.New call and
typedArray variable intact to preserve types and behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 15c0c68f-b3a0-456a-962b-0a2806596a92
📒 Files selected for processing (3)
bridge/core/arraybuffer.gobridge/core/console.gobridge/core/reflection.go
| // @optimized: Fast path for []byte to avoid interface overhead. | ||
| if v.Type().Elem() == reflect.TypeOf(byte(0)) { | ||
| var data []byte | ||
| if v.CanAddr() || v.Kind() == reflect.Slice { | ||
| data = v.Bytes() | ||
| } else { | ||
| data = make([]byte, v.Len()) | ||
| reflect.Copy(reflect.ValueOf(data), v) | ||
| } | ||
| return ToArrayBuffer(vm, data), nil | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for JS/test files that may consume bound byte slices
rg -n --type-add 'jslike:*.{js,ts,mjs}' --type=jslike -C2 '\.Bytes|byteArray|buffer' || true
# Search Go files that call BindStruct with types containing []byte fields
ast-grep --pattern $'BindStruct($_, $_, $_)'Repository: repyh/typego
Length of output: 5029
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the target snippet with line numbers
echo "=== bridge/core/reflection.go (around 320-370) ==="
nl -ba bridge/core/reflection.go | sed -n '300,390p'
# Locate ToArrayBuffer implementation and relevant call sites
echo "=== rg ToArrayBuffer ==="
rg -n "ToArrayBuffer" bridge/core bridge -S || true
# Inspect ToArrayBuffer implementation
echo "=== ToArrayBuffer definition (narrow search) ==="
TOA_FILE="$(rg -l --glob='*.go' "func\s+ToArrayBuffer" . || true | head -n 1)"
if [ -n "${TOA_FILE:-}" ]; then
echo "Found: $TOA_FILE"
nl -ba "$TOA_FILE" | sed -n '1,220p'
fi
# Search for other []byte reflection/binding branches
echo "=== rg for []byte fast-path or binding ==="
rg -n --glob='*.go' "\[\]byte" bridge/core/reflection.go bridge/core -S || true
# Search for any existing JS/TS docs or examples referring to []byte as Array vs ArrayBuffer
echo "=== rg examples/tests mentions of byte slice binding ==="
rg -n --glob='*.{js,ts,mjs}' "Uint8Array|ArrayBuffer|Array\(" examples bridge -S || trueRepository: repyh/typego
Length of output: 168
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== bridge/core/reflection.go (around 320-390) ==="
sed -n '320,390p' bridge/core/reflection.go | cat -n | sed 's/^ *[0-9]*\t/ /'
echo "=== rg ToArrayBuffer (bridge/core) ==="
rg -n "ToArrayBuffer" bridge/core -S || true
echo "=== rg for ToArrayBuffer definition ==="
rg -n "func\s+ToArrayBuffer" -S . || true
echo "=== ToArrayBuffer definition excerpt ==="
TOA_FILE="$(rg -l --glob='*.go' -S "func\s+ToArrayBuffer" . | head -n 1 || true)"
if [ -n "${TOA_FILE:-}" ]; then
echo "Found: $TOA_FILE"
# Print only the first ~220 lines around the definition area
sed -n '1,220p' "$TOA_FILE" | cat -n | sed 's/^ *[0-9]*\t/ /'
fi
echo "=== rg for []byte fast-path or binding branches ==="
rg -n --glob='*.go' "\[\]byte" bridge/core/reflection.go bridge/core -S || true
echo "=== rg examples mentioning Uint8Array/ArrayBuffer ==="
rg -n --glob='*.{js,ts,mjs}' "Uint8Array|ArrayBuffer" examples bridge -S || trueRepository: repyh/typego
Length of output: 8359
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== rg for []byte mapping / ArrayBuffer in codegen & types ==="
rg -n --glob='*.go' --glob='*.ts' --glob='*.d.ts' "\[\]byte|byte slice|ArrayBuffer|ToArrayBuffer" . -S || true
echo "=== rg for BindStruct / struct binding logic ==="
rg -n --glob='*.go' "BindStruct" bridge/core . -S || true
echo "=== rg for array-like usage of byte slices (heuristic) ==="
rg -n --glob='*.{js,ts,mjs}' -S "forEach\(|map\(|filter\(|\[\s*0\s*\]" examples bridge -S || trueRepository: repyh/typego
Length of output: 9343
Expose []byte as ArrayBuffer (not JS Array)
bindSlice has a []byte fast-path that returns ToArrayBuffer(vm, data); ToArrayBuffer copies the bytes and produces a JS ArrayBuffer. This bypasses the generic slice path that builds a JS Array via vm.NewArray(...), so JS expecting array semantics (forEach, map, arr[i]) on []byte fields will break—wrap with new Uint8Array(buffer) to iterate/access bytes.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@bridge/core/reflection.go` around lines 341 - 351, The []byte fast-path
currently returns ToArrayBuffer(vm, data) which yields a raw ArrayBuffer;
instead construct and return a Uint8Array backed by that buffer so JS sees
array-like semantics (indexing, forEach, map). In the bindSlice fast-path (the
block using v, data and calling ToArrayBuffer), replace the direct ArrayBuffer
return with creating a Uint8Array via the VM global Uint8Array constructor (pass
the buffer from ToArrayBuffer) and return that Uint8Array value.
This patch addresses several "Optimization Debt" findings in the
bridge/coremodule as part of a daily incremental audit. It eliminates unnecessary slice allocations and interface boxing during reflection, improves cache utilization, reducesfmtstring conversion overheads, and enforces safety boundaries between Go and JavaScript memory environments.PR created automatically by Jules for task 7583457338219206839 started by @repyh
Summary by CodeRabbit
Bug Fixes
Performance