Extract shared ToPropertyDescriptor helper#239
Conversation
…roperty Unify descriptor-parsing logic that was duplicated between Object.defineProperty and Reflect.defineProperty into a single ToPropertyDescriptor function (ES2026 §6.2.5.5), ensuring both APIs share identical validation semantics including mixed data+accessor rejection and getter/setter callable checks. Closes #236 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughExtracted descriptor-parsing logic from Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
Co-locate the helper with the descriptor types it constructs instead of a separate utility unit. The implementation uses clause imports ObjectValue to avoid a circular interface dependency. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@units/Goccia.PropertyDescriptor.Utils.pas`:
- Around line 42-56: The code inherits Writable into PropertyFlags even when
creating an accessor descriptor, causing pfWritable to remain set despite
TGocciaPropertyDescriptorAccessor treating writability via FSetter; update the
creation path (e.g., inside ToPropertyDescriptor / the branch that constructs
TGocciaPropertyDescriptorAccessor when AExistingDescriptor is an accessor) to
clear pfWritable from PropertyFlags before constructing or assigning to
TGocciaPropertyDescriptorAccessor so the flag set matches accessor semantics
(remove pfWritable from PropertyFlags passed to
TGocciaPropertyDescriptorAccessor).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 7fac9f91-fa12-49c9-bb11-667bb2c4b6d0
📒 Files selected for processing (7)
AGENTS.mddocs/value-system.mdtests/built-ins/Object/defineProperty.jstests/built-ins/Reflect/defineProperty.jsunits/Goccia.Builtins.GlobalObject.pasunits/Goccia.Builtins.GlobalReflect.pasunits/Goccia.PropertyDescriptor.Utils.pas
Benchmark Results274 benchmarks Interpreted: 🟢 66 improved · 🔴 171 regressed · 37 unchanged · avg -3.1% arraybuffer.js — Interp: 🟢 1, 🔴 13 · avg -10.0% · Bytecode: 🟢 11, 🔴 1, 2 unch. · avg +6.8%
arrays.js — Interp: 🔴 19 · avg -7.5% · Bytecode: 🟢 18, 🔴 1 · avg +12.6%
async-await.js — Interp: 🔴 6 · avg -10.6% · Bytecode: 🟢 6 · avg +7.1%
classes.js — Interp: 🔴 31 · avg -9.1% · Bytecode: 🟢 8, 🔴 9, 14 unch. · avg -3.8%
closures.js — Interp: 🔴 11 · avg -6.4% · Bytecode: 🟢 2, 🔴 3, 6 unch. · avg -1.8%
collections.js — Interp: 🔴 10, 2 unch. · avg -7.0% · Bytecode: 🟢 10, 🔴 2 · avg +9.6%
destructuring.js — Interp: 🔴 19, 3 unch. · avg -4.6% · Bytecode: 🟢 3, 🔴 11, 8 unch. · avg -3.9%
fibonacci.js — Interp: 🔴 6, 2 unch. · avg -4.1% · Bytecode: 🟢 5, 🔴 1, 2 unch. · avg +4.5%
for-of.js — Interp: 🟢 4, 3 unch. · avg +2.5% · Bytecode: 🟢 3, 4 unch. · avg +3.4%
helpers/bench-module.js — Interp: 0 · Bytecode: 0
iterators.js — Interp: 🟢 9, 🔴 5, 6 unch. · avg +0.5% · Bytecode: 🟢 9, 🔴 4, 7 unch. · avg +3.3%
json.js — Interp: 🔴 17, 3 unch. · avg -5.5% · Bytecode: 🟢 15, 5 unch. · avg +5.9%
jsx.jsx — Interp: 🔴 13, 8 unch. · avg -2.4% · Bytecode: 🟢 4, 🔴 7, 10 unch. · avg -0.9%
modules.js — Interp: 🟢 7, 🔴 2 · avg +1.1% · Bytecode: 🟢 8, 🔴 1 · avg +5.1%
numbers.js — Interp: 🟢 2, 🔴 9 · avg -3.3% · Bytecode: 🟢 7, 🔴 1, 3 unch. · avg +4.8%
objects.js — Interp: 🟢 6, 1 unch. · avg +4.1% · Bytecode: 🟢 1, 🔴 2, 4 unch. · avg +1.3%
promises.js — Interp: 🟢 9, 3 unch. · avg +5.7% · Bytecode: 🟢 2, 🔴 3, 7 unch. · avg +0.1%
regexp.js — Interp: 🟢 7, 🔴 2, 2 unch. · avg +3.2% · Bytecode: 🟢 7, 4 unch. · avg +4.2%
strings.js — Interp: 🟢 7, 🔴 1, 3 unch. · avg +3.0% · Bytecode: 🟢 6, 5 unch. · avg +6.5%
typed-arrays.js — Interp: 🟢 14, 🔴 7, 1 unch. · avg +2.4% · Bytecode: 🟢 5, 🔴 6, 11 unch. · avg -0.5%
Measured on ubuntu-latest x64. Benchmark ranges compare cached main-branch min/max ops/sec with the PR run; overlapping ranges are treated as unchanged noise. Percentage deltas are secondary context. |
Suite Timing
Measured on ubuntu-latest x64. |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@units/Goccia.Values.ObjectPropertyDescriptor.pas`:
- Around line 150-155: The code calls DescObj.HasProperty(PROP_WRITABLE)
multiple times which may reenter observable descriptor objects; cache the
presence check once in a local boolean (e.g. HasWritable :=
DescObj.HasProperty(PROP_WRITABLE)) and use that cached value for all subsequent
logic that uses the presence of PROP_WRITABLE when setting Writable and any
other branches that currently re-call HasProperty; apply the same caching
pattern where DescObj.HasProperty(PROP_ENUMERABLE) or
DescObj.HasProperty(PROP_CONFIGURABLE) are called multiple times to avoid extra
observable traps.
- Around line 55-60: To harden ToPropertyDescriptor, move the ES spec step-1
object validation into the helper: inside ToPropertyDescriptor check that
ADescriptorObject is a TGocciaObjectValue (or otherwise an object) before
casting, and if not raise the appropriate TypeError (e.g. EGocciaTypeError or
the project’s TGoccia type error) instead of performing the hard cast to
TGocciaObjectValue; update the code paths that previously assumed callers
validated (callers can keep their guards) and replace the direct cast with a
safe check + cast so ToPropertyDescriptor (TGocciaValue ->
TGocciaPropertyDescriptor) never causes an invalid-cast/AV when reused.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: af9c9771-9d40-499c-bf09-f74457de3aef
📒 Files selected for processing (4)
docs/value-system.mdunits/Goccia.Builtins.GlobalObject.pasunits/Goccia.Builtins.GlobalReflect.pasunits/Goccia.Values.ObjectPropertyDescriptor.pas
✅ Files skipped from review due to trivial changes (1)
- docs/value-system.md
- Add step-1 object type guard so the helper throws TypeError on non-object input instead of relying on callers to validate - Cache HasProperty(PROP_WRITABLE) in a local HasWritable boolean to avoid repeated observable property lookups (was called 3 times) - Strip pfWritable from PropertyFlags before constructing accessor descriptors, since TGocciaPropertyDescriptorAccessor determines writability via FSetter, not the flag Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
ToPropertyDescriptorfunction (units/Goccia.PropertyDescriptor.Utils.pas) implementing ES2026 §6.2.5.5Object.definePropertyandReflect.definePropertywith a single call to the shared helperObject.definePropertywas missing the mixed data+accessor rejection check (ES2026 §6.2.5.5 step 10) thatReflect.definePropertyhad — both APIs now throwTypeErrorfor descriptors with both (value/writable) and (get/set)IsCallableVMT dispatch (Reflect previously usedis TGocciaFunctionBase)Test plan
Object.definePropertyandReflect.definePropertytests pass--asi)Closes #236
🤖 Generated with Claude Code