@@ -42,9 +42,8 @@ const vm = require('vm');
4242 assert . notStrictEqual ( queue , other ) ;
4343}
4444
45- // `vm.MicrotaskQueue` (the constructor) is intentionally not exposed: only
46- // `vm.createMicrotaskQueue()` is public, so there is no way to construct or
47- // name the class directly from user code.
45+ // The constructor is intentionally not exported from `vm`; the public API for
46+ // creating queues is `vm.createMicrotaskQueue()`.
4847assert . strictEqual ( vm . MicrotaskQueue , undefined ) ;
4948
5049// `microtaskQueue.runMicrotasks()` throws when called on an unrelated `this`.
@@ -129,28 +128,69 @@ assert.strictEqual(vm.MicrotaskQueue, undefined);
129128 assert . deepStrictEqual ( trace , [ 'a' , 'b' ] ) ;
130129}
131130
132- // `vm.runInNewContext()` and `script.runInNewContext()` also accept the
133- // object form (they build their context via `getContextOptions()` +
134- // `createContext()`, both of which now go through `getMicrotaskModeOptions()`).
131+ // All context-creation APIs parse the object form once. In particular, they
132+ // must not read either property a second time after validation.
135133{
136- const queue = vm . createMicrotaskQueue ( ) ;
137- const trace = [ ] ;
138- const record = ( entry ) => trace . push ( entry ) ;
139-
140- vm . runInNewContext (
141- "Promise.resolve().then(() => record('runInNewContext'));" ,
142- { record } ,
143- { microtaskMode : { type : 'manual' , queue } } ,
134+ const script = new vm . Script (
135+ "Promise.resolve().then(() => record('script.runInNewContext'));" ,
144136 ) ;
145- assert . deepStrictEqual ( trace , [ ] ) ;
146- queue . runMicrotasks ( ) ;
147- assert . deepStrictEqual ( trace , [ 'runInNewContext' ] ) ;
148-
149- const script = new vm . Script ( "Promise.resolve().then(() => record('script.runInNewContext'));" ) ;
150- script . runInNewContext ( { record } , { microtaskMode : { type : 'manual' , queue } } ) ;
151- assert . deepStrictEqual ( trace , [ 'runInNewContext' ] ) ;
152- queue . runMicrotasks ( ) ;
153- assert . deepStrictEqual ( trace , [ 'runInNewContext' , 'script.runInNewContext' ] ) ;
137+ const createAndRun = [
138+ {
139+ expected : 'createContext' ,
140+ run ( sandbox , options ) {
141+ const context = vm . createContext ( sandbox , options ) ;
142+ vm . runInContext (
143+ "Promise.resolve().then(() => record('createContext'));" ,
144+ context ,
145+ ) ;
146+ } ,
147+ } ,
148+ {
149+ expected : 'runInNewContext' ,
150+ run ( sandbox , options ) {
151+ vm . runInNewContext (
152+ "Promise.resolve().then(() => record('runInNewContext'));" ,
153+ sandbox ,
154+ options ,
155+ ) ;
156+ } ,
157+ } ,
158+ {
159+ expected : 'script.runInNewContext' ,
160+ run ( sandbox , options ) {
161+ script . runInNewContext ( sandbox , options ) ;
162+ } ,
163+ } ,
164+ ] ;
165+
166+ for ( const { expected, run } of createAndRun ) {
167+ const queue = vm . createMicrotaskQueue ( ) ;
168+ const trace = [ ] ;
169+ const record = ( entry ) => trace . push ( entry ) ;
170+ let typeGetCount = 0 ;
171+ let queueGetCount = 0 ;
172+ const microtaskMode = {
173+ get type ( ) {
174+ if ( ++ typeGetCount > 1 ) {
175+ throw new Error ( 'microtaskMode.type was read more than once' ) ;
176+ }
177+ return 'manual' ;
178+ } ,
179+ get queue ( ) {
180+ if ( ++ queueGetCount > 1 ) {
181+ throw new Error ( 'microtaskMode.queue was read more than once' ) ;
182+ }
183+ return queue ;
184+ } ,
185+ } ;
186+
187+ run ( { record } , { microtaskMode } ) ;
188+ assert . strictEqual ( typeGetCount , 1 ) ;
189+ assert . strictEqual ( queueGetCount , 1 ) ;
190+ assert . deepStrictEqual ( trace , [ ] ) ;
191+ queue . runMicrotasks ( ) ;
192+ assert . deepStrictEqual ( trace , [ expected ] ) ;
193+ }
154194}
155195
156196// `vm.constants.DONT_CONTEXTIFY` sandboxes (no wrapper object, the V8
0 commit comments