Skip to content

Conversation

@anivar
Copy link
Contributor

@anivar anivar commented Aug 31, 2025

This PR implements ES2021 WeakRef and FinalizationRegistry with improved architecture addressing previous review feedback.

Recent Updates (Oct 2025)

WeakRef Symbol Rejection Fix: Fixed critical ECMAScript compliance issue where WeakRef incorrectly accepted Symbol values as targets. The implementation now properly rejects Symbols per the basic WeakRef specification, resolving Test262 compliance failures including throws-when-target-cannot-be-held-weakly.js.

Full Test262 Regeneration: Completed comprehensive Test262 properties regeneration with 102,862 tests to ensure no regressions were introduced. WeakRef maintains 5/29 test pass rate (17.24%) with the Symbol rejection fix resolving the originally failing test without breaking existing functionality.

Architecture

The implementation addresses @aardvark179's architectural concerns through:

PhantomReference-based cleanup: Switched from WeakReference to PhantomReference for correct garbage collection semantics. PhantomReferences ensure cleanup callbacks only execute after objects are fully finalized, preventing object resurrection and matching ECMAScript specification requirements.

Shared ReferenceQueue: Implemented single shared queue across all FinalizationRegistry instances to avoid the performance overhead of multiple processor queues. This design improves efficiency and makes cleanup processing more predictable.

Context integration: Cleanup processing integrates with Rhino's execution model through the Context's micro-task processing loop, providing better timing control and thread safety.

Code Organization

The implementation separates concerns across focused classes:

  • NativeFinalizationRegistry.java (277 lines): JavaScript API implementation and callback execution
  • FinalizationValidation.java (121 lines): ECMAScript validation logic for targets, tokens, and SameValue semantics
  • FinalizationRegistrationManager.java (165 lines): Registration lifecycle with thread-safe operations
  • FinalizationQueue.java (83 lines): Shared PhantomReference infrastructure and Context integration
  • NativeWeakRef.java: Fixed Symbol validation to properly reject Symbol targets per ECMAScript spec

ECMAScript 2021 Compliance

WeakRef Symbol Validation: WeakRef constructor now correctly rejects Symbol values as targets, implementing the CanBeHeldWeakly specification requirement that only objects can be held weakly.

Symbol.for() handling: Correctly rejects registered symbols as unregister tokens per CanBeHeldWeakly specification. Symbols created with Symbol.for() persist in the global registry and violate weak reference semantics.

Thread safety: Eliminates stored Context references to prevent multi-threaded execution issues. Context is always passed as parameters and used immediately.

Validation: Centralized validation with consistent ECMAScript-compliant error messages and proper handling of edge cases.

What's Not Covered

symbols-as-weakmap-keys feature: Advanced Symbol support for weak references requires the symbols-as-weakmap-keys ECMAScript feature which is not yet implemented in Rhino. Related Test262 tests expecting Symbols to be accepted will continue to fail until this feature is added.

Cross-realm interactions: FinalizationRegistry instances across different realms don't share cleanup state. This requires deeper integration with Rhino's realm management system.

Advanced GC timing scenarios: Some test262 tests expect precise control over garbage collection timing that's difficult to achieve reliably across different JVM implementations.

Iterator protocol integration: The cleanup callback execution doesn't integrate with JavaScript iterators. This would require additional infrastructure for async iteration support.

WeakRef integration edge cases: Some advanced scenarios involving WeakRef and FinalizationRegistry interaction need more sophisticated reference tracking than the current implementation provides.

These limitations represent architectural challenges that would require significant changes to Rhino's core execution model and are beyond the scope of this foundational implementation.

Testing

WeakRef: Passes core functionality tests including Symbol rejection validation. Remaining 24/29 test failures are due to missing symbols-as-weakmap-keys feature support and other advanced ECMAScript features not yet implemented in Rhino.

FinalizationRegistry: Currently passes 11/47 test262 tests covering core functionality including constructor validation, registration/unregistration, and cleanup processing.

All existing Rhino tests continue to pass with full functionality preserved. Comprehensive Test262 regeneration confirms no regressions introduced.

Addresses #943 and incorporates architectural feedback from @aardvark179's review.

@anivar anivar force-pushed the es2021-finalization-registry branch 6 times, most recently from ab4f5a2 to 67a3ea1 Compare September 12, 2025 08:30
Copy link
Collaborator

@gbrail gbrail left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks -- this looks like good progress. I have a bunch of questions, please take a look -- thanks!

@anivar anivar force-pushed the es2021-finalization-registry branch 6 times, most recently from 11cb533 to b10db34 Compare September 21, 2025 14:09
@anivar anivar changed the title Implement ES2021 FinalizationRegistry Implement ES2021 WeakRef and FinalizationRegistry with ES2023 symbol support Sep 21, 2025
@anivar anivar changed the title Implement ES2021 WeakRef and FinalizationRegistry with ES2023 symbol support Implement ES2021 WeakRef and FinalizationRegistry Sep 21, 2025
@anivar anivar force-pushed the es2021-finalization-registry branch 3 times, most recently from cdbeab4 to c0a925d Compare September 21, 2025 17:04
Copy link
Contributor

@aardvark179 aardvark179 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought I'd written a review of this, but I must have closed the tab or something.

So, according to the spec the cleanup routines should be run when the weak ref target is no EMPTY, so this should probably be written using PhantomReferences (they are put on the queue when their referent has been collected). I am also unsure what the effect is on the JVM of create a large number of processor queues, and they can be tricky to manage correctly.

Could you try implementing this using a single queue (maybe using a java.lang.ref.Cleaner and putting the tasks from there onto a queue in each Context. We could likely combine processing of those with the micro task loop run as the context is exited.

I think that would help avoid large reference queues hanging around, and would make it easier to understand when things can be run.

@anivar anivar force-pushed the es2021-finalization-registry branch from acc97e5 to ae802e2 Compare September 21, 2025 18:26
@anivar
Copy link
Contributor Author

anivar commented Sep 21, 2025

@aardvark179 Thank you for the detailed review! Yes, he did leave inline comments earlier and I responded to them and followed the suggestions. However, after refactoring the implementation, the filenames changed and I can't access those same comments anymore.

I understand your architectural suggestions:

  1. Using PhantomReferences instead of WeakReferences for more deterministic cleanup behavior
  2. Implementing a single shared ReferenceQueue across all FinalizationRegistry instances for efficiency
  3. Integrating the cleanup process with the Context's micro-task loop for better timing control

anivar added a commit to anivar/rhino that referenced this pull request Sep 21, 2025
…ferences

Complete rewrite addressing all PR mozilla#2058 feedback from aardvark179:

**Architecture Changes:**
- Replace WeakReference with PhantomReference for correct GC semantics
- Implement singleton FinalizationQueueManager with shared ReferenceQueue
- Add Context integration for proper JavaScript execution timing
- Thread-safe operations with ConcurrentHashMap and synchronized blocks

**Key Improvements:**
- Memory leak prevention with proper WeakReference usage in TokenKey
- O(1) token-based unregistration with reverse index
- Bounded cleanup queue (MAX_PENDING_CLEANUPS=10000) prevents OOM
- Complete cleanupSome() implementation with callback override support
- Comprehensive error handling with thread safety

**New Components:**
- FinalizationQueueManager.java - Singleton Cleaner-like infrastructure
- FinalizationRegistryCleanupBehaviorTest.java - 10 cleanup behavior tests
- FinalizationRegistryCrossRealmTest.java - 6 cross-realm scenarios

**Test Coverage:**
- 72+ tests across 5 test files
- Cross-realm support validated
- Internal implementation white-box testing
- GC integration verified

Ready for PR submission to Mozilla Rhino.
@anivar
Copy link
Contributor Author

anivar commented Sep 21, 2025

@aardvark179 Implementation completely rewritten per your feedback:

PhantomReferences now replace WeakReferences for correct GC semantics. Added shared ReferenceQueue with Context integration for cleanup processing in the JavaScript execution loop as requested.

Key improvements:

  • Thread-safe operations with ConcurrentHashMap
  • O(1) token-based unregistration
  • Memory leak prevention with proper cleanup
  • Significant code simplification
  • All deprecation and ErrorProne warnings properly resolved
  • 72+ tests passing including cross-realm scenarios

Ready for re-review!

anivar added a commit to anivar/rhino that referenced this pull request Oct 11, 2025
…ferences

Complete rewrite addressing all PR mozilla#2058 feedback from aardvark179:

**Architecture Changes:**
- Replace WeakReference with PhantomReference for correct GC semantics
- Implement singleton FinalizationQueueManager with shared ReferenceQueue
- Add Context integration for proper JavaScript execution timing
- Thread-safe operations with ConcurrentHashMap and synchronized blocks

**Key Improvements:**
- Memory leak prevention with proper WeakReference usage in TokenKey
- O(1) token-based unregistration with reverse index
- Bounded cleanup queue (MAX_PENDING_CLEANUPS=10000) prevents OOM
- Complete cleanupSome() implementation with callback override support
- Comprehensive error handling with thread safety

**New Components:**
- FinalizationQueueManager.java - Singleton Cleaner-like infrastructure
- FinalizationRegistryCleanupBehaviorTest.java - 10 cleanup behavior tests
- FinalizationRegistryCrossRealmTest.java - 6 cross-realm scenarios

**Test Coverage:**
- 72+ tests across 5 test files
- Cross-realm support validated
- Internal implementation white-box testing
- GC integration verified

Ready for PR submission to Mozilla Rhino.
@anivar anivar force-pushed the es2021-finalization-registry branch from ac0dde3 to fd1cb89 Compare October 11, 2025 17:19
@aardvark179
Copy link
Contributor

Sorry I haven’t got to re-reviewing this one yet. It’s been a busy few weeks, but I promise I will get to it.

It would also be great to have a chat at some point so I can understand your Rhino use case better and maybe help plan out which features are the best ones to tackle first to enable that.

Adds ES2021 WeakRef and FinalizationRegistry constructors to allow JavaScript
code to hold weak references to objects and register cleanup callbacks.

WeakRef provides a deref() method to access weakly held objects and symbols.
FinalizationRegistry supports register(), unregister(), and cleanupSome() methods
for managing cleanup callbacks when objects are garbage collected.

Key features:
- Full ES2021 symbol support for targets and unregister tokens
- Uses PhantomReference for correct garbage collection timing
- Shared ReferenceQueue for efficient memory management
- Thread-safe implementation with proper cleanup
- Context-safe design prevents threading issues

Includes comprehensive test suite covering constructor validation,
registration behavior, garbage collection interactions, symbol support,
cross-realm behavior, and memory management.

Test262 results: FinalizationRegistry 11/47 tests passing, WeakRef 5/29 tests passing.
Symbol support fixes the main ES2021 compliance issues.

Addresses mozilla#943
@anivar anivar force-pushed the es2021-finalization-registry branch 3 times, most recently from 96d9ff3 to 9c07c68 Compare October 11, 2025 21:54
@anivar anivar force-pushed the es2021-finalization-registry branch from 9c07c68 to db27c5f Compare October 11, 2025 22:01
- Create FinalizationValidation utility class for all validation operations
- Remove duplicated validation methods from NativeFinalizationRegistry
- Reduce main class from 567 to 474 lines (16% reduction)
- Update test to avoid private implementation details
- All functionality preserved, tests passing

Improves code organization and reduces duplication while maintaining
full ECMAScript specification compliance.
Major refactoring to improve code organization and maintainability:

- Create FinalizationRegistrationManager for all registration logic
- Centralize registration, unregistration, and cleanup processing
- Simplify NativeFinalizationRegistry to focus on JavaScript API
- Reduce main class from 474 to 359 lines (24% reduction)
- Remove complex internal methods and duplicate logic
- All tests passing, functionality preserved

Architecture improvements:
- Single responsibility: manager handles complex registration logic
- Cleaner separation between API and implementation
- More testable components with focused responsibilities
- Simplified Context integration patterns

Total impact: 567 → 359 lines in main class (37% reduction)
Better organized across 3 focused classes vs 1 monolithic class
- Remove RegistrationReference and TokenKey inner classes
- Remove unused imports
- Clean up formatting and blank lines
- Reduce from 359 to 277 lines
- All tests passing
@anivar
Copy link
Contributor Author

anivar commented Oct 12, 2025

No worries @aardvark179!

I'd be interested in that chat about Rhino use cases and feature prioritization. Feel free to reach out whenever works for your schedule.

Thanks for the architectural guidance on PhantomReference and shared queue - it made the implementation much cleaner.

Symbols cannot be held weakly according to the ECMAScript specification.
The isValidTarget method now properly rejects Symbol values by adding
an explicit check: !(target instanceof Symbol).

This fixes the failing test:
- NativeWeakRefTest.weakRefRejectsSymbol
- Test262: built-ins/WeakRef/throws-when-target-cannot-be-held-weakly.js
- Updated test expectations following WeakRef Symbol validation fix
- 102,862 Test262 tests processed with comprehensive regeneration
- Confirmed no new test failures introduced
- WeakRef Symbol rejection test now passes correctly
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.

3 participants