package com.oracle.truffle.js.builtins;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.js.builtins.MapIteratorPrototypeBuiltinsFactory.MapIteratorNextNodeGen;
import com.oracle.truffle.js.nodes.access.CreateIterResultObjectNode;
import com.oracle.truffle.js.nodes.access.HasHiddenKeyCacheNode;
import com.oracle.truffle.js.nodes.access.PropertyGetNode;
import com.oracle.truffle.js.nodes.access.PropertySetNode;
import com.oracle.truffle.js.nodes.function.JSBuiltin;
import com.oracle.truffle.js.nodes.function.JSBuiltinNode;
import com.oracle.truffle.js.runtime.Errors;
import com.oracle.truffle.js.runtime.JSContext;
import com.oracle.truffle.js.runtime.JSRuntime;
import com.oracle.truffle.js.runtime.builtins.BuiltinEnum;
import com.oracle.truffle.js.runtime.builtins.JSArray;
import com.oracle.truffle.js.runtime.builtins.JSMap;
import com.oracle.truffle.js.runtime.objects.Undefined;
import com.oracle.truffle.js.runtime.util.JSHashMap;
public final class MapIteratorPrototypeBuiltins extends JSBuiltinsContainer.SwitchEnum<MapIteratorPrototypeBuiltins.MapIteratorPrototype> {
public static final JSBuiltinsContainer BUILTINS = new MapIteratorPrototypeBuiltins();
protected MapIteratorPrototypeBuiltins() {
super(JSMap.ITERATOR_PROTOTYPE_NAME, MapIteratorPrototype.class);
}
public enum MapIteratorPrototype implements BuiltinEnum<MapIteratorPrototype> {
next(0);
private final int length;
MapIteratorPrototype(int length) {
this.length = length;
}
@Override
public int getLength() {
return length;
}
}
@Override
protected Object createNode(JSContext context, JSBuiltin builtin, boolean construct, boolean newTarget, MapIteratorPrototype builtinEnum) {
switch (builtinEnum) {
case next:
return MapIteratorNextNodeGen.create(context, builtin, args().withThis().createArgumentNodes(context));
}
return null;
}
public abstract static class MapIteratorNextNode extends JSBuiltinNode {
@Child private HasHiddenKeyCacheNode isMapIteratorNode;
@Child private PropertyGetNode getIteratedObjectNode;
@Child private PropertyGetNode getNextIndexNode;
@Child private PropertyGetNode getIterationKindNode;
@Child private PropertySetNode setIteratedObjectNode;
@Child private CreateIterResultObjectNode createIterResultObjectNode;
private final ConditionProfile detachedProf = ConditionProfile.createBinaryProfile();
private final ConditionProfile doneProf = ConditionProfile.createBinaryProfile();
private final ConditionProfile iterKindKey = ConditionProfile.createBinaryProfile();
private final ConditionProfile iterKindValue = ConditionProfile.createBinaryProfile();
public MapIteratorNextNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
this.isMapIteratorNode = HasHiddenKeyCacheNode.create(JSMap.MAP_ITERATION_KIND_ID);
this.getIteratedObjectNode = PropertyGetNode.createGetHidden(JSRuntime.ITERATED_OBJECT_ID, context);
this.getNextIndexNode = PropertyGetNode.createGetHidden(JSRuntime.ITERATOR_NEXT_INDEX, context);
this.getIterationKindNode = PropertyGetNode.createGetHidden(JSMap.MAP_ITERATION_KIND_ID, context);
this.setIteratedObjectNode = PropertySetNode.createSetHidden(JSRuntime.ITERATED_OBJECT_ID, context);
this.createIterResultObjectNode = CreateIterResultObjectNode.create(context);
}
@Specialization(guards = "isMapIterator(iterator)")
protected DynamicObject doMapIterator(VirtualFrame frame, DynamicObject iterator) {
Object map = getIteratedObjectNode.getValue(iterator);
if (detachedProf.profile(map == Undefined.instance)) {
return createIterResultObjectNode.execute(frame, Undefined.instance, true);
}
JSHashMap.Cursor mapCursor = (JSHashMap.Cursor) getNextIndexNode.getValue(iterator);
int itemKind = getIterationKind(iterator);
if (doneProf.profile(!mapCursor.advance())) {
setIteratedObjectNode.setValue(iterator, Undefined.instance);
return createIterResultObjectNode.execute(frame, Undefined.instance, true);
}
Object elementKey = mapCursor.getKey();
Object elementValue = mapCursor.getValue();
Object result;
if (iterKindKey.profile(itemKind == JSRuntime.ITERATION_KIND_KEY)) {
result = elementKey;
} else if (iterKindValue.profile(itemKind == JSRuntime.ITERATION_KIND_VALUE)) {
result = elementValue;
} else {
assert itemKind == JSRuntime.ITERATION_KIND_KEY_PLUS_VALUE;
result = JSArray.createConstantObjectArray(getContext(), new Object[]{elementKey, elementValue});
}
return createIterResultObjectNode.execute(frame, result, false);
}
@SuppressWarnings("unused")
@Fallback
protected DynamicObject doIncompatibleReceiver(Object iterator) {
throw Errors.createTypeError("not a Map Iterator");
}
protected final boolean isMapIterator(Object thisObj) {
return isMapIteratorNode.executeHasHiddenKey(thisObj);
}
private int getIterationKind(DynamicObject iterator) {
try {
return getIterationKindNode.getValueInt(iterator);
} catch (UnexpectedResultException e) {
throw Errors.shouldNotReachHere();
}
}
}
}