Skip to content

Fix proxy defineProperty descriptors to omit absent fields#1936

Open
Kenaz123 wants to merge 2 commits intofacebook:static_hfrom
Kenaz123:fix-proxy-accessor-descriptor
Open

Fix proxy defineProperty descriptors to omit absent fields#1936
Kenaz123 wants to merge 2 commits intofacebook:static_hfrom
Kenaz123:fix-proxy-accessor-descriptor

Conversation

@Kenaz123
Copy link

Summary

Fix proxy defineProperty descriptor conversion to omit absent fields.

Before this change, objectFromPropertyDescriptor() in lib/VM/Operations.cpp would materialize some descriptor fields unconditionally based on descriptor kind, instead of only emitting fields that were actually present in the input
property descriptor.

For data descriptors, it always emitted value, even when [[Value]] was not present:

if (!dpFlags.isAccessor()) {
  // Data Descriptor
  auto result = JSObject::defineOwnProperty(
      obj,
      runtime,
      Predefined::getSymbolID(Predefined::value),
      dpf,
      valueOrAccessor,
      PropOpFlags().plusThrowOnError());
  ...
}

For accessor descriptors, it always emitted both get and set, even when one side was absent:

auto result = JSObject::defineOwnProperty(
    obj,
    runtime,
    Predefined::getSymbolID(Predefined::get),
    dpf,
    getter,
    PropOpFlags().plusThrowOnError());
...

result = JSObject::defineOwnProperty(
    obj,
    runtime,
    Predefined::getSymbolID(Predefined::set),
    dpf,
    setter,
    PropOpFlags().plusThrowOnError());

That is incorrect for forwarding proxies. When a proxy defineProperty trap receives a descriptor object, absent fields must stay absent. Emitting them unconditionally changes semantics:

  • Accessor-only updates could inject set: undefined and silently remove an existing setter.
  • Attribute-only data updates could inject value: undefined and silently clobber an existing property value.

This change fixes both cases by only emitting value, get, and set when their corresponding DefinePropertyFlags bits are set.

accessor-poc.js

Original accessor PoC reproduction:

./build/bin/hermes ./accessor-poc.js

Original accessor output before the fix:

BUG CONFIRMED: setter was silently removed (spurious 'set: undefined' injected into descriptor)

value-poc.js

Original value PoC reproduction:

./build/bin/hermes ./value-poc.js

Original value output before the fix:

target.x = undefined

Test Plan

cmake -S ./hermes -B ./hermes/build -G Ninja -DCMAKE_BUILD_TYPE=Debug
cmake --build ./hermes/build --target hermes -j4

./hermes/build/bin/hermes -Xes6-proxy -non-strict -O -target=HBC \
  ./hermes/test/hermes/regress-proxy-accessor-descriptor.js

./hermes/build/bin/hermes ./accessor-poc.js

./hermes/build/bin/hermes -Xes6-proxy -non-strict -O -target=HBC \
  ./hermes/test/hermes/regress-proxy-defineproperty-value.js

./hermes/build-static_h/bin/hermes ./value-poc.js

Observed results:

$ hermes -Xes6-proxy -non-strict -O -target=HBC test/hermes/regress-proxy-accessor-descriptor.js
has get: true
has set: false
configurable: true
setter called: 5
setter intact: true

$ hermes ./accessor-poc.js
setter called: 5
No bug: setter still intact

$ hermes -Xes6-proxy -non-strict -O -target=HBC test/hermes/regress-proxy-defineproperty-value.js
has value: false
has writable: true
target.x: 42
descriptor.writable: false

$ hermes ./value-poc.js
target.x = 42

@meta-cla meta-cla bot added the CLA Signed Do not delete this pull request or issue due to inactivity. label Mar 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed Do not delete this pull request or issue due to inactivity.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant