@@ -18,8 +18,15 @@ import { QuickJSDeferredPromise } from "./deferred-promise"
18
18
// eslint-disable-next-line @typescript-eslint/no-unused-vars
19
19
import type { shouldInterruptAfterDeadline } from "./interrupt-helpers"
20
20
import { QuickJSPromisePending , QuickJSUnwrapError } from "./errors"
21
- import type { Disposable } from "./lifetime"
22
- import { Lifetime , Scope , StaticLifetime , UsingDisposable , WeakLifetime } from "./lifetime"
21
+ import type { Disposable , DisposableArray } from "./lifetime"
22
+ import {
23
+ Lifetime ,
24
+ Scope ,
25
+ StaticLifetime ,
26
+ UsingDisposable ,
27
+ WeakLifetime ,
28
+ createDisposableArray ,
29
+ } from "./lifetime"
23
30
import type { HeapTypedArray } from "./memory"
24
31
import { ModuleMemory } from "./memory"
25
32
import type { ContextCallbacks , QuickJSModuleCallbacks } from "./module"
@@ -28,15 +35,23 @@ import type {
28
35
// eslint-disable-next-line @typescript-eslint/no-unused-vars
29
36
ExecutePendingJobsResult ,
30
37
} from "./runtime"
31
- import type { ContextEvalOptions , JSValue , PromiseExecutor , QuickJSHandle } from "./types"
32
- import { evalOptionsToFlags } from "./types"
38
+ import {
39
+ ContextEvalOptions ,
40
+ GetOwnPropertyNamesOptions ,
41
+ JSValue ,
42
+ PromiseExecutor ,
43
+ QuickJSHandle ,
44
+ StaticJSValue ,
45
+ } from "./types"
46
+ import { evalOptionsToFlags , getOwnPropertyNamesOptionsToFlags } from "./types"
33
47
import type {
34
48
LowLevelJavascriptVm ,
35
49
SuccessOrFail ,
36
50
VmCallResult ,
37
51
VmFunctionImplementation ,
38
52
VmPropertyDescriptor ,
39
53
} from "./vm-interface"
54
+ import { QuickJSIterator } from "./QuickJSIterator"
40
55
41
56
/**
42
57
* Property key for getting or setting a property on a handle with
@@ -109,6 +124,15 @@ class ContextMemory extends ModuleMemory implements Disposable {
109
124
heapValueHandle ( ptr : JSValuePointer ) : JSValue {
110
125
return new Lifetime ( ptr , this . copyJSValue , this . freeJSValue , this . owner )
111
126
}
127
+
128
+ /** Manage a heap pointer with the lifetime of the context */
129
+ staticHeapValueHandle ( ptr : JSValuePointer | JSValueConstPointer ) : StaticJSValue {
130
+ this . manage ( this . heapValueHandle ( ptr as JSValuePointer ) )
131
+ // This isn't technically a static lifetime, but since it has the same
132
+ // lifetime as the VM, it's okay to fake one since when the VM is
133
+ // disposed, no other functions will accept the value.
134
+ return new StaticLifetime ( ptr as JSValueConstPointer , this . owner ) as StaticJSValue
135
+ }
112
136
}
113
137
114
138
/**
@@ -178,6 +202,12 @@ export class QuickJSContext
178
202
protected _BigInt : QuickJSHandle | undefined = undefined
179
203
/** @private */
180
204
protected uint32Out : HeapTypedArray < Uint32Array , UInt32Pointer >
205
+ /** @private */
206
+ protected _Symbol : QuickJSHandle | undefined = undefined
207
+ /** @private */
208
+ protected _SymbolIterator : QuickJSHandle | undefined = undefined
209
+ /** @private */
210
+ protected _SymbolAsyncIterator : QuickJSHandle | undefined = undefined
181
211
182
212
/**
183
213
* Use {@link QuickJSRuntime#newContext} or {@link QuickJSWASMModule#newContext}
@@ -297,12 +327,7 @@ export class QuickJSContext
297
327
const ptr = this . ffi . QTS_GetGlobalObject ( this . ctx . value )
298
328
299
329
// Automatically clean up this reference when we dispose
300
- this . memory . manage ( this . memory . heapValueHandle ( ptr ) )
301
-
302
- // This isn't technically a static lifetime, but since it has the same
303
- // lifetime as the VM, it's okay to fake one since when the VM is
304
- // disposed, no other functions will accept the value.
305
- this . _global = new StaticLifetime ( ptr , this . runtime )
330
+ this . _global = this . memory . staticHeapValueHandle ( ptr )
306
331
return this . _global
307
332
}
308
333
@@ -349,6 +374,14 @@ export class QuickJSContext
349
374
return this . memory . heapValueHandle ( ptr )
350
375
}
351
376
377
+ /**
378
+ * Access a well-known symbol that is a property of the global Symbol object, like `Symbol.iterator`.
379
+ */
380
+ getWellKnownSymbol ( name : string ) : QuickJSHandle {
381
+ this . _Symbol ??= this . memory . manage ( this . getProp ( this . global , "Symbol" ) )
382
+ return this . getProp ( this . _Symbol , name )
383
+ }
384
+
352
385
/**
353
386
* Create a QuickJS [bigint](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) value.
354
387
*/
@@ -781,6 +814,73 @@ export class QuickJSContext
781
814
return this . uint32Out . value . typedArray [ 0 ]
782
815
}
783
816
817
+ /**
818
+ * `Object.getOwnPropertyNames(handle)`.
819
+ * Similar to the [standard semantics](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyNames).
820
+ */
821
+ getPropNames (
822
+ handle : QuickJSHandle ,
823
+ options : GetOwnPropertyNamesOptions ,
824
+ ) : SuccessOrFail < DisposableArray < JSValue > , QuickJSHandle > {
825
+ this . runtime . assertOwned ( handle )
826
+ handle . value // assert alive
827
+ const flags = getOwnPropertyNamesOptionsToFlags ( options )
828
+ return Scope . withScope ( ( scope ) => {
829
+ const outPtr = scope . manage ( this . memory . newMutablePointerArray < JSValuePointerPointer > ( 1 ) )
830
+ const errorPtr = this . ffi . QTS_GetOwnPropertyNames (
831
+ this . ctx . value ,
832
+ outPtr . value . ptr ,
833
+ this . uint32Out . value . ptr ,
834
+ handle . value ,
835
+ flags ,
836
+ )
837
+ if ( errorPtr ) {
838
+ return { error : this . memory . heapValueHandle ( errorPtr ) }
839
+ }
840
+ const len = this . uint32Out . value . typedArray [ 0 ]
841
+ const ptr = outPtr . value . typedArray [ 0 ]
842
+ const pointerArray = new Uint32Array ( this . module . HEAP8 . buffer , ptr , len )
843
+ const handles = Array . from ( pointerArray ) . map ( ( ptr ) =>
844
+ this . memory . heapValueHandle ( ptr as JSValuePointer ) ,
845
+ )
846
+ this . module . _free ( ptr )
847
+ return { value : createDisposableArray ( handles ) }
848
+ } )
849
+ }
850
+
851
+ /**
852
+ * `handle[Symbol.iterator]()`. See {@link QuickJSIterator}.
853
+ * Returns a host iterator that wraps and proxies calls to a guest iterator handle.
854
+ * Each step of the iteration returns a result, either an error or a handle to the next value.
855
+ * Once the iterator is done, the handle is automatically disposed, and the iterator
856
+ * is considered done if the handle is disposed.
857
+ *
858
+ * ```typescript
859
+ * for (const nextResult of context.unwrapResult(context.getIterator(arrayHandle)) {
860
+ * const nextHandle = context.unwrapResult(nextResult)
861
+ * try {
862
+ * // Do something with nextHandle
863
+ * console.log(context.dump(nextHandle))
864
+ * } finally {
865
+ * nextHandle.dispose()
866
+ * }
867
+ * }
868
+ * ```
869
+ */
870
+ getIterator ( handle : QuickJSHandle ) : SuccessOrFail < QuickJSIterator , QuickJSHandle > {
871
+ const SymbolIterator = ( this . _SymbolIterator ??= this . memory . manage (
872
+ this . getWellKnownSymbol ( "iterator" ) ,
873
+ ) )
874
+ return Scope . withScope ( ( scope ) => {
875
+ const methodHandle = scope . manage ( this . getProp ( handle , SymbolIterator ) )
876
+ const iteratorCallResult = this . callFunction ( methodHandle , handle )
877
+ if ( iteratorCallResult . error ) {
878
+ return iteratorCallResult
879
+ }
880
+ return { value : new QuickJSIterator ( iteratorCallResult . value , this ) }
881
+ } )
882
+ }
883
+
784
884
/**
785
885
* `handle[key] = value`.
786
886
* Set a property on a JSValue.
0 commit comments