package com.oracle.truffle.js.builtins;
import java.io.PrintWriter;
import java.util.Map;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.js.builtins.ConsoleBuiltinsFactory.JSConsoleAssertNodeGen;
import com.oracle.truffle.js.builtins.ConsoleBuiltinsFactory.JSConsoleClearNodeGen;
import com.oracle.truffle.js.builtins.ConsoleBuiltinsFactory.JSConsoleCountNodeGen;
import com.oracle.truffle.js.builtins.ConsoleBuiltinsFactory.JSConsoleCountResetNodeGen;
import com.oracle.truffle.js.builtins.ConsoleBuiltinsFactory.JSConsoleGroupEndNodeGen;
import com.oracle.truffle.js.builtins.ConsoleBuiltinsFactory.JSConsoleGroupNodeGen;
import com.oracle.truffle.js.builtins.ConsoleBuiltinsFactory.JSConsoleTimeEndNodeGen;
import com.oracle.truffle.js.builtins.ConsoleBuiltinsFactory.JSConsoleTimeLogNodeGen;
import com.oracle.truffle.js.builtins.ConsoleBuiltinsFactory.JSConsoleTimeNodeGen;
import com.oracle.truffle.js.builtins.GlobalBuiltins.JSGlobalPrintNode;
import com.oracle.truffle.js.builtins.GlobalBuiltinsFactory.JSGlobalPrintNodeGen;
import com.oracle.truffle.js.nodes.cast.JSToBooleanNode;
import com.oracle.truffle.js.nodes.cast.JSToStringNode;
import com.oracle.truffle.js.nodes.function.JSBuiltin;
import com.oracle.truffle.js.nodes.function.JSBuiltinNode;
import com.oracle.truffle.js.runtime.JSConsoleUtil;
import com.oracle.truffle.js.runtime.JSContext;
import com.oracle.truffle.js.runtime.JSRealm;
import com.oracle.truffle.js.runtime.JSRuntime;
import com.oracle.truffle.js.runtime.builtins.BuiltinEnum;
import com.oracle.truffle.js.runtime.objects.Undefined;
public final class ConsoleBuiltins extends JSBuiltinsContainer.SwitchEnum<ConsoleBuiltins.Console> {
public static final JSBuiltinsContainer BUILTINS = new ConsoleBuiltins();
protected ConsoleBuiltins() {
super(JSRealm.CONSOLE_CLASS_NAME, Console.class);
}
public enum Console implements BuiltinEnum<Console> {
log(0),
info(0),
debug(0),
dir(0),
error(0),
warn(0),
assert_(0),
clear(0),
count(0),
countReset(0),
group(0),
groupCollapsed(0),
groupEnd(0),
time(0),
timeEnd(0),
timeLog(0);
private final int length;
Console(int length) {
this.length = length;
}
@Override
public int getLength() {
return length;
}
}
@Override
protected Object createNode(JSContext context, JSBuiltin builtin, boolean construct, boolean newTarget, Console builtinEnum) {
switch (builtinEnum) {
case log:
case info:
case debug:
case dir:
return JSGlobalPrintNodeGen.create(context, builtin, false, args().varArgs().createArgumentNodes(context));
case error:
case warn:
return JSGlobalPrintNodeGen.create(context, builtin, true, args().varArgs().createArgumentNodes(context));
case assert_:
return JSConsoleAssertNodeGen.create(context, builtin, args().varArgs().createArgumentNodes(context));
case clear:
return JSConsoleClearNodeGen.create(context, builtin, args().varArgs().createArgumentNodes(context));
case count:
return JSConsoleCountNodeGen.create(context, builtin, args().fixedArgs(1).createArgumentNodes(context));
case countReset:
return JSConsoleCountResetNodeGen.create(context, builtin, args().fixedArgs(1).createArgumentNodes(context));
case group:
case groupCollapsed:
return JSConsoleGroupNodeGen.create(context, builtin, args().varArgs().createArgumentNodes(context));
case groupEnd:
return JSConsoleGroupEndNodeGen.create(context, builtin, args().fixedArgs(0).createArgumentNodes(context));
case time:
return JSConsoleTimeNodeGen.create(context, builtin, args().fixedArgs(1).createArgumentNodes(context));
case timeEnd:
return JSConsoleTimeEndNodeGen.create(context, builtin, args().fixedArgs(1).createArgumentNodes(context));
case timeLog:
return JSConsoleTimeLogNodeGen.create(context, builtin, args().varArgs().createArgumentNodes(context));
}
return null;
}
public abstract static class JSConsoleOperation extends JSBuiltinNode {
protected static final String DEFAULT = "default";
public JSConsoleOperation(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
public JSConsoleUtil getConsoleUtil() {
return getContext().getRealm().getConsoleUtil();
}
}
public abstract static class JSConsoleAssertNode extends JSConsoleOperation {
@Child private JSGlobalPrintNode printNode;
@Child private JSToBooleanNode toBooleanNode;
public JSConsoleAssertNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
printNode = JSGlobalPrintNodeGen.create(context, null, false, null);
toBooleanNode = JSToBooleanNode.create();
}
@Specialization
protected DynamicObject assertImpl(Object... data) {
boolean result = data.length > 0 ? toBooleanNode.executeBoolean(data[0]) : false;
if (!result) {
Object[] arr = new Object[data.length > 0 ? data.length : 1];
if (data.length > 1) {
System.arraycopy(data, 1, arr, 1, data.length - 1);
}
arr[0] = data.length > 1 ? "Assertion failed:" : "Assertion failed";
printNode.executeObjectArray(arr);
}
return Undefined.instance;
}
}
public abstract static class JSConsoleClearNode extends JSConsoleOperation {
public JSConsoleClearNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@Specialization
@TruffleBoundary
protected DynamicObject clear() {
PrintWriter writer = getContext().getRealm().getOutputWriter();
writer.append("\033[H\033[2J");
writer.flush();
return Undefined.instance;
}
}
public abstract static class JSConsoleCountNode extends JSConsoleOperation {
@Child private JSToStringNode toStringNode;
public JSConsoleCountNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
toStringNode = JSToStringNode.create();
}
@Specialization
@TruffleBoundary
protected DynamicObject count(Object label) {
String key = label == Undefined.instance ? DEFAULT : toStringNode.executeString(label);
int count = 0;
JSConsoleUtil console = getConsoleUtil();
Map<String, Integer> countMap = console.getCountMap();
if (countMap.containsKey(key)) {
count = countMap.get(key);
}
countMap.put(key, ++count);
PrintWriter writer = getContext().getRealm().getOutputWriter();
writer.append(console.getConsoleIndentationString());
writer.append(key);
writer.append(": ");
writer.append(String.valueOf(count));
writer.append(JSRuntime.LINE_SEPARATOR);
writer.flush();
return Undefined.instance;
}
}
public abstract static class JSConsoleCountResetNode extends JSConsoleOperation {
@Child private JSToStringNode toStringNode;
public JSConsoleCountResetNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
toStringNode = JSToStringNode.create();
}
@Specialization
@TruffleBoundary
protected DynamicObject count(Object label) {
String key = label == Undefined.instance ? DEFAULT : toStringNode.executeString(label);
getConsoleUtil().getCountMap().remove(key);
return Undefined.instance;
}
}
public abstract static class JSConsoleGroupNode extends JSConsoleOperation {
@Child private JSGlobalPrintNode printNode;
@Child private JSToStringNode toStringNode;
public JSConsoleGroupNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
toStringNode = JSToStringNode.create();
printNode = JSGlobalPrintNodeGen.create(context, null, false, null);
}
@Specialization
@TruffleBoundary
protected DynamicObject group(Object[] label) {
if (label.length > 0) {
printNode.executeObjectArray(label);
}
getConsoleUtil().incConsoleIndentation();
return Undefined.instance;
}
}
public abstract static class JSConsoleGroupEndNode extends JSConsoleOperation {
public JSConsoleGroupEndNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
}
@Specialization
@TruffleBoundary
protected DynamicObject groupEnd() {
getConsoleUtil().decConsoleIndentation();
return Undefined.instance;
}
}
public abstract static class JSConsoleTimeNode extends JSConsoleOperation {
@Child private JSToStringNode toStringNode;
public JSConsoleTimeNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
toStringNode = JSToStringNode.create();
}
@Specialization
@TruffleBoundary
protected DynamicObject time(Object label) {
String key = label == Undefined.instance ? DEFAULT : toStringNode.executeString(label);
getConsoleUtil().getTimeMap().put(key, getContext().getRealm().currentTimeMillis());
return Undefined.instance;
}
}
public abstract static class JSConsoleTimeEndNode extends JSConsoleOperation {
@Child private JSToStringNode toStringNode;
@Child private JSGlobalPrintNode printNode;
public JSConsoleTimeEndNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
toStringNode = JSToStringNode.create();
printNode = JSGlobalPrintNodeGen.create(context, null, false, null);
}
@Specialization
@TruffleBoundary
protected DynamicObject timeEnd(Object label) {
String key = label == Undefined.instance ? DEFAULT : toStringNode.executeString(label);
Map<String, Long> timeMap = getConsoleUtil().getTimeMap();
if (timeMap.containsKey(key)) {
long start = timeMap.remove(key);
long end = getContext().getRealm().currentTimeMillis();
long delta = end - start;
printNode.executeObjectArray(new Object[]{key + ":", String.valueOf(delta) + "ms"});
}
return Undefined.instance;
}
}
public abstract static class JSConsoleTimeLogNode extends JSConsoleOperation {
@Child private JSGlobalPrintNode printNode;
@Child private JSToStringNode toStringNode;
public JSConsoleTimeLogNode(JSContext context, JSBuiltin builtin) {
super(context, builtin);
printNode = JSGlobalPrintNodeGen.create(context, null, false, null);
toStringNode = JSToStringNode.create();
}
@Specialization
@TruffleBoundary
protected DynamicObject timeLog(Object... data) {
String key = data.length == 0 || data[0] == Undefined.instance ? DEFAULT : toStringNode.executeString(data[0]);
Map<String, Long> timeMap = getConsoleUtil().getTimeMap();
if (timeMap.containsKey(key)) {
long start = timeMap.get(key);
long end = getContext().getRealm().currentTimeMillis();
long delta = end - start;
Object[] arr = new Object[Math.max(2, data.length + 1)];
if (data.length > 1) {
System.arraycopy(data, 1, arr, 2, data.length - 1);
}
arr[0] = key + ":";
arr[1] = String.valueOf(delta) + "ms";
printNode.executeObjectArray(arr);
}
return Undefined.instance;
}
}
}