package com.oracle.truffle.js.nodes.control;
import java.util.ArrayDeque;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.js.nodes.JSGuards;
import com.oracle.truffle.js.nodes.JavaScriptBaseNode;
import com.oracle.truffle.js.nodes.access.HasHiddenKeyCacheNode;
import com.oracle.truffle.js.nodes.access.PropertyGetNode;
import com.oracle.truffle.js.nodes.function.JSFunctionCallNode;
import com.oracle.truffle.js.nodes.promise.NewPromiseCapabilityNode;
import com.oracle.truffle.js.runtime.Errors;
import com.oracle.truffle.js.runtime.JSArguments;
import com.oracle.truffle.js.runtime.JSContext;
import com.oracle.truffle.js.runtime.builtins.JSFunction;
import com.oracle.truffle.js.runtime.builtins.JSFunction.AsyncGeneratorState;
import com.oracle.truffle.js.runtime.objects.AsyncGeneratorRequest;
import com.oracle.truffle.js.runtime.objects.Completion;
import com.oracle.truffle.js.runtime.objects.PromiseCapabilityRecord;
import com.oracle.truffle.js.runtime.objects.Undefined;
public class AsyncGeneratorEnqueueNode extends JavaScriptBaseNode {
@Child private PropertyGetNode getGeneratorStateNode;
@Child private PropertyGetNode getAsyncGeneratorQueueNode;
@Child private HasHiddenKeyCacheNode hasAsyncGeneratorInternalSlotsNode;
@Child private JSFunctionCallNode callPromiseRejectNode;
@Child private NewPromiseCapabilityNode newPromiseCapabilityNode;
@Child private AsyncGeneratorResumeNextNode asyncGeneratorResumeNextNode;
private final ConditionProfile notExecutingProf = ConditionProfile.createBinaryProfile();
private final JSContext context;
protected AsyncGeneratorEnqueueNode(JSContext context) {
this.context = context;
this.getGeneratorStateNode = PropertyGetNode.createGetHidden(JSFunction.ASYNC_GENERATOR_STATE_ID, context);
this.getAsyncGeneratorQueueNode = PropertyGetNode.createGetHidden(JSFunction.ASYNC_GENERATOR_QUEUE_ID, context);
this.hasAsyncGeneratorInternalSlotsNode = HasHiddenKeyCacheNode.create(JSFunction.ASYNC_GENERATOR_QUEUE_ID);
this.newPromiseCapabilityNode = NewPromiseCapabilityNode.create(context);
this.asyncGeneratorResumeNextNode = AsyncGeneratorResumeNextNode.create(context);
}
public static AsyncGeneratorEnqueueNode create(JSContext context) {
return new AsyncGeneratorEnqueueNode(context);
}
@SuppressWarnings("unchecked")
public Object execute(VirtualFrame frame, Object generator, Completion completion) {
PromiseCapabilityRecord promiseCapability = newPromiseCapability();
if (!JSGuards.isJSObject(generator) || !hasAsyncGeneratorInternalSlotsNode.executeHasHiddenKey(generator)) {
enterErrorBranch();
return badGeneratorError(promiseCapability);
}
ArrayDeque<AsyncGeneratorRequest> queue = (ArrayDeque<AsyncGeneratorRequest>) getAsyncGeneratorQueueNode.getValue(generator);
AsyncGeneratorRequest request = AsyncGeneratorRequest.create(completion, promiseCapability);
queueAdd(queue, request);
AsyncGeneratorState state = (AsyncGeneratorState) getGeneratorStateNode.getValue(generator);
if (notExecutingProf.profile(state != AsyncGeneratorState.Executing)) {
asyncGeneratorResumeNextNode.execute(frame, (DynamicObject) generator);
}
return promiseCapability.getPromise();
}
private PromiseCapabilityRecord newPromiseCapability() {
return newPromiseCapabilityNode.executeDefault();
}
@TruffleBoundary
private static void queueAdd(ArrayDeque<AsyncGeneratorRequest> queue, AsyncGeneratorRequest request) {
queue.addLast(request);
}
private void enterErrorBranch() {
if (callPromiseRejectNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
callPromiseRejectNode = insert(JSFunctionCallNode.createCall());
}
}
private Object badGeneratorError(PromiseCapabilityRecord promiseCapability) {
Object badGeneratorError = Errors.createTypeErrorAsyncGeneratorObjectExpected().getErrorObjectEager(context);
Object reject = promiseCapability.getReject();
callPromiseRejectNode.executeCall(JSArguments.createOneArg(Undefined.instance, reject, badGeneratorError));
return promiseCapability.getPromise();
}
}