/**
* Copyright (c) 2012-2016 André Bargull
* Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms.
*
* <https://github.com/anba/es6draft>
*/
package com.github.anba.es6draft.runtime.objects.async;
import static com.github.anba.es6draft.runtime.types.Undefined.UNDEFINED;
import com.github.anba.es6draft.runtime.ExecutionContext;
import com.github.anba.es6draft.runtime.internal.CodeContinuation;
import com.github.anba.es6draft.runtime.internal.Continuation;
import com.github.anba.es6draft.runtime.internal.ResumptionPoint;
import com.github.anba.es6draft.runtime.internal.RuntimeInfo;
import com.github.anba.es6draft.runtime.internal.ScriptException;
import com.github.anba.es6draft.runtime.objects.promise.PromiseCapability;
import com.github.anba.es6draft.runtime.objects.promise.PromiseObject;
/**
* <h1>Async Functions</h1>
*
* {@link Async} implementation for async functions.
*/
public final class AsyncObject implements Async {
public enum AsyncState {
SuspendedAwait, Executing, Completed
}
private final PromiseCapability<PromiseObject> promiseCapability;
private RuntimeInfo.Function code;
private ExecutionContext context;
private AsyncState state;
private Continuation<Void> continuation;
AsyncObject(PromiseCapability<PromiseObject> promiseCapability) {
this.promiseCapability = promiseCapability;
}
/**
* Returns the current async state.
*
* @return the async state
*/
public AsyncState getState() {
return state;
}
/**
* Proceeds to the "SuspendedAwait" async state.
*/
private void suspend() {
assert state == AsyncState.Executing : "suspend from: " + state;
state = AsyncState.SuspendedAwait;
}
/**
* Proceeds to the "completed" async state and releases internal resources.
*/
private void close() {
assert state == AsyncState.Executing : "close from: " + state;
state = AsyncState.Completed;
context = null;
code = null;
continuation = null;
}
/**
* Starts async function execution.
*
* @param cx
* the execution context
* @param functionCode
* the runtime function code
*/
void start(ExecutionContext cx, RuntimeInfo.Function functionCode) {
assert state == null;
context = cx;
code = functionCode;
state = AsyncState.Executing;
context.setCurrentAsync(this);
continuation = new CodeContinuation<>(new AsyncHandler(this));
continuation.start(cx);
}
@Override
public void resume(ExecutionContext cx, Object value) {
switch (state) {
case Executing:
case Completed:
throw new AssertionError();
case SuspendedAwait:
state = AsyncState.Executing;
continuation.resume(cx, value);
return;
default:
throw new AssertionError();
}
}
@Override
public void _throw(ExecutionContext cx, Object value) {
switch (state) {
case Executing:
case Completed:
throw new AssertionError();
case SuspendedAwait:
state = AsyncState.Executing;
continuation._throw(cx, ScriptException.create(value));
return;
default:
throw new AssertionError();
}
}
private static final class AsyncHandler implements Continuation.Handler<Void> {
private final AsyncObject asyncObject;
AsyncHandler(AsyncObject asyncObject) {
this.asyncObject = asyncObject;
}
@Override
public Object evaluate(ResumptionPoint resumptionPoint) throws Throwable {
AsyncObject asyncObj = asyncObject;
return asyncObj.code.handle().invokeExact(asyncObj.context, resumptionPoint);
}
@Override
public void close() {
asyncObject.close();
}
@Override
public Void suspendWith(ExecutionContext cx, Object value) {
asyncObject.suspend();
return null;
}
@Override
public Void returnWith(ExecutionContext cx, Object result) {
// Async execution finished, run AsyncFunctionStart steps 4-9.
asyncObject.promiseCapability.getResolve().call(cx, UNDEFINED, result);
return null;
}
@Override
public Void returnWith(ExecutionContext cx, ScriptException exception) {
// Async execution finished, run AsyncFunctionStart steps 4-9.
asyncObject.promiseCapability.getReject().call(cx, UNDEFINED, exception.getValue());
return null;
}
}
}