package com.oracle.truffle.js.builtins;
import java.util.function.Consumer;
import org.graalvm.collections.EconomicMap;
import com.oracle.truffle.js.nodes.function.BuiltinArgumentBuilder;
import com.oracle.truffle.js.nodes.function.BuiltinNodeFactory;
import com.oracle.truffle.js.nodes.function.JSBuiltin;
import com.oracle.truffle.js.runtime.JSConfig;
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.objects.JSAttributes;
public class JSBuiltinsContainer {
private final String name;
final EconomicMap<String, JSBuiltin> builtins = EconomicMap.create();
protected JSBuiltinsContainer(String name) {
this.name = name;
}
public final void putAll(JSBuiltinsContainer container) {
builtins.putAll(container.builtins);
}
public final JSBuiltin lookupByName(String methodName) {
return builtins.get(methodName);
}
public final void forEachBuiltin(Consumer<? super JSBuiltin> consumer) {
builtins.getValues().forEach(consumer);
}
protected static BuiltinArgumentBuilder args() {
return BuiltinArgumentBuilder.builder();
}
public final String getName() {
return name;
}
public static <E extends Enum<E> & BuiltinEnum<E>> JSBuiltinsContainer (String name, Class<E> builtinEnum) {
return new SwitchEnum<>(name, builtinEnum);
}
public static <E extends Enum<E> & BuiltinEnum<E>> JSBuiltinsContainer (Class<E> builtinEnum) {
return fromEnum(null, builtinEnum);
}
public abstract static class Switch extends JSBuiltinsContainer {
protected Switch(String name) {
super(name);
}
protected final void defineFunction(String name, int length) {
defineFunction(name, length, JSAttributes.getDefaultNotEnumerable());
}
protected final void defineFunction(String name, int length, int attributeFlags) {
defineBuiltin(name, length, attributeFlags, false, false);
}
protected final void defineConstructor(String name, int length, boolean isNewTargetConstructor) {
assert !name.isEmpty();
defineBuiltin(name, length, JSAttributes.getDefaultNotEnumerable(), true, isNewTargetConstructor);
}
private void defineBuiltin(String name, int length, int attributeFlags, boolean isConstructor, boolean isNewTargetConstructor) {
class FactoryImpl implements BuiltinNodeFactory {
private final boolean construct;
private final boolean newTarget;
FactoryImpl(boolean construct, boolean newTarget) {
this.construct = construct;
this.newTarget = newTarget;
}
@Override
public Object createObject(JSContext context, JSBuiltin builtin) {
return Switch.this.createNode(context, builtin, construct, newTarget);
}
}
assert !name.isEmpty();
BuiltinNodeFactory call = new FactoryImpl(false, false);
BuiltinNodeFactory construct = isConstructor ? new FactoryImpl(true, false) : null;
BuiltinNodeFactory constructNewTarget = isNewTargetConstructor ? new FactoryImpl(true, true) : null;
builtins.put(name, new JSBuiltin(getName(), name, length, attributeFlags, 5, false, call, construct, constructNewTarget));
}
protected abstract Object createNode(JSContext context, JSBuiltin builtin, boolean construct, boolean newTarget);
}
public static class SwitchEnum<E extends Enum<E> & BuiltinEnum<E>> extends JSBuiltinsContainer {
private final Class<E> enumType;
protected SwitchEnum(String name, Class<E> enumType) {
super(name);
this.enumType = enumType;
for (E builtin : enumType.getEnumConstants()) {
if (builtin.isEnabled() && (!JSConfig.SubstrateVM || builtin.isAOTSupported())) {
loadBuiltin(builtin);
}
}
}
protected SwitchEnum(Class<E> enumType) {
this(null, enumType);
}
private void loadBuiltin(E builtinEnum) {
class FactoryImpl implements BuiltinNodeFactory {
private final boolean construct;
private final boolean newTarget;
FactoryImpl(boolean construct, boolean newTarget) {
this.construct = construct;
this.newTarget = newTarget;
}
@Override
public Object createObject(JSContext context, JSBuiltin builtin) {
return SwitchEnum.this.createNode(context, builtin, construct, newTarget, builtinEnum);
}
}
BuiltinNodeFactory call = new FactoryImpl(false, false);
BuiltinNodeFactory construct = builtinEnum.isConstructor() ? new FactoryImpl(true, false) : null;
BuiltinNodeFactory constructNewTarget = builtinEnum.isNewTargetConstructor() ? new FactoryImpl(true, true) : null;
builtins.put(builtinEnum.getName(), createBuiltin(builtinEnum, call, construct, constructNewTarget));
}
private JSBuiltin createBuiltin(E builtinEnum, BuiltinNodeFactory functionNodeFactory, BuiltinNodeFactory constructorNodeFactory, BuiltinNodeFactory newTargetConstructorFactory) {
Object key = builtinEnum.getKey();
assert JSRuntime.isPropertyKey(key);
int length = builtinEnum.getLength();
int attributeFlags = JSAttributes.fromConfigurableEnumerableWritable(builtinEnum.isConfigurable(), builtinEnum.isEnumerable(), builtinEnum.isWritable());
return new JSBuiltin(getName(), key, length, attributeFlags, builtinEnum.getECMAScriptVersion(), builtinEnum.isAnnexB(), functionNodeFactory, constructorNodeFactory,
newTargetConstructorFactory);
}
public Class<E> getEnumType() {
return enumType;
}
protected Object createNode(JSContext context, JSBuiltin builtin, boolean construct, boolean newTarget, E builtinEnum) {
return builtinEnum.createNode(context, builtin, construct, newTarget);
}
}
public abstract static class Lambda extends JSBuiltinsContainer {
protected Lambda(String name) {
super(name);
}
protected final void defineFunction(String name, int length, BuiltinNodeFactory nodeFactory) {
assert !name.isEmpty();
builtins.put(name, new JSBuiltin(getName(), name, length, JSAttributes.getDefaultNotEnumerable(), nodeFactory));
}
protected final void defineFunction(String name, int length, int attributeFlags, BuiltinNodeFactory nodeFactory) {
assert !name.isEmpty();
builtins.put(name, new JSBuiltin(getName(), name, length, attributeFlags, nodeFactory));
}
protected final void defineConstructor(String name, int length, BuiltinNodeFactory nodeFactory, BuiltinNodeFactory constructorFactory) {
assert !name.isEmpty();
builtins.put(name, new JSBuiltin(getName(), name, length, JSAttributes.getDefaultNotEnumerable(), 5, false, nodeFactory, constructorFactory, null));
}
}
}