Skip to content

Commit 651529d

Browse files
authored
fix(codemode): coerce any value to a property key (#50479)
1 parent 532f25d commit 651529d

4 files changed

Lines changed: 24 additions & 24 deletions

File tree

‎packages/codemode/interpreter-support.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ ultimate source of truth. Upstream test262 files run verbatim from `test/test262
243243
accepted, including `.then`/`.catch` handlers and collection callbacks, and vanish at the data boundary like
244244
any function.
245245
- [x] `Promise.withResolvers()`: the same promise and resolver callables as the constructor, as a `{ promise, resolve,
246-
reject }` object.
246+
reject }` object.
247247
- [x] `Promise.try(fn, ...args)`: calls `fn` synchronously; a throw rejects, a return fulfils, and a returned promise or
248248
thenable is adopted.
249249
- [x] Recursive assimilation of objects with an own callable `then` field across `Promise.resolve`, combinators,
@@ -266,7 +266,9 @@ ultimate source of truth. Upstream test262 files run verbatim from `test/test262
266266
not construct.
267267
- [x] `Object()` and `new Object()` return `{}` for nullish arguments and pass objects through unchanged;
268268
primitive wrapper objects (`Object(1)`) are rejected explicitly.
269-
- [x] Computed property names and object spread.
269+
- [x] Computed property names and object spread. Any value works as a key (ToPropertyKey): strings, numbers, and the
270+
two confined symbols as themselves, everything else as its string form (`o[null]` is `o["null"]`, `o[{}]` is
271+
`o["[object Object]"]`), in reads, writes, literals, `in`, and destructuring.
270272
- [x] `Object.keys`, `Object.values`, `Object.entries`, `Object.hasOwn`, `Object.assign`, and `Object.fromEntries`, with
271273
synchronous iterator support for `fromEntries`. Sources follow ToObject: strings enumerate by index, other
272274
primitives and wrappers contribute nothing, and `null`/`undefined` throw. `Object.assign` accepts array

‎packages/codemode/src/interpreter/interpreter.ts‎

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,7 +1244,7 @@ class Frame<R> {
12441244
}
12451245
const keyNode = property.key
12461246
if (property.computed) {
1247-
return Effect.map(this.evaluateExpression(keyNode), (value) => this.toPropertyKey(value, keyNode))
1247+
return Effect.map(this.evaluateExpression(keyNode), (value) => this.toPropertyKey(value))
12481248
}
12491249
if (keyNode.type === "Identifier") return Effect.succeed(keyNode.name)
12501250
if (keyNode.type === "Literal") return Effect.succeed(String(keyNode.value))
@@ -1943,11 +1943,11 @@ class Frame<R> {
19431943
let key: PropertyKey
19441944

19451945
if (property.computed) {
1946-
key = self.toPropertyKey(yield* self.evaluateExpression(keyNode), keyNode)
1946+
key = self.toPropertyKey(yield* self.evaluateExpression(keyNode))
19471947
} else if (keyNode.type === "Identifier") {
19481948
key = keyNode.name
19491949
} else if (keyNode.type === "Literal") {
1950-
key = self.toPropertyKey(literal(keyNode), keyNode)
1950+
key = self.toPropertyKey(literal(keyNode))
19511951
} else {
19521952
throw typeError("Unsupported object property key shape.", keyNode)
19531953
}
@@ -2048,10 +2048,10 @@ class Frame<R> {
20482048
if ((objectValue === null || objectValue === undefined) && node.optional) return OptionalShortCircuit
20492049

20502050
const key = node.computed
2051-
? self.toPropertyKey(yield* self.evaluateExpression(propertyNode), propertyNode)
2051+
? self.toPropertyKey(yield* self.evaluateExpression(propertyNode))
20522052
: propertyNode.type === "Identifier"
20532053
? propertyNode.name
2054-
: self.toPropertyKey(yield* self.evaluateExpression(propertyNode), propertyNode)
2054+
: self.toPropertyKey(yield* self.evaluateExpression(propertyNode))
20552055

20562056
if (objectValue instanceof ToolReference) {
20572057
if (typeof key !== "string") {
@@ -2168,12 +2168,9 @@ class Frame<R> {
21682168
throw typeError(`Cannot assign to read only property '${String(key)}'.`, node)
21692169
}
21702170

2171-
private toPropertyKey(value: Value, node: AstNode): PropertyKey {
2172-
if (typeof value === "string" || typeof value === "number") {
2173-
return value
2174-
}
2175-
if (value === AsyncIteratorSymbol || value === IteratorSymbol) return value
2176-
2177-
throw typeError("Property key must be a string or number, or Symbol.asyncIterator/Symbol.iterator.", node)
2171+
// ToPropertyKey: anything else becomes its string form, so `counts[row.category]` works when the field is null.
2172+
private toPropertyKey(value: Value): PropertyKey {
2173+
if (typeof value === "string" || typeof value === "number" || typeof value === "symbol") return value
2174+
return coerceToString(value)
21782175
}
21792176
}

‎packages/codemode/test/parity.test.ts‎

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -784,9 +784,17 @@ describe("destructuring assignment", () => {
784784
).toEqual({ declared: "a", declarationRest: { 1: "b" }, assigned: "c", assignmentRest: { 1: "d" } })
785785
})
786786

787-
test("rejects computed keys that are not confined property keys", async () => {
788-
const err = await error(`const key = {}; const { [key]: value } = {}`)
789-
expect(err.message).toContain("Property key must be a string or number")
787+
test("computed keys of any type become their string form, as in JS", async () => {
788+
expect(
789+
await value(`
790+
const counts = {}
791+
for (const category of ["a", null, undefined, "a", true, 1.5]) counts[category] = (counts[category] ?? 0) + 1
792+
const key = {}
793+
const { [key]: value } = { "[object Object]": 7 }
794+
const o = { null: 1, "1,2": 2 }
795+
return [counts, value, o[null], o[[1, 2]], undefined in o]
796+
`),
797+
).toEqual([{ a: 2, null: 1, undefined: 1, true: 1, "1.5": 1 }, 7, 1, 2, false])
790798
})
791799
})
792800

‎packages/codemode/test/test262/skipped.txt‎

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ built-ins/Array/prototype/copyWithin/return-abrupt-from-target.js # Expected a
1616
built-ins/Array/prototype/every/15.4.4.16-7-6.js # res Expected SameValue(«true», «false») to be true
1717
built-ins/Array/prototype/every/15.4.4.16-7-c-i-8.js # [, , , ].every(callbackfn) Expected SameValue(«true», «false») to be true
1818
built-ins/Array/prototype/every/15.4.4.16-8-10.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
19-
built-ins/Array/prototype/every/15.4.4.16-8-13.js # Property key must be a string or number, or Symbol.asyncIterator/Symbol.iterator.
2019
built-ins/Array/prototype/every/15.4.4.16-8-2.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
2120
built-ins/Array/prototype/every/15.4.4.16-8-3.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
2221
built-ins/Array/prototype/every/15.4.4.16-8-4.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
@@ -28,7 +27,6 @@ built-ins/Array/prototype/fill/coerced-indexes.js # Array.fill expects start to
2827
built-ins/Array/prototype/fill/return-abrupt-from-end.js # Expected a Test262Error but got a TypeError
2928
built-ins/Array/prototype/fill/return-abrupt-from-start.js # Expected a Test262Error but got a TypeError
3029
built-ins/Array/prototype/filter/15.4.4.20-10-3.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
31-
built-ins/Array/prototype/filter/15.4.4.20-10-4.js # Property key must be a string or number, or Symbol.asyncIterator/Symbol.iterator.
3230
built-ins/Array/prototype/filter/15.4.4.20-6-2.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
3331
built-ins/Array/prototype/filter/15.4.4.20-6-3.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
3432
built-ins/Array/prototype/filter/15.4.4.20-6-4.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
@@ -44,7 +42,6 @@ built-ins/Array/prototype/flat/non-object-ctor-throws.js # null value Expected
4442
built-ins/Array/prototype/forEach/15.4.4.18-7-5.js # callCnt Expected SameValue(«4», «5») to be true
4543
built-ins/Array/prototype/forEach/15.4.4.18-7-c-i-8.js # testResult !== true
4644
built-ins/Array/prototype/forEach/15.4.4.18-8-10.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
47-
built-ins/Array/prototype/forEach/15.4.4.18-8-12.js # Property key must be a string or number, or Symbol.asyncIterator/Symbol.iterator.
4845
built-ins/Array/prototype/forEach/15.4.4.18-8-2.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
4946
built-ins/Array/prototype/forEach/15.4.4.18-8-3.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
5047
built-ins/Array/prototype/forEach/15.4.4.18-8-4.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
@@ -107,7 +104,6 @@ built-ins/Array/prototype/lastIndexOf/length-zero-returns-minus-one.js # Array.
107104
built-ins/Array/prototype/map/15.4.4.19-8-6.js # resArr[4] Expected SameValue(«undefined», «1») to be true
108105
built-ins/Array/prototype/map/15.4.4.19-8-c-i-8.js # newArr[1] Expected SameValue(«13», «true») to be true
109106
built-ins/Array/prototype/map/15.4.4.19-9-3.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
110-
built-ins/Array/prototype/map/15.4.4.19-9-4.js # Property key must be a string or number, or Symbol.asyncIterator/Symbol.iterator.
111107
built-ins/Array/prototype/map/create-ctor-non-object.js # null value Expected a TypeError to be thrown but no exception was thrown at all
112108
built-ins/Array/prototype/pop/S15.4.4.6_A2_T1.js # Array.prototype.pop called on incompatible receiver a data object.
113109
built-ins/Array/prototype/pop/S15.4.4.6_A2_T4.js # Array.prototype.pop called on incompatible receiver a data object.
@@ -127,7 +123,6 @@ built-ins/Array/prototype/reduce/15.4.4.21-10-3.js # … cannot be constructed:
127123
built-ins/Array/prototype/reduce/15.4.4.21-10-4.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
128124
built-ins/Array/prototype/reduce/15.4.4.21-10-6.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
129125
built-ins/Array/prototype/reduce/15.4.4.21-10-7.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
130-
built-ins/Array/prototype/reduce/15.4.4.21-10-8.js # Property key must be a string or number, or Symbol.asyncIterator/Symbol.iterator.
131126
built-ins/Array/prototype/reduce/15.4.4.21-5-2.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
132127
built-ins/Array/prototype/reduce/15.4.4.21-5-3.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
133128
built-ins/Array/prototype/reduce/15.4.4.21-5-4.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
@@ -151,7 +146,6 @@ built-ins/Array/prototype/reduceRight/15.4.4.22-10-3.js # … cannot be constru
151146
built-ins/Array/prototype/reduceRight/15.4.4.22-10-4.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
152147
built-ins/Array/prototype/reduceRight/15.4.4.22-10-6.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
153148
built-ins/Array/prototype/reduceRight/15.4.4.22-10-7.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
154-
built-ins/Array/prototype/reduceRight/15.4.4.22-10-8.js # Property key must be a string or number, or Symbol.asyncIterator/Symbol.iterator.
155149
built-ins/Array/prototype/reduceRight/15.4.4.22-5-2.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
156150
built-ins/Array/prototype/reduceRight/15.4.4.22-5-3.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
157151
built-ins/Array/prototype/reduceRight/15.4.4.22-5-4.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
@@ -197,7 +191,6 @@ built-ins/Array/prototype/slice/create-ctor-non-object.js # null value Expected
197191
built-ins/Array/prototype/some/15.4.4.17-7-6.js # res Expected SameValue(«false», «true») to be true
198192
built-ins/Array/prototype/some/15.4.4.17-7-c-i-8.js # [, ].some(callbackfn) !== true
199193
built-ins/Array/prototype/some/15.4.4.17-8-10.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
200-
built-ins/Array/prototype/some/15.4.4.17-8-13.js # Property key must be a string or number, or Symbol.asyncIterator/Symbol.iterator.
201194
built-ins/Array/prototype/some/15.4.4.17-8-2.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
202195
built-ins/Array/prototype/some/15.4.4.17-8-3.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct
203196
built-ins/Array/prototype/some/15.4.4.17-8-4.js # … cannot be constructed: user-defined constructors and classes are not supported. Call it as a funct

0 commit comments

Comments
 (0)