Skip to content

Commit 3f8a9ba

Browse files
wondengevados-cosmonic
authored andcommitted
fix(bindgen): fix async future JS codegen producing invalid output
The async future intrinsic templates produced invalid JavaScript that failed to parse when transpiling any component with future<T> types. These bugs made P3 async futures completely unusable. - Removed a stray closing parenthesis in the elementTypeRep range check - Changed self to this in four places inside drop() - in JavaScript class methods, self refers to globalThis, not the instance - Used the current variant name instead of always emitting futureWrite, which caused a duplicate declaration when rendering futureRead - Same for futureCancelRead which was emitted for both cancel variants - Renamed the second futureIdx parameter to futureEndIdx — the duplicate left futureEndIdx undefined at runtime - Also fixed the matching duplicate in the debug log object - Added the missing throw keyword before new Error in the instanceof guard, which was constructing an error and silently discarding it - Removed a broken line that called .remaining(bufferID) without the BUFFER_MGR receiver and had no closing brace - Added componentIdx to the cancel function parameter list and debug log, since the function body referenced it but never received it - Added the missing lookup to fetch the future from the global map before comparing element type reps - Replaced streamEndIdx with futureEndIdx, a copy-paste leftover from the stream equivalent - Added the missing closing parenthesis in the instanceof check
1 parent 97b7512 commit 3f8a9ba

1 file changed

Lines changed: 17 additions & 13 deletions

File tree

crates/js-component-bindgen/src/intrinsics/p3/async_future.rs

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ impl AsyncFutureIntrinsic {
217217
if (!args?.elementTypeRep || typeof args.elementTypeRep !== 'number') {{
218218
throw new TypeError('missing elementTypeRep [' + args.elementTypeRep + ']');
219219
}}
220-
if (args.elementTypeRep <= 0 || args.elementTypeRep > 2_147_483_647 )) {{
220+
if (args.elementTypeRep <= 0 || args.elementTypeRep > 2_147_483_647) {{
221221
throw new TypeError('invalid elementTypeRep [' + args.elementTypeRep + ']');
222222
}}
223223
this.#elementTypeRep = args.elementTypeRep;
@@ -256,14 +256,14 @@ impl AsyncFutureIntrinsic {
256256
isCopying() {{ return this.#copying; }}
257257
258258
drop() {{
259-
if (self.#dropped) {{ throw new Error('already dropped'); }}
260-
if (self.#copying) {{ throw new Error('cannot drop while copying'); }}
259+
if (this.#dropped) {{ throw new Error('already dropped'); }}
260+
if (this.#copying) {{ throw new Error('cannot drop while copying'); }}
261261
262-
if (!self.#{future_var_name}) {{ throw new Error('missing/invalid {future_var_name}'); }}
262+
if (!this.#{future_var_name}) {{ throw new Error('missing/invalid {future_var_name}'); }}
263263
this.#{future_var_name}.close();
264264
265265
super.drop();
266-
self.#dropped = true;
266+
this.#dropped = true;
267267
}}
268268
}}
269269
"));
@@ -308,7 +308,7 @@ impl AsyncFutureIntrinsic {
308308
// TODO: fix return from processFn
309309
Self::FutureWrite | Self::FutureRead => {
310310
let debug_log_fn = Intrinsic::DebugLog.name();
311-
let future_write_fn = Self::FutureWrite.name();
311+
let future_write_fn = self.name();
312312
let global_future_map = Self::GlobalFutureMap.name();
313313
let global_buffer_mgr = Intrinsic::GlobalBufferManager.name();
314314
let is_write = matches!(self, Self::FutureWrite);
@@ -331,7 +331,7 @@ impl AsyncFutureIntrinsic {
331331
isAsync,
332332
futureIdx,
333333
typeIdx,
334-
futureIdx,
334+
futureEndIdx,
335335
ptr,
336336
count,
337337
) {{
@@ -343,7 +343,7 @@ impl AsyncFutureIntrinsic {
343343
isAsync,
344344
futureIdx,
345345
typeIdx,
346-
futureIdx,
346+
futureEndIdx,
347347
ptr,
348348
count,
349349
}});
@@ -356,7 +356,7 @@ impl AsyncFutureIntrinsic {
356356
357357
const futureEnd = {global_future_map}.get(futureEndIdx);
358358
if (!futureEnd) {{ throw new Error('missing future end'); }}
359-
if (!(futureEnd instanceof {future_end_class})) {{ new Error('invalid future end, expected [{future_end_class}]'); }}
359+
if (!(futureEnd instanceof {future_end_class})) {{ throw new Error('invalid future end, expected [{future_end_class}]'); }}
360360
361361
if (future.elementTypeRep() !== futureEnd.elementTypeRep()) {{
362362
throw new Error('future type rep [' + future.elementTypeRep() + '] does not match future end [' + futureEnd.elementTypeRep() + ']');
@@ -379,7 +379,6 @@ impl AsyncFutureIntrinsic {
379379
}});
380380
381381
const processFn = (result) => {{
382-
if (.remaining(bufferID) !== 0) {{
383382
if ({global_buffer_mgr}.remaining(bufferID) === 0 && result != CopyResult.COMPLETED) {{
384383
throw new Error('incomplete copy with future data remanining');
385384
}}
@@ -433,7 +432,7 @@ impl AsyncFutureIntrinsic {
433432
} else {
434433
Self::FutureReadableEndClass.name()
435434
};
436-
let future_cancel_fn = Self::FutureCancelRead.name();
435+
let future_cancel_fn = self.name();
437436
let get_or_create_async_state_fn =
438437
Intrinsic::Component(ComponentIntrinsic::GetOrCreateAsyncState).name();
439438
let global_future_map = Self::GlobalFutureMap.name();
@@ -442,11 +441,13 @@ impl AsyncFutureIntrinsic {
442441
let async_event_code_enum = Intrinsic::AsyncEventCodeEnum.name();
443442
output.push_str(&format!("
444443
async function {future_cancel_fn}(
444+
componentIdx,
445445
futureIdx,
446446
isAsync,
447447
futureEndIdx,
448448
) {{
449449
{debug_log_fn}('[{future_cancel_fn}()] args', {{
450+
componentIdx,
450451
futureIdx,
451452
isAsync,
452453
futureEndIdx,
@@ -459,6 +460,9 @@ impl AsyncFutureIntrinsic {
459460
if (!futureEnd) {{ throw new Error('missing future end with idx [' + futureEndIdx + ']'); }}
460461
if (!(futureEnd instanceof {future_end_class})) {{ throw new Error('invalid future end, expected value of type [{future_end_class}]'); }}
461462
463+
const future = {global_future_map}.get(futureIdx);
464+
if (!future) {{ throw new Error('missing future with idx [' + futureIdx + ']'); }}
465+
462466
if (futureEnd.elementTypeRep() !== future.elementTypeRep()) {{
463467
throw new Error('future type [' + future.elementTypeRep() + '], does not match future end type [' + futureEnd.elementTypeRep() + ']');
464468
}}
@@ -481,7 +485,7 @@ impl AsyncFutureIntrinsic {
481485
const {{ code, payload0: index, payload1: payload }} = futureEnd.getPendingEvent();
482486
if (futureEnd.isCopying()) {{ throw new Error('future end is still in copying state'); }}
483487
if (code !== {async_event_code_enum}) {{ throw new Error('unexpected event code [' + code + '], expected [' + {async_event_code_enum} + ']'); }}
484-
if (index !== streamEndIdx) {{ throw new Error('index does not match stream end'); }}
488+
if (index !== futureEndIdx) {{ throw new Error('index does not match future end'); }}
485489
486490
return payload;
487491
}}
@@ -517,7 +521,7 @@ impl AsyncFutureIntrinsic {
517521
const future = {global_future_map}.remove(futureIdx);
518522
519523
const futureEnd = {global_future_map}.remove(futureEndIdx);
520-
if (!(futureEnd instanceof {future_end_class}) {{ throw new Error('invalid future end, expected [{future_end_class}]'); }}
524+
if (!(futureEnd instanceof {future_end_class})) {{ throw new Error('invalid future end, expected [{future_end_class}]'); }}
521525
522526
if (futureEnd.elementTypeRep() !== future.elementTypeRep()) {{
523527
throw new Error('future type [' + future.elementTypeRep() + '], does not match future end type [' + futureEnd.elementTypeRep() + ']');

0 commit comments

Comments
 (0)