Comparing v4.6.4...v4.7.0 · sebastienros/jint (original) (raw)
- Fix async test reporting and resolve 58 async test262 failures
Improve test262 runner to properly detect async test failures via $DONE/doneprintHandle.js markers, then fix the root causes surfaced:
- AsyncFromSyncIterator.Next(): forward value argument to sync iterator using cached [[NextMethod]] per spec (was passing Arguments.Empty)
- Await: remove extra microtask tick by eliminating unnecessary resultCapability and AddToEventLoop wrapper in promise reaction handlers
- yield* delegation: cache nextMethod once before loop per spec, and always Await inner iterator results (handles thenables, not just JsPromise)
- AsyncFromSyncIterator: wrap GetMethod/done/value access in try-catch to reject promise on error; reject with TypeError for non-object results
Remaining async failures excluded by category for incremental fixing.
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
- Fix yield* delegation return/throw and async generator return handling
Resume execution with Return/Throw completion types instead of bypassing the generator body, fixing three interconnected issue categories:
- AsyncGeneratorResumeNext: for Return completions on SuspendedYield, resume execution with _resumeCompletionType=Return instead of calling AsyncGeneratorAwaitReturn directly. This allows try/finally blocks to execute and yield* delegation to forward return to inner iterators.
- ContinueAsyncYieldDelegate: override _delegationResumeType with _resumeCompletionType when user called .throw()/.return() during yield* delegation, so the completion flows to inner iterator.
- AwaitAndYieldDelegation: capture delegationResumeType and propagate Return completion when inner iterator returns done=true, enabling proper cleanup through try/finally in the outer generator.
Resolves 132 additional test262 failures (190 total with prior commit). Remaining 244 failures excluded by category for incremental fixing.
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
- Fix abrupt done/value getters in yield* async delegation
Wrap IteratorComplete and IteratorValue calls in AwaitAndYieldDelegation's onFulfilled handler with try-catch to properly reject the promise when the result object's done or value getters throw. Previously, these exceptions would propagate uncaught, breaking the generator state.
Resolves 48 additional test262 failures (238 total across all commits).
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
- Fix Array.fromAsync iterator close and error handling
- Close async iterators when mapfn throws, mapped value rejects, or CreateDataPropertyOrThrow fails (IfAbruptCloseAsyncIterator per spec)
- Fix onRejected handlers to pass args.At(0) instead of full args array
- Add RangeError check for array-like length > 2^32-1
Resolves 12 additional Array.fromAsync test262 failures (250 total).
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
- Fix async generator return with try/finally blocks
Clear _returnRequested for async generators (not just sync) before executing finally blocks in JintTryStatement. Without this, a yield inside finally would be treated as a return, overwriting the pending return value and preventing proper completion after finally.
This enables the PendingCompletionType/Value mechanism to correctly restore the original Return completion after the finally block yields and completes.
Resolves 8 additional test262 failures (258 total across all commits).
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
- Fix async generator return to properly await (unwrap) return values
Per spec, when an async generator returns, the return value must be Awaited via PromiseResolve to unwrap promise values:
- SuspendedStart + Return: use AsyncGeneratorAwaitReturn instead of directly resolving (unwraps promise return values)
- Completed + Return: same treatment
- ResumeExecution: when body returns with Return completion, await the value via AsyncGeneratorAwaitReturn before completing
This ensures .return(promise) unwraps the promise value, and
.return(brokenPromise) properly rejects when PromiseResolve fails.
Resolves 6 additional test262 failures (264 total across all commits).
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
- Fix multiple async edge cases: yield* delegation, Promise.any, disposal
- AsyncFromSyncIterator throw: set closeOnRejection to true per spec
- yield* delegation: propagate IteratorValue errors to generator body as throw completions instead of rejecting the promise directly
- Async generator return at yield: try PromiseResolve at yield point so broken promise errors reach the body's try/catch
- AsyncGeneratorAwaitReturn: catch PromiseResolve failures and reject
- AsyncDisposableStack.disposeAsync: return rejected promise on invalid receiver instead of throwing synchronously
- Promise.any: set iteratorRecord.[[Done]] to true before propagating IteratorStep/IteratorValue errors to prevent unnecessary iterator close
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
- Fix for-await-of destructuring with yield and async generator completion
- Destructuring: generalize yield/return handling to work with both sync and async generators via ISuspendable interface (was sync-only)
- For-await-of: save current iterator value when yield suspends inside destructuring, replay it on resume instead of re-calling next()
- For-await-of: preserve IsResuming flag when resuming from yield in destructuring so the yield expression can detect the resume
- AsyncGeneratorInstance: use undefined for normal completion value per spec (was incorrectly using the body's last expression value)
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
- Fix async function disposal timing and defer tick ordering tests
- ScriptFunction: skip DisposeResources for async functions (disposal must be deferred until the async body truly completes)
- AsyncBlockStart: call DisposeResources after body completes, before resolving/rejecting the async function's promise
- AsyncFunctionResume: same disposal at completion, skip when suspended
- Fixes using/await-using disposal timing in async functions: resources are no longer disposed while the function is suspended at an await
- Tick ordering and deferred-await-in-async-generator tests remain excluded (require full async await suspension architecture)
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
- Fix Array.fromAsync error handling and un-exclude passing tests
- Array.fromAsync: catch TypeErrorException and RangeErrorException (thrown by TypeConverter without Engine context) and reject the promise instead of letting them escape as synchronous throws
- Un-exclude expression-yield-newline (already passes)
- Un-exclude asyncitems-arraylike-length-accessor-throws (now passes)
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
- Implement proper await suspension in async generators
Previously, await inside async generator bodies fell through to the
blocking UnwrapIfPromise path because JintAwaitExpression only checked
for AsyncFunctionInstance (null for async generators). This caused
deadlocks on deferred promises and wrong microtask tick ordering.
- AsyncGeneratorInstance: add _awaitSuspended, _resumeWithThrow, and _completedAwaits fields; update IsSuspended to check _awaitSuspended
- JintAwaitExpression: add async generator resume/cache/suspend paths with SuspendForAwaitInAsyncGenerator method using promise handlers that resume via AsyncGeneratorContinueForAwait
- ResumeExecution/ResumeAfterDelegation: detect _awaitSuspended and return early (await handler resumes later)
- AsyncGeneratorYield: clear _completedAwaits on yield
- All loop statements: clear _completedAwaits for async generators at iteration boundaries (matching async function pattern)
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
- Fix async generator body disposal timing
- ResumeExecution: call DisposeResources on the lexical environment before leaving the execution context when the body completes
- ResumeAfterDelegation: same disposal at completion
- Both catch blocks: dispose resources before rejecting
- Fixes using/await-using in async generators: resources are now properly disposed when the generator body completes
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
- Fix Array.fromAsync edge cases, async generator return tick ordering, and un-exclude passing tests
Array.fromAsync: don't await async iterator values when no mapfn (spec 3.j.ii.8), use ulong for length overflow check, wrap SetLength in try-catch to reject promise. Async generators: distinguish return; (no await) from return expr; (await per 13.10.1), cache PromiseResolve from yield return path to avoid double thenable "then" access. Un-exclude 14 previously over-excluded tests that were already passing.
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
- Fix for-await-of PromiseResolve and AsyncGeneratorYield tick ordering
For-await-of: add PromiseResolve call per spec Await step 1 (13.7.5.13) to make Promise.constructor lookups observable. Fixes 6 ticks-with tests.
AsyncGeneratorYield: use PromiseResolve instead of CreateResolvedPromise to avoid creating an extra thenable job when yielding Promise values. This fixes microtask interleaving between for-await-of and Promise.then.
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
- Fix await-using implicit Await tick for null/undefined resources
Per spec Dispose step 3.a, even when method is undefined (null/undefined resource), async-dispose must Await(result). This introduces a microtask tick that suspends the async function.
DisposeCapability signals NeedsAsyncTick when it encounters an async-dispose resource with no method. JintBlockStatement detects this after block exit and creates a resolved promise to suspend the async function, ensuring statements after the block run in a new microtask.
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
- Fix yield* delegation return tick ordering with two-phase Await
Per spec AsyncGeneratorUnwrapYieldResumption step 2, the resumption value must be Awaited before yield* delegation continues. Additionally, when GetMethod("return") returns undefined, spec step 25.iii.1 requires a second Await on the received value.
Split the yield* Return branch into async phases: Phase A: Await(resumptionValue) — makes "then" getter observable before "return" Phase B: GetMethod("return") lookup, then second Await if return is undefined
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
remove unused usings
Fix module async evaluation ordering by making counter agent-level
Per spec IncrementModuleAsyncEvaluationCount, the [[ModuleAsyncEvaluationCount]] is a field of the Agent Record, not local to each Evaluate() call. When dynamic import() triggers a new Evaluate() call, the counter must persist to maintain correct relative ordering between pending async modules.
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
- Simplify async code: extract helpers, remove dead code, fix safety issues
- Remove dead Agent.ModuleAsyncEvaluationCount (duplicate of Engine field)
- Extract ExecutionContext.ClearCompletedAwaitsIfNotResuming() replacing 4 copy-pasted blocks
- Extract AsyncGeneratorInstance.AbortDelegation()/ClearDelegationIterator() replacing ~13 inline blocks
- Fix bare catch to catch(Exception) in ArrayConstructor.AsyncIteratorClose
- Use CommonProperties.Return instead of string literals in JintYieldExpression
- Revert CreateResolvedPromise to private (no external callers)
- Clear _completedAwaits on generator completion to release references
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
- Fix dynamic import test262 exclusions: import.meta prototype and async generator yield
Two fixes:
import.meta now uses OrdinaryObjectCreate(null) per spec instead of Object.Construct(), which gave it Object.prototype. This caused import(import.meta) to reject with a string instead of TypeError.
JintMemberExpression.GetValue fast path now checks IsSuspended() after evaluating the object expression. Without this, an await suspension inside a chained expression like (await x).value.x would fall through to the slow path, re-evaluating the await and preventing the statement list _index from being saved correctly. This caused side-effectful statements (like generator .next() calls) to re-execute on every async function resume, corrupting generator state.
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
- Fix Atomics.waitAsync agent tests and for-of let+await hang
Two fixes to enable all 60 previously excluded Atomics.waitAsync tests:
Agent worker event loop draining: worker threads now drain the event loop after engine.Execute() until agent.leaving() is called, allowing async continuations from await Atomics.waitAsync() to be processed.
For-of loop environment corruption on async resume: when a for-of body contains let declarations and await, the saved execution context captured a block-scoped environment. On resume, BodyEvaluation captured this as oldEnv instead of the function scope, corrupting the environment chain for subsequent iterations. Fixed by saving/restoring oldEnv in ForOfSuspendData.
Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com
Co-authored-by: Claude Opus 4.6 (1M context) noreply@anthropic.com