5858 Tagged,
5959 ObjC,
6060 Protocol,
61+ AsyncSequence,
6162 };
6263 Kind kind = Kind::Void;
6364 int bits = 64 ;
@@ -106,6 +107,13 @@ bool ParseCode(std::string text, ValueCode& code) {
106107 if (text.empty ()) {
107108 return false ;
108109 }
110+ if (text.front () == ' ~' ) {
111+ code.kind = ValueCode::Kind::AsyncSequence;
112+ code.element .emplace_back ();
113+ ValueCode& element = code.element .back ();
114+ return ParseCode (text.substr (1 ), element) && element.kind != ValueCode::Kind::Void &&
115+ element.kind != ValueCode::Kind::Function && !element.optional ;
116+ }
109117 if ((text.front () == ' [' && text.back () == ' ]' ) || (text.front () == ' {' && text.back () == ' }' )) {
110118 code.kind = text.front () == ' [' ? ValueCode::Kind::Array : ValueCode::Kind::Map;
111119 code.element .emplace_back ();
@@ -397,6 +405,9 @@ bool IsA(uint32_t typeIndex, int expected) const {
397405 case ValueCode::Kind::ObjC:
398406 name = code.className ;
399407 break ;
408+ case ValueCode::Kind::AsyncSequence:
409+ name = " AsyncSequence<" + TypeDisplayName (module , code.element .at (0 )) + " >" ;
410+ break ;
400411 }
401412 return (code.inout ? " inout " : " " ) + (code.optional ? name + " ?" : name);
402413}
@@ -546,6 +557,13 @@ void ApiCompleteError(void* token, const uint8_t* message, intptr_t messageLengt
546557void ApiReturnJSObject (NSSwiftCall* call, void * object);
547558void ApiCompleteJSObject (void * token, void * object);
548559void ApiRegisterTracker (NSSwiftRegistrar* registrar, NSSwiftTracker tracker);
560+ void ApiReturnAsyncSequence (NSSwiftCall* call, void * iterator, NSSwiftThunk next);
561+ void ApiCompleteAsyncSequence (void * token, void * iterator, NSSwiftThunk next);
562+ struct IsolateState ;
563+ // A JS async iterator over a Swift iterator (+1, taken over).
564+ MaybeLocal<Value> MakeAsyncIterator (Isolate* isolate, Local<Context> context,
565+ const ModuleInfo* module , void * iterator, NSSwiftThunk next,
566+ const ValueCode& code, const std::string& name);
549567struct SwiftCallback ;
550568Local<Value> JSObjectOf (SwiftCallback* handle, Isolate* isolate);
551569void ReleaseCallback (SwiftCallback* callback);
@@ -567,7 +585,7 @@ void ApiCompleteError(void* token, const uint8_t* message, intptr_t messageLengt
567585 ApiWriteBackEnd, ApiReturnObjC, ApiCompleteObjC,
568586 ApiRegisterUnitModule, ApiObjectCallBegin, ApiObjectCallEnd,
569587 ApiObjectResultRelease, ApiReturnJSObject, ApiCompleteJSObject,
570- ApiRegisterTracker,
588+ ApiRegisterTracker, ApiReturnAsyncSequence, ApiCompleteAsyncSequence,
571589};
572590
573591// "Polygon.Style" -> "Style".
@@ -818,9 +836,12 @@ void BuildGroups(ModuleInfo& module) {
818836};
819837
820838// An async call's Promise, settled on the isolate's thread.
839+ // Its own copy of what the result is: the call's member may be gone by the
840+ // time it settles (an iterator step's).
821841struct PendingAsync {
822842 Global<Promise::Resolver> resolver;
823- const MemberInfo* member;
843+ ValueCode code;
844+ std::string name;
824845};
825846
826847struct IsolateState {
@@ -833,6 +854,8 @@ void BuildGroups(ModuleInfo& module) {
833854 robin_hood::unordered_map<void *, SwiftObjectWrapper*> identity;
834855 // Thunk calls in progress on the isolate's thread, innermost last.
835856 std::vector<CallContext*> calls;
857+ // Builds async iterators from step functions (MakeAsyncIterator).
858+ Global<v8::Function> iteratorFactory;
836859};
837860
838861// Owns one retain of a Swift object for as long as its JS wrapper lives.
@@ -1015,6 +1038,22 @@ bool IsObjCInstance(id object, const ValueCode& code) {
10151038 return object != nil && (code.objcClass == nil || [object isKindOfClass: code.objcClass]);
10161039}
10171040
1041+ void ApiReturnAsyncSequence (NSSwiftCall* call, void * iterator, NSSwiftThunk next) {
1042+ CallContext* context = ContextOf (call);
1043+ const ValueCode* code = CurrentCode (context);
1044+ Local<Value> value;
1045+ if (code == nullptr || code->kind != ValueCode::Kind::AsyncSequence ||
1046+ !MakeAsyncIterator (context->isolate , context->context , context->module , iterator, next, *code,
1047+ context->member ->swiftName )
1048+ .ToLocal (&value)) {
1049+ if (code == nullptr || code->kind != ValueCode::Kind::AsyncSequence) {
1050+ context->module ->release (iterator);
1051+ }
1052+ return Fail (context);
1053+ }
1054+ Complete (context, value);
1055+ }
1056+
10181057void ApiReturnJSObject (NSSwiftCall* call, void * object) {
10191058 CallContext* context = ContextOf (call);
10201059 Complete (context, JSObjectOf (static_cast <SwiftCallback*>(object), context->isolate ));
@@ -1127,7 +1166,9 @@ void ApiReturnNull(NSSwiftCall* call) {
11271166void BeginContainer (CallContext* context, bool isMap, intptr_t count) {
11281167 const ValueCode* code = CurrentCode (context);
11291168 ValueCode::Kind expected = isMap ? ValueCode::Kind::Map : ValueCode::Kind::Array;
1130- if (code == nullptr || code->kind != expected || code->element .empty ()) {
1169+ // An iterator step reports its element as an array.
1170+ bool step = !isMap && code != nullptr && code->kind == ValueCode::Kind::AsyncSequence;
1171+ if (code == nullptr || (code->kind != expected && !step) || code->element .empty ()) {
11311172 Fail (context);
11321173 context->isolate ->ThrowException (Exception::Error (tns::ToV8String (
11331174 context->isolate , context->member ->swiftName + " reported an unexpected collection" )));
@@ -1354,6 +1395,8 @@ void ApiThrowTypeError(NSSwiftCall* call, const uint8_t* message, intptr_t lengt
13541395 ObjC,
13551396 // `object` is a JS object handle the outcome holds a reference to.
13561397 JSObject,
1398+ // `object` is a Swift iterator, stepped by `next`.
1399+ Sequence,
13571400 };
13581401 Kind kind = Kind::Void;
13591402 bool boolean = false ;
@@ -1365,6 +1408,7 @@ void ApiThrowTypeError(NSSwiftCall* call, const uint8_t* message, intptr_t lengt
13651408 void * object = nullptr ;
13661409 uint32_t typeIndex = 0 ;
13671410 const ModuleInfo* module = nullptr ;
1411+ NSSwiftThunk next = nullptr ;
13681412 std::vector<std::shared_ptr<AsyncOutcome>> children;
13691413
13701414 // An undelivered object (the isolate went away) still owns its retain.
@@ -1464,6 +1508,16 @@ bool OutcomeToJS(Isolate* isolate, Local<Context> context, IsolateState* state,
14641508 case AsyncOutcome::Kind::JSObject:
14651509 out = JSObjectOf (static_cast <SwiftCallback*>(node.object ), isolate);
14661510 return true ;
1511+ case AsyncOutcome::Kind::Sequence: {
1512+ if (code.kind != ValueCode::Kind::AsyncSequence) {
1513+ return failed (" returned an unexpected sequence" );
1514+ }
1515+ void * iterator = node.object ;
1516+ node.object = nullptr ;
1517+ return MakeAsyncIterator (isolate, context, node.module , iterator, node.next , code, name)
1518+ .ToLocal (&out) ||
1519+ failed (" returned a sequence JavaScript cannot iterate" );
1520+ }
14671521 case AsyncOutcome::Kind::ObjC: {
14681522 id object = static_cast <id >(node.object );
14691523 node.object = nullptr ;
@@ -1547,8 +1601,8 @@ void SettleOnIsolate(Isolate* isolate, uint64_t id, AsyncOutcome& outcome) {
15471601 Local<Promise::Resolver> resolver = pending->resolver .Get (isolate);
15481602 Local<Value> value;
15491603 Local<Value> rejection;
1550- if (OutcomeToJS (isolate, context, state, pending->member -> swiftName , outcome ,
1551- pending-> member -> signature . result , value, rejection)) {
1604+ if (OutcomeToJS (isolate, context, state, pending->name , outcome, pending-> code , value ,
1605+ rejection)) {
15521606 resolver->Resolve (context, value).Check ();
15531607 } else {
15541608 resolver->Reject (context, rejection).Check ();
@@ -1740,7 +1794,8 @@ void Emit(void* rawToken, std::shared_ptr<AsyncOutcome> node) {
17401794 if (Promise::Resolver::New (context->context ).ToLocal (&resolver)) {
17411795 auto pending = std::make_unique<PendingAsync>();
17421796 pending->resolver .Reset (isolate, resolver);
1743- pending->member = context->member ;
1797+ pending->code = *context->resultCode ;
1798+ pending->name = context->member ->swiftName ;
17441799 context->state ->pending .emplace (id , std::move (pending));
17451800 Complete (context, resolver->GetPromise ());
17461801 } else {
@@ -1828,6 +1883,13 @@ void ApiCompleteMapBegin(void* token, intptr_t /*count*/) {
18281883 Emit (token, Outcome (AsyncOutcome::Kind::Map));
18291884}
18301885
1886+ void ApiCompleteAsyncSequence (void * token, void * iterator, NSSwiftThunk next) {
1887+ auto outcome = Outcome (AsyncOutcome::Kind::Sequence);
1888+ outcome->object = iterator;
1889+ outcome->next = next;
1890+ Emit (token, outcome);
1891+ }
1892+
18311893void ApiCompleteJSObject (void * token, void * object) {
18321894 auto outcome = Outcome (AsyncOutcome::Kind::JSObject);
18331895 ApiCallbackRetain (object);
@@ -1932,6 +1994,7 @@ bool Accepts(Isolate* isolate, const ModuleInfo& module, const ValueCode& code,
19321994 ? wrapper->Module () == &module
19331995 : value->IsObject () && !value->IsArray () && ObjCObjectOf (isolate, value) == nil ;
19341996 }
1997+ case ValueCode::Kind::AsyncSequence:
19351998 case ValueCode::Kind::Void:
19361999 return false ;
19372000 }
@@ -2321,6 +2384,7 @@ bool Convert(Isolate* isolate, Local<Context> context, const ModuleInfo& module,
23212384 return true ;
23222385 }
23232386
2387+ case ValueCode::Kind::AsyncSequence:
23242388 case ValueCode::Kind::Void:
23252389 break ;
23262390 }
@@ -2580,6 +2644,111 @@ bool Select(Isolate* isolate, Local<Context> context, const MemberGroup& group,
25802644 return false ;
25812645}
25822646
2647+ LoadedModule* LoadedFor (Isolate* isolate, const ModuleInfo* module );
2648+ Local<External> DataFor (Isolate* isolate, const void * pointer);
2649+
2650+ // A Swift iterator owned by the step function JS calls; released when that
2651+ // function is collected.
2652+ struct IteratorBox {
2653+ Global<v8::Function> step;
2654+ const ModuleInfo* module ;
2655+ void * iterator;
2656+ NSSwiftThunk next;
2657+ // Names the steps in errors; its result is the sequence's code.
2658+ MemberInfo member;
2659+ };
2660+
2661+ void StepCallback (const FunctionCallbackInfo<Value>& info) {
2662+ Isolate* isolate = info.GetIsolate ();
2663+ auto * box = static_cast <IteratorBox*>(
2664+ info.Data ().As <External>()->Value (v8::kExternalPointerTypeTagDefault ));
2665+ auto * state = Caches::StateFor<IsolateState>(isolate);
2666+ LoadedModule* loaded = LoadedFor (isolate, box->module );
2667+ if (state == nullptr || loaded == nullptr ) {
2668+ return ;
2669+ }
2670+ NSSwiftValue slot{};
2671+ slot.tag = NSSwiftTagObject;
2672+ slot.payload .object = box->iterator ;
2673+
2674+ CallContext call;
2675+ call.api = &kAPI ;
2676+ call.isolate = isolate;
2677+ call.context = isolate->GetCurrentContext ();
2678+ call.module = box->module ;
2679+ call.loaded = loaded;
2680+ call.state = state;
2681+ call.member = &box->member ;
2682+ call.resultCode = &box->member .signature .result ;
2683+ state->calls .push_back (&call);
2684+ box->next (&call, &slot, 1 );
2685+ state->calls .pop_back ();
2686+ if (!call.callbackException .IsEmpty ()) {
2687+ isolate->ThrowException (call.callbackException .Get (isolate));
2688+ } else if (!call.threw && call.completed ) {
2689+ info.GetReturnValue ().Set (call.result );
2690+ }
2691+ }
2692+
2693+ // Turns a step function (a Promise of `[element]`, or `[]` at the end) into
2694+ // an async iterator, one step at a time.
2695+ constexpr const char * kIteratorFactory = R"js( (function (step) {
2696+ let pending = Promise.resolve();
2697+ return {
2698+ next() {
2699+ const result = pending.then(() => step()).then((values) =>
2700+ values.length ? { value: values[0], done: false } : { value: undefined, done: true });
2701+ pending = result.then(() => undefined, () => undefined);
2702+ return result;
2703+ },
2704+ [Symbol.asyncIterator]() {
2705+ return this;
2706+ },
2707+ };
2708+ }))js" ;
2709+
2710+ MaybeLocal<Value> MakeAsyncIterator (Isolate* isolate, Local<Context> context,
2711+ const ModuleInfo* module , void * iterator, NSSwiftThunk next,
2712+ const ValueCode& code, const std::string& name) {
2713+ auto * state = Caches::StateFor<IsolateState>(isolate);
2714+ if (state == nullptr ) {
2715+ module ->release (iterator);
2716+ return MaybeLocal<Value>();
2717+ }
2718+ if (state->iteratorFactory .IsEmpty ()) {
2719+ Local<Script> script;
2720+ Local<Value> factory;
2721+ if (!Script::Compile (context, tns::ToV8String (isolate, kIteratorFactory )).ToLocal (&script) ||
2722+ !script->Run (context).ToLocal (&factory) || !factory->IsFunction ()) {
2723+ module ->release (iterator);
2724+ return MaybeLocal<Value>();
2725+ }
2726+ state->iteratorFactory .Reset (isolate, factory.As <v8::Function>());
2727+ }
2728+
2729+ auto * box = new IteratorBox{{}, module , iterator, next, MemberInfo ()};
2730+ box->member .swiftName = name + " (iterator)" ;
2731+ box->member .signature .result = code;
2732+ Local<v8::Function> step;
2733+ if (!v8::Function::New (context, StepCallback, DataFor (isolate, box)).ToLocal (&step)) {
2734+ module ->release (iterator);
2735+ delete box;
2736+ return MaybeLocal<Value>();
2737+ }
2738+ box->step .Reset (isolate, step);
2739+ box->step .SetWeak (
2740+ box,
2741+ [](const WeakCallbackInfo<IteratorBox>& info) {
2742+ IteratorBox* collected = info.GetParameter ();
2743+ collected->step .Reset ();
2744+ collected->module ->release (collected->iterator );
2745+ delete collected;
2746+ },
2747+ WeakCallbackType::kParameter );
2748+ Local<Value> argv[] = {step};
2749+ return state->iteratorFactory .Get (isolate)->Call (context, v8::Undefined (isolate), 1 , argv);
2750+ }
2751+
25832752LoadedModule* LoadedFor (Isolate* isolate, const ModuleInfo* module ) {
25842753 auto * state = Caches::StateFor<IsolateState>(isolate);
25852754 if (state == nullptr ) {
0 commit comments