mirror of
https://github.com/anomalyco/opencode.git
synced 2026-09-08 23:44:59 +00:00
fix(codemode): close symbol cycle gaps
This commit is contained in:
parent
a7deeaf296
commit
2a849c0fef
3 changed files with 27 additions and 1 deletions
|
|
@ -109,7 +109,7 @@ export const rejectCircularInsertion = (container: object, value: unknown, label
|
|||
throw new InterpreterRuntimeError(`${label} contains a circular value.`, node, "InvalidDataValue")
|
||||
if (current === null || typeof current !== "object" || isRuntimeReference(current) || seen.has(current)) continue
|
||||
seen.add(current)
|
||||
pending.push(Array.isArray(current) ? current[Symbol.iterator]() : childValues(current))
|
||||
pending.push(childValues(current))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ export const invokeObjectMethod = (name: string, args: Array<unknown>, node: Ast
|
|||
continue
|
||||
}
|
||||
if (key !== AsyncIteratorSymbol && key !== IteratorSymbol) continue
|
||||
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue
|
||||
const item = Reflect.get(source, key)
|
||||
rejectCircularInsertion(out, item, "Object.assign result", node)
|
||||
Reflect.set(out, key, item)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
import { describe, expect, test } from "bun:test"
|
||||
import { Effect, Schema } from "effect"
|
||||
import { CodeMode, Tool } from "../src/index.js"
|
||||
import { IteratorSymbol } from "../src/interpreter/model.js"
|
||||
import { invokeObjectMethod } from "../src/stdlib/object.js"
|
||||
|
||||
// Standard-library value types: Date, RegExp, Map, Set. Programs use them as ordinary JS;
|
||||
// intra-CodeMode checkpoints (Object.* helpers, spread, coercion inputs) preserve the live
|
||||
|
|
@ -824,6 +826,29 @@ describe("stdlib integration", () => {
|
|||
expect(await value(`try { Object.assign(null, { a: 1 }); return false } catch { return true }`)).toBe(true)
|
||||
})
|
||||
|
||||
test("Object.assign ignores non-enumerable supported symbols without reading them", () => {
|
||||
const target = {}
|
||||
const reads: Array<boolean> = []
|
||||
const source = Object.defineProperty({}, IteratorSymbol, {
|
||||
get() {
|
||||
reads.push(true)
|
||||
return target
|
||||
},
|
||||
})
|
||||
expect(invokeObjectMethod("assign", [target, source], { type: "CallExpression" })).toBe(target)
|
||||
expect(reads).toEqual([])
|
||||
expect(Object.hasOwn(target, IteratorSymbol)).toBe(false)
|
||||
})
|
||||
|
||||
test("Object.assign rejects cycles through supported symbols on nested arrays", () => {
|
||||
const target = {}
|
||||
const nested = Object.defineProperty([], IteratorSymbol, { enumerable: true, value: target })
|
||||
expect(() => invokeObjectMethod("assign", [target, { nested }], { type: "CallExpression" })).toThrow(
|
||||
"Object.assign result contains a circular value.",
|
||||
)
|
||||
expect(Object.hasOwn(target, "nested")).toBe(false)
|
||||
})
|
||||
|
||||
test("Object.assign rejects direct and nested cycles", async () => {
|
||||
expect(
|
||||
await value(`
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue