package com.oracle.truffle.js.builtins;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.js.builtins.SetPrototypeBuiltinsFactory.CreateSetIteratorNodeGen;
import com.oracle.truffle.js.builtins.SetPrototypeBuiltinsFactory.JSSetAddNodeGen;
import com.oracle.truffle.js.builtins.SetPrototypeBuiltinsFactory.JSSetClearNodeGen;
import com.oracle.truffle.js.builtins.SetPrototypeBuiltinsFactory.JSSetDeleteNodeGen;
import com.oracle.truffle.js.builtins.SetPrototypeBuiltinsFactory.JSSetForEachNodeGen;
import com.oracle.truffle.js.builtins.SetPrototypeBuiltinsFactory.JSSetHasNodeGen;
import com.oracle.truffle.js.builtins.helper.JSCollectionsNormalizeNode;
import com.oracle.truffle.js.builtins.helper.JSCollectionsNormalizeNodeGen;
import com.oracle.truffle.js.nodes.access.CreateObjectNode;
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.nodes.function.JSFunctionCallNode;
import com.oracle.truffle.js.nodes.unary.IsCallableNode;
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.JSRuntime;
import com.oracle.truffle.js.runtime.builtins.BuiltinEnum;
import com.oracle.truffle.js.runtime.builtins.JSSet;
import com.oracle.truffle.js.runtime.objects.Undefined;
import com.oracle.truffle.js.runtime.util.JSHashMap;
public final class SetPrototypeBuiltins extends JSBuiltinsContainer.SwitchEnum<SetPrototypeBuiltins.SetPrototype> {
public static final JSBuiltinsContainer BUILTINS = new SetPrototypeBuiltins();
protected SetPrototypeBuiltins() {
super(JSSet.PROTOTYPE_NAME, SetPrototype.class);
}
public enum SetPrototype implements BuiltinEnum<SetPrototype> {
clear(0),
delete(1),
add(1),
has(1),
forEach(1),
values(0),
entries(0);
private final int length;
SetPrototype(int length) {
this.length = length;
}
@Override
public int getLength() {
return length;
}
}
@Override
protected Object createNode(JSContext context, JSBuiltin builtin, boolean construct, boolean newTarget, SetPrototype builtinEnum) {
switch (builtinEnum) {
case clear:
return JSSetClearNodeGen.create(context, builtin, args().withThis().createArgumentNodes(context));
case delete:
return JSSetDeleteNodeGen.create(context, builtin, args().withThis().fixedArgs(1).createArgumentNodes(context));
case add:
return JSSetAddNodeGen.create(context, builtin, args().withThis().fixedArgs(1).createArgumentNodes(context));
case has:
return JSSetHasNodeGen.create(context, builtin, args().withThis().fixedArgs(1).createArgumentNodes(context));
case forEach:
return JSSetForEachNodeGen.create(context, builtin, args().withThis().fixedArgs(2).createArgumentNodes(context));
case values:
return CreateSetIteratorNodeGen.create(context, builtin, JSRuntime.ITERATION_KIND_VALUE, args().withThis().createArgumentNodes(context));
case entries:
return CreateSetIteratorNodeGen.create(context, builtin, JSRuntime.ITERATION_KIND_KEY_PLUS_VALUE, args().withThis().createArgumentNodes(context));
}
return null;
}
public abstract static class JSSetOperation extends JSBuiltinNode {
protected static final Object PRESENT = new Object();
@Child private JSCollectionsNormalizeNode normalizeNode;
public JSSetOperation(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
protected Object normalize(Object value) {
if (normalizeNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
normalizeNode = insert(JSCollectionsNormalizeNodeGen.create());
}
return normalizeNode.execute(value);
}
}
public abstract static class JSSetClearNode extends JSBuiltinNode {
public JSSetClearNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@Specialization(guards = "isJSSet(thisObj)")
protected static DynamicObject clear(DynamicObject thisObj) {
JSSet.getInternalSet(thisObj).clear();
return Undefined.instance;
}
@Specialization(guards = "!isJSSet(thisObj)")
protected static DynamicObject notSet(@SuppressWarnings("unused") Object thisObj) {
throw Errors.createTypeErrorSetExpected();
}
}
public abstract static class JSSetDeleteNode extends JSSetOperation {
public JSSetDeleteNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@Specialization(guards = "isJSSet(thisObj)")
protected boolean delete(DynamicObject thisObj, Object key) {
Object normalizedKey = normalize(key);
return JSSet.getInternalSet(thisObj).remove(normalizedKey);
}
@SuppressWarnings("unused")
@Specialization(guards = "!isJSSet(thisObj)")
protected static boolean notSet(Object thisObj, Object key) {
throw Errors.createTypeErrorSetExpected();
}
}
public abstract static class JSSetAddNode extends JSSetOperation {
public JSSetAddNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@Specialization(guards = "isJSSet(thisObj)")
protected DynamicObject add(DynamicObject thisObj, Object key) {
Object normalizedKey = normalize(key);
JSSet.getInternalSet(thisObj).put(normalizedKey, PRESENT);
return thisObj;
}
@SuppressWarnings("unused")
@Specialization(guards = "!isJSSet(thisObj)")
protected static DynamicObject notSet(Object thisObj, Object key) {
throw Errors.createTypeErrorSetExpected();
}
}
public abstract static class JSSetHasNode extends JSSetOperation {
public JSSetHasNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@Specialization(guards = "isJSSet(thisObj)")
protected boolean has(DynamicObject thisObj, Object key) {
Object normalizedKey = normalize(key);
return JSSet.getInternalSet(thisObj).has(normalizedKey);
}
@SuppressWarnings("unused")
@Specialization(guards = "!isJSSet(thisObj)")
protected boolean hasNoObject(Object thisObj, Object key) {
throw Errors.createTypeErrorSetExpected();
}
}
public abstract static class JSSetForEachNode extends JSBuiltinNode {
public JSSetForEachNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@Specialization(guards = {"isJSSet(thisObj)", "isCallable.executeBoolean(callback)"}, limit = "1")
protected Object forEachFunction(DynamicObject thisObj, DynamicObject callback, Object thisArg,
@Cached @Shared("isCallable") @SuppressWarnings("unused") IsCallableNode isCallable,
@Cached("createCall()") JSFunctionCallNode callNode) {
JSHashMap map = JSSet.getInternalSet(thisObj);
JSHashMap.Cursor cursor = map.getEntries();
while (cursor.advance()) {
Object key = cursor.getKey();
callNode.executeCall(JSArguments.create(thisArg, callback, new Object[]{key, key, thisObj}));
}
return Undefined.instance;
}
@SuppressWarnings("unused")
@Specialization(guards = {"isJSSet(thisObj)", "!isCallable.executeBoolean(callback)"}, limit = "1")
protected static Object forEachFunctionNoFunction(Object thisObj, Object callback, Object thisArg,
@Cached @Shared("isCallable") @SuppressWarnings("unused") IsCallableNode isCallable) {
throw Errors.createTypeErrorCallableExpected();
}
@SuppressWarnings("unused")
@Specialization(guards = "!isJSSet(thisObj)")
protected static Object forEachFunctionNoSet(Object thisObj, Object callback, Object thisArg) {
throw Errors.createTypeErrorSetExpected();
}
}
public abstract static class CreateSetIteratorNode extends JSBuiltinNode {
private final int iterationKind;
@Child private CreateObjectNode.CreateObjectWithPrototypeNode createObjectNode;
@Child private PropertySetNode setNextIndexNode;
@Child private PropertySetNode setIteratedObjectNode;
@Child private PropertySetNode setIterationKindNode;
public CreateSetIteratorNode(JSContext context, JSBuiltin builtin, int iterationKind) {
super(context, builtin);
this.iterationKind = iterationKind;
this.createObjectNode = CreateObjectNode.createOrdinaryWithPrototype(context);
this.setIteratedObjectNode = PropertySetNode.createSetHidden(JSRuntime.ITERATED_OBJECT_ID, context);
this.setNextIndexNode = PropertySetNode.createSetHidden(JSRuntime.ITERATOR_NEXT_INDEX, context);
this.setIterationKindNode = PropertySetNode.createSetHidden(JSSet.SET_ITERATION_KIND_ID, context);
}
@Specialization(guards = "isJSSet(set)")
protected DynamicObject doSet(VirtualFrame frame, DynamicObject set) {
DynamicObject iterator = createObjectNode.execute(frame, getContext().getRealm().getSetIteratorPrototype());
setIteratedObjectNode.setValue(iterator, set);
setNextIndexNode.setValue(iterator, JSSet.getInternalSet(set).getEntries());
setIterationKindNode.setValueInt(iterator, iterationKind);
return iterator;
}
@SuppressWarnings("unused")
@Specialization(guards = "!isJSSet(thisObj)")
protected DynamicObject doIncompatibleReceiver(Object thisObj) {
throw Errors.createTypeError("not a Set");
}
}
}