blob: b0ac40381dff05677bcec9400c131038eb42c960 [file] [log] [blame]
/*
* Copyright (C) 2017 Oleksandr Skachkov <gskachkov@gmail.com>.
* Copyright (C) 2019 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
@globalPrivate
function asyncGeneratorQueueIsEmpty(generator)
{
"use strict";
return @getAsyncGeneratorInternalField(generator, @asyncGeneratorFieldQueueFirst) === null;
}
@globalPrivate
function asyncGeneratorQueueEnqueue(generator, item)
{
"use strict";
@assert(@getByIdDirectPrivate(item, "asyncGeneratorQueueItemNext") === null);
if (@getAsyncGeneratorInternalField(generator, @asyncGeneratorFieldQueueFirst) === null) {
@assert(@getAsyncGeneratorInternalField(generator, @asyncGeneratorFieldQueueLast) === null);
@putAsyncGeneratorInternalField(generator, @asyncGeneratorFieldQueueFirst, item);
@putAsyncGeneratorInternalField(generator, @asyncGeneratorFieldQueueLast, item);
} else {
var last = @getAsyncGeneratorInternalField(generator, @asyncGeneratorFieldQueueLast);
@putByIdDirectPrivate(last, "asyncGeneratorQueueItemNext", item);
@putAsyncGeneratorInternalField(generator, @asyncGeneratorFieldQueueLast, item);
}
}
@globalPrivate
function asyncGeneratorQueueDequeue(generator)
{
"use strict";
@assert(!@asyncGeneratorQueueIsEmpty(generator), "Async genetator's Queue is an empty List.");
var result = @getAsyncGeneratorInternalField(generator, @asyncGeneratorFieldQueueFirst);
var updatedFirst = @getByIdDirectPrivate(result, "asyncGeneratorQueueItemNext");
@putAsyncGeneratorInternalField(generator, @asyncGeneratorFieldQueueFirst, updatedFirst);
if (updatedFirst === null)
@putAsyncGeneratorInternalField(generator, @asyncGeneratorFieldQueueLast, null);
return result;
}
@globalPrivate
function isExecutionState(generator)
{
"use strict";
var state = @getAsyncGeneratorInternalField(generator, @generatorFieldState);
var reason = @getAsyncGeneratorInternalField(generator, @asyncGeneratorFieldSuspendReason);
return (state > 0 && reason === @AsyncGeneratorSuspendReasonNone)
|| state === @AsyncGeneratorStateExecuting
|| reason === @AsyncGeneratorSuspendReasonAwait;
}
@globalPrivate
function isSuspendYieldState(generator)
{
"use strict";
var state = @getAsyncGeneratorInternalField(generator, @generatorFieldState);
return (state > 0 && @getAsyncGeneratorInternalField(generator, @asyncGeneratorFieldSuspendReason) === @AsyncGeneratorSuspendReasonYield)
|| state === @AsyncGeneratorStateSuspendedYield;
}
@globalPrivate
function asyncGeneratorReject(generator, exception)
{
"use strict";
@assert(@isAsyncGenerator(generator), "Generator is not an AsyncGenerator instance.");
var promise = @asyncGeneratorQueueDequeue(generator).promise;
@assert(@isPromise(promise));
@rejectPromiseWithFirstResolvingFunctionCallCheck(promise, exception);
return @asyncGeneratorResumeNext(generator);
}
@globalPrivate
function asyncGeneratorResolve(generator, value, done)
{
"use strict";
@assert(@isAsyncGenerator(generator), "Generator is not an AsyncGenerator instance.");
var promise = @asyncGeneratorQueueDequeue(generator).promise;
@assert(@isPromise(promise));
@resolvePromiseWithFirstResolvingFunctionCallCheck(promise, { value, done });
return @asyncGeneratorResumeNext(generator);
}
@globalPrivate
function asyncGeneratorYield(generator, value, resumeMode)
{
"use strict";
function asyncGeneratorYieldAwaited(result)
{
@putAsyncGeneratorInternalField(generator, @asyncGeneratorFieldSuspendReason, @AsyncGeneratorSuspendReasonYield);
@asyncGeneratorResolve(generator, result, false);
}
@putAsyncGeneratorInternalField(generator, @asyncGeneratorFieldSuspendReason, @AsyncGeneratorSuspendReasonAwait);
@awaitValue(generator, value, asyncGeneratorYieldAwaited);
}
@globalPrivate
function awaitValue(generator, value, onFulfilled)
{
"use strict";
var onRejected = function (result) { @doAsyncGeneratorBodyCall(generator, result, @GeneratorResumeModeThrow); };
@resolveWithoutPromise(value, onFulfilled, onRejected);
}
@globalPrivate
function doAsyncGeneratorBodyCall(generator, resumeValue, resumeMode)
{
"use strict";
var value = @undefined;
var state = @getAsyncGeneratorInternalField(generator, @generatorFieldState);
@putAsyncGeneratorInternalField(generator, @generatorFieldState, @AsyncGeneratorStateExecuting);
@putAsyncGeneratorInternalField(generator, @asyncGeneratorFieldSuspendReason, @AsyncGeneratorSuspendReasonNone);
try {
value = @getAsyncGeneratorInternalField(generator, @generatorFieldNext).@call(@getAsyncGeneratorInternalField(generator, @generatorFieldThis), generator, state, resumeValue, resumeMode, @getAsyncGeneratorInternalField(generator, @generatorFieldFrame));
state = @getAsyncGeneratorInternalField(generator, @generatorFieldState);
if (state === @AsyncGeneratorStateExecuting) {
@putAsyncGeneratorInternalField(generator, @generatorFieldState, @AsyncGeneratorStateCompleted);
state = @AsyncGeneratorStateCompleted;
}
} catch (error) {
@putAsyncGeneratorInternalField(generator, @generatorFieldState, @AsyncGeneratorStateCompleted);
@putAsyncGeneratorInternalField(generator, @asyncGeneratorFieldSuspendReason, @AsyncGeneratorSuspendReasonNone);
return @asyncGeneratorReject(generator, error);
}
var reason = @getAsyncGeneratorInternalField(generator, @asyncGeneratorFieldSuspendReason);
if (reason === @AsyncGeneratorSuspendReasonAwait) {
var onFulfilled = function(result) { @doAsyncGeneratorBodyCall(generator, result, @GeneratorResumeModeNormal); };
@awaitValue(generator, value, onFulfilled);
return;
}
if (reason === @AsyncGeneratorSuspendReasonYield)
return @asyncGeneratorYield(generator, value, resumeMode);
if (state === @AsyncGeneratorStateCompleted) {
@assert(@getAsyncGeneratorInternalField(generator, @generatorFieldState) == @AsyncGeneratorStateCompleted);
@putAsyncGeneratorInternalField(generator, @asyncGeneratorFieldSuspendReason, @AsyncGeneratorSuspendReasonNone);
return @asyncGeneratorResolve(generator, value, true);
}
}
@globalPrivate
function asyncGeneratorResumeNext(generator)
{
"use strict";
@assert(@isAsyncGenerator(generator), "Generator is not an AsyncGenerator instance.");
var state = @getAsyncGeneratorInternalField(generator, @generatorFieldState);
@assert(state !== @AsyncGeneratorStateExecuting, "Async generator should not be in executing state");
if (state === @AsyncGeneratorStateAwaitingReturn)
return;
if (@asyncGeneratorQueueIsEmpty(generator))
return;
var next = @getAsyncGeneratorInternalField(generator, @asyncGeneratorFieldQueueFirst);
if (next.resumeMode !== @GeneratorResumeModeNormal) {
if (state === @AsyncGeneratorStateSuspendedStart) {
@putAsyncGeneratorInternalField(generator, @generatorFieldState, @AsyncGeneratorStateCompleted);
state = @AsyncGeneratorStateCompleted;
}
if (state === @AsyncGeneratorStateCompleted) {
if (next.resumeMode === @GeneratorResumeModeReturn) {
@putAsyncGeneratorInternalField(generator, @generatorFieldState, @AsyncGeneratorStateAwaitingReturn);
@resolveWithoutPromise(next.value,
function (result) {
@putAsyncGeneratorInternalField(generator, @generatorFieldState, @AsyncGeneratorStateCompleted);
@asyncGeneratorResolve(generator, result, true);
},
function (error) {
@putAsyncGeneratorInternalField(generator, @generatorFieldState, @AsyncGeneratorStateCompleted);
@asyncGeneratorReject(generator, error);
});
return;
}
@assert(next.resumeMode === @GeneratorResumeModeThrow, "Async generator has wrong mode");
return @asyncGeneratorReject(generator, next.value);;
}
} else if (state === @AsyncGeneratorStateCompleted)
return @asyncGeneratorResolve(generator, @undefined, true);
@assert(state === @AsyncGeneratorStateSuspendedStart || @isSuspendYieldState(generator), "Async generator has wrong state");
@doAsyncGeneratorBodyCall(generator, next.value, next.resumeMode);
}
@globalPrivate
function asyncGeneratorEnqueue(generator, value, resumeMode)
{
"use strict";
var promise = @newPromise();
if (!@isAsyncGenerator(generator)) {
@rejectPromiseWithFirstResolvingFunctionCallCheck(promise, @makeTypeError('|this| should be an async generator'));
return promise;
}
@asyncGeneratorQueueEnqueue(generator, {resumeMode, value, promise, @asyncGeneratorQueueItemNext: null});
if (!@isExecutionState(generator))
@asyncGeneratorResumeNext(generator);
return promise;
}
function next(value)
{
"use strict";
return @asyncGeneratorEnqueue(this, value, @GeneratorResumeModeNormal);
}
function return(value)
{
"use strict";
return @asyncGeneratorEnqueue(this, value, @GeneratorResumeModeReturn);
}
function throw(exception)
{
"use strict";
return @asyncGeneratorEnqueue(this, exception, @GeneratorResumeModeThrow);
}