fix(codemode): parse canonical index keys (#37851)

This commit is contained in:
Aiden Cline 2026-07-19 23:55:53 -05:00 committed by GitHub
commit deee40c572
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 64 additions and 20 deletions

View file

@ -96,6 +96,15 @@ const globalStaticMembers: Partial<Record<GlobalNamespaceName, Set<string>>> = {
URL: urlStatics,
}
const MAX_ARRAY_LENGTH = 4_294_967_295
const parseArrayIndex = (key: string | number): number | undefined => {
const property = String(key)
if (!/^(0|[1-9]\d*)$/.test(property)) return undefined
const index = Number(property)
return index < MAX_ARRAY_LENGTH ? index : undefined
}
const calleeDescription = (callee: AstNode): string => {
if (callee.type === "Identifier") return getString(callee, "name")
if (callee.type === "MemberExpression") {
@ -1916,8 +1925,8 @@ export class Interpreter<R> {
if (typeof objectValue === "string") {
if (key === "length") return new ComputedValue(objectValue.length)
if (typeof key === "number") return new ComputedValue(objectValue[key])
if (typeof key === "string" && /^\d+$/.test(key)) return new ComputedValue(objectValue[Number(key)])
const index = parseArrayIndex(key)
if (index !== undefined) return new ComputedValue(objectValue[index])
if (typeof key === "string" && stringMethods.has(key)) return new IntrinsicReference(objectValue, key)
return new ComputedValue(undefined)
}
@ -2011,18 +2020,14 @@ export class Interpreter<R> {
if (Array.isArray(objectValue)) {
if (operation === "delete") return { target: objectValue, key }
if (
key !== "length" &&
!(typeof key === "string" && arrayMethods.has(key)) &&
typeof key !== "number" &&
!/^\d+$/.test(key)
) {
const index = parseArrayIndex(key)
if (key !== "length" && !(typeof key === "string" && arrayMethods.has(key)) && index === undefined) {
if (typeof key === "string" && Object.hasOwn(objectValue, key)) {
return new ComputedValue((objectValue as Record<string, unknown> & Array<unknown>)[key])
}
return new ComputedValue(undefined)
}
return { target: objectValue, key }
return { target: objectValue, key: index ?? key }
}
return { target: objectValue as SafeObject, key }
@ -2043,10 +2048,9 @@ export class Interpreter<R> {
)
return reference
if (Array.isArray(reference.target)) {
if (typeof reference.key === "string" && arrayMethods.has(reference.key)) {
return new IntrinsicReference(reference.target, reference.key)
}
return reference.key === "length" ? reference.target.length : reference.target[Number(reference.key)]
if (reference.key === "length") return reference.target.length
if (typeof reference.key === "string") return new IntrinsicReference(reference.target, reference.key)
return reference.target[reference.key]
}
if (reference.target instanceof CodeModeURL) {
return (reference.target.url as unknown as Record<string, unknown>)[String(reference.key)]
@ -2109,7 +2113,7 @@ export class Interpreter<R> {
throw new InterpreterRuntimeError("Array methods cannot be assigned in CodeMode.", node)
}
}
const key = Array.isArray(reference.target) ? Number(reference.key) : String(reference.key)
const key = Array.isArray(reference.target) ? reference.key : String(reference.key)
const current =
reference.target instanceof CodeModeURL
? (reference.target.url as unknown as Record<string, unknown>)[key]
@ -2123,16 +2127,15 @@ export class Interpreter<R> {
private assignToReference(reference: MemberReference, key: number | string, next: unknown, node: AstNode): void {
if (Array.isArray(reference.target)) {
const target = reference.target
const index = key as number
if (!Number.isInteger(index) || index < 0) {
if (typeof key !== "number" || parseArrayIndex(key) === undefined) {
throw new InterpreterRuntimeError(
"Array assignment index must be a non-negative integer.",
"Array assignment index must be a valid array index.",
node,
"InvalidDataValue",
)
}
rejectCircularInsertion(target, next, "Array assignment result", node)
target[index] = next
target[key] = next
return
}
if (reference.target instanceof CodeModeURL) {