Fix increment/decrement for computed member expressions #54
Conversation
- Interpreter: Evaluate PropertyExpression for computed member operands in TGocciaIncrementExpression.Evaluate instead of reading empty PropertyName. Use AssignProperty instead of DefinePropertyOnValue so arrays are updated via virtual SetProperty dispatch. - Compiler: Add member expression support to CompileIncrement — non-computed members use OP_RT_GET_PROP/OP_RT_SET_PROP, computed members use OP_ARRAY_GET/OP_ARRAY_SET. - Tests: Add computed member, property access, and variable-key tests for both increment and decrement operators. Closes #52 Co-authored-by: Johannes Stein <frostney@users.noreply.github.com>
|
Cursor Agent can help with this pull request. Just |
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThese changes extend increment and decrement operators to work with object properties and computed member expressions. New test coverage validates post- and pre-increment/decrement behavior on object properties, array indices, and dynamic keys. Corresponding runtime evaluation and compiler code generation support member expression operands. Changes
Sequence Diagram(s)sequenceDiagram
participant Source as Source Code
participant Compiler as Compiler
participant CodeGen as Code Generator
participant Runtime as Runtime Engine
Source->>Compiler: Parse member increment (obj.count++)
Compiler->>Compiler: Detect TGocciaMemberExpression operand
alt Computed Member
Compiler->>CodeGen: CompileIncrementComputedMember
CodeGen->>CodeGen: Emit get array element opcode
CodeGen->>CodeGen: Emit increment opcode
CodeGen->>CodeGen: Emit set array element opcode
else Non-computed Member
Compiler->>CodeGen: CompileIncrementMember
CodeGen->>CodeGen: Emit get property opcode
CodeGen->>CodeGen: Emit increment opcode
CodeGen->>CodeGen: Emit set property opcode
end
CodeGen-->>Runtime: Compiled bytecode
Runtime->>Runtime: Execute: evaluate object reference
Runtime->>Runtime: Execute: retrieve current property value
Runtime->>Runtime: Execute: increment and store back
Runtime-->>Source: Return pre/post increment value
sequenceDiagram
participant Evaluator as AST Evaluator
participant Object as Object Context
participant Property as Property Access
Evaluator->>Evaluator: Detect member expression in increment
Evaluator->>Object: Evaluate ObjectExpr
Object-->>Evaluator: Object reference
Evaluator->>Property: Resolve property name (computed or static)
Property-->>Evaluator: Property name string
Evaluator->>Object: Get current property value (OldValue)
Object-->>Evaluator: Current value
Evaluator->>Evaluator: Calculate new value (OldValue + 1)
Evaluator->>Object: AssignProperty(Obj, PropName, NewValue)
Object-->>Evaluator: Assignment result
Evaluator-->>Evaluator: Return prefix/postfix result
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Benchmark Results254 benchmarks · 🟢 12 improved · 🔴 1 regressed · 241 unchanged · avg +1.4% arraybuffer.js — 14 unchanged · avg +0.7%
arrays.js — 19 unchanged · avg -0.5%
async-await.js — 6 unchanged · avg -0.6%
classes.js — 31 unchanged · avg +0.2%
closures.js — 11 unchanged · avg -0.5%
collections.js — 🔴 1 regressed, 11 unchanged · avg -0.4%
destructuring.js — 22 unchanged · avg +0.2%
fibonacci.js — 8 unchanged · avg +1.0%
for-of.js — 7 unchanged · avg -2.4%
iterators.js — 🟢 7 improved, 13 unchanged · avg +5.8%
json.js — 🟢 3 improved, 17 unchanged · avg +3.5%
jsx.jsx — 21 unchanged · avg +1.5%
numbers.js — 🟢 1 improved, 10 unchanged · avg +2.6%
objects.js — 🟢 1 improved, 6 unchanged · avg +3.8%
promises.js — 12 unchanged · avg +2.7%
strings.js — 11 unchanged · avg +2.6%
typed-arrays.js — 22 unchanged · avg +1.8%
Measured on ubuntu-latest x64. Changes within ±7% are considered insignificant. |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
units/Goccia.AST.Expressions.pas (1)
1145-1157:⚠️ Potential issue | 🟠 MajorComputed-member updates currently drop symbol keys.
This branch coerces every computed key through
ToStringLiteraland always writes viaAssignProperty, soobj[sym]++/obj[sym]--will bypass the existing symbol-property path used by computed assignment and compound assignment in this same unit. Please mirror that symbol handling here for both the read and the write.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@units/Goccia.AST.Expressions.pas` around lines 1145 - 1157, The computed-member update branch currently coerces computed keys via PropertyExpression.Evaluate(...).ToStringLiteral and always uses AssignProperty, which drops symbol keys; update this path in the block handling MemberExpr.Computed so that after evaluating MemberExpr.PropertyExpression you detect if the evaluated key is a symbol (rather than blindly calling ToStringLiteral), and when it is use the same symbol-based lookup/write used elsewhere (use Obj.GetProperty with a symbol-aware getter and the symbol-aware assignment path used for computed assignment/compound assignment) for both the read (OldValue) and the write (AssignProperty replacement for symbols), otherwise keep the existing ToStringLiteral and string-key code; keep usage of PerformIncrement and return TGocciaUndefinedLiteralValue.UndefinedValue on error as before.
🧹 Nitpick comments (1)
tests/language/expressions/arithmetic/increment.js (1)
30-72: Add one failing-write regression for the new member-update path.These tests cover successful writes, but they do not pin the behavior that motivated the interpreter change: routing updates through the real property-assignment path. Please add one error/boundary case here as well (for example, a getter-only or non-writable property) so a future fallback to the old direct-define behavior gets caught.
Based on learnings: "JavaScript tests should cover happy paths, edge cases, and error cases. Keep tests isolated and grouped by feature/filename."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/language/expressions/arithmetic/increment.js` around lines 30 - 72, Add a failing-write regression test that ensures member-update uses the real property-assignment path by asserting an increment against a non-writable/getter-only property throws; for example add a new test (e.g. "post-increment on getter-only property throws") that creates an object with Object.defineProperty(obj, "x", { get: () => 1 }) or a non-writable value via defineProperty, then assert expect(() => obj.x++).toThrow(TypeError) (or expect().toThrow()) so any fallback to direct-define behavior will fail the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@units/Goccia.AST.Expressions.pas`:
- Around line 1145-1157: The computed-member update branch currently coerces
computed keys via PropertyExpression.Evaluate(...).ToStringLiteral and always
uses AssignProperty, which drops symbol keys; update this path in the block
handling MemberExpr.Computed so that after evaluating
MemberExpr.PropertyExpression you detect if the evaluated key is a symbol
(rather than blindly calling ToStringLiteral), and when it is use the same
symbol-based lookup/write used elsewhere (use Obj.GetProperty with a
symbol-aware getter and the symbol-aware assignment path used for computed
assignment/compound assignment) for both the read (OldValue) and the write
(AssignProperty replacement for symbols), otherwise keep the existing
ToStringLiteral and string-key code; keep usage of PerformIncrement and return
TGocciaUndefinedLiteralValue.UndefinedValue on error as before.
---
Nitpick comments:
In `@tests/language/expressions/arithmetic/increment.js`:
- Around line 30-72: Add a failing-write regression test that ensures
member-update uses the real property-assignment path by asserting an increment
against a non-writable/getter-only property throws; for example add a new test
(e.g. "post-increment on getter-only property throws") that creates an object
with Object.defineProperty(obj, "x", { get: () => 1 }) or a non-writable value
via defineProperty, then assert expect(() => obj.x++).toThrow(TypeError) (or
expect().toThrow()) so any fallback to direct-define behavior will fail the
test.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 3d600438-3d99-4eff-9824-27032fd04bd7
📒 Files selected for processing (4)
tests/language/expressions/arithmetic/decrement.jstests/language/expressions/arithmetic/increment.jsunits/Goccia.AST.Expressions.pasunits/Goccia.Compiler.Expressions.pas
Mirror the symbol-aware property read/write pattern from TGocciaComputedPropertyAssignmentExpression and TGocciaComputedPropertyCompoundAssignmentExpression so that obj[sym]++ / obj[sym]-- correctly routes through GetSymbolProperty/AssignSymbolProperty instead of coercing the symbol key via ToStringLiteral. Add regression tests for symbol-keyed increment/decrement and non-writable property increment/decrement. Made-with: Cursor
Addressing CodeRabbit review findings1. Symbol key handling in computed member increment/decrement (Major — True Positive) Fixed in 76a0308. The computed member path in 2. Regression test for non-writable property increment/decrement (Nitpick — Actionable) Fixed in 76a0308. Added All 3,428 tests pass in both interpreted and bytecode modes. |
Benchmark regressions are within CI noise range — the 13 flagged regressions are scattered across unrelated benchmarks (strings, promises, JSX, etc.) with no correlation to the increment/decrement change. The -2.2% average is consistent with normal run-to-run variance on shared CI runners. |
No action needed — standard Cursor Agent introduction. |
Fix increment/decrement for computed member expressions and array elements.
This PR addresses issue #52 by ensuring that increment/decrement operations on computed member expressions (e.g.,
arr[i]++,obj[key]--) correctly mutate values in both interpreter and bytecode modes. It also fixes a latent bug whereDefinePropertyOnValueincorrectly handled array updates, now usingAssignPropertyto dispatch to the virtualSetPropertymethod.Summary by CodeRabbit
Release Notes
Bug Fixes
Tests