isbpl/ISBPL.java
2022-08-01 18:37:51 +02:00

3103 lines
130 KiB
Java

import java.io.*;
import java.lang.reflect.*;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.file.Files;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import sun.misc.Unsafe; // the Safe
/**
* @author TudbuT
* @since 04 Mar 2022
*/
public class ISBPL {
// Compile flags begin
public static final boolean ENABLE_JINTEROP = true;
public static final long JVM_GC_REQUEST_DELAY = 60000;
public static final boolean JVM_REQUEST_GC = true;
// Compile flags end
static Unsafe theSafe;
static {
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
theSafe = (Unsafe) f.get(null);
} catch (Throwable e) {
throw new Error(e);
}
}
public static PrintStream gOutputStream = System.out;
public static PrintStream gErrorStream = System.err;
static boolean debug = false, printCalls = false;
public static ISBPLDebugger.IPC debuggerIPC = new ISBPLDebugger.IPC();
ArrayList<ISBPLType> types = new ArrayList<>();
ISBPLFrame level0 = new ISBPLFrame("isbpl", this);
final ISBPLThreadLocal<Stack<File>> fileStack = ISBPLThreadLocal.withInitial(Stack::new);
final ISBPLThreadLocal<Stack<ISBPLFrame>> frameStack = ISBPLThreadLocal.withInitial(() -> {
Stack<ISBPLFrame> frames = new Stack<>();
frames.push(level0);
return frames;
});
final ISBPLThreadLocal<ArrayList<String>> lastWords = ISBPLThreadLocal.withInitial(() -> new ArrayList<>(16));
int exitCode;
private final ISBPLStreamer streamer = new ISBPLStreamer(this);
ArrayList<String> included = new ArrayList<>();
HashMap<String, ISBPLCallable> natives = new HashMap<>();
boolean stopExceptions = false;
HashMap<Throwable, Stack<ISBPLFrame>> stackTraces = new HashMap<>();
static int uncollected = 0;
static {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
if(debug) {
for(int i = 0; i < 10 && uncollected > 0; i++) {
System.gc();
System.runFinalization();
}
if(uncollected > 50) {
System.err.println("JVM didn't GC " + uncollected + " objects.");
}
}
}));
}
private final Object syncMakeThread = new Object();
private ISBPLKeyword getKeyword(String word) {
switch (word) {
case "native":
return (idx, words, file, stack) -> {
idx++;
addNative(words[idx]);
return idx;
};
case "func":
return (idx, words, file, stack) -> createFunction(idx, words, file);
case "def":
return (idx, words, file, stack) -> {
idx++;
frameStack.get().peek().define(words[idx], getNullObject());
return idx;
};
case "if":
return (idx, words, file, stack) -> {
idx++;
AtomicInteger i = new AtomicInteger(idx);
ISBPLCallable callable = readCallable("if", i, words, file);
callable.addUse();
if(stack.pop().isTruthy()) {
callable.call(stack);
}
callable.removeUse();
return i.get();
};
case "while":
return (idx, words, file, stack) -> {
idx++;
AtomicInteger i = new AtomicInteger(idx);
ISBPLCallable cond = readCallable("while-condition", i, words, file);
i.getAndIncrement();
ISBPLCallable block = readCallable("while", i, words, file);
cond.addUse();
block.addUse();
cond.call(stack);
while (stack.pop().isTruthy()) {
block.call(stack);
cond.call(stack);
}
cond.removeUse();
block.removeUse();
return i.get();
};
case "stop":
return (idx, words, file, stack) -> {
ISBPLObject o = stack.pop();
o.checkType(getType("int"));
throw new ISBPLStop((int) o.object);
};
case "try":
return (idx, words, file, stack) -> {
idx++;
ISBPLObject array = stack.pop();
array.checkTypeMulti(getType("array"), getType("string"));
String[] allowed;
if(debug)
ISBPL.gOutputStream.println("try with " + array);
if(array.type.name.equals("string")) {
allowed = new String[] { toJavaString(array) };
}
else {
ISBPLArray arr = ((ISBPLArray) array.object);
allowed = new String[arr.length];
for (int i = 0 ; i < arr.length ; i++) {
allowed[i] = toJavaString(arr.get(i));
}
}
AtomicInteger i = new AtomicInteger(idx);
ISBPLCallable block = readCallable("try", i, words, file);
i.getAndIncrement();
ISBPLCallable catcher = readCallable("catch", i, words, file);
block.addUse();
catcher.addUse();
int stackHeight = stack.size();
try {
try {
block.call(stack);
} catch (ISBPLError error) {
if (Arrays.asList(allowed).contains(error.type) || allowed.length == 1 && allowed[0].equals("all")) {
stack.push(new ISBPLObject(getType("error"), error));
stack.push(toISBPLString(error.message));
stack.push(toISBPLString(error.type));
catcher.call(stack);
}
else {
throw error;
}
}
} catch (Throwable e) {
if (Arrays.asList(allowed).contains("Java")) {
stack.push(new ISBPLObject(getType("error"), e));
stack.push(toISBPL(e));
stack.push(toISBPLString(e.getClass().getName()));
stack.push(toISBPLString("Java"));
catcher.call(stack);
}
else {
throw e;
}
} finally {
if(stack.size() > stackHeight) {
stack.setSize(stackHeight);
stack.trimToSize();
}
while(stack.size() < stackHeight) {
stack.push(getNullObject());
}
block.removeUse();
catcher.removeUse();
}
return i.get();
};
case "do":
return (idx, words, file, stack) -> {
idx++;
AtomicInteger i = new AtomicInteger(idx);
ISBPLCallable block = readCallable("do-main", i, words, file);
i.getAndIncrement();
ISBPLCallable catcher = readCallable("do-finally", i, words, file);
block.addUse();
catcher.addUse();
try {
block.call(stack);
} finally {
catcher.call(stack);
block.removeUse();
catcher.removeUse();
}
return i.get();
};
// Returns closed function (func behaviour)
case "{":
return (idx, words, file, stack) -> {
AtomicInteger i = new AtomicInteger(idx);
ISBPLCallable block = readCallable("lambda", i, words, file);
stack.push(new ISBPLObject(getType("func"), block));
return i.get();
};
case "fork":
return (idx, words, file, stack) -> {
synchronized(syncMakeThread) {
idx++;
AtomicInteger i = new AtomicInteger(idx);
ISBPLStack<ISBPLObject> s = new ISBPLStack<>();
for(ISBPLObject obj : stack) {
s.push(obj);
}
ISBPLCallable block = readCallable("thread", i, words, file);
block.addUse();
long tid = Thread.currentThread().getId();
Stack<ISBPLFrame> fstack = (Stack<ISBPLFrame>) frameStack.get().clone();
new Thread(() -> {
debuggerIPC.run.put(Thread.currentThread().getId(), debuggerIPC.run.getOrDefault(tid, -1) == -1 ? -1 : 0);
debuggerIPC.stack.put(Thread.currentThread().getId(), s);
frameStack.set(fstack);
try {
block.call(s);
} catch (ISBPLStop stop) {
if(stop.amount == -1) {
System.exit(exitCode);
}
} catch (ISBPLError err) {
if(err.type.equals("Java"))
((Throwable)s.pop().object).printStackTrace();
else
throw err;
} finally {
block.removeUse();
}
}).start();
return i.get();
}
};
case "construct":
return (idx, words, file, stack) -> {
idx++;
String typename = words[idx++];
// Chop string quote
if(typename.startsWith("\"")) typename = typename.substring(1);
// Create type
ISBPLType type = registerType(typename);
for(; !words[idx].equals("{"); idx++) {
ISBPLType t = getType(words[idx].startsWith("\"") ? words[idx].substring(1) : words[idx]);
if(!type.superTypes.contains(t))
type.superTypes.add(t);
}
AtomicInteger i = new AtomicInteger(idx);
String[] words2 = readBlock(i, words, file);
boolean definingMethods = false;
for(int j = 0; j < words2.length; j++) {
String word2 = words2[j];
// Ignore empty
if(word2.equals("")) continue;
if(word2.equals("\"")) continue;
if(word2.equals("#")) {
j++;
continue;
}
if(definingMethods) {
AtomicInteger idx2 = new AtomicInteger(++j);
addFunction(type, word2, readCallable(word2, idx2, words2, file));
j = idx2.get();
}
else {
if(word2.equals(";")) {
definingMethods = true;
continue;
}
Object var = new Object();
addFunction(type, word2, (stack1) -> stack1.push(type.varget(stack1.pop()).getOrDefault(var, getNullObject())));
addFunction(type, "=" + word2, (stack1) -> {
ISBPLObject o = stack1.pop();
ISBPLObject p = stack1.pop();
p.usedBy(o);
p = type.varget(o).put(var, p);
if(p != null) p.unusedBy(o);
});
}
}
stack.push(new ISBPLObject(getType("int"), type.id));
if(debug)
ISBPL.gErrorStream.println("Constructing type " + type);
return i.get();
};
case "with":
return (idx, words, file, stack) -> {
idx++;
int i = 1;
for(; !words[idx].equals(";"); idx++, i++);
for(idx--; !words[idx].equals("with"); idx--) {
frameStack.get().peek().define(words[idx], stack.pop());
}
return idx + i;
};
case "string!":
return (idx, words, file, stack) -> {
idx++;
AtomicInteger i = new AtomicInteger(idx);
String s = "";
for(String w : readBlock(i, words, file)) {
s += w;
if(w.length() > 0 && w.charAt(0) == '"')
s += '"';
s += " ";
}
s = s.substring(0, s.length() - 1);
stack.push(toISBPLString(s));
return i.get();
};
default:
return null;
}
}
@SuppressWarnings("RedundantCast")
private void addNative(String name) {
ISBPLCallable func;
switch (name) {
case "alen":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o = stack.pop();
o.checkType(getType("array"));
stack.push(new ISBPLObject(getType("int"), ((ISBPLArray) o.object).length));
};
break;
case "aget":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject i = stack.pop();
ISBPLObject o = stack.pop();
i.checkType(getType("int"));
o.checkType(getType("array"));
stack.push(((ISBPLArray) o.object).get(((int) i.object)));
};
break;
case "aput":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject toPut = stack.pop();
ISBPLObject i = stack.pop();
ISBPLObject o = stack.pop();
i.checkType(getType("int"));
o.checkType(getType("array"));
((ISBPLArray) o.object).set(((int) i.object), toPut);
};
break;
case "anew":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject i = stack.pop();
i.checkType(getType("int"));
stack.push(new ISBPLObject(getType("array"), new ISBPLArray(((int) i.object), getNullObject())));
};
break;
case "acopy":
func = (stack) -> {
ISBPLObject len = stack.pop();
ISBPLObject idx2 = stack.pop();
ISBPLObject idx1 = stack.pop();
ISBPLObject arr2 = stack.pop();
ISBPLObject arr1 = stack.pop();
arr1.checkType(getType("array"));
arr2.checkType(getType("array"));
ISBPLArray o1 = (ISBPLArray) arr1.object;
ISBPLArray o2 = (ISBPLArray) arr2.object;
int idx1_ = (int) idx1.toLong();
int idx2_ = (int) idx2.toLong();
int len_ = (int) len.toLong();
for(int i1 = idx1_, i2 = idx2_; i1 < idx1_ + len_ && i1 < o1.length && i2 < o2.length; i1++, i2++) {
o2.set(i2, o1.get(i1));
}
stack.push(arr2);
};
break;
case "_array":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject a = stack.pop();
if(a.type.equals(getType("array")))
stack.push(a);
else if(a.object instanceof ISBPLArray) {
stack.push(new ISBPLObject(getType("array"), a.object));
} else
typeError(a.type.name, "array");
};
break;
case "_char":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o = stack.pop();
o.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double"));
stack.push(new ISBPLObject(getType("char"), ((char) o.toLong())));
};
break;
case "_byte":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o = stack.pop();
o.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double"));
stack.push(new ISBPLObject(getType("byte"), ((byte) o.toLong())));
};
break;
case "_int":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o = stack.pop();
o.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double"));
stack.push(new ISBPLObject(getType("int"), ((int) o.toLong())));
};
break;
case "_float":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o = stack.pop();
o.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double"));
stack.push(new ISBPLObject(getType("float"), ((float) o.toDouble())));
};
break;
case "_long":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o = stack.pop();
o.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double"));
stack.push(new ISBPLObject(getType("long"), o.toLong()));
};
break;
case "_double":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o = stack.pop();
o.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double"));
stack.push(new ISBPLObject(getType("double"), o.toDouble()));
};
break;
case "ischar":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o = stack.pop();
stack.push(new ISBPLObject(getType("int"), o.type.equals(getType("char")) ? 1 : 0));
};
break;
case "isbyte":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o = stack.pop();
stack.push(new ISBPLObject(getType("int"), o.type.equals(getType("byte")) ? 1 : 0));
};
break;
case "isint":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o = stack.pop();
stack.push(new ISBPLObject(getType("int"), o.type.equals(getType("int")) ? 1 : 0));
};
break;
case "isfloat":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o = stack.pop();
stack.push(new ISBPLObject(getType("int"), o.type.equals(getType("float")) ? 1 : 0));
};
break;
case "islong":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o = stack.pop();
stack.push(new ISBPLObject(getType("int"), o.type.equals(getType("long")) ? 1 : 0));
};
break;
case "isdouble":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o = stack.pop();
stack.push(new ISBPLObject(getType("int"), o.type.equals(getType("double")) ? 1 : 0));
};
break;
case "isarray":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o = stack.pop();
stack.push(new ISBPLObject(getType("int"), o.type.equals(getType("array")) ? 1 : 0));
};
break;
case "_layer_call":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject i = stack.pop();
ISBPLObject s = stack.pop();
i.checkType(getType("int"));
frameStack.get().get(frameStack.get().size() - 1 - ((int) i.object)).resolve(toJavaString(s)).call(stack);
};
break;
case "getfile":
func = (ISBPLStack<ISBPLObject> stack) -> {
stack.push(toISBPLString(fileStack.get().peek().getAbsolutePath()));
};
break;
case "reload":
func = (ISBPLStack<ISBPLObject> stack) -> {
String filepath = getFilePathForInclude((ISBPLStack<ISBPLObject>) stack, fileStack.get());
File f = new File(filepath).getAbsoluteFile();
try {
interpret(f, readFile(f), stack);
}
catch (IOException e) {
throw new ISBPLError("IO", "Couldn't find file " + filepath + " required by reload keyword.");
}
};
break;
case "include":
func = (ISBPLStack<ISBPLObject> stack) -> {
String filepath = getFilePathForInclude((ISBPLStack<ISBPLObject>) stack, fileStack.get());
if(!included.contains(filepath)) {
File f = new File(filepath).getAbsoluteFile();
try {
interpret(f, readFile(f), stack);
}
catch (IOException e) {
throw new ISBPLError("IO", "Couldn't find file " + filepath + " required by include keyword.");
}
included.add(filepath);
}
};
break;
case "putchar":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject c = stack.pop();
c.checkType(getType("char"));
ISBPL.gOutputStream.print(((char) c.object));
};
break;
case "eputchar":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject c = stack.pop();
c.checkType(getType("char"));
ISBPL.gErrorStream.print(((char) c.object));
};
break;
case "_file":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject s = stack.pop();
File f = new File(toJavaString(s));
stack.push(new ISBPLObject(getType("file"), f));
};
break;
case "read":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject end = stack.pop();
ISBPLObject begin = stack.pop();
ISBPLObject fileToRead = stack.pop();
end.checkType(getType("int"));
begin.checkType(getType("int"));
fileToRead.checkType(getType("file"));
try(FileInputStream f = new FileInputStream((File) fileToRead.object)) {
int b = ((int) begin.object);
int e = ((int) end.object);
byte[] bytes = new byte[e - b];
f.read(bytes, b, e);
ISBPLArray arr = new ISBPLArray(bytes.length, getNullObject());
for (int i = 0 ; i < arr.length ; i++) {
arr.set(i, new ISBPLObject(getType("byte"), bytes[i]));
}
stack.push(new ISBPLObject(getType("array"), arr));
}
catch (FileNotFoundException e) {
throw new ISBPLError("FileNotFound", "File not found.");
}
catch (IOException e) {
throw new ISBPLError("IO", "File couldn't be read from" + (e.getMessage() != null ? ": " + e.getMessage() : "."));
}
};
break;
case "flength":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject f = stack.pop();
f.checkType(getType("file"));
stack.push(new ISBPLObject(getType("int"), ((int) ((File) f.object).length())));
};
break;
case "write":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject content = stack.pop();
ISBPLObject fileToWrite = stack.pop();
content.checkType(getType("array"));
fileToWrite.checkType(getType("file"));
throw new ISBPLError("NotImplemented", "_file write is not implemented");
};
break;
case "getos":
func = (ISBPLStack<ISBPLObject> stack) -> {
stack.push(toISBPLString(System.getProperty("os.name")));
};
break;
case "mktype":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject s = stack.pop();
ISBPLType type = registerType(toJavaString(s));
stack.push(new ISBPLObject(getType("int"), type.id));
};
break;
case "typename":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject i = stack.pop();
i.checkType(getType("int"));
stack.push(toISBPLString(types.get(((int) i.object)).name));
};
break;
case "typeid":
func = (stack) -> {
stack.push(new ISBPLObject(getType("int"), getType(toJavaString(stack.pop())).id));
};
break;
case "gettype":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o = stack.pop();
stack.push(new ISBPLObject(getType("int"), o.type.id));
};
break;
case "settype":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject i = stack.pop();
ISBPLObject o = stack.pop();
i.checkType(getType("int"));
// TODO fix: o.object relocation if array
stack.push(new ISBPLObject(types.get(((int) i.object)), o.object));
};
break;
case "extends":
func = (stack) -> {
ISBPLObject b = stack.pop();
ISBPLObject a = stack.pop();
if(a.type.extendsOther(getType("int")) && b.type.extendsOther(getType("int"))) {
stack.push(new ISBPLObject(getType("int"), types.get((int) a.object).extendsOther(types.get((int) b.object)) ? 1 : 0));
}
else {
stack.push(new ISBPLObject(getType("int"), a.type.extendsOther(b.type) ? 1 : 0));
}
};
break;
case "throw":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject message = stack.pop();
ISBPLObject type = stack.pop();
String msg = toJavaString(message);
String t = toJavaString(type);
throw new ISBPLError(t, msg);
};
break;
case "exit":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject code = stack.pop();
code.checkType(getType("int"));
exitCode = ((int) code.object);
throw new ISBPLStop(0);
};
break;
case "eq":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o1 = stack.pop();
ISBPLObject o2 = stack.pop();
stack.push(new ISBPLObject(getType("int"), o1.equals(o2) ? 1 : 0));
};
break;
case "gt":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o2 = stack.pop();
ISBPLObject o1 = stack.pop();
o1.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double"));
o2.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double"));
stack.push(new ISBPLObject(getType("int"), o1.toDouble() > o2.toDouble() ? 1 : 0));
};
break;
case "lt":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o2 = stack.pop();
ISBPLObject o1 = stack.pop();
o1.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double"));
o2.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double"));
stack.push(new ISBPLObject(getType("int"), o1.toDouble() < o2.toDouble() ? 1 : 0));
};
break;
case "not":
func = (ISBPLStack<ISBPLObject> stack) -> stack.push(new ISBPLObject(getType("int"), stack.pop().isTruthy() ? 0 : 1));
break;
case "neg":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o = stack.pop();
o.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double"));
stack.push(new ISBPLObject(o.type, o.negative()));
};
break;
case "or":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o2 = stack.pop();
ISBPLObject o1 = stack.pop();
if(o1.isTruthy())
stack.push(o1);
else
stack.push(o2);
};
break;
case "and":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o2 = stack.pop();
ISBPLObject o1 = stack.pop();
// Pushes either 1 or the failed object
if (o1.isTruthy()) {
if (o2.isTruthy())
stack.push(new ISBPLObject(getType("int"), 1));
else
stack.push(o2);
}
else
stack.push(o1);
};
break;
case "+":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o2 = stack.pop();
ISBPLObject o1 = stack.pop();
o1.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double"));
o2.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double"));
Object object1 = o1.object;
Object object2 = o2.object;
ISBPLObject r = null;
if(object1 instanceof Integer && object2 instanceof Integer) {
r = new ISBPLObject(getType("int"), (int) ((int) (Integer) object1 + (int) (Integer) object2));
}
if(object1 instanceof Long && object2 instanceof Long) {
r = new ISBPLObject(getType("long"), (long) ((long) (Long) object1 + (long) (Long) object2));
}
if(object1 instanceof Character && object2 instanceof Character) {
r = new ISBPLObject(getType("char"), (char) ((char) (Character) object1 + (char) (Character) object2));
}
if(object1 instanceof Byte && object2 instanceof Byte) {
r = new ISBPLObject(getType("byte"), (byte) (Byte.toUnsignedInt((Byte) object1) + Byte.toUnsignedInt((Byte) object2)));
}
if(object1 instanceof Float && object2 instanceof Float) {
r = new ISBPLObject(getType("float"), (float) ((float) (Float) object1 + (float) (Float) object2));
}
if(object1 instanceof Double && object2 instanceof Double) {
r = new ISBPLObject(getType("double"), (double) ((double) (Double) object1 + (double) (Double) object2));
}
if(r != null)
stack.push(r);
else
typeError(o1.type.name, o2.type.name);
};
break;
case "-":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o2 = stack.pop();
ISBPLObject o1 = stack.pop();
o1.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double"));
o2.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double"));
Object object1 = o1.object;
Object object2 = o2.object;
ISBPLObject r = null;
if(object1 instanceof Integer && object2 instanceof Integer) {
r = new ISBPLObject(getType("int"), (int) ((int) (Integer) object1 - (int) (Integer) object2));
}
if(object1 instanceof Long && object2 instanceof Long) {
r = new ISBPLObject(getType("long"), (long) ((long) (Long) object1 - (long) (Long) object2));
}
if(object1 instanceof Character && object2 instanceof Character) {
r = new ISBPLObject(getType("char"), (char) ((char) (Character) object1 - (char) (Character) object2));
}
if(object1 instanceof Byte && object2 instanceof Byte) {
r = new ISBPLObject(getType("byte"), (byte) (Byte.toUnsignedInt((Byte) object1) - Byte.toUnsignedInt((Byte) object2)));
}
if(object1 instanceof Float && object2 instanceof Float) {
r = new ISBPLObject(getType("float"), (float) ((float) (Float) object1 - (float) (Float) object2));
}
if(object1 instanceof Double && object2 instanceof Double) {
r = new ISBPLObject(getType("double"), (double) ((double) (Double) object1 - (double) (Double) object2));
}
if(r != null)
stack.push(r);
else
typeError(o1.type.name, o2.type.name);
};
break;
case "/":
func = (ISBPLStack<ISBPLObject> stack) -> {
try {
ISBPLObject o2 = stack.pop();
ISBPLObject o1 = stack.pop();
o1.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double"));
o2.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double"));
Object object1 = o1.object;
Object object2 = o2.object;
ISBPLObject r = null;
if (object1 instanceof Integer && object2 instanceof Integer) {
r = new ISBPLObject(getType("int"), (int) ((int) (Integer) object1 / (int) (Integer) object2));
}
if (object1 instanceof Long && object2 instanceof Long) {
r = new ISBPLObject(getType("long"), (long) ((long) (Long) object1 / (long) (Long) object2));
}
if (object1 instanceof Character && object2 instanceof Character) {
r = new ISBPLObject(getType("char"), (char) ((char) (Character) object1 / (char) (Character) object2));
}
if (object1 instanceof Byte && object2 instanceof Byte) {
r = new ISBPLObject(getType("byte"), (byte) (Byte.toUnsignedInt((Byte) object1) / Byte.toUnsignedInt((Byte) object2)));
}
if (object1 instanceof Float && object2 instanceof Float) {
r = new ISBPLObject(getType("float"), (float) ((float) (Float) object1 / (float) (Float) object2));
}
if (object1 instanceof Double && object2 instanceof Double) {
r = new ISBPLObject(getType("double"), (double) ((double) (Double) object1 / (double) (Double) object2));
}
if (r != null)
stack.push(r);
else
typeError(o1.type.name, o2.type.name);
} catch (ArithmeticException ex) {
throw new ISBPLError("Arithmetic", "Division by 0");
}
};
break;
case "*":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o2 = stack.pop();
ISBPLObject o1 = stack.pop();
o1.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double"));
o2.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double"));
Object object1 = o1.object;
Object object2 = o2.object;
ISBPLObject r = null;
if(object1 instanceof Integer && object2 instanceof Integer) {
r = new ISBPLObject(getType("int"), (int) ((int) (Integer) object1 * (int) (Integer) object2));
}
if(object1 instanceof Long && object2 instanceof Long) {
r = new ISBPLObject(getType("long"), (long) ((long) (Long) object1 * (long) (Long) object2));
}
if(object1 instanceof Character && object2 instanceof Character) {
r = new ISBPLObject(getType("char"), (char) ((char) (Character) object1 * (char) (Character) object2));
}
if(object1 instanceof Byte && object2 instanceof Byte) {
r = new ISBPLObject(getType("byte"), (byte) (Byte.toUnsignedInt((Byte) object1) * Byte.toUnsignedInt((Byte) object2)));
}
if(object1 instanceof Float && object2 instanceof Float) {
r = new ISBPLObject(getType("float"), (float) ((float) (Float) object1 * (float) (Float) object2));
}
if(object1 instanceof Double && object2 instanceof Double) {
r = new ISBPLObject(getType("double"), (double) ((double) (Double) object1 * (double) (Double) object2));
}
if(r != null)
stack.push(r);
else
typeError(o1.type.name, o2.type.name);
};
break;
case "**":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o2 = stack.pop();
ISBPLObject o1 = stack.pop();
o1.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double"));
o2.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double"));
Object object1 = o1.object;
Object object2 = o2.object;
ISBPLObject r = null;
if(object1 instanceof Integer && object2 instanceof Integer) {
r = new ISBPLObject(getType("int"), (int) Math.pow((int) (Integer) object1, (int) (Integer) object2));
}
if(object1 instanceof Long && object2 instanceof Long) {
r = new ISBPLObject(getType("long"), (long) Math.pow((long) (Long) object1, (long) (Long) object2));
}
if(object1 instanceof Character && object2 instanceof Character) {
r = new ISBPLObject(getType("char"), (char) Math.pow((char) (Character) object1, (char) (Character) object2));
}
if(object1 instanceof Byte && object2 instanceof Byte) {
r = new ISBPLObject(getType("byte"), (byte) Math.pow(Byte.toUnsignedInt((Byte) object1), Byte.toUnsignedInt((Byte) object2)));
}
if(object1 instanceof Float && object2 instanceof Float) {
r = new ISBPLObject(getType("float"), (float) Math.pow((float) (Float) object1, (float) (Float) object2));
}
if(object1 instanceof Double && object2 instanceof Double) {
r = new ISBPLObject(getType("double"), (double) Math.pow((double) (Double) object1, (double) (Double) object2));
}
if(r != null)
stack.push(r);
else
typeError(o1.type.name, o2.type.name);
};
break;
case "%":
func = (ISBPLStack<ISBPLObject> stack) -> {
try {
ISBPLObject o2 = stack.pop();
ISBPLObject o1 = stack.pop();
o1.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double"));
o2.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("float"), getType("long"), getType("double"));
Object object1 = o1.object;
Object object2 = o2.object;
ISBPLObject r = null;
if (object1 instanceof Integer && object2 instanceof Integer) {
r = new ISBPLObject(getType("int"), (int) ((int) (Integer) object1 % (int) (Integer) object2));
}
if (object1 instanceof Long && object2 instanceof Long) {
r = new ISBPLObject(getType("long"), (long) ((long) (Long) object1 % (long) (Long) object2));
}
if (object1 instanceof Character && object2 instanceof Character) {
r = new ISBPLObject(getType("char"), (char) ((char) (Character) object1 % (char) (Character) object2));
}
if (object1 instanceof Byte && object2 instanceof Byte) {
r = new ISBPLObject(getType("byte"), (byte) (Byte.toUnsignedInt((Byte) object1) % Byte.toUnsignedInt((Byte) object2)));
}
if (object1 instanceof Float && object2 instanceof Float) {
r = new ISBPLObject(getType("float"), (float) ((float) (Float) object1 % (float) (Float) object2));
}
if (object1 instanceof Double && object2 instanceof Double) {
r = new ISBPLObject(getType("double"), (double) ((double) (Double) object1 % (double) (Double) object2));
}
if (r != null)
stack.push(r);
else
typeError(o1.type.name, o2.type.name);
} catch (ArithmeticException ex) {
throw new ISBPLError("Arithmetic", "Division by 0");
}
};
break;
case "^":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o2 = stack.pop();
ISBPLObject o1 = stack.pop();
o1.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("long"));
o2.checkTypeMulti(getType("int"), getType("byte"), getType("char"), getType("long"));
Object object1 = o1.object;
Object object2 = o2.object;
ISBPLObject r = null;
if(object1 instanceof Integer && object2 instanceof Integer) {
r = new ISBPLObject(getType("int"), (int) ((int) (Integer) object1 ^ (int) (Integer) object2));
}
if(object1 instanceof Long && object2 instanceof Long) {
r = new ISBPLObject(getType("long"), (long) ((long) (Long) object1 ^ (long) (Long) object2));
}
if(object1 instanceof Character && object2 instanceof Character) {
r = new ISBPLObject(getType("char"), (char) ((char) (Character) object1 ^ (char) (Character) object2));
}
if(object1 instanceof Byte && object2 instanceof Byte) {
r = new ISBPLObject(getType("byte"), Byte.toUnsignedInt((Byte) object1) ^ Byte.toUnsignedInt((Byte) object2));
}
if(r != null)
stack.push(r);
else
typeError(o1.type.name, o2.type.name);
};
break;
case "dup":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o = stack.pop();
stack.push(o);
stack.push(o);
};
break;
case "pop":
func = Stack::pop;
break;
case "swap":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o1 = stack.pop();
ISBPLObject o2 = stack.pop();
stack.push(o1);
stack.push(o2);
};
break;
case "_last_word":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject i = stack.pop();
i.checkType(getType("int"));
int n = (int) i.object;
if(n >= lastWords.get().size())
throw new ISBPLError("IllegalArgument", "_last_words called with wrong argument");
stack.push(toISBPLString(lastWords.get().get(n)));
};
break;
case "time":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject i = stack.pop();
long n = (long) i.toLong();
try {
Thread.sleep(n);
}
catch (InterruptedException e) {
e.printStackTrace();
}
stack.push(new ISBPLObject(getType("long"), System.currentTimeMillis()));
};
break;
case "stream":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject action = stack.pop();
action.checkType(getType("int"));
int n = ((int) action.object);
try {
streamer.action(stack, n);
}
catch (IOException e) {
throw new ISBPLError("IO", e.getMessage());
}
};
break;
case "_enable_debug":
func = (ISBPLStack<ISBPLObject> stack) -> {
if(debuggerIPC.threadID == -1) {
ISBPLDebugger debugger = new ISBPLDebugger(this);
debugger.start();
try {
while(debuggerIPC.threadID == -1) Thread.sleep(1);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
stack.push(new ISBPLObject(getType("int"), debuggerIPC.port));
};
break;
case "_getvars":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLArray objects = new ISBPLArray(frameStack.get().size(), getNullObject());
int i = 0;
for (ISBPLFrame map : frameStack.get()) {
ArrayList<ISBPLObject> strings = new ArrayList<>();
for (String key : map.all().keySet()) {
if(key.startsWith("=")) {
strings.add(toISBPLString(key.substring(1)));
}
}
objects.set(i++, new ISBPLObject(getType("array"), new ISBPLArray(strings.toArray(new ISBPLObject[0]))));
}
stack.push(new ISBPLObject(getType("array"), objects));
};
break;
case "stacksize":
func = (ISBPLStack<ISBPLObject> stack) -> stack.push(new ISBPLObject(getType("int"), stack.size()));
break;
case "fcall":
func = (ISBPLStack<ISBPLObject> stack) -> {
ISBPLObject o = stack.pop();
o.checkType(getType("func"));
o.addUse();
((ISBPLCallable) o.object).call(stack);
o.removeUse();
};
break;
case "subprocess":
func = (ISBPLStack<ISBPLObject> stack) -> {
try {
Runtime.getRuntime().exec(toJavaString(stack.pop()));
} catch(IOException e) {
throw new ISBPLError("IO", "Couldn't start process");
}
};
break;
case "deffunc":
func = (stack) -> {
ISBPLObject str = stack.pop();
ISBPLObject callable = stack.pop();
String s = toJavaString(str);
callable.checkType(getType("func"));
frameStack.get().peek().add(s, (ISBPLCallable) callable.object);
};
break;
case "defmethod":
func = (stack) -> {
ISBPLObject type = stack.pop();
ISBPLObject callable = stack.pop();
ISBPLObject str = stack.pop();
type.checkType(getType("int"));
callable.checkType(getType("func"));
ISBPLType t = types.get((int) type.object);
String s = toJavaString(str);
addFunction(t, s, (ISBPLCallable) callable.object);
};
break;
case "deffield":
func = (stack) -> {
ISBPLObject type = stack.pop();
ISBPLObject str = stack.pop();
type.checkType(getType("int"));
ISBPLType t = types.get((int) type.object);
String s = toJavaString(str);
Object var = new Object();
addFunction(t, s, (stack1) -> stack1.push(t.varget(stack1.pop()).getOrDefault(var, getNullObject())));
addFunction(t, "=" + s, (stack1) -> {
ISBPLObject o = stack1.pop();
ISBPLObject p = stack1.pop();
p.usedBy(o);
p = t.varget(o).put(var, p);
if(p != null) p.unusedBy(o);
});
};
break;
case "defsuper":
func = (stack) -> {
ISBPLObject type = stack.pop();
ISBPLObject otherType = stack.pop();
type.checkType(getType("int"));
otherType.checkType(getType("int"));
ISBPLType t = types.get((int) type.object);
ISBPLType s = types.get((int) otherType.object);
s.usedBy(t);
t.superTypes.add(s);
};
break;
case "callmethod":
func = (stack) -> {
ISBPLObject obj = stack.pop();
ISBPLObject str = stack.pop();
String s = toJavaString(str);
stack.push(obj);
obj.type.methods.get(s).call(stack);
};
break;
case "jio.class":
func = (stack) -> {
if(ENABLE_JINTEROP) {
ISBPLObject str = stack.pop();
String s = toJavaString(str);
try {
stack.push(toISBPL(Class.forName(s)));
}
catch (ClassNotFoundException e) {
throw new ISBPLError("JIO", "Class not found");
}
}
else {
throw new ISBPLError("JIODSBL", "JIO disabled");
}
};
break;
case "jio.getclass":
func = (stack) -> {
if(ENABLE_JINTEROP) {
ISBPLObject str = stack.pop();
String s = toJavaString(str);
try {
stack.push(toISBPL((Object) Class.forName(s)));
}
catch (ClassNotFoundException e) {
throw new ISBPLError("JIO", "Class not found");
}
}
else {
throw new ISBPLError("JIODSBL", "JIO disabled");
}
};
break;
case "jio.context":
func = (stack) -> {
if(ENABLE_JINTEROP) {
stack.push(toISBPL(this));
}
else {
throw new ISBPLError("JIODSBL", "JIO disabled");
}
};
break;
case "jio.mirror":
// Do nothing, we are in the java interpreter already!
func = (stack) -> {};
break;
case "null":
func = (stack) -> stack.push(new ISBPLObject(getType("null"), 0));
break;
case "delete":
func = (stack) -> {
ISBPLObject o = stack.pop();
o.type.vars.remove(o);
};
break;
case "mkinstance":
func = (stack) -> {
ISBPLObject type = stack.pop();
ISBPLType t = types.get((int) type.toLong());
stack.push(new ISBPLObject(t, new Object()));
if(t.methods.containsKey("construct")) {
t.methods.get("construct").call(stack);
}
};
break;
case "error.stacktrace":
func = (stack) -> {
ISBPLObject error = stack.pop();
error.checkType(getType("error"));
Throwable t = (Throwable) error.object;
Stack<ISBPLFrame> frames = stackTraces.get(t);
ISBPLArray array = new ISBPLArray(frames.size(), getNullObject());
for(int i = 0; i < frames.size(); i++) {
ISBPLFrame frame = frames.get(i);
ArrayList<ISBPLObject> arr = new ArrayList<>();
while(frame != null) {
arr.add(toISBPLString(frame.name));
frame = frame.parent;
}
array.set(i, new ISBPLObject(getType("array"), arr.toArray(new ISBPLObject[0])));
}
stack.push(new ISBPLObject(getType("array"), array));
};
break;
default:
func = natives.get(name);
break;
}
frameStack.get().peek().add(name, func);
}
ISBPLObject nullObj = null;
public ISBPLObject getNullObject() {
if(nullObj == null || nullObj.type.id == -4) {
nullObj = new ISBPLObject(getType("null"), 0);
nullObj.addUse();
}
return nullObj;
}
private final Object syncJIOClass = new Object();
public ISBPLObject toISBPL(Class<?> clazz) {
synchronized(syncJIOClass) {
ISBPLType type = getType(clazz.getName());
if(type == null) {
type = registerType(clazz.getName());
Class<?> superClass = clazz.getSuperclass();
if (superClass != null)
type.superTypes.add(toISBPL(superClass).type);
Class<?>[] interfaces = clazz.getInterfaces();
for(Class<?> c : interfaces) {
type.superTypes.add(toISBPL(c).type);
}
for (Class<?> subclass : clazz.getDeclaredClasses()) {
addFunction(type, subclass.getSimpleName(), stack -> {
if(debug)
ISBPL.gErrorStream.println("Java GetSubclass: " + subclass.getName());
stack.drop();
stack.push(toISBPL(subclass));
});
}
if (clazz.isEnum()) {
for (Object o : clazz.getEnumConstants()) {
addFunction(type, o.toString(), stack -> {
if(debug)
ISBPL.gErrorStream.println("Java GetEnum: " + o);
stack.push(toISBPL(o));
});
}
}
for (Field field : clazz.getDeclaredFields()) {
addFunction(type, field.getName(), stack -> {
forceAccessible(field);
if(debug)
ISBPL.gErrorStream.println("Java Get: " + field);
try {
ISBPLObject obj = stack.pop();
stack.push(toISBPL(field.get(fromISBPL(obj, clazz))));
}
catch (IllegalAccessException ignored) {
}
});
addFunction(type, "=" + field.getName(), stack -> {
forceAccessible(field);
if(debug)
ISBPL.gErrorStream.println("Java Set: " + field);
try {
ISBPLObject obj = stack.pop();
ISBPLObject val = stack.pop();
field.set(fromISBPL(obj, clazz), fromISBPL(val, field.getType()));
}
catch (IllegalAccessException ignored) {
}
});
}
HashMap<String, ArrayList<Method>> methods = new HashMap<>();
for (Method method : clazz.getDeclaredMethods()) {
ArrayList<Method> methodList = methods.get(method.getName() + method.getParameterCount());
if(methodList == null)
methodList = new ArrayList<>();
methodList.add(method);
methods.put(method.getName() + method.getParameterCount(), methodList);
}
for (Map.Entry<String, ArrayList<Method>> entry : methods.entrySet()) {
addFunction(type, entry.getKey(), stack -> {
Object o = fromISBPL(stack.peek(), clazz); stack.drop();
// Resolve
AtomicInteger mid = new AtomicInteger(0);
ArrayList<Method> ms = entry.getValue();
Class<?>[][] paramTypes = new Class<?>[ms.size()][ms.get(0).getParameterCount()];
for (int i = 0 ; i < ms.size() ; i++) {
paramTypes[i] = ms.get(i).getParameterTypes();
}
Object[] params = resolve(mid, stack, entry.getValue().get(0).getParameterCount(), paramTypes);
Method method = ms.get(mid.get());
forceAccessible(method);
if(debug)
ISBPL.gErrorStream.println("Java Call: " + method + " - " + Arrays.toString(params));
try {
Object r = method.invoke(o, params);
if(method.getReturnType() != void.class)
stack.push(toISBPL(r));
}
catch (IllegalAccessException ignored) { }
catch (InvocationTargetException e) {
stack.push(toISBPL(e));
throw new ISBPLError("Java", "Java error");
}
});
}
HashMap<Integer, ArrayList<Constructor<?>>> constructors = new HashMap<>();
for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
ArrayList<Constructor<?>> constructorList = constructors.get(constructor.getParameterCount());
if (constructorList == null)
constructorList = new ArrayList<>();
constructorList.add(constructor);
constructors.put(constructor.getParameterCount(), constructorList);
}
for (Map.Entry<Integer, ArrayList<Constructor<?>>> entry : constructors.entrySet()) {
addFunction(type, "new" + entry.getKey(), stack -> {
stack.drop();
AtomicInteger mid = new AtomicInteger(0);
ArrayList<Constructor<?>> ms = entry.getValue();
Class<?>[][] paramTypes = new Class<?>[ms.size()][ms.get(0).getParameterCount()];
for (int i = 0 ; i < ms.size() ; i++) {
paramTypes[i] = ms.get(i).getParameterTypes();
}
Object[] params = resolve(mid, stack, entry.getValue().get(0).getParameterCount(), paramTypes);
Constructor<?> constructor = ms.get(mid.get());
forceAccessible(constructor);
if(debug)
ISBPL.gErrorStream.println("Java Call: " + constructor + " - " + Arrays.toString(params));
try {
Object r = constructor.newInstance(params);
stack.push(toISBPL(r));
}
catch (IllegalAccessException ignored) { }
catch (InvocationTargetException | InstantiationException e) {
stack.push(toISBPL(e));
throw new ISBPLError("Java", "Java error");
}
});
}
}
return new ISBPLObject(type, null);
}
}
// TODO: Resolve with subclass weight
public Object[] resolve(AtomicInteger mid, ISBPLStack<ISBPLObject> stack, int paramCount, Class<?>[][] paramTypes) {
ISBPLObject[] o = new ISBPLObject[paramCount];
Object[][] params = new Object[paramTypes.length][paramCount];
int[] methodIDs = new int[paramCount];
Arrays.fill(methodIDs, 0);
if(debug)
ISBPL.gErrorStream.println("Consideration of " + paramTypes.length + " methods...");
for (int i = paramCount - 1 ; i >= 0 ; i--) {
o[i] = stack.pop();
for (int j = 0 ; j < paramTypes.length ; j++) {
Class<?>[] m = paramTypes[j];
try {
params[j][i] = fromISBPL(o[i], m[i]);
methodIDs[i] += 1 << j;
if(debug)
ISBPL.gErrorStream.println("Consideration: Pass " + i + " " + Arrays.toString(paramTypes[j]));
}
catch (ISBPLError ignored) {
if(debug)
ISBPL.gErrorStream.println("Consideration: Fail " + i + " " + Arrays.toString(paramTypes[j]));
}
}
if(debug)
ISBPL.gErrorStream.println("Considerartion: " + i + " " + Integer.toBinaryString(methodIDs[i] + (1 << 31)).substring(32 - paramTypes.length));
}
int msize = paramTypes.length;
for (int i = 0 ; i < msize; i++) {
boolean works = true;
for (int j = 0 ; j < methodIDs.length ; j++) {
if ((methodIDs[j] & (1 << i)) == 0) {
works = false;
break;
}
}
if(works) {
mid.set(i);
if(debug)
ISBPL.gErrorStream.println("Considering: " + Arrays.toString(paramTypes[i]));
}
}
if(debug)
ISBPL.gErrorStream.println("Considered " + paramTypes.length + " methods. Result: " + Arrays.toString(paramTypes[mid.get()]));
return params[mid.get()];
}
public Object primitiveDefault(Class<?> expectedType) {
if(expectedType == long.class)
return 0L;
if(expectedType == int.class)
return 0;
if(expectedType == double.class)
return 0.0d;
if(expectedType == float.class)
return 0.0f;
if(expectedType == short.class)
return (short) 0;
if(expectedType == boolean.class)
return false;
if(expectedType == char.class)
return (char) 0;
if(expectedType == byte.class)
return (byte) 0;
return null;
}
public Object fromISBPL(ISBPLObject o, Class<?> expectedType) {
ISBPLType type = o.type;
if (type.equals(getType("null")))
return null;
if(o.object == null)
return null;
if (type.equals(getType("string"))) {
if(expectedType.isAssignableFrom(String.class))
return toJavaString(o);
else
typeError("string", expectedType.getName());
}
if (type.equals(getType("array"))) {
ISBPLArray isbplArray = ((ISBPLArray) o.object);
Object array = new Object[isbplArray.length];
if(expectedType.isArray())
array = Array.newInstance(expectedType.getComponentType(), isbplArray.length);
else
typeError("array", "matching-array");
for (int i = 0 ; i < isbplArray.length ; i++) {
Object obj = fromISBPL(isbplArray.get(i), expectedType.getComponentType());
Array.set(array, i, obj == null && expectedType.getComponentType().isPrimitive() ? primitiveDefault(expectedType.getComponentType()) : obj);
}
return array;
}
if(expectedType.isPrimitive()) {
if(expectedType == long.class)
return o.toLong();
if(expectedType == int.class)
return (int) o.toLong();
if(expectedType == double.class)
return o.toDouble();
if(expectedType == float.class)
return (float) o.toDouble();
if(expectedType == short.class)
return (short) o.toLong();
if(expectedType == boolean.class)
return o.isTruthy();
if(expectedType == char.class)
return (char) o.toLong();
if(expectedType == byte.class)
return (byte) o.toLong();
}
if(expectedType == Runnable.class) {
return (Runnable) () -> {
((ISBPLCallable) o.object).call(new ISBPLStack<>());
};
}
if(!expectedType.isAssignableFrom(o.object.getClass()))
typeError(o.type.name, expectedType.getName());
return o.object;
}
public ISBPLObject toISBPL(Object object) {
if(object == null) {
return getNullObject();
}
ISBPLObject o = toISBPL(object.getClass());
if (object instanceof String) {
return toISBPLString(((String) object));
}
if (object instanceof Integer)
return new ISBPLObject(getType("int"), object);
if (object instanceof Character)
return new ISBPLObject(getType("char"), object);
if (object instanceof Byte)
return new ISBPLObject(getType("byte"), object);
if (object instanceof Float)
return new ISBPLObject(getType("float"), object);
if (object instanceof Long)
return new ISBPLObject(getType("long"), object);
if (object instanceof Double)
return new ISBPLObject(getType("double"), object);
if (object.getClass().isArray()) {
ISBPLArray isbplArray = new ISBPLArray(Array.getLength(object), getNullObject());
for (int i = 0 ; i < isbplArray.length ; i++) {
isbplArray.set(i, toISBPL(Array.get(object, i)));
}
o.type = getType("array");
object = isbplArray;
}
o.object = object;
return o;
}
public void addFunction(ISBPLType type, String name, ISBPLCallable callable) {
callable.usedBy(type);
type.methods.put(name, callable);
type.methods.put("&" + name, stack -> {stack.drop(); stack.push(new ISBPLObject(getType("func"), callable)); });
}
private String getFilePathForInclude(ISBPLStack<ISBPLObject> stack, Stack<File> file) {
ISBPLObject s = stack.pop();
String filepath = toJavaString(s);
for (File f : file) {
filepath = toJavaString(s);
processPath:
{
if (filepath.startsWith("/"))
break processPath;
if (filepath.startsWith("#")) {
filepath = System.getenv().getOrDefault("ISBPL_PATH", "/usr/lib/isbpl") + "/" + filepath.substring(1);
break processPath;
}
filepath = f.getParentFile().getAbsolutePath() + "/" + filepath;
}
if(new File(filepath).exists())
return filepath;
}
return filepath;
}
private int createFunction(int i, String[] words, File file) {
i++;
String name = words[i];
AtomicInteger integer = new AtomicInteger(++i);
ISBPLCallable callable = readCallable(name, integer, words, file);
i = integer.get();
frameStack.get().peek().add(name, callable);
return i;
}
private String[] readBlock(AtomicInteger idx, String[] words, File file) {
ArrayList<String> newWords = new ArrayList<>();
int i = idx.get();
i++;
int lvl = 1;
for (; i < words.length && lvl > 0 ; i++) {
String word = words[i];
if(word.equals("{"))
lvl++;
if(word.equals("}")) {
if(--lvl == 0)
break;
}
newWords.add(word);
}
idx.set(i);
return newWords.toArray(new String[0]);
}
private ISBPLCallable readCallable(String name, AtomicInteger idx, String[] words, File file) {
String[] theWords = readBlock(idx, words, file);
ISBPLFrame frame = frameStack.get().peek();
ISBPLCallable it = (stack) -> {
fileStack.get().push(file);
frameStack.get().push(new ISBPLFrame(name, this, frame));
frameStack.get().peek().addUse();
try {
interpretRaw(theWords, stack);
} finally {
frameStack.get().pop().removeUse();
fileStack.get().pop();
}
};
frame.usedBy(it);
return it;
}
public String toJavaString(ISBPLObject string) {
string.checkType(getType("string"));
ISBPLArray array = ((ISBPLArray) string.object);
char[] chars = new char[array.length];
try {
for (int i = 0 ; i < array.length ; i++) {
chars[i] = ((char) array.get(i).object);
}
} catch(NullPointerException e) {
throw new IllegalStateException(string + " has null-chars.");
}
return new String(chars);
}
public ISBPLObject toISBPLString(String s) {
char[] chars = s.toCharArray();
ISBPLArray objects = new ISBPLArray(chars.length, getNullObject());
ISBPLType type = getType("char");
for (int i = 0 ; i < chars.length ; i++) {
objects.set(i, new ISBPLObject(type, chars[i]));
}
return new ISBPLObject(getType("string"), objects);
}
public synchronized ISBPLType registerType(String name) {
ISBPLType old = getType(name);
old = getType(name);
if(old != null) {
types.remove(old);
old.removeUse();
}
ISBPLType type = new ISBPLType(name, types.size());
types.add(type);
type.addUse();
return type;
}
// These will die as soon as std creates the real types and any types created before these are replaced become invalid.
final ISBPLType defaultTypeNull = new ISBPLType("null", -4);
final ISBPLType defaultTypeInt = new ISBPLType("int", -3);
final ISBPLType defaultTypeChar = new ISBPLType("char", -2);
final ISBPLType defaultTypeString = new ISBPLType("string", -1);
{
defaultTypeNull.addUse();
defaultTypeNull.addUse();
defaultTypeInt.addUse();
defaultTypeChar.addUse();
defaultTypeString.addUse();
}
public ISBPLType getType(String name) {
for (int i = 0 ; i < types.size() ; i++) {
if(types.get(i).name.equals(name))
return types.get(i);
}
if(name.equals("null"))
return defaultTypeNull;
if(name.equals("int"))
return defaultTypeInt;
if(name.equals("char"))
return defaultTypeChar;
if(name.equals("string"))
return defaultTypeString;
return null;
}
public void typeError(String got, String wanted) {
throw new ISBPLError("IncompatibleTypes", "Incompatible types: " + got + " - " + wanted);
}
public void interpret(File file, String code, ISBPLStack<ISBPLObject> stack) {
if(code.startsWith("#!"))
code = code.substring(code.indexOf("\n") + 1);
debuggerIPC.stack.put(Thread.currentThread().getId(), stack);
fileStack.get().push(file);
code = cleanCode(code);
String[] words = splitWords(code);
interpretRaw(words, stack);
fileStack.get().pop();
}
private void interpretRaw(String[] words, ISBPLStack<ISBPLObject> stack) {
try {
nextWord: for (int i = 0 ; i < words.length ; i++) {
try {
String word = words[i];
if (word.length() == 0)
continue;
if(printCalls) {
StringBuilder s = new StringBuilder();
for (int x = 0 ; x < frameStack.get().size() ; x++) {
s.append("\t");
}
ISBPL.gErrorStream.println(s + word + "\t\t" + (debug ? stack : ""));
}
while (debuggerIPC.run.getOrDefault(Thread.currentThread().getId(), -1) == 0) Thread.sleep(1);
int rid = debuggerIPC.run.getOrDefault(Thread.currentThread().getId(), -1);
if(rid < 0) {
if(rid < -1) {
if (rid == -2) {
if (word.equals(debuggerIPC.until)) {
debuggerIPC.run.put(Thread.currentThread().getId(), 0);
while (debuggerIPC.run.get(Thread.currentThread().getId()) == 0) Thread.sleep(1);
}
}
if (rid == -3 && Thread.currentThread().getId() != debuggerIPC.threadID) {
while (debuggerIPC.run.get(Thread.currentThread().getId()) == -3) Thread.sleep(1);
}
}
} else
debuggerIPC.run.put(Thread.currentThread().getId(), debuggerIPC.run.get(Thread.currentThread().getId()) - 1);
lastWords.get().add(0, word);
while(lastWords.get().size() > 16)
lastWords.get().remove(lastWords.get().size() - 1);
ISBPLKeyword keyword = getKeyword(word);
if (keyword != null) {
i = keyword.call(i, words, fileStack.get().peek(), stack);
continue;
}
if(stack.size() > 0) {
// Doing this nonrecursively because it's much better despite the slight readability disadvantage.
if(stack.peek() == null) {
// We need to dump things immediately.
ISBPL.gErrorStream.println("!!! ISBPL WORD PARSER ERROR !!!");
ISBPL.gErrorStream.println("This is most likely due to a garbage collector malfunction.");
ISBPL.gErrorStream.println("LastWords: " + lastWords);
ISBPL.gErrorStream.println("FileStack: " + fileStack);
ISBPL.gErrorStream.println("Stack: " + stack);
throw new Error("ISBPL WORD PARSER");
}
try {
ISBPLType type = stack.peek().type;
if(type == null) {
throw new NullPointerException("type of initial is null");
}
Queue<ISBPLType> types = new LinkedList<>();
types.add(type);
while (!types.isEmpty()) {
type = types.poll();
if(type == null) {
throw new NullPointerException("type is null");
}
if(type.superTypes == null) {
throw new NullPointerException("type was already garbage-collected");
}
types.addAll(type.superTypes);
if(type.methods.containsKey(word)) {
type.methods.get(word).call(stack);
continue nextWord;
}
}
} catch (Exception e) {
e.printStackTrace(ISBPL.gErrorStream);
ISBPL.gErrorStream.println("!!! ISBPL WORD PARSER ERROR !!!");
ISBPL.gErrorStream.println("This is most likely due to a garbage collector malfunction.");
ISBPL.gErrorStream.println("LastWords: " + lastWords);
ISBPL.gErrorStream.println("FileStack: " + fileStack);
ISBPL.gErrorStream.println("Stack: " + stack);
throw new Error("ISBPL WORD PARSER");
}
}
ISBPLCallable func = frameStack.get().peek().resolve(word);
if(func != null) {
func.call(stack);
continue;
}
if (word.startsWith("\"")) {
stack.push(toISBPLString(word.substring(1)));
continue;
}
try {
stack.push(new ISBPLObject(getType("int"), Integer.parseInt(word)));
continue;
} catch (Exception ignore) {}
try {
stack.push(new ISBPLObject(getType("long"), Long.parseLong(word)));
continue;
} catch (Exception ignore) {}
try {
stack.push(new ISBPLObject(getType("float"), Float.parseFloat(word)));
continue;
} catch (Exception ignore) {}
try {
stack.push(new ISBPLObject(getType("double"), Double.parseDouble(word)));
continue;
} catch (Exception ignore) {}
throw new ISBPLError("InvalidWord", word + " is not a function, object, or keyword.");
} finally {
stack.update();
}
}
} catch (ISBPLStop stop) {
if(stop.amount == 0)
return;
throw new ISBPLStop(stop.amount);
}
catch (InterruptedException e) {
e.printStackTrace();
}
catch (Throwable t) {
if(stackTraces.get(t) == null)
stackTraces.put(t, (Stack<ISBPLFrame>)frameStack.get().clone());
if(debug) ISBPL.gOutputStream.println("Passing exception " + t + " to caller.");
if(stopExceptions) {
t.printStackTrace();
ISBPL.gOutputStream.println(printStackTrace(stackTraces.get(t)));
ISBPL.gOutputStream.println("Current Words: ");
ISBPL.gOutputStream.println(Arrays.toString(words));
dump(stack);
}
throw t;
}
}
public String printStackTrace(Stack<ISBPLFrame> frameStack) {
String s = "INTERPRET";
try {
String indent = " ";
for (ISBPLFrame frame : frameStack) {
s += "\n" + indent + "\\ ";
String p = "";
while(frame != null) {
p = "/" + frame.name + p;
frame = frame.parent;
}
s += p;
indent += " ";
}
} catch(Throwable t) { t.printStackTrace(); }
return s;
}
// Magic, please test before pushing changes!
private String[] splitWords(String code) {
ArrayList<String> words = new ArrayList<>();
char[] chars = code.toCharArray();
boolean isInString = false;
boolean escaping = false;
StringBuilder word = new StringBuilder();
for (int i = 0 ; i < chars.length ; i++) {
char c = chars[i];
if(isInString) {
if(c == '\\') {
escaping = !escaping;
if(escaping)
continue;
}
if(c == 'n' && escaping) {
word.append('\n');
escaping = false;
continue;
}
if(c == 'r' && escaping) {
escaping = false;
word.append('\r');
continue;
}
if(c == 't' && escaping) {
escaping = false;
word.append('\t');
continue;
}
if(c == '"') {
if (escaping) {
escaping = false;
}
else {
isInString = false;
continue;
}
}
word.append(c);
if(escaping)
throw new RuntimeException("Error parsing code: Invalid Escape.");
}
else if(c == '"') {
word.append('"');
isInString = true;
}
else if(c == ' ' || c == '\'' || c == ':' || c == '°' || c == 'ß' || c == '§') {
String w = word.toString();
while(w.startsWith("(") && w.length() > 1)
w = w.substring(1);
if(!w.startsWith("\""))
while(w.endsWith(")") && w.length() > 1)
w = w.substring(0, w.length() - 1);
words.add(w);
word = new StringBuilder();
}
else {
word.append(c);
}
}
String w = word.toString();
while(w.startsWith("(") && w.length() > 1)
w = w.substring(1);
if(!w.startsWith("\""))
while(w.endsWith(")") && w.length() > 1)
w = w.substring(0, w.length() - 1);
words.add(w);
ArrayList<String> cleanWords = new ArrayList<>();
for(int i = 0; i < words.size(); i++) {
if(!words.get(i).isEmpty()) {
cleanWords.add(words.get(i));
}
}
return cleanWords.toArray(new String[0]);
}
private String cleanCode(String code) {
return code
.replaceAll("\r\n", "\n")
.replaceAll("\n", " ")
;
}
public void dump(ISBPLStack<ISBPLObject> stack) {
try {
ISBPL.gErrorStream.println("VAR DUMP\n----------------");
for (ISBPLFrame map : frameStack.get()) {
HashMap<String, ISBPLCallable> all = map.all();
for (String key : all.keySet()) {
if (key.startsWith("=")) {
all.get(key.substring(1)).call(stack);
ISBPLObject o = stack.pop();
ISBPL.gErrorStream.println("\t" + key.substring(1) + ": \t" + o);
}
}
ISBPL.gErrorStream.println("----------------");
}
}
catch (Exception e) {
e.printStackTrace();
ISBPL.gErrorStream.println("!!! VARS CORRUPTED! CANNOT FIX AUTOMATICALLY.");
}
boolean fixed = false;
while (!fixed) {
try {
ISBPL.gErrorStream.println("STACK DUMP");
for (ISBPLObject object : stack) {
ISBPL.gErrorStream.println("\t" + object);
}
fixed = true;
}
catch (Exception e) {
e.printStackTrace();
ISBPL.gErrorStream.println("!!! STACK CORRUPTED!");
stack.drop();
ISBPL.gErrorStream.println("Popped. Trying again.");
}
}
}
public static void main(String[] args) {
ISBPLStack<ISBPLObject> stack = new ISBPLStack<>();
ISBPL isbpl = new ISBPL();
isbpl.debuggerIPC.stack.put(Thread.currentThread().getId(), stack);
debug = !System.getenv().getOrDefault("DEBUG", "").equals("");
isbpl.debuggerIPC.run.put(Thread.currentThread().getId(), -1);
if(debug) {
new ISBPLDebugger(isbpl).start();
isbpl.debuggerIPC.run.put(Thread.currentThread().getId(), 0);
}
try {
File std = new File(System.getenv().getOrDefault("ISBPL_PATH", "/usr/lib/isbpl") + "/std.isbpl");
isbpl.interpret(std, readFile(std), stack);
if(args.length > 0) {
File file = new File(args[0]).getAbsoluteFile();
isbpl.interpret(file, readFile(file), stack);
stack.push(argarray(isbpl, args));
isbpl.interpret(file, "main exit", stack);
} else {
isbpl.level0.add("dump", isbpl::dump);
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String line;
ISBPL.gOutputStream.print("> ");
while ((line = reader.readLine()) != null) {
try {
isbpl.interpret(new File("_shell"), line, stack);
} catch(ISBPLError e) {
ISBPL.gOutputStream.println("Error: " + e.type + ": " + e.message);
} catch(Throwable e) {
e.printStackTrace();
ISBPL.gErrorStream.println(isbpl.printStackTrace(isbpl.stackTraces.get(e)));
}
ISBPL.gOutputStream.print("\n> ");
}
}
} catch (ISBPLStop stop) {
System.exit(isbpl.exitCode);
} catch (Throwable e) {
e.printStackTrace();
ISBPL.gErrorStream.println(isbpl.printStackTrace(isbpl.stackTraces.get(e)));
ISBPL.gOutputStream.println(stack);
}
}
private static ISBPLObject argarray(ISBPL isbpl, String[] args) {
ISBPLArray array = new ISBPLArray(args.length - 1, null);
for (int i = 1 ; i < args.length ; i++) {
array.set(i - 1, isbpl.toISBPLString(args[i]));
}
return new ISBPLObject(isbpl.getType("array"), array);
}
private static String readFile(File f) throws IOException {
FileInputStream fis = new FileInputStream(f);
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
byte[] currentBytes = new byte[4096];
int len;
while ((len = fis.read(currentBytes)) > 0) {
bytes.write(currentBytes, 0, len);
}
fis.close();
return bytes.toString();
}
// Used for theSafe
private static class FakeAccessibleObject {
boolean override;
}
private static void forceAccessible(AccessibleObject thing) {
try {
theSafe.putBoolean(thing, theSafe.objectFieldOffset(FakeAccessibleObject.class.getDeclaredField("override")), true);
} catch(Exception e) { //we are doomed
e.printStackTrace();
ISBPL.gErrorStream.println("Failed to set accessible property. We are doomed.");
System.exit(1);
}
}
public static long lastGC = System.currentTimeMillis();
public static void requestGC() {
if(JVM_REQUEST_GC) {
if(System.currentTimeMillis() - lastGC >= JVM_GC_REQUEST_DELAY) {
lastGC = System.currentTimeMillis();
System.gc();
System.runFinalization();
}
}
}
}
class ISBPLFastList<T> {
private final LinkedList<T>[] items;
private final int[] lengths;
private final int cap;
private int size = 0;
public ISBPLFastList(int hashcap) {
cap = hashcap;
items = new LinkedList[hashcap];
lengths = new int[hashcap];
for(int i = 0; i < hashcap; i++) {
items[i] = new LinkedList<>();
lengths[i] = 0;
}
}
public boolean contains(T item) {
return items[Math.abs(item.hashCode() % cap)].contains(item);
}
public void add(T item) {
int i = Math.abs(item.hashCode() % cap);
items[i].add(item);
lengths[i]++;
size++;
}
public T get(int i) {
int n = 0; // end of current scope
int x = 0; // start of current scope
int l = 0; // scope
for(; l < cap; l++) {
n += lengths[l];
if(n > i) // if end of current scope is bigger than i (that means i is within current scope)
break;
x += lengths[l];
}
return items[l].get(i - x);
}
public LinkedList<T> getByHash(int hash) {
return items[Math.abs(hash % cap)];
}
public T set(int i, T item) {
int n = 0; // end of current scope
int x = 0; // start of current scope
int l = 0; // scope
for(; l < cap; l++) {
n += lengths[l];
if(n > i) // if end of current scope is bigger than i (that means i is within current scope)
break;
x += lengths[l];
}
return items[l].set(i - x, item);
}
public T[] toArray(T... in) {
T[] ts = (T[]) Array.newInstance(in.getClass().getComponentType(), size);
for(int i = 0, scope = 0, idx = 0; i < size && scope < cap; i++, idx++) {
while(idx == lengths[scope]) {
idx = 0;
scope++;
}
ts[i] = items[scope].get(idx);
}
return ts;
}
public T remove(int i) {
int n = 0; // end of current scope
int x = 0; // start of current scope
int l = 0; // scope
for(; l < cap; l++) {
n += lengths[l];
if(n > i) // if end of current scope is bigger than i (that means i is within current scope)
break;
x += lengths[l];
}
T it = items[l].remove(i - x);
lengths[l]--;
size--;
return it;
}
public boolean remove(T item) {
int i = Math.abs(item.hashCode() % cap);
if(items[i].remove(item)) {
lengths[i]--;
size--;
return true;
}
return false;
}
public boolean isEmpty() {
return size == 0;
}
public int size() {
return size;
}
}
class ISBPLInstanceMap<T, O> {
ISBPLFastList<Pair<T, O>> pairs = new ISBPLFastList<>(32);
private static class Pair<T, O> {
T t;
O o;
public Pair(T t, O o) {
this.t = t;
this.o = o;
}
@Override
public int hashCode() { return t.hashCode(); }
}
public O get(T t) {
LinkedList<Pair<T, O>> list = pairs.getByHash(t.hashCode());
for(int i = 0; i < list.size(); i++) {
// instance map, use strict equal!
if(list.get(i).t == t) {
return list.get(i).o;
}
}
return null;
}
public O put(T t, O o) {
LinkedList<Pair<T, O>> list = pairs.getByHash(t.hashCode());
for(int i = 0; i < list.size(); i++) {
// instance map, use strict equal!
if(list.get(i).t == t) {
O r = list.get(i).o;
list.get(i).o = o;
return r;
}
}
list.add(new Pair<>(t, o));
return null;
}
public boolean containsKey(T t) {
return get(t) != null;
}
public boolean remove(T t) {
LinkedList<Pair<T, O>> list = pairs.getByHash(t.hashCode());
for(int i = 0; i < list.size(); i++) {
// instance map, use strict equal!
if(list.get(i).t == t) {
O r = list.get(i).o;
list.remove(i);
return true;
}
}
return false;
}
public boolean removeValue(O o) {
for(int i = 0; i < pairs.size(); i++) {
// instance map, use strict equal!
if(pairs.get(i).o == o) {
T r = pairs.get(i).t;
pairs.remove(i);
return true;
}
}
return false;
}
}
interface ISBPLUsable {
static ISBPLInstanceMap<ISBPLUsable, Integer> uses = new ISBPLInstanceMap<>();
static ISBPLInstanceMap<ISBPLUsable, ISBPLFastList<ISBPLUsable>> children = new ISBPLInstanceMap<>();
default void usedBy(ISBPLUsable other) {
setUses(getUses() + 1);
other.addChild(this);
}
default void unusedBy(ISBPLUsable other) {
if(!other.isUseless())
if(other.removeChild(this))
removeUse();
}
default void addUse() {
setUses(getUses() + 1);
}
default void removeUse() {
if(isUseless()) {
return;
}
setUses(getUses() - 1);
if(getUses() <= 0) {
// Now useless, delete.
if(ISBPL.debug) {
ISBPL.gErrorStream.println("Garbage collecting " + this);
}
ISBPLFastList<ISBPLUsable> usables = getChildren();
ISBPLUsable[] arr = usables.toArray();
for(int i = 0; i < arr.length; i++) {
arr[i].unusedBy(this);
}
useless();
uses.remove(this);
children.remove(this);
ISBPL.requestGC();
}
}
default void setUses(int i) {
uses.put(this, i);
}
default int getUses() {
if(!uses.containsKey(this))
uses.put(this, 0);
return uses.get(this);
}
default ISBPLFastList<ISBPLUsable> getChildren() {
if(!children.containsKey(this))
children.put(this, new ISBPLFastList<>(8));
return children.get(this);
}
default void addChild(ISBPLUsable it) {
getChildren().add(it);
}
default boolean removeChild(ISBPLUsable it) {
return getChildren().remove(it);
}
default boolean isUseless() {
return !children.containsKey(this);
}
default void useless() {}
}
interface ISBPLKeyword {
int call(int idx, String[] words, File file, ISBPLStack<ISBPLObject> stack);
}
interface ISBPLCallable extends ISBPLUsable {
void call(ISBPLStack<ISBPLObject> stack);
}
class ISBPLType implements ISBPLUsable {
int id;
String name;
HashMap<String, ISBPLCallable> methods = new HashMap<>();
HashMap<ISBPLObject, HashMap<Object, ISBPLObject>> vars = new HashMap<>();
ArrayList<ISBPLType> superTypes = new ArrayList<>();
public ISBPLType(String name, int id) {
this.name = name;
this.id = id;
}
public void useless() {
id = -100;
name = null;
methods = null;
vars = null;
superTypes = null;
}
public HashMap<Object, ISBPLObject> varget(ISBPLObject o) {
if(!vars.containsKey(o)) {
vars.put(o, new HashMap<>());
}
return vars.get(o);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ISBPLType)) return false;
ISBPLType type = (ISBPLType) o;
return id == type.id;
}
@Override
public int hashCode() {
return id;
}
@Override
public String toString() {
return "ISBPLType{" +
"id=" + id +
", name='" + name + '\'' +
", superTypes=" + superTypes +
'}';
}
public boolean extendsOther(ISBPLType other) {
Queue<ISBPLType> q = new LinkedList<>();
q.add(this);
while(!q.isEmpty()) {
ISBPLType t = q.poll();
q.addAll(t.superTypes);
if(other.equals(t))
return true;
}
return false;
}
protected void finalize() throws Throwable {
if(!isUseless())
ISBPL.gErrorStream.println("[ISBPL] [Please report] JVM GC'd " + this + ", but it was not picked up by ISBPL's GC!");
if(ISBPL.debug) {
ISBPL.gErrorStream.println("JVM deleting " + this);
}
super.finalize();
}
}
class ISBPLArray implements ISBPLUsable {
private ISBPLObject[] backend;
public int length;
public ISBPLArray(ISBPLObject[] back) {
this.length = back.length;
backend = back;
for(int i = 0; i < length; i++) {
backend[i].usedBy(this);
}
}
public ISBPLArray(int length, ISBPLObject initial) {
this.length = length;
backend = new ISBPLObject[length];
for(int i = 0; i < length; i++) {
backend[i] = initial;
if(initial != null)
initial.usedBy(this);
}
}
public void set(int i, ISBPLObject item) {
item.usedBy(this);
if(backend[i] != null)
backend[i].unusedBy(this);
backend[i] = item;
}
public ISBPLObject get(int i) {
return backend[i];
}
public String toString() {
StringBuilder s = new StringBuilder("[");
for(int i = 0; i < length; i++) {
s.append(backend[i]);
if(i != length - 1) {
s.append(", ");
}
}
s.append("]");
return s.toString();
}
public boolean equals(Object o) {
if(this == o) return true;
if(!(o instanceof ISBPLArray)) return false;
ISBPLArray object = (ISBPLArray) o;
if(object.length != length) return false;
for(int i = 0; i < length; i++) {
if(!object.backend[i].equals(backend[i])) return false;
}
return true;
}
public void useless() {
backend = null;
length = -1;
}
protected void finalize() throws Throwable {
if(!isUseless())
ISBPL.gErrorStream.println("[ISBPL] [Please report] JVM GC'd " + this + ", but it was not picked up by ISBPL's GC!");
if(ISBPL.debug) {
ISBPL.gErrorStream.println("JVM deleting " + this);
}
super.finalize();
}
}
class ISBPLObject implements ISBPLUsable {
ISBPLType type;
Object object;
public ISBPLObject(ISBPLType type, Object object) {
this.type = type;
this.object = object;
type.usedBy(this);
if(object instanceof ISBPLUsable) {
((ISBPLUsable)object).usedBy(this);
}
}
public void useless() {
type.vars.remove(this);
object = null;
type = null;
}
public boolean isTruthy() {
return object != null && object != Integer.valueOf(0) && !object.equals(Boolean.FALSE);
}
// This has heavy optimizations, please do not change unless necessary
public boolean equals(Object o) {
if(this == o) return true;
if(!(o instanceof ISBPLObject)) return false;
ISBPLObject object = (ISBPLObject) o;
if(this.type == null)
return false;
if(object.type == null)
return false;
if(!this.type.equals(object.type))
return false;
if(this.object == object.object)
return true;
// These can return false because the strict equality check has already been performed.
if(this.object == null)
return false;
if(object.object == null)
return false;
return this.object.equals(object.object);
}
public void checkType(ISBPLType wanted) {
if(type == null) {
throw new IllegalStateException("ISBPLObject was checked for its type, but the object has already been garbage-collected.");
}
if(wanted == null) {
throw new IllegalArgumentException("ISBPLObject was checked for its type with the wanted type being null.");
}
Queue<ISBPLType> types = new LinkedList<>();
types.add(type);
while (!types.isEmpty()) {
ISBPLType t = types.poll();
types.addAll(t.superTypes);
if(t.id == wanted.id) {
return;
}
}
throw new ISBPLError("IncompatibleTypes", "Incompatible types: " + type.name + " - " + wanted.name);
}
public void checkTypeMulti(ISBPLType... wanted) {
if(type == null) {
throw new IllegalStateException("ISBPLObject was checked for its type, but the object has already been garbage-collected.");
}
StringBuilder wantedNames = new StringBuilder();
for (int i = 0 ; i < wanted.length ; i++) {
wantedNames.append(" ").append(wanted[i].name);
Queue<ISBPLType> types = new LinkedList<>();
types.add(type);
while (!types.isEmpty()) {
ISBPLType t = types.poll();
types.addAll(t.superTypes);
if(t.id == wanted[i].id) {
return;
}
}
}
throw new ISBPLError("IncompatibleTypes", "Incompatible types: " + type.name + " - " + wantedNames.substring(1));
}
@Override
public String toString() {
return "ISBPLObject{" +
"type=" + type +
", object=" + object +
'}';
}
public double toDouble() {
if(object instanceof Integer) {
return (double) (int) (Integer) object;
}
if(object instanceof Long) {
return (double) (long) (Long) object;
}
if(object instanceof Character) {
return (double) (char) (Character) object;
}
if(object instanceof Byte) {
return Byte.toUnsignedInt((Byte) object);
}
if(object instanceof Float) {
return (double) (float) (Float) object;
}
if(object instanceof Double) {
return (double) (Double) object;
}
throw new ISBPLError("InvalidArgument", "The argument is not a number.");
}
public long toLong() {
if(object instanceof Integer) {
return (long) (int) (Integer) object;
}
if(object instanceof Long) {
return (long) (Long) object;
}
if(object instanceof Character) {
return (long) (char) (Character) object;
}
if(object instanceof Byte) {
return Byte.toUnsignedInt((Byte) object);
}
if(object instanceof Float) {
return (long) (float) (Float) object;
}
if(object instanceof Double) {
return (long) (double) (Double) object;
}
throw new ISBPLError("InvalidArgument", "The argument is not a number.");
}
public Object negative() {
if(object instanceof Integer) {
return -(int) (Integer) object;
}
if(object instanceof Long) {
return -(long) (Long) object;
}
if(object instanceof Float) {
return -(float) (Float) object;
}
if(object instanceof Double) {
return -(double) (Double) object;
}
throw new ISBPLError("InvalidArgument", "This type of number can't be negated!");
}
protected void finalize() throws Throwable {
if(!isUseless())
ISBPL.gErrorStream.println("[ISBPL] [Please report] JVM GC'd " + this + ", but it was not picked up by ISBPL's GC!");
if(ISBPL.debug) {
ISBPL.gErrorStream.println("JVM deleting " + this);
}
super.finalize();
}
}
class ISBPLError extends RuntimeException {
final String type;
final String message;
public ISBPLError(String type, String message) {
this.type = type;
this.message = message;
}
@Override
public String getMessage() {
return type + ": " + message;
}
}
class ISBPLStop extends RuntimeException {
int amount;
public ISBPLStop(int amount) {
this.amount = amount - 1;
}
}
class ISBPLDebugger extends Thread {
private final ISBPL isbpl;
int port = -1;
long mainID = Thread.currentThread().getId();
public ISBPLDebugger(ISBPL isbpl) {
this.isbpl = isbpl;
}
public ISBPLStack<ISBPLObject> stack(long tid) {
return isbpl.debuggerIPC.stack.get(tid);
}
@Override
public void run() {
try {
ServerSocket socket = null;
try {
//noinspection resource
socket = new ServerSocket(Integer.parseInt(System.getenv().getOrDefault("DEBUG", "9736")));
port = socket.getLocalPort();
ISBPL.gErrorStream.println("Debugger listening on :" + socket.getLocalPort());
}
catch (BindException e) {
while (socket == null) {
try {
//noinspection resource
socket = new ServerSocket((int) (Math.random() * 5000 + 5000));
port = socket.getLocalPort();
ISBPL.gErrorStream.println("Debugger listening on :" + socket.getLocalPort());
}
catch (BindException ignored) { }
}
}
isbpl.debuggerIPC.port = port;
isbpl.debuggerIPC.threadID = Thread.currentThread().getId();
while (true) {
Socket s = socket.accept();
new Thread(() -> {
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line;
long tid = mainID;
while ((line = reader.readLine()) != null) {
try {
switch (line.split(" ")[0]) {
case "continue":
case "cont":
case "c":
isbpl.debuggerIPC.run.put(tid, -1);
break;
case "stop":
case "s":
isbpl.debuggerIPC.run.put(tid, 0);
break;
case "next":
case "n":
isbpl.debuggerIPC.run.put(tid, 1);
break;
case "nextall":
case "na":
isbpl.debuggerIPC.run.replaceAll((i, v) -> 1);
break;
case "do":
case "d":
isbpl.debuggerIPC.run.put(tid, Integer.parseInt(line.split(" ")[1]));
break;
case "until":
case "u":
isbpl.debuggerIPC.until = line.split(" ")[1];
isbpl.debuggerIPC.run.put(tid, -2);
break;
case "eval":
isbpl.debuggerIPC.run.put(tid, -3);
isbpl.debuggerIPC.threadID = Thread.currentThread().getId();
try {
isbpl.interpret(new File("_debug").getAbsoluteFile(), line.substring(5), stack(tid));
}
catch (ISBPLStop stop) {
System.exit(isbpl.exitCode);
}
catch (Throwable e) {
e.printStackTrace();
boolean fixed = false;
while (!fixed) {
try {
ISBPL.gErrorStream.println("Stack recovered: " + stack(tid));
fixed = true;
}
catch (Exception e1) {
e.printStackTrace();
ISBPL.gErrorStream.println("!!! STACK CORRUPTED!");
stack(tid).drop();
ISBPL.gErrorStream.println("Popped. Trying again.");
}
}
}
break;
case "dump":
try {
ISBPL.gErrorStream.println("VAR DUMP\n----------------");
for (ISBPLFrame map : isbpl.frameStack.map.get(tid)) {
HashMap<String, ISBPLCallable> all = map.all();
for (String key : all.keySet()) {
if (key.startsWith("=")) {
all.get(key.substring(1)).call(stack(tid));
ISBPLObject o = stack(tid).pop();
ISBPL.gErrorStream.println("\t" + key.substring(1) + ": \t" + o);
}
}
ISBPL.gErrorStream.println("----------------");
}
}
catch (Exception e) {
e.printStackTrace();
ISBPL.gErrorStream.println("!!! VARS CORRUPTED! CANNOT FIX AUTOMATICALLY.");
}
case "stack":
boolean fixed = false;
while (!fixed) {
try {
ISBPL.gErrorStream.println("STACK DUMP");
for (ISBPLObject object : stack(tid)) {
ISBPL.gErrorStream.println("\t" + object);
}
fixed = true;
}
catch (Exception e) {
e.printStackTrace();
ISBPL.gErrorStream.println("!!! STACK CORRUPTED!");
stack(tid).drop();
ISBPL.gErrorStream.println("Popped. Trying again.");
}
}
break;
case "son":
ISBPL.debug = true;
ISBPL.printCalls = true;
break;
case "sonf":
ISBPL.debug = false;
ISBPL.printCalls = true;
break;
case "soff":
ISBPL.debug = false;
ISBPL.printCalls = false;
break;
case "exit":
System.exit(255);
break;
case "threads":
ISBPL.gErrorStream.println("THREAD DUMP");
for (Thread thread : Thread.getAllStackTraces().keySet()) {
if (isbpl.debuggerIPC.stack.containsKey(thread.getId()))
ISBPL.gErrorStream.println(thread.getId() + "\t" + thread.getName());
}
break;
case "setthread":
case "st":
long l = Long.parseLong(line.split(" ")[1]);
if (isbpl.debuggerIPC.stack.containsKey(l)) {
tid = l;
ISBPL.gErrorStream.println("Set TID=" + l);
}
else
ISBPL.gErrorStream.println("Thread not valid");
break;
default:
break;
}
}
catch (Exception e) {
try {
e.printStackTrace(new PrintStream(s.getOutputStream()));
}
catch (IOException ex) {
ex.printStackTrace();
}
}
}
} catch (IOException e) {
try {
s.close();
}
catch (IOException ex) {
ex.printStackTrace();
}
}
}).start();
}
}
catch (IOException e) {
e.printStackTrace();
}
}
static class IPC {
long threadID = -1;
String until = null;
HashMap<Long, Integer> run = new HashMap<>();
HashMap<Long, ISBPLStack<ISBPLObject>> stack = new HashMap<>();
int port = -1;
}
}
class ISBPLStreamer {
public static final int CREATE_FILE_IN = 0;
public static final int CREATE_FILE_OUT = 1;
public static final int CREATE_SOCKET = 2;
public static final int CLOSE = 3;
public static final int READ = 4;
public static final int WRITE = 5;
@SuppressWarnings("unused")
public static final int AREAD = 6;
@SuppressWarnings("unused")
public static final int AWRITE = 7;
public static final int CREATE_SERVER = 9;
static class ISBPLStream {
final InputStream in;
final OutputStream out;
static int gid = 0;
final int id = gid++;
public ISBPLStream(InputStream in, OutputStream out) {
this.in = in;
this.out = out;
}
public void close() throws IOException {
this.in.close();
this.out.close();
}
}
final ISBPL isbpl;
public ISBPLStreamer(ISBPL isbpl) {
this.isbpl = isbpl;
}
public static ArrayList<ISBPLStream> streams = new ArrayList<>();
public synchronized void action(ISBPLStack<ISBPLObject> stack, int action) throws IOException {
ISBPLStream stream;
ISBPLObject s, i;
File f;
switch (action) {
case CREATE_FILE_IN:
s = stack.pop();
s.checkType(isbpl.getType("string"));
f = new File(isbpl.toJavaString(s));
stream = new ISBPLStream(Files.newInputStream(f.toPath()), new OutputStream() {
@Override
public void write(int b) {
throw new ISBPLError("IllegalArgument", "Can't write to a FILE_IN stream!");
}
});
streams.add(stream);
stack.push(new ISBPLObject(isbpl.getType("int"), stream.id));
break;
case CREATE_FILE_OUT:
s = stack.pop();
s.checkType(isbpl.getType("string"));
f = new File(isbpl.toJavaString(s));
stream = new ISBPLStream(new InputStream() {
@Override
public int read() {
throw new ISBPLError("IllegalArgument", "Can't read a FILE_OUT stream!");
}
}, Files.newOutputStream(f.toPath()));
streams.add(stream);
stack.push(new ISBPLObject(isbpl.getType("int"), stream.id));
break;
case CREATE_SOCKET:
i = stack.pop();
s = stack.pop();
i.checkType(isbpl.getType("int"));
s.checkType(isbpl.getType("string"));
Socket socket = new Socket(isbpl.toJavaString(s), ((int) i.object));
stream = new ISBPLStream(socket.getInputStream(), socket.getOutputStream());
streams.add(stream);
stack.push(new ISBPLObject(isbpl.getType("int"), stream.id));
break;
case CREATE_SERVER:
i = stack.pop();
i.checkType(isbpl.getType("int"));
ServerSocket server = new ServerSocket(((int) i.object));
stream = new ISBPLStream(new InputStream() {
@Override
public int read() throws IOException {
Socket socket = server.accept();
ISBPLStream stream = new ISBPLStream(socket.getInputStream(), socket.getOutputStream());
return stream.id;
}
}, new OutputStream() {
@Override
public void write(int b) {
throw new ISBPLError("IllegalArgument", "Can't write to a SERVER stream!");
}
});
streams.add(stream);
stack.push(new ISBPLObject(isbpl.getType("int"), stream.id));
break;
case READ:
i = stack.pop();
i.checkType(isbpl.getType("int"));
try {
stack.push(new ISBPLObject(isbpl.getType("int"), streams.get(((int) i.object)).in.read()));
} catch (IndexOutOfBoundsException e) {
throw new ISBPLError("IllegalArgument", "streamid STREAM_READ stream called with non-existing stream argument");
}
break;
case WRITE:
i = stack.pop();
i.checkType(isbpl.getType("int"));
ISBPLObject bte = stack.pop();
bte.checkTypeMulti(isbpl.getType("int"), isbpl.getType("char"), isbpl.getType("byte"));
try {
streams.get(((int) i.object)).out.write(((int) bte.toLong()));
} catch (IndexOutOfBoundsException e) {
throw new ISBPLError("IllegalArgument", "byte streamid STREAM_WRITE stream called with non-existing stream argument");
}
break;
case CLOSE:
i = stack.pop();
i.checkType(isbpl.getType("int"));
try {
ISBPLStream strm = streams.get(((int) i.object));
strm.close();
} catch (IndexOutOfBoundsException e) {
throw new ISBPLError("IllegalArgument", "streamid STREAM_CLOSE stream called with non-existing stream argument");
}
break;
default:
throw new ISBPLError("NotImplemented", "Not implemented");
}
}
}
class ISBPLThreadLocal<T> {
HashMap<Long, T> map = new HashMap<>();
Supplier<? extends T> supplier;
public ISBPLThreadLocal(Supplier<? extends T> supplier) {
this.supplier = supplier;
}
public static <T> ISBPLThreadLocal<T> withInitial(Supplier<? extends T> supplier) {
return new ISBPLThreadLocal<>(supplier);
}
public synchronized T get() {
long tid = Thread.currentThread().getId();
if(!map.containsKey(tid))
map.put(tid, supplier.get());
return map.get(tid);
}
public synchronized void set(T t) {
map.put(Thread.currentThread().getId(), t);
}
@Override
public String toString() {
return get().toString();
}
}
class ISBPLStack<T extends ISBPLUsable> extends Stack<T> implements ISBPLUsable {
final ISBPLFastList<T> toBeMarked = new ISBPLFastList<>(3);
@Override
public T push(T t) {
if(t == null)
new IllegalArgumentException("item is null").printStackTrace();
if(t.isUseless()) {
throw new IllegalStateException("item was garbage-collected (" + t + ")");
}
if(t instanceof ISBPLObject && ((ISBPLObject) t).type == null) {
throw new IllegalStateException("item was garbage-collected (" + t + ") but was not marked");
}
t.usedBy(this);
return super.push(t);
}
@Override
public T peek() {
T t = super.peek();
if(t.isUseless()) {
throw new IllegalStateException("item was garbage-collected (" + t + ") WHILE ON STACK!");
}
return t;
}
@Override
public T pop() {
T t = super.pop();
toBeMarked.add(t);
if(t.isUseless()) {
throw new IllegalStateException("item was garbage-collected (" + t + ") WHILE ON STACK!");
}
return t;
}
public void drop() {
super.pop().unusedBy(this);
}
public void update() {
while(!toBeMarked.isEmpty()) {
toBeMarked.get(0).unusedBy(this);
toBeMarked.remove(0);
}
}
protected void finalize() throws Throwable {
if(!isUseless())
ISBPL.gErrorStream.println("[ISBPL] [Please report] JVM GC'd " + this + ", but it was not picked up by ISBPL's GC!");
if(ISBPL.debug) {
ISBPL.gErrorStream.println("JVM deleting " + this);
}
super.finalize();
}
}
class ISBPLFrame implements ISBPLUsable {
public ISBPL context;
public ISBPLFrame parent;
public HashMap<String, ISBPLCallable> map = new HashMap<>();
public HashMap<Object, ISBPLObject> variables = new HashMap<>();
public String name;
public ISBPLFrame(String name, ISBPL context) {
this.name = name;
this.context = context;
this.addUse();
}
public ISBPLFrame(String name, ISBPL context, ISBPLFrame parentFrame) {
this.name = name;
this.context = context;
parent = parentFrame;
parentFrame.usedBy(this);
}
public void useless() {
context = null;
if(map != null)
map.clear();
map = null;
if(variables != null)
variables.clear();
variables = null;
}
public ISBPLCallable resolve(String name) {
if(name.startsWith("&")) {
ISBPLCallable c = resolve(name.substring(1));
if(c != null) {
return (stack -> stack.push(new ISBPLObject(context.getType("func"), c)));
}
}
if(map.containsKey(name))
return map.get(name);
if(parent != null) {
return parent.resolve(name);
}
return null;
}
public void add(String name, ISBPLCallable callable) {
callable.usedBy(this);
callable = map.put(name, callable);
if(callable != null) {
callable.unusedBy(this);
}
}
public void define(String name, ISBPLObject value) {
value.usedBy(this);
Object var = new Object();
ISBPLObject old = variables.put(var, value);
if(old != null) {
old.unusedBy(this);
}
add(name, (stack) -> stack.push(variables.get(var)));
add("=" + name, (stack) -> {
ISBPLObject o = stack.pop();
o.usedBy(this);
variables.put(var, o).unusedBy(this);
});
}
public HashMap<String, ISBPLCallable> all() {
HashMap<String, ISBPLCallable> r = parent == null ? new HashMap<>() : parent.all();
r.putAll(map);
return r;
}
protected void finalize() throws Throwable {
if(!isUseless())
ISBPL.gErrorStream.println("[ISBPL] [Please report] JVM GC'd " + this + ", but it was not picked up by ISBPL's GC!");
if(ISBPL.debug) {
ISBPL.gErrorStream.println("JVM deleting " + this);
}
super.finalize();
}
}