Fix edge cases for special values#11
Conversation
📝 WalkthroughWalkthroughThis PR adds comprehensive test cases for Number and String built-in methods while implementing corresponding type-guard improvements and garbage collection safeguards in the Goccia runtime to handle edge cases such as Symbol arguments, non-number inputs, and improved resource management during object construction. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
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)
Comment |
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.Values.ClassValue.pas`:
- Around line 425-432: TGocciaGC.Instance can be nil, so wrap the temporary root
calls with an Assigned check: before calling
TGocciaGC.Instance.AddTempRoot(Instance) and before
TGocciaGC.Instance.RemoveTempRoot(Instance) in the constructor invocation block
(around ConstructorToCall.Call(AArguments, Instance)), guard both calls with if
Assigned(TGocciaGC.Instance) then ...; apply the same defensive pattern to other
unguarded sites that call AddTempRoot/RemoveTempRoot such as Engine.Destroy,
Builtins.Benchmark/Globals, Builtins.GlobalPromise, MicrotaskQueue and
Goccia.Scope to avoid nil dereferences.
| begin | ||
| TGocciaGC.Instance.AddTempRoot(Instance); | ||
| try | ||
| ConstructorToCall.Call(AArguments, Instance); | ||
| finally | ||
| TGocciaGC.Instance.RemoveTempRoot(Instance); | ||
| end; | ||
| end; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, find the TGocciaGC class definition and how Instance is implemented
fd -e pas | xargs grep -l "TGocciaGC" | head -20Repository: frostney/GocciaScript
Length of output: 752
🏁 Script executed:
# Find TGocciaGC class definition and Instance property
rg -n "class TGocciaGC|class var.*Instance|property Instance" --type=pas -A 3Repository: frostney/GocciaScript
Length of output: 92
🏁 Script executed:
# Check how and when TGocciaGC is initialized
rg -n "TGocciaGC\.Create|InitializeGC|Setup|Initialize.*GC" --type=pas -B 2 -A 5Repository: frostney/GocciaScript
Length of output: 92
🏁 Script executed:
# Examine the specific file and lines in question
cat -n units/Goccia.Values.ClassValue.pas | sed -n '415,440p'Repository: frostney/GocciaScript
Length of output: 1067
🏁 Script executed:
# Check if TGocciaGC.Instance is guarded anywhere else in the codebase
rg -n "if Assigned.*TGocciaGC|if.*TGocciaGC.*nil" --type=pasRepository: frostney/GocciaScript
Length of output: 92
🏁 Script executed:
# Search for TGocciaGC class definition and Instance property
grep -rn "class TGocciaGC\|property Instance" units/Goccia.GarbageCollector.pas | head -20Repository: frostney/GocciaScript
Length of output: 47
🏁 Script executed:
# Find where TGocciaGC.Instance is used and check for guards
grep -n "TGocciaGC\.Instance" units/*.pas | head -30Repository: frostney/GocciaScript
Length of output: 2679
🏁 Script executed:
# Read the GarbageCollector unit to understand singleton implementation
head -100 units/Goccia.GarbageCollector.pasRepository: frostney/GocciaScript
Length of output: 2842
🏁 Script executed:
# Check Engine initialization to see when GC is set up
grep -n "GarbageCollector\|TGocciaGC" units/Goccia.Engine.pas | head -30Repository: frostney/GocciaScript
Length of output: 362
🏁 Script executed:
# Check if Instantiate can be called before engine initialization
grep -n "\.Instantiate\|TGocciaClassValue" units/Goccia.Engine.pasRepository: frostney/GocciaScript
Length of output: 436
🏁 Script executed:
# Check GlobalPromise unguarded calls for context
sed -n '535,550p' units/Goccia.Builtins.GlobalPromise.pasRepository: frostney/GocciaScript
Length of output: 635
🏁 Script executed:
# Check Engine line 157 and surrounding context
sed -n '150,175p' units/Goccia.Engine.pasRepository: frostney/GocciaScript
Length of output: 983
🏁 Script executed:
# Search for any other unguarded TGocciaGC.Instance calls
grep -n "TGocciaGC\.Instance\." units/*.pas | grep -v "if Assigned\|if not Assigned" | head -20Repository: frostney/GocciaScript
Length of output: 1865
Add Assigned check for TGocciaGC.Instance in AddTempRoot/RemoveTempRoot calls.
TGocciaGC.Instance is a lazy singleton that can be nil. While class instantiation is currently ordered after engine initialization, other parts of the codebase guard these calls (e.g., Engine.Destroy line 170, Builtins.Benchmark, Builtins.Globals, MicrotaskQueue). Lines 426 and 430 should follow the defensive pattern for consistency.
Additionally, this issue exists in other unguarded locations (Engine.pas:157, 197; Builtins.GlobalPromise:539, 544; Goccia.Scope:182) and should be addressed systematically.
🔧 Suggested guard
begin
- TGocciaGC.Instance.AddTempRoot(Instance);
+ if Assigned(TGocciaGC.Instance) then
+ TGocciaGC.Instance.AddTempRoot(Instance);
try
ConstructorToCall.Call(AArguments, Instance);
finally
- TGocciaGC.Instance.RemoveTempRoot(Instance);
+ if Assigned(TGocciaGC.Instance) then
+ TGocciaGC.Instance.RemoveTempRoot(Instance);
end;
end;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| begin | |
| TGocciaGC.Instance.AddTempRoot(Instance); | |
| try | |
| ConstructorToCall.Call(AArguments, Instance); | |
| finally | |
| TGocciaGC.Instance.RemoveTempRoot(Instance); | |
| end; | |
| end; | |
| begin | |
| if Assigned(TGocciaGC.Instance) then | |
| TGocciaGC.Instance.AddTempRoot(Instance); | |
| try | |
| ConstructorToCall.Call(AArguments, Instance); | |
| finally | |
| if Assigned(TGocciaGC.Instance) then | |
| TGocciaGC.Instance.RemoveTempRoot(Instance); | |
| end; | |
| end; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@units/Goccia.Values.ClassValue.pas` around lines 425 - 432,
TGocciaGC.Instance can be nil, so wrap the temporary root calls with an Assigned
check: before calling TGocciaGC.Instance.AddTempRoot(Instance) and before
TGocciaGC.Instance.RemoveTempRoot(Instance) in the constructor invocation block
(around ConstructorToCall.Call(AArguments, Instance)), guard both calls with if
Assigned(TGocciaGC.Instance) then ...; apply the same defensive pattern to other
unguarded sites that call AddTempRoot/RemoveTempRoot such as Engine.Destroy,
Builtins.Benchmark/Globals, Builtins.GlobalPromise, MicrotaskQueue and
Goccia.Scope to avoid nil dereferences.
Benchmark Results113 benchmarks · 113 unchanged · avg +0.7% arrays.js — 11 unchanged · avg +0.5%
classes.js — 10 unchanged · avg -0.2%
closures.js — 11 unchanged · avg +1.1%
collections.js — 12 unchanged · avg -0.5%
destructuring.js — 14 unchanged · avg +1.1%
fibonacci.js — 3 unchanged · avg +1.1%
json.js — 11 unchanged · avg +1.2%
numbers.js — 11 unchanged · avg +1.9%
objects.js — 7 unchanged · avg +0.5%
promises.js — 12 unchanged · avg +0.3%
strings.js — 11 unchanged · avg +0.4%
Measured on ubuntu-latest x64. Changes within ±7% are considered insignificant. |
Summary by CodeRabbit
Tests
Number.isFinite()andNumber.isInteger()with additional edge cases.String.charAt()andString.charCodeAt()tests, including Symbol argument handling and Infinity edge cases.Bug Fixes