@@ -502,31 +502,34 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
502502
503503 // Callbacks are PesistendObjects. When the scope ends, we need
504504 // to free every callback we created.
505- callbacks : std .ArrayListUnmanaged (v8 .Persistent (v8 .Function )) = .{} ,
505+ callbacks : std .ArrayListUnmanaged (v8 .Persistent (v8 .Function )) = .empty ,
506506
507507 // Serves two purposes. Like `callbacks` above, this is used to free
508508 // every PeristentObjet we've created during the lifetime of the scope.
509509 // More importantly, it serves as an identity map - for a given Zig
510510 // instance, we map it to the same PersistentObject.
511511 // The key is the @intFromPtr of the Zig value
512- identity_map : std .AutoHashMapUnmanaged (usize , PersistentObject ) = .{} ,
512+ identity_map : std .AutoHashMapUnmanaged (usize , PersistentObject ) = .empty ,
513513
514514 // Similar to the identity map, but used much less frequently. Some
515515 // web APIs have to manage opaque values. Ideally, they use an
516516 // JsObject, but the JsObject has no lifetime guarantee beyond the
517517 // current call. They can call .persist() on their JsObject to get
518518 // a `*PersistentObject()`. We need to track these to free them.
519519 // The key is the @intFromPtr of the v8.Object.handle.
520- js_object_map : std .AutoHashMapUnmanaged (usize , PersistentObject ) = .{} ,
520+ js_object_map : std .AutoHashMapUnmanaged (usize , PersistentObject ) = .empty ,
521521
522522 // When we need to load a resource (i.e. an external script), we call
523523 // this function to get the source. This is always a reference to the
524524 // Page's fetchModuleSource, but we use a function pointer
525525 // since this js module is decoupled from the browser implementation.
526526 module_loader : ModuleLoader ,
527527
528+ // Some Zig types have code to execute to cleanup
529+ destructor_callbacks : std .ArrayListUnmanaged (DestructorCallback ) = .empty ,
530+
528531 // Some Zig types have code to execute when the call scope ends
529- call_scope_end_callbacks : std .ArrayListUnmanaged (CallScopeEndCallback ) = .{} ,
532+ call_scope_end_callbacks : std .ArrayListUnmanaged (CallScopeEndCallback ) = .empty ,
530533
531534 const ModuleLoader = struct {
532535 ptr : * anyopaque ,
@@ -536,6 +539,17 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
536539 // no init, started with executor.startScope()
537540
538541 fn deinit (self : * Scope ) void {
542+ {
543+ // reverse order, as this has more chance of respecting any
544+ // dependencies objects might have with each other.
545+ const items = self .destructor_callbacks .items ;
546+ var i = items .len ;
547+ while (i > 0 ) {
548+ i -= 1 ;
549+ items [i ].destructor ();
550+ }
551+ }
552+
539553 {
540554 var it = self .identity_map .valueIterator ();
541555 while (it .next ()) | p | {
@@ -694,6 +708,10 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
694708 return gop .value_ptr .* ;
695709 }
696710
711+ if (comptime @hasDecl (ptr .child , "destructor" )) {
712+ try self .destructor_callbacks .append (scope_arena , DestructorCallback .init (value ));
713+ }
714+
697715 if (comptime @hasDecl (ptr .child , "jsCallScopeEnd" )) {
698716 try self .call_scope_end_callbacks .append (scope_arena , CallScopeEndCallback .init (value ));
699717 }
@@ -1700,20 +1718,48 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
17001718 }
17011719 }
17021720
1703- // An interface for types that want to their jsScopeEnd function to be
1721+ // An interface for types that want to have their jsDeinit function to be
1722+ // called when the call scope ends
1723+ const DestructorCallback = struct {
1724+ ptr : * anyopaque ,
1725+ destructorFn : * const fn (ptr : * anyopaque ) void ,
1726+
1727+ fn init (ptr : anytype ) DestructorCallback {
1728+ const T = @TypeOf (ptr );
1729+ const ptr_info = @typeInfo (T );
1730+
1731+ const gen = struct {
1732+ pub fn destructor (pointer : * anyopaque ) void {
1733+ const self : T = @ptrCast (@alignCast (pointer ));
1734+ return ptr_info .pointer .child .destructor (self );
1735+ }
1736+ };
1737+
1738+ return .{
1739+ .ptr = ptr ,
1740+ .destructorFn = gen .destructor ,
1741+ };
1742+ }
1743+
1744+ pub fn destructor (self : DestructorCallback ) void {
1745+ self .destructorFn (self .ptr );
1746+ }
1747+ };
1748+
1749+ // An interface for types that want to have their jsScopeEnd function be
17041750 // called when the call scope ends
17051751 const CallScopeEndCallback = struct {
17061752 ptr : * anyopaque ,
1707- callScopeEndFn : * const fn (ptr : * anyopaque , scope : * Scope ) void ,
1753+ callScopeEndFn : * const fn (ptr : * anyopaque ) void ,
17081754
17091755 fn init (ptr : anytype ) CallScopeEndCallback {
17101756 const T = @TypeOf (ptr );
17111757 const ptr_info = @typeInfo (T );
17121758
17131759 const gen = struct {
1714- pub fn callScopeEnd (pointer : * anyopaque , scope : * Scope ) void {
1760+ pub fn callScopeEnd (pointer : * anyopaque ) void {
17151761 const self : T = @ptrCast (@alignCast (pointer ));
1716- return ptr_info .pointer .child .jsCallScopeEnd (self , scope );
1762+ return ptr_info .pointer .child .jsCallScopeEnd (self );
17171763 }
17181764 };
17191765
@@ -1723,8 +1769,8 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
17231769 };
17241770 }
17251771
1726- pub fn callScopeEnd (self : CallScopeEndCallback , scope : * Scope ) void {
1727- self .callScopeEndFn (self .ptr , scope );
1772+ pub fn callScopeEnd (self : CallScopeEndCallback ) void {
1773+ self .callScopeEndFn (self .ptr );
17281774 }
17291775 };
17301776 };
@@ -1804,7 +1850,7 @@ fn Caller(comptime E: type, comptime State: type) type {
18041850 // when a top-level (call_depth == 0) function ends.
18051851 if (call_depth == 0 ) {
18061852 for (scope .call_scope_end_callbacks .items ) | cb | {
1807- cb .callScopeEnd (scope );
1853+ cb .callScopeEnd ();
18081854 }
18091855
18101856 const arena : * ArenaAllocator = @alignCast (@ptrCast (scope .call_arena .ptr ));
0 commit comments