@@ -10,6 +10,7 @@ import {
1010 setSurroundingAgent ,
1111 Agent ,
1212 HostReportErrors ,
13+ HostCleanupFinalizationGroup ,
1314 FEATURES ,
1415} from './engine.mjs' ;
1516import {
@@ -22,7 +23,7 @@ import {
2223 AbruptCompletion ,
2324 Completion ,
2425 NormalCompletion ,
25- Q ,
26+ Q , X ,
2627 ThrowCompletion ,
2728 EnsureCompletion ,
2829} from './completion.mjs' ;
@@ -45,10 +46,67 @@ export {
4546
4647export { inspect } from './inspect.mjs' ;
4748
49+ function mark ( ) {
50+ const marked = new Set ( ) ;
51+ const weakrefs = new Set ( ) ;
52+ const fgs = new Set ( ) ;
53+
54+ const markCb = ( o ) => {
55+ if ( o === undefined || o === null ) {
56+ return ;
57+ }
58+ if ( marked . has ( o ) ) {
59+ return ;
60+ }
61+ marked . add ( o ) ;
62+ if ( 'WeakRefTarget' in o ) {
63+ weakrefs . add ( o ) ;
64+ }
65+ if ( 'Cells' in o ) {
66+ fgs . add ( o ) ;
67+ }
68+ o . mark ( markCb ) ;
69+ } ;
70+ markCb ( surroundingAgent ) ;
71+
72+ // https://tc39.es/proposal-weakrefs/#sec-weakref-execution
73+ // At any time, if an object obj is not live, an ECMAScript implementation may perform the following steps atomically:
74+ // 1. For each WeakRef ref such that ref.[[WeakRefTarget]] is obj,
75+ // a. Set ref.[[WeakRefTarget]] to empty.
76+ // 2. For each FinalizationGroup fg such that fg.[[Cells]] contains cell, such that cell.[[WeakRefTarget]] is obj,
77+ // a. Set cell.[[WeakRefTarget]] to empty.
78+ // b. Optionally, perform ! HostCleanupFinalizationGroup(fg).
79+
80+ weakrefs . forEach ( ( w ) => {
81+ if ( ! marked . has ( w . WeakRefTarget ) ) {
82+ w . WeakRefTarget = undefined ;
83+ }
84+ } ) ;
85+
86+ fgs . forEach ( ( fg ) => {
87+ let foundEmptyCell = false ;
88+ fg . Cells . forEach ( ( cell ) => {
89+ if ( ! marked . has ( cell . WeakRefTarget ) ) {
90+ cell . WeakRefTarget = undefined ;
91+ foundEmptyCell = true ;
92+ }
93+ } ) ;
94+ if ( foundEmptyCell ) {
95+ X ( HostCleanupFinalizationGroup ( fg ) ) ;
96+ }
97+ } ) ;
98+ }
99+
48100function runJobQueue ( ) {
101+ if ( surroundingAgent . executionContextStack . length !== 0 ) {
102+ return ;
103+ }
104+
105+
49106 while ( true ) { // eslint-disable-line no-constant-condition
50107 const nextQueue = surroundingAgent . jobQueue ;
51- if ( nextQueue . length === 0 ) {
108+ if ( nextQueue . length === 0
109+ || nextQueue . find ( ( j ) => j . HostDefined . queueName !== 'FinalizationCleanup' ) === undefined ) {
52110 break ;
53111 }
54112 const nextPending = nextQueue . shift ( ) ;
@@ -58,10 +116,16 @@ function runJobQueue() {
58116 newContext . ScriptOrModule = nextPending . ScriptOrModule ;
59117 surroundingAgent . executionContextStack . push ( newContext ) ;
60118 const result = nextPending . Job ( ...nextPending . Arguments ) ;
61- surroundingAgent . executionContextStack . pop ( newContext ) ;
62119 if ( result instanceof AbruptCompletion ) {
63120 HostReportErrors ( result . Value ) ;
64121 }
122+
123+ if ( surroundingAgent . feature ( 'WeakRefs' ) ) {
124+ AbstractOps . ClearKeptObjects ( ) ;
125+ mark ( ) ;
126+ }
127+
128+ surroundingAgent . executionContextStack . pop ( newContext ) ;
65129 }
66130}
67131
@@ -132,7 +196,8 @@ class APIRealm {
132196 if ( typeof sourceText !== 'string' ) {
133197 throw new TypeError ( 'sourceText must be a string' ) ;
134198 }
135- return this . scope ( ( ) => {
199+
200+ const res = this . scope ( ( ) => {
136201 // BEGIN ScriptEvaluationJob
137202 const realm = surroundingAgent . currentRealmRecord ;
138203 const s = ParseScript ( sourceText , realm , {
@@ -146,12 +211,14 @@ class APIRealm {
146211 }
147212 // END ScriptEvaluationJob
148213
149- const res = Q ( ScriptEvaluation ( s ) ) ;
214+ return EnsureCompletion ( ScriptEvaluation ( s ) ) ;
215+ } ) ;
150216
217+ if ( ! ( res instanceof AbruptCompletion ) ) {
151218 runJobQueue ( ) ;
219+ }
152220
153- return EnsureCompletion ( res ) ;
154- } ) ;
221+ return res ;
155222 }
156223
157224 createSourceTextModule ( specifier , sourceText ) {
@@ -167,11 +234,13 @@ class APIRealm {
167234 specifier,
168235 Link : ( ) => this . scope ( ( ) => module . Link ( ) ) ,
169236 GetNamespace : ( ) => this . scope ( ( ) => GetModuleNamespace ( module ) ) ,
170- Evaluate : ( ) => this . scope ( ( ) => {
171- const result = module . Evaluate ( ) ;
172- runJobQueue ( ) ;
173- return result ;
174- } ) ,
237+ Evaluate : ( ) => {
238+ const res = this . scope ( ( ) => module . Evaluate ( ) ) ;
239+ if ( ! ( res instanceof AbruptCompletion ) ) {
240+ runJobQueue ( ) ;
241+ }
242+ return res ;
243+ } ,
175244 } ,
176245 } ) ) ;
177246 if ( Array . isArray ( module ) ) {
@@ -229,6 +298,7 @@ export {
229298export function Throw ( realm , V , ...args ) {
230299 return realm . scope ( ( ) => {
231300 if ( typeof V === 'string' ) {
301+ // eslint-disable-next-line engine262/valid-throw
232302 return surroundingAgent . Throw ( V , 'Raw' , args [ 0 ] ) ;
233303 }
234304 return new ThrowCompletion ( V ) ;
0 commit comments