package com.oracle.truffle.js.builtins;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.js.builtins.FinalizationRegistryPrototypeBuiltinsFactory.JSFinalizationRegistryCleanupSomeNodeGen;
import com.oracle.truffle.js.builtins.FinalizationRegistryPrototypeBuiltinsFactory.JSFinalizationRegistryRegisterNodeGen;
import com.oracle.truffle.js.builtins.FinalizationRegistryPrototypeBuiltinsFactory.JSFinalizationRegistryUnregisterNodeGen;
import com.oracle.truffle.js.nodes.access.IsObjectNode;
import com.oracle.truffle.js.nodes.binary.JSIdenticalNode;
import com.oracle.truffle.js.nodes.function.JSBuiltin;
import com.oracle.truffle.js.nodes.function.JSBuiltinNode;
import com.oracle.truffle.js.nodes.unary.IsCallableNode;
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.JSFinalizationRegistry;
import com.oracle.truffle.js.runtime.builtins.JSFinalizationRegistryObject;
import com.oracle.truffle.js.runtime.objects.Undefined;
public final class FinalizationRegistryPrototypeBuiltins extends JSBuiltinsContainer.SwitchEnum<FinalizationRegistryPrototypeBuiltins.FinalizationRegistryPrototype> {
public static final JSBuiltinsContainer BUILTINS = new FinalizationRegistryPrototypeBuiltins();
protected FinalizationRegistryPrototypeBuiltins() {
super(JSFinalizationRegistry.PROTOTYPE_NAME, FinalizationRegistryPrototype.class);
}
public enum FinalizationRegistryPrototype implements BuiltinEnum<FinalizationRegistryPrototype> {
register(2),
unregister(1),
cleanupSome(0);
private final int length;
FinalizationRegistryPrototype(int length) {
this.length = length;
}
@Override
public int getLength() {
return length;
}
}
@Override
protected Object createNode(JSContext context, JSBuiltin builtin, boolean construct, boolean newTarget, FinalizationRegistryPrototype builtinEnum) {
switch (builtinEnum) {
case register:
return JSFinalizationRegistryRegisterNodeGen.create(context, builtin, args().withThis().fixedArgs(3).createArgumentNodes(context));
case unregister:
return JSFinalizationRegistryUnregisterNodeGen.create(context, builtin, args().withThis().fixedArgs(1).createArgumentNodes(context));
case cleanupSome:
return JSFinalizationRegistryCleanupSomeNodeGen.create(context, builtin, args().withThis().fixedArgs(1).createArgumentNodes(context));
}
return null;
}
public abstract static class FinalizationRegistryOperation extends JSBuiltinNode {
protected final BranchProfile errorBranch = BranchProfile.create();
public FinalizationRegistryOperation(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
protected void invalidUnregisterToken(Object token) {
errorBranch.enter();
throw Errors.createTypeErrorFormat("unregisterToken ('%s') must be an object", JSRuntime.safeToString(token));
}
}
public abstract static class JSFinalizationRegistryRegisterNode extends FinalizationRegistryOperation {
@Child protected JSIdenticalNode sameValueNode = JSIdenticalNode.createSameValue();
@Child protected IsObjectNode isObjectNode = IsObjectNode.create();
public JSFinalizationRegistryRegisterNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@Specialization
protected DynamicObject register(JSFinalizationRegistryObject thisObj, Object target, Object holdings, Object unregisterTokenArg) {
if (!isObjectNode.executeBoolean(target)) {
errorBranch.enter();
throw Errors.createTypeError("FinalizationRegistry.prototype.register: target must be an object");
}
if (sameValueNode.executeBoolean(target, holdings)) {
errorBranch.enter();
throw Errors.createTypeError("FinalizationRegistry.prototype.register: target and holdings must not be same");
}
Object unregisterToken = unregisterTokenArg;
if (!isObjectNode.executeBoolean(unregisterToken)) {
if (unregisterToken != Undefined.instance) {
invalidUnregisterToken(unregisterToken);
}
unregisterToken = Undefined.instance;
}
JSFinalizationRegistry.appendToCells(thisObj, target, holdings, unregisterToken);
return Undefined.instance;
}
@SuppressWarnings("unused")
@Specialization(guards = "!isJSFinalizationRegistry(thisObj)")
protected static DynamicObject notFinalizationRegistry(@SuppressWarnings("unused") Object thisObj, Object target, Object holdings, Object unregisterToken) {
throw Errors.createTypeErrorFinalizationRegistryExpected();
}
}
public abstract static class JSFinalizationRegistryUnregisterNode extends FinalizationRegistryOperation {
@Child protected IsObjectNode isObjectNode = IsObjectNode.create();
public JSFinalizationRegistryUnregisterNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@Specialization
protected boolean unregister(JSFinalizationRegistryObject thisObj, Object unregisterToken) {
if (!isObjectNode.executeBoolean(unregisterToken)) {
invalidUnregisterToken(unregisterToken);
}
return JSFinalizationRegistry.removeFromCells(thisObj, unregisterToken);
}
@SuppressWarnings("unused")
@Specialization(guards = "!isJSFinalizationRegistry(thisObj)")
protected static boolean notFinalizationRegistry(@SuppressWarnings("unused") Object thisObj, Object unregisterToken) {
throw Errors.createTypeErrorFinalizationRegistryExpected();
}
}
public abstract static class JSFinalizationRegistryCleanupSomeNode extends FinalizationRegistryOperation {
@Child protected IsCallableNode isCallableNode = IsCallableNode.create();
public JSFinalizationRegistryCleanupSomeNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@Specialization
protected DynamicObject cleanupSome(JSFinalizationRegistryObject thisObj, Object callback) {
if (callback != Undefined.instance && !isCallableNode.executeBoolean(callback)) {
errorBranch.enter();
throw Errors.createTypeError("FinalizationRegistry: cleanup must be callable");
}
JSFinalizationRegistry.cleanupFinalizationRegistry(thisObj, callback);
return Undefined.instance;
}
@SuppressWarnings("unused")
@Specialization(guards = "!isJSFinalizationRegistry(thisObj)")
protected static DynamicObject notFinalizationRegistry(@SuppressWarnings("unused") Object thisObj, Object callback) {
throw Errors.createTypeErrorFinalizationRegistryExpected();
}
}
}