Skip to content

Fix Object.defineProperties evaluation order - #1834

Open
ybai08 wants to merge 1 commit into
quickjs-ng:masterfrom
ybai08:fix-object-defineproperties-order
Open

ybai08 wants to merge 1 commit into
quickjs-ng:masterfrom
ybai08:fix-object-defineproperties-order

Conversation

@ybai08

@ybai08 ybai08 commented Oct 18, 2026

Copy link
Copy Markdown

Fixes #1794.

ObjectDefineProperties (ECMA-262 20.1.2.3.1) does two passes. The first pass goes through each own key. For each key it calls [[GetOwnProperty]], and if the property is enumerable it reads the descriptor object with Get and converts it with ToPropertyDescriptor. Only then does it move on to the next key. The second pass defines the properties after every descriptor has been converted.

JS_ObjectDefineProperties, which Object.defineProperties and Object.create both use, differed in two ways:

  • It filtered all the keys for enumerability up front (JS_GPN_ENUM_ONLY). A proxy therefore saw every getOwnPropertyDescriptor call before the first get: ownKeys, gopd a, gopd b, get a, get b instead of ownKeys, gopd a, get a, gopd b, get b. A getter that made a later key non-enumerable, or deleted it, was also not taken into account.
  • It defined each property right after reading its descriptor. An invalid descriptor for a later key then threw after the earlier keys were already defined. The existing XXX comment pointed this out.

This change checks each key and converts its descriptor in a single loop, storing the results in an array. A second loop defines the properties. Object.defineProperty is unchanged.

V8 (Node 26.10.0) passes every assertion in the new test.

Validation:

  • Added tests/bug1794.js. On master it fails at line 12 with got |ownKeys,gopd a,gopd b,get a,get b|, expected |ownKeys,gopd a,get a,gopd b,get b|. With this change it passes.
  • The staging test test262/test/staging/sm/object/defineProperties-order.js (staging is excluded in test262.conf) fails on master and passes with this change.
  • run-test262 -m -c test262.conf -a: 40/82220 errors, the same as on master, and they match test262_errors.txt.
  • run-test262 -c tests.conf: 1/121 errors. The one failure is tests/test_std.js:254 ("second pipe handler was not called"), which fails the same way on master on this macOS machine.
  • ASan+UBSan debug build: tests/bug1794.js, the test262 built-ins/Object/defineProperties, built-ins/Object/create and built-ins/Object/defineProperty directories (0 errors), and a script that exercises the error paths (a throwing getter, a throwing descriptor field, a define failure on a frozen target, a throwing proxy trap) all ran clean.
  • cmake -DQJS_BUILD_WERROR=ON build, make ctest cxxtest, and a -DJS_CHECK_JSVALUE -Wall -Werror -fsyntax-only check of quickjs.c all pass. make codegen leaves no diff.
  • Not run: the Windows/MSVC, WASI, emscripten, Android and iOS CI builds, and valgrind.

ObjectDefineProperties (ECMA-262 20.1.2.3.1) looks up each own key with
[[GetOwnProperty]] and reads and converts its descriptor before moving
on to the next key, and defines the properties only after every
descriptor has been converted.

Object.defineProperties and Object.create filtered all the keys for
enumerability up front, so a proxy saw every getOwnPropertyDescriptor
call before the first get, and a getter that changed a later key was
not seen. Each property was also defined right after its descriptor was
read, so an invalid descriptor for a later key left the earlier ones
defined.

Check each key and convert its descriptor in one loop, then define the
properties in a second loop, as the XXX comment suggested.

Fixes quickjs-ng#1794

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Object.defineProperties reads all descriptors before Get, and defines before validating

1 participant