package au.notzed.scripta;
import java.io.PrintStream;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.NoSuchElementException;
import java.util.Optional;
-import java.util.function.Consumer;
+import java.util.function.BiFunction;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
field (imported)
method() (imported)
+ - values need lvalue and rvalue inserters!
+
- syntax
can it be simpler?
arrays at least would be nice
boolean valid = false;
ClassWriter cw;
MethodVisitor mw;
- int variableID = 1;
+ int variableID;
Map<String, Variable> variables = new HashMap<>();
LinkedList<Value> stack = new LinkedList<>();
+ StringBuilder refname = new StringBuilder();
ClassLoader classLoader = new ClassLoader() {
};
Imports imports = new Imports();
static final Type STRING_TYPE = Type.getType(String.class);
static final Type BOOLEAN_OBJECT_TYPE = Type.getType(Boolean.class);
+ static final Type OBJECT_OBJECT_TYPE = Type.getType(Object.class);
+ static final Type NUMBER_OBJECT_TYPE = Type.getType(Number.class);
static final Type BYTE_OBJECT_TYPE = Type.getType(Byte.class);
static final Type INTEGER_OBJECT_TYPE = Type.getType(Integer.class);
static final Type LONG_OBJECT_TYPE = Type.getType(Long.class);
static final Type DOUBLE_OBJECT_TYPE = Type.getType(Double.class);
record Variable(String name, int id, Type type) {
+ public Value value() {
+ return new Value(
+ type,
+ Value.promoteFrom(type),
+ (mv, o) -> {
+ mv.visitVarInsn(type.getOpcode(ISTORE), id);
+ }, (mv, o) -> {
+ mv.visitVarInsn(type.getOpcode(ILOAD), id);
+ });
+ }
}
- record Value(Type type, Consumer<MethodVisitor> insert) {
- public Value then(Consumer<MethodVisitor> then) {
- return new Value(type, insert.andThen(then));
- }
+ public interface InsertFunction {
+ void accept(MethodVisitor mv, Value recv);
- static public Value constant(float v) {
- return new Value(Type.FLOAT_TYPE, mv -> mv.visitLdcInsn(v));
+ default void accept(MethodVisitor mv) {
+ accept(mv, null);
}
+ }
- static public Value constant(double v) {
- return new Value(Type.DOUBLE_TYPE, mv -> mv.visitLdcInsn(v));
- }
+ record Value(Type type, BiFunction<Value, Type, Value> promote, InsertFunction lval, InsertFunction rval) {
- static public Value constant(byte v) {
- return new Value(Type.BYTE_TYPE, mv -> mv.visitLdcInsn(v));
+ public Value(Type type, BiFunction<Value, Type, Value> promote, InsertFunction rval) {
+ this(type, promote, null, rval);
}
- static public Value constant(short v) {
- return new Value(Type.SHORT_TYPE, mv -> mv.visitLdcInsn(v));
+ public Value(Type type, InsertFunction lval, InsertFunction rval) {
+ this(type, promoteFrom(type), lval, rval);
}
- static public Value constant(int v) {
- return new Value(Type.INT_TYPE, mv -> mv.visitLdcInsn(v));
+ public Value(Type type, InsertFunction rval) {
+ this(type, promoteFrom(type), null, rval);
}
- static public Value constant(long v) {
- return new Value(Type.LONG_TYPE, mv -> mv.visitLdcInsn(v));
+ public Value rval(Type newtype, BiFunction<Value, Type, Value> promote, InsertFunction then) {
+ return new Value(newtype, promote, lval, (mv, o) -> {
+ rval.accept(mv, o);
+ then.accept(mv, o);
+ });
}
- static public Value constant(boolean v) {
- return new Value(Type.BOOLEAN_TYPE, mv -> mv.visitLdcInsn(v));
+ public Value rval(Type newtype, InsertFunction then) {
+ return new Value(newtype, lval, (mv, o) -> {
+ rval.accept(mv, o);
+ then.accept(mv, o);
+ });
}
- static public Value constant(String v) {
- return new Value(STRING_TYPE, mv -> mv.visitLdcInsn(v));
+ public Value promote(Type to) {
+ return promote.apply(this, to);
}
- static public Value constant(Object v) {
- return new Value(Type.getType(v.getClass()), mv -> mv.visitLdcInsn(v));
- }
+ static public final Value NULL = new Value(OBJECT_OBJECT_TYPE,
+ (self, type) -> {
+ if (type.getSort() == Type.OBJECT)
+ return self;
+ else
+ throw new UnsupportedOperationException("null is only Object");
+ }, (mv, recv) -> {
+ mv.visitInsn(ACONST_NULL);
+ });
- public boolean isNumber() {
- return type.equals(INTEGER_OBJECT_TYPE)
- || type.equals(FLOAT_OBJECT_TYPE)
- || type.equals(LONG_OBJECT_TYPE)
- || type.equals(DOUBLE_OBJECT_TYPE)
- || type.equals(BYTE_OBJECT_TYPE);
+ static BiFunction<Value, Type, Value> promoteFrom(Type from) {
+ switch (from.getSort()) {
+ case Type.FLOAT:
+ return Value::floatTo;
+ case Type.DOUBLE:
+ return Value::doubleTo;
+ case Type.INT:
+ return Value::intTo;
+ case Type.LONG:
+ return Value::longTo;
+ case Type.BOOLEAN:
+ return Value::booleanTo;
+ case Type.OBJECT:
+ if (from.equals(STRING_TYPE)) {
+ return Value::stringTo;
+ } else if (from.equals(FLOAT_OBJECT_TYPE)) {
+ return Value::FloatTo;
+ } else if (from.equals(DOUBLE_OBJECT_TYPE)) {
+ return Value::DoubleTo;
+ } else if (from.equals(INTEGER_OBJECT_TYPE)) {
+ return Value::IntegerTo;
+ } else if (from.equals(LONG_OBJECT_TYPE)) {
+ return Value::LongTo;
+ } else if (from.equals(BOOLEAN_OBJECT_TYPE)) {
+ return Value::BooleanTo;
+ } else if (from.equals(OBJECT_OBJECT_TYPE)) {
+ return (v, t) -> v;
+ }
+ default:
+ return (v, t) -> {
+ throw new UnsupportedOperationException();
+ };
+ }
}
- public boolean isBoolean() {
- return type.equals(BOOLEAN_OBJECT_TYPE);
+ static Value floatTo(Value self, Type type) {
+ switch (type.getSort()) {
+ case Type.FLOAT:
+ return self;
+ case Type.DOUBLE:
+ return self.rval(type, Value::doubleTo, (mv, o) -> mv.visitInsn(F2D));
+ case Type.INT:
+ return self.rval(type, Value::intTo, (mv, o) -> mv.visitInsn(F2I));
+ case Type.LONG:
+ return self.rval(type, Value::longTo, (mv, o) -> mv.visitInsn(F2L));
+ case Type.OBJECT:
+ if (type.equals(STRING_TYPE)) {
+ return self.rval(type, Value::stringTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "toString", "(F)Ljava/lang/String;", false));
+ } else if (type.equals(FLOAT_OBJECT_TYPE) || type.equals(NUMBER_OBJECT_TYPE) || type.equals(OBJECT_OBJECT_TYPE)) {
+ return self.rval(type, Value::FloatTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false));
+ } else if (type.equals(DOUBLE_OBJECT_TYPE)) {
+ return self.rval(type, Value::DoubleTo, (mv, o) -> {
+ mv.visitInsn(F2D);
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false);
+ });
+ } else if (type.equals(INTEGER_OBJECT_TYPE)) {
+ return self.rval(type, Value::IntegerTo, (mv, o) -> {
+ mv.visitInsn(F2I);
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
+ });
+ } else if (type.equals(LONG_OBJECT_TYPE)) {
+ return self.rval(type, Value::LongTo, (mv, o) -> {
+ mv.visitInsn(F2L);
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(L)Ljava/lang/Long;", false);
+ });
+ }
+ default:
+ throw new UnsupportedOperationException();
+ }
}
- public Value promote(Type to) {
- switch (to.getSort()) {
- case Type.BOOLEAN:
- return promoteBoolean();
+ static Value doubleTo(Value self, Type type) {
+ switch (type.getSort()) {
+ case Type.FLOAT:
+ return self.rval(type, Value::floatTo, (mv, o) -> mv.visitInsn(D2F));
+ case Type.DOUBLE:
+ return self;
case Type.INT:
- return promoteInt();
+ return self.rval(type, Value::intTo, (mv, o) -> mv.visitInsn(D2I));
case Type.LONG:
- return promoteLong();
+ return self.rval(type, Value::longTo, (mv, o) -> mv.visitInsn(D2L));
+ case Type.OBJECT:
+ if (type.equals(STRING_TYPE)) {
+ return self.rval(type, Value::stringTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "toString", "(D)Ljava/lang/String;", false));
+ } else if (type.equals(FLOAT_OBJECT_TYPE)) {
+ return self.rval(type, Value::FloatTo, (mv, o) -> {
+ mv.visitInsn(D2F);
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false);
+ });
+ } else if (type.equals(DOUBLE_OBJECT_TYPE) || type.equals(NUMBER_OBJECT_TYPE) || type.equals(OBJECT_OBJECT_TYPE)) {
+ return self.rval(type, Value::DoubleTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false));
+ } else if (type.equals(INTEGER_OBJECT_TYPE)) {
+ return self.rval(type, Value::IntegerTo, (mv, o) -> {
+ mv.visitInsn(D2I);
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
+ });
+ } else if (type.equals(LONG_OBJECT_TYPE)) {
+ return self.rval(type, Value::LongTo, (mv, o) -> {
+ mv.visitInsn(D2L);
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(L)Ljava/lang/Long;", false);
+ });
+ }
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ static Value intTo(Value self, Type type) {
+ switch (type.getSort()) {
case Type.FLOAT:
- return promoteFloat();
+ return self.rval(type, Value::floatTo, (mv, o) -> mv.visitInsn(I2F));
case Type.DOUBLE:
- return promoteDouble();
+ return self.rval(type, Value::doubleTo, (mv, o) -> mv.visitInsn(I2D));
+ case Type.INT:
+ return self;
+ case Type.LONG:
+ return self.rval(type, Value::longTo, (mv, o) -> mv.visitInsn(I2L));
case Type.OBJECT:
- if (to.equals(STRING_TYPE))
- return promoteString();
- else if (to.equals(INTEGER_OBJECT_TYPE))
- return promoteInt().then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false));
- else if (to.equals(FLOAT_OBJECT_TYPE))
- return promoteFloat().then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false));
- else if (to.equals(BOOLEAN_OBJECT_TYPE))
- return promoteBoolean().then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false));
- else if (to.equals(LONG_OBJECT_TYPE))
- return promoteLong().then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false));
- else if (to.equals(DOUBLE_OBJECT_TYPE))
- return promoteDouble().then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false));
+ if (type.equals(STRING_TYPE)) {
+ return self.rval(type, Value::stringTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "toString", "(I)Ljava/lang/String;", false));
+ } else if (type.equals(FLOAT_OBJECT_TYPE)) {
+ return self.rval(type, Value::FloatTo, (mv, o) -> {
+ mv.visitInsn(I2F);
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false);
+ });
+ } else if (type.equals(DOUBLE_OBJECT_TYPE)) {
+ return self.rval(type, Value::DoubleTo, (mv, o) -> {
+ mv.visitInsn(I2D);
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false);
+ });
+ } else if (type.equals(INTEGER_OBJECT_TYPE) || type.equals(NUMBER_OBJECT_TYPE) || type.equals(OBJECT_OBJECT_TYPE)) {
+ return self.rval(type, Value::IntegerTo, (mv, o) -> {
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
+ });
+ } else if (type.equals(LONG_OBJECT_TYPE)) {
+ return self.rval(type, Value::LongTo, (mv, o) -> {
+ mv.visitInsn(D2L);
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(L)Ljava/lang/Long;", false);
+ });
+ }
default:
- throw new IllegalArgumentException("expecting " + to + ": " + this);
+ throw new UnsupportedOperationException();
}
}
- Value promoteBoolean() {
- if (type == Type.BOOLEAN_TYPE) {
- return this;
- } else if (isBoolean()) {
- return then(mv -> mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Boolean", "booleanValue", "()Z", false));
- } else if (type.equals(STRING_TYPE)) {
- return then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "parseBoolean", "(Ljava/lang/String;)Z", false));
- } else {
- throw new IllegalArgumentException("expecting boolean or boolean string: " + this);
+ static Value longTo(Value self, Type type) {
+ switch (type.getSort()) {
+ case Type.FLOAT:
+ return self.rval(type, Value::floatTo, (mv, o) -> mv.visitInsn(L2F));
+ case Type.DOUBLE:
+ return self.rval(type, Value::doubleTo, (mv, o) -> mv.visitInsn(L2D));
+ case Type.INT:
+ return self.rval(type, Value::longTo, (mv, o) -> mv.visitInsn(L2I));
+ case Type.LONG:
+ return self;
+ case Type.OBJECT:
+ if (type.equals(STRING_TYPE)) {
+ return self.rval(type, Value::stringTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "toString", "(J)Ljava/lang/String;", false));
+ } else if (type.equals(FLOAT_OBJECT_TYPE)) {
+ return self.rval(type, Value::FloatTo, (mv, o) -> {
+ mv.visitInsn(L2F);
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false);
+ });
+ } else if (type.equals(DOUBLE_OBJECT_TYPE)) {
+ return self.rval(type, Value::DoubleTo, (mv, o) -> {
+ mv.visitInsn(L2D);
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false);
+ });
+ } else if (type.equals(INTEGER_OBJECT_TYPE)) {
+ return self.rval(type, Value::IntegerTo, (mv, o) -> {
+ mv.visitInsn(L2I);
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false);
+ });
+ } else if (type.equals(LONG_OBJECT_TYPE) || type.equals(NUMBER_OBJECT_TYPE) || type.equals(OBJECT_OBJECT_TYPE)) {
+ return self.rval(type, Value::LongTo, (mv, o) -> {
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false);
+ });
+ }
+ default:
+ throw new UnsupportedOperationException();
}
}
- Value promoteInt() {
- if (type == Type.INT_TYPE) {
- return this;
- } else if (type == Type.FLOAT_TYPE) {
- return then(mv -> mv.visitInsn(F2I));
- } else if (type == Type.LONG_TYPE) {
- return then(mv -> mv.visitInsn(L2I));
- } else if (type == Type.DOUBLE_TYPE) {
- return then(mv -> mv.visitInsn(D2I));
- } else if (isNumber()) {
- return then(mv -> mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "intValue", "()I", true));
- } else if (type.equals(STRING_TYPE)) {
- return then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "parseInt", "(Ljava/lang/String;)I", false));
- } else {
- throw new IllegalArgumentException("expecting number or numerical string " + this);
- }
+ static Value booleanTo(Value self, Type type) {
+ if (type.getSort() == Type.BOOLEAN)
+ return self;
+ else if (type.equals(Type.BOOLEAN_TYPE))
+ return self.rval(type, Value::BooleanTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false));
+ else if (type.equals(STRING_TYPE))
+ return self.rval(type, Value::stringTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "toString", "(Z)Ljava/lang/String;", false));
+ else
+ throw new UnsupportedOperationException();
}
- Value promoteLong() {
- if (type == Type.LONG_TYPE) {
- return this;
- } else if (type == Type.FLOAT_TYPE) {
- return then(mv -> mv.visitInsn(F2L));
- } else if (type == Type.INT_TYPE) {
- return then(mv -> mv.visitInsn(I2L));
- } else if (type == Type.DOUBLE_TYPE) {
- return then(mv -> mv.visitInsn(D2L));
- } else if (isNumber()) {
- return then(mv -> mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "longValue", "()J", true));
- } else if (type.equals(STRING_TYPE)) {
- return then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "parseLong", "(Ljava/lang/String;)J", false));
- } else {
- throw new IllegalArgumentException("expecting number or numerical string");
+ static Value stringTo(Value self, Type type) {
+ switch (type.getSort()) {
+ case Type.FLOAT:
+ return self.rval(type, Value::floatTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "parseFloat", "(Ljava/lang/String;)F", false));
+ case Type.DOUBLE:
+ return self.rval(type, Value::doubleTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "parseFloat", "(Ljava/lang/String;)F", false));
+ case Type.INT:
+ return self.rval(type, Value::intTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "parseInt", "(Ljava/lang/String;)I", false));
+ case Type.LONG:
+ return self.rval(type, Value::longTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "parseLong", "(Ljava/lang/String;)J", false));
+ case Type.BOOLEAN:
+ return self.rval(type, Value::booleanTo, (mv, o) -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "parseBoolean", "(Ljava/lang/String;)Z", false));
+ case Type.OBJECT:
+ if (type.equals(STRING_TYPE)) {
+ return self;
+ } else if (type.equals(FLOAT_OBJECT_TYPE)) {
+ return self.rval(type, Value::FloatTo, (mv, o) -> {
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(Ljava/lang/String;)Ljava/lang/Float;", false);
+ });
+ } else if (type.equals(DOUBLE_OBJECT_TYPE)) {
+ return self.rval(type, Value::DoubleTo, (mv, o) -> {
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(Ljava/lang/String;)Ljava/lang/Double;", false);
+ });
+ } else if (type.equals(INTEGER_OBJECT_TYPE)) {
+ return self.rval(type, Value::IntegerTo, (mv, o) -> {
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(Ljava/lang/String;)Ljava/lang/Integer;", false);
+ });
+ } else if (type.equals(LONG_OBJECT_TYPE)) {
+ return self.rval(type, Value::LongTo, (mv, o) -> {
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(Ljava/lang/String;)Ljava/lang/Long;", false);
+ });
+ } else if (type.equals(BOOLEAN_OBJECT_TYPE)) {
+ return self.rval(type, Value::BooleanTo, (mv, o) -> {
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Ljava/lang/String;)Ljava/lang/Boolean;", false);
+ });
+ }
+ default:
+ throw new UnsupportedOperationException();
}
}
- public Value promoteFloat() {
- if (type == Type.FLOAT_TYPE) {
- return this;
- } else if (type == Type.INT_TYPE) {
- return then(mv -> mv.visitInsn(I2F));
- } else if (type == Type.DOUBLE_TYPE) {
- return then(mv -> mv.visitInsn(D2F));
- } else if (type == Type.LONG_TYPE) {
- return then(mv -> mv.visitInsn(L2F));
- } else if (isNumber()) {
- return then(mv -> mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "floatValue", "()F", false));
- } else if (type.equals(STRING_TYPE)) {
- return then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "parseFloat", "(Ljava/lang/String;)F", false));
- } else {
- throw new IllegalArgumentException("expecting number or numerical string " + this);
+ static Value NumberTo(Value self, Type type) {
+ switch (type.getSort()) {
+ case Type.FLOAT:
+ return self.rval(type, Value::floatTo, (mv, o) -> mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "floatValue", "()F", true));
+ case Type.DOUBLE:
+ return self.rval(type, Value::doubleTo, (mv, o) -> mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "doubleValue", "()D", true));
+ case Type.INT:
+ return self.rval(type, Value::intTo, (mv, o) -> mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "intValue", "()I", true));
+ case Type.LONG:
+ return self.rval(type, Value::longTo, (mv, o) -> mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "longValue", "()J", true));
+ case Type.OBJECT:
+ if (type.equals(STRING_TYPE)) {
+ return self.rval(type, Value::stringTo, (mv, o) -> mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "toString", "()Ljava/lang/String;", false));
+ } else if (type.equals(FLOAT_OBJECT_TYPE)) {
+ return self.rval(type, Value::FloatTo, (mv, o) -> {
+ mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "floatValue", "()F", true);
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(Ljava/lang/String;)Ljava/lang/Float;", false);
+ });
+ } else if (type.equals(DOUBLE_OBJECT_TYPE)) {
+ return self.rval(type, Value::DoubleTo, (mv, o) -> {
+ mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "doubleValue", "()D", true);
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(Ljava/lang/String;)Ljava/lang/Double;", false);
+ });
+ } else if (type.equals(INTEGER_OBJECT_TYPE)) {
+ return self.rval(type, Value::IntegerTo, (mv, o) -> {
+ mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "intValue", "()I", true);
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(Ljava/lang/String;)Ljava/lang/Integer;", false);
+ });
+ } else if (type.equals(LONG_OBJECT_TYPE)) {
+ return self.rval(type, Value::LongTo, (mv, o) -> {
+ mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "longValue", "()J", true);
+ mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(Ljava/lang/String;)Ljava/lang/Long;", false);
+ });
+ }
+ default:
+ throw new UnsupportedOperationException();
}
}
- Value promoteDouble() {
- if (type == Type.DOUBLE_TYPE) {
- return this;
- } else if (type == Type.INT_TYPE) {
- return then(mv -> mv.visitInsn(I2D));
- } else if (type == Type.FLOAT_TYPE) {
- return then(mv -> mv.visitInsn(F2D));
- } else if (type == Type.LONG_TYPE) {
- return then(mv -> mv.visitInsn(L2D));
- } else if (isNumber()) {
- return then(mv -> mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Number", "doubleValue", "()D", true));
- } else if (type.equals(STRING_TYPE)) {
- return then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "parseDouble", "(Ljava/lang/String;)D", false));
- } else {
- throw new IllegalArgumentException("expecting number or numerical string");
- }
+ static Value FloatTo(Value self, Type type) {
+ if (type.equals(FLOAT_OBJECT_TYPE))
+ return self;
+ else
+ return NumberTo(self, type);
}
- Value promoteString() {
- if (type.equals(STRING_TYPE)) {
- return this;
- } else {
- switch (type.getSort()) {
- case Type.BOOLEAN:
- return then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "toString", "(Z)Ljava/lang/String;", false));
- case Type.INT:
- return then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "toString", "(I)Ljava/lang/String;", false));
- case Type.LONG:
- return then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "toString", "(J)Ljava/lang/String;", false));
- case Type.FLOAT:
- return then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "toString", "(F)Ljava/lang/String;", false));
- case Type.DOUBLE:
- return then(mv -> mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "toString", "(D)Ljava/lang/String;", false));
- case Type.OBJECT:
- default:
- return then(mv -> mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "toString", "(Ljava/lang/Object;)Ljava/lang/String;", false));
- }
- }
+ static Value DoubleTo(Value self, Type type) {
+ if (type.equals(DOUBLE_OBJECT_TYPE))
+ return self;
+ else
+ return NumberTo(self, type);
+ }
+
+ static Value IntegerTo(Value self, Type type) {
+ if (type.equals(INTEGER_OBJECT_TYPE))
+ return self;
+ else
+ return NumberTo(self, type);
+ }
+
+ static Value LongTo(Value self, Type type) {
+ if (type.equals(LONG_OBJECT_TYPE))
+ return self;
+ else
+ return NumberTo(self, type);
+ }
+
+ static Value BooleanTo(Value self, Type type) {
+ if (type.getSort() == Type.BOOLEAN)
+ return self.rval(type, Value::booleanTo, (mv, o) -> mv.visitMethodInsn(INVOKEINTERFACE, "java/lang/Boolean", "booleanValue", "()Z", true));
+ else if (type.equals(Type.BOOLEAN_TYPE))
+ return self;
+ else if (type.equals(STRING_TYPE))
+ return self.rval(type, Value::stringTo, (mv, o) -> mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "toString", "()Ljava/lang/String;", false));
+ else
+ throw new UnsupportedOperationException();
+ }
+
+ static public Value constant(float v) {
+ return new Value(Type.FLOAT_TYPE, Value::floatTo, (mv, o) -> mv.visitLdcInsn(v));
+ }
+
+ static public Value constant(double v) {
+ return new Value(Type.DOUBLE_TYPE, Value::doubleTo, (mv, o) -> mv.visitLdcInsn(v));
+ }
+
+ /*
+ static public Value constant(byte v) {
+ return new Value(Type.BYTE_TYPE, Value::byteTo, (mv, o) -> mv.visitLdcInsn(v));
+ }
+
+ static public Value constant(short v) {
+ return new Value(Type.SHORT_TYPE, Value::shortTo, (mv, o) -> mv.visitLdcInsn(v));
+ }
+ */
+ static public Value constant(int v) {
+ return new Value(Type.INT_TYPE, Value::intTo, (mv, o) -> mv.visitLdcInsn(v));
+ }
+
+ static public Value constant(long v) {
+ return new Value(Type.LONG_TYPE, Value::longTo, (mv, o) -> mv.visitLdcInsn(v));
+ }
+
+ static public Value constant(boolean v) {
+ return new Value(Type.BOOLEAN_TYPE, Value::booleanTo, (mv, o) -> mv.visitLdcInsn(v));
+ }
+
+ static public Value constant(String v) {
+ return new Value(STRING_TYPE, Value::stringTo, (mv, o) -> mv.visitLdcInsn(v));
+ }
+
+ /*
+ static public Value constant(Object v) {
+ return new Value(Type.getType(v.getClass()), (mv, o) -> mv.visitLdcInsn(v));
+ }*/
+ public boolean isNumber() {
+ return type.equals(INTEGER_OBJECT_TYPE)
+ || type.equals(FLOAT_OBJECT_TYPE)
+ || type.equals(LONG_OBJECT_TYPE)
+ || type.equals(DOUBLE_OBJECT_TYPE)
+ || type.equals(BYTE_OBJECT_TYPE);
+ }
+
+ public boolean isBoolean() {
+ return type.equals(BOOLEAN_OBJECT_TYPE);
}
}
LinkedList<Block> blocks = new LinkedList<>();
+ public Optional<Variable> findVariable(String name) {
+ return Optional.ofNullable(variables.get(name));
+ }
+
public Variable addVariable(String name, Type type) {
Variable var;
if (type == Type.DOUBLE_TYPE || type == Type.LONG_TYPE) {
public Generator(String file) {
this.file = file;
- imports.importClass(String.class, f -> false, m -> true);
- imports.importClass(System.class, f -> f.getName().matches("out|error"), m -> false);
- imports.importClass(PrintStream.class, f -> false, m -> m.getName().matches("^(print|append).*"));
- imports.importClass(Math.class, f -> true, m -> true);
+ variables.put("this", new Variable("this", variableID++, Type.getType(Script.class)));
+
+ // testing constructors
+ for (Class<?> c: new Class[]{Boolean.class, Number.class, Integer.class, Long.class, Float.class, Double.class}) {
+ imports.importClass(Double.class, false, f -> true, m -> true);
+ }
+
+ imports.importClass(List.class, false, f -> false, m -> true);
+ imports.importClass(ArrayList.class, false, f -> false, m -> true);
+
+ //
+ imports.importClass(Script.class, false, f -> true, m -> !m.getName().equals("run"));
+ imports.importClass(Object.class, false, f -> false, m -> m.getName().matches("toString|equals"));
+ imports.importClass(String.class, false, f -> false, m -> true);
+ imports.importClass(System.class, false, f -> f.getName().matches("out|error"), m -> false);
+ imports.importClass(PrintStream.class, true, f -> false, m -> m.getName().matches("^(print|append).*"));
+ imports.importClass(Math.class, false, f -> true, m -> true);
List<Imports.Import> list = imports.importInstance[Imports.IMPORT_METHODS].getOrDefault(Type.getType(PrintStream.class), Map.of()).entrySet().stream()
.filter(e -> e.getKey().matches("^print.*"))
.flatMap(e -> e.getValue().stream())
.toList();
for (Imports.Import im: list) {
- //System.out.println(im);
- imports.importStatic(new Imports.Import(im.recv(), im.name(), im.type(), (m, mv, params) -> {
- mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", m.recv().getDescriptor());
- // TODO: promote/cast arguments
- //System.out.println(m.name() + " INSERT params " + params);
- params.forEach(a -> a.insert().accept(mv));
- mv.visitMethodInsn(INVOKEVIRTUAL, m.recv().getInternalName(), m.name(), m.type().getDescriptor(), false);
- }));
+ imports.importStatic(new Imports.Import(im.recv(), im.name(), im.type(),
+ (m, ro, params) -> new Value(im.type().getReturnType(), (mv, o) -> {
+ mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", m.recv().getDescriptor());
+ // TODO: promote/cast arguments
+ //System.out.println(m.name() + " INSERT params " + params);
+ params.forEach(a -> a.rval().accept(mv));
+ mv.visitMethodInsn(INVOKEVIRTUAL, m.recv().getInternalName(), m.name(), m.type().getDescriptor(), false);
+ })));
}
-
+ //imports.dump();
}
@Override
cw.visit(V1_8, ACC_PUBLIC,
"au/notzed/scripta/script",
null,
- "java/lang/Object",
- new String[]{"au/notzed/scripta/Script"});
+ "au/notzed/scripta/Script",
+ new String[]{});
// TODO: work out debuginfo (jsr45)
cw.visitSource(file, null);
mw.visitCode();
mw.visitVarInsn(ALOAD, 0);
- mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+ mw.visitMethodInsn(INVOKESPECIAL, "au/notzed/scripta/Script", "<init>", "()V", false);
mw.visitInsn(RETURN);
mw.visitMaxs(1, 1);
mw.visitEnd();
}
mw = cw.visitMethod(ACC_PUBLIC,
- "eval", "()V",
+ "run", "()V",
null, null);
Label start = new Label();
blocks.push(new Block(s.label, loop, exit));
mw.visitLabel(loop);
- visitExpression(s.test).insert.accept(mw);
+ visitExpression(s.test).rval.accept(mw);
s.then.ifPresentOrElse(r -> {
if (s.rest.isPresent()) {
mw.visitJumpInsn(IFEQ, then);
mw.visitLabel(test);
Value testX = visitExpression(s.test);
- testX.promoteBoolean().insert.accept(mw);
+ testX.promote(Type.BOOLEAN_TYPE).rval.accept(mw);
mw.visitJumpInsn(IFNE, loop);
mw.visitLabel(exit);
val = i.value.map(x -> visitExpression(x));
- if (s.type.type == AST.XType.OBJECT) {
- var = addVariable(i.name, Type.getObjectType(s.type.name.get().name()));
+ switch (s.type.type) {
+ case OBJECT:
+ var = addVariable(i.name, Type.getObjectType(s.type.name.get().name().replace('.', '/')));
val.ifPresent(v -> {
- v.insert.accept(mw);
+ v.rval.accept(mw);
mw.visitVarInsn(ASTORE, var.id);
});
- } else {
+ break;
+ case ANY:
+ if (val.isPresent()) {
+ var v = val.get();
+ System.out.println("any = " + v.type);
+ var = addVariable(i.name, v.type());
+ v.rval.accept(mw);
+ mw.visitVarInsn(v.type().getOpcode(ISTORE), var.id);
+ } else {
+ throw new UnsupportedOperationException("var must have assignment");
+ }
+ break;
+ default:
var = addVariable(i.name, typeMap[s.type.type.ordinal()]);
val = val.map(v -> v.promote(var.type));
System.out.printf(" var: %s = %s\n", var, val);
val.ifPresent(v -> {
- v.insert.accept(mw);
+ v.rval.accept(mw);
mw.visitVarInsn(var.type().getOpcode(ISTORE), var.id);
});
+ break;
}
variables.put(var.name, var);
}
@Override
public void visitAssign(AST.SAssign s) {
- //System.out.printf("%s%s = ", d, s.ref.name());
-
Value var = visitExpression(s.ref);
- // Variable var = variables.get(s.ref.name());
Value val = visitExpression(s.value);
+ System.out.print("** ");
+ System.out.print(var);
+ System.out.print(" = ");
+ System.out.println(val);
val = val.promote(var.type);
- val.insert.accept(mw);
- //mw.visitVarInsn(var.type.getOpcode(ISTORE), var.id);
+ var.lval.accept(mw, val);
}
@Override
public void visitCall(AST.SCall s) {
Value val = visitExpression(s.call);
- val.insert.accept(mw);
+ val.rval.accept(mw);
System.out.printf("call result %s\n", val.type);
switch (val.type.getSort()) {
Optional<Value> res = s.res.map(r -> visitExpression(r));
res.ifPresentOrElse(r -> {
- r.insert.accept(mw);
+ r.rval.accept(mw);
mw.visitInsn(r.type.getOpcode(IRETURN));
}, () -> {
mw.visitInsn(RETURN);
switch (e.op) {
case NEG:
- stack.push(a.then(mv -> mv.visitInsn(a.type.getOpcode(INEG))));
+ stack.push(a.rval(a.type(), (mv, o) -> mv.visitInsn(a.type.getOpcode(INEG))));
break;
case NOT:
- Value b = a.promoteBoolean();
- stack.push(new Value(Type.BOOLEAN_TYPE, mv -> {
- b.insert.accept(mv);
+ Value b = a.promote(Type.BOOLEAN_TYPE);
+ stack.push(new Value(Type.BOOLEAN_TYPE, Value::booleanTo, (mv, o) -> {
+ b.rval.accept(mv);
mw.visitInsn(ICONST_1);
mw.visitInsn(IXOR);
}));
break;
case INV:
- Value i = a.promoteInt();
- stack.push(new Value(Type.INT_TYPE, mv -> {
- i.insert.accept(mv);
+ // What about long!
+ Value i = a.promote(Type.INT_TYPE);
+ stack.push(new Value(Type.INT_TYPE, (mv, o) -> {
+ i.rval.accept(mv);
mw.visitInsn(ICONST_M1);
mw.visitInsn(IXOR);
}));
};
static Value compareFloat(Value c, Value d, int fcmp, int cmp) {
- return new Value(Type.BOOLEAN_TYPE, mv -> {
+ return new Value(Type.BOOLEAN_TYPE, Value::booleanTo, (mv, o) -> {
Label t = new Label();
Label f = new Label();
- c.insert.accept(mv);
- d.insert.accept(mv);
+ c.rval.accept(mv);
+ d.rval.accept(mv);
mv.visitInsn(fcmp);
mv.visitJumpInsn(cmp, t);
}
static Value compareInt(Value c, Value d, int lcmp, int cmp) {
- return new Value(Type.BOOLEAN_TYPE, mv -> {
+ return new Value(Type.BOOLEAN_TYPE, Value::booleanTo, (mv, o) -> {
Label t = new Label();
Label f = new Label();
- c.insert.accept(mv);
- d.insert.accept(mv);
+ c.rval.accept(mv);
+ d.rval.accept(mv);
if (lcmp != 0)
mv.visitInsn(lcmp);
mv.visitJumpInsn(cmp, t);
Type type;
int opcode;
+ System.out.printf("* %s %s %s\n", a, e.op, b);
+
switch (e.op) {
case ADD:
- if (a.type == STRING_TYPE || b.type == STRING_TYPE) {
- stack.addFirst(new Value(STRING_TYPE, mv -> {
- a.promoteString().insert.accept(mv);
- b.promoteString().insert.accept(mv);
+ if (a.type.equals(STRING_TYPE) || b.type.equals(STRING_TYPE)) {
+ stack.addFirst(new Value(STRING_TYPE, Value::stringTo, (mv, o) -> {
+ a.promote(STRING_TYPE).rval.accept(mv);
+ b.promote(STRING_TYPE).rval.accept(mv);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "concat", "(Ljava/lang/String;)Ljava/lang/String;", false);
}));
return;
case SUB, MUL, DIV:
if (a.type == Type.DOUBLE_TYPE || b.type == Type.DOUBLE_TYPE) {
type = Type.DOUBLE_TYPE;
- c = a.promoteDouble();
- d = b.promoteDouble();
} else if (a.type == Type.FLOAT_TYPE || b.type == Type.FLOAT_TYPE) {
type = Type.FLOAT_TYPE;
- c = a.promoteFloat();
- d = b.promoteFloat();
} else if (a.type == Type.LONG_TYPE || b.type == Type.LONG_TYPE) {
type = Type.LONG_TYPE;
- c = a.promoteLong();
- d = b.promoteLong();
} else if (a.type == Type.INT_TYPE || b.type == Type.INT_TYPE) {
type = Type.INT_TYPE;
- c = a.promoteInt();
- d = b.promoteInt();
} else {
type = Type.FLOAT_TYPE;
- c = a.promoteFloat();
- d = b.promoteFloat();
}
+ c = a.promote(type);
+ d = b.promote(type);
opcode = type.getOpcode(intOp[e.op.ordinal()]);
System.out.printf("%s: %d -> %d\n", e.op, intOp[e.op.ordinal()], opcode);
- stack.addFirst(new Value(type, mv -> {
- c.insert.accept(mv);
- d.insert.accept(mv);
+ stack.addFirst(new Value(type, (mv, o) -> {
+ c.rval.accept(mv);
+ d.rval.accept(mv);
mv.visitInsn(opcode);
}));
break;
case LSR, LSL, ASR, AND, ORR, XOR:
if (a.type == Type.LONG_TYPE) {
type = a.type;
- c = a.promoteLong();
- d = b.promoteInt();
} else if (a.type == Type.INT_TYPE) {
type = a.type;
- c = a.promoteInt();
- d = b.promoteInt();
} else {
type = Type.LONG_TYPE;
- c = a.promoteLong();
- d = b.promoteInt();
}
+ c = a.promote(type);
+ d = b.promote(Type.INT_TYPE);
opcode = type.getOpcode(intOp[e.op.ordinal()]);
- stack.addFirst(new Value(type, mv -> {
- c.insert.accept(mv);
- d.insert.accept(mv);
+ stack.addFirst(new Value(type, (mv, o) -> {
+ c.rval.accept(mv);
+ d.rval.accept(mv);
mv.visitInsn(opcode);
}));
break;
int index = e.op.ordinal() - AST.XBinaryOp.CLT.ordinal();
if (a.type == Type.DOUBLE_TYPE || b.type == Type.DOUBLE_TYPE) {
- stack.addFirst(compareFloat(a.promoteDouble(), b.promoteDouble(), cmpFloat[index] + 2, cmpOp[index]));
+ stack.addFirst(compareFloat(a.promote(Type.DOUBLE_TYPE), b.promote(Type.DOUBLE_TYPE), cmpFloat[index] + 2, cmpOp[index]));
} else if (a.type == Type.FLOAT_TYPE || b.type == Type.FLOAT_TYPE) {
- stack.addFirst(compareFloat(a.promoteFloat(), b.promoteFloat(), cmpFloat[index], cmpOp[index]));
+ stack.addFirst(compareFloat(a.promote(Type.FLOAT_TYPE), b.promote(Type.FLOAT_TYPE), cmpFloat[index], cmpOp[index]));
} else if (a.type == Type.LONG_TYPE || b.type == Type.LONG_TYPE) {
- stack.addFirst(compareInt(a.promoteLong(), b.promoteLong(), LCMP, cmpJump[index]));
+ stack.addFirst(compareInt(a.promote(Type.LONG_TYPE), b.promote(Type.LONG_TYPE), LCMP, cmpJump[index]));
} else if (a.type == Type.INT_TYPE || b.type == Type.INT_TYPE) {
- stack.addFirst(compareInt(a.promoteInt(), b.promoteInt(), 0, cmpJump[index]));
+ stack.addFirst(compareInt(a.promote(Type.INT_TYPE), b.promote(Type.INT_TYPE), 0, cmpJump[index]));
} else {
- stack.addFirst(compareInt(a.promoteInt(), b.promoteInt(), 0, cmpJump[index]));
+ stack.addFirst(compareInt(a.promote(Type.INT_TYPE), b.promote(Type.INT_TYPE), 0, cmpJump[index]));
}
break;
case AAND, OOR, XXOR:
- c = a.promoteBoolean();
- d = b.promoteBoolean();
+ type = Type.BOOLEAN_TYPE;
+ c = a.promote(type);
+ d = b.promote(type);
opcode = boolOp[e.op.ordinal() - AST.XBinaryOp.AAND.ordinal()];
- stack.addFirst(new Value(Type.BOOLEAN_TYPE, mv -> {
- c.insert.accept(mv);
- d.insert.accept(mv);
+ stack.addFirst(new Value(type, Value::booleanTo, (mv, o) -> {
+ c.rval.accept(mv);
+ d.rval.accept(mv);
mv.visitInsn(opcode);
}
}
+ @Override
+ public void visit(AST.XNew e) {
+ List<Value> args = e.args.stream().map(this::visitExpression).toList();
+ stack.push(imports.findConstructor(e.name, args).orElseThrow().value(null, args));
+ }
+
@Override
public void visit(AST.XCall e) {
List<Value> args = e.args.stream().map(this::visitExpression).toList();
- //imports.dump();
if (e.ref.isPresent()) {
Value recv = visitExpression(e.ref.get());
- System.out.printf("receiver: %s\n", recv);
- Optional<Value> im = imports.findImport(recv.type(), e.name, args);
-
- if (im.isPresent()) {
- Value call = im.get();
- stack.push(new Value(call.type(), mv -> {
- recv.insert.accept(mv);
- call.insert.accept(mv);
- }));
+ if (recv.type() != null) {
+ System.out.printf("receiver: %s\n", recv);
+ stack.push(imports.findImport(e.name, recv, args).orElseThrow().value(recv, args));
} else {
- throw new NoSuchElementException();
+ Type type = Type.getType("L" + refname.toString().replace('.', '/') + ";");
+ stack.push(imports.findImport(e.name, type, args).orElseThrow().value(null, args));
}
} else {
- stack.push(imports.findImport(e.name, args).orElseThrow());
+ stack.push(imports.findImport(e.name, args).orElseThrow().value(null, args));
}
}
@Override
public void visit(AST.XField e) {
- System.exit(2);
+ System.out.println("field: " + e.ref + " . " + e.field);
+ Value recv = visitExpression(e.ref);
+
+ // an incomplete reference, see if it resolved to anything yet
+ // TODO: long variable names?
+ if (recv.type() == null) {
+ Type type = Type.getType("L" + refname.toString().replace('.', '/') + ";");
+
+ System.out.println(" $ " + type);
+ imports.findField(e.field, type)
+ .ifPresentOrElse(f -> {
+ refname.setLength(0);
+ stack.push(f.value(recv, stack));
+ }, () -> {
+ refname.append('.');
+ refname.append(e.field);
+ stack.push(new Value(null, null));
+ });
+ } else {
+ stack.push(imports.findField(e.field, recv).orElseThrow());
+ }
}
@Override
- public void visit(AST.XReference e) {
- Variable var = variables.get(e.name());
-
- if (var != null) {
- stack.push(new Value(var.type, mv -> {
- mv.visitVarInsn(var.type.getOpcode(ILOAD), var.id);
- }));
- return;
+ public void visit(AST.XID e) {
+ Variable self = variables.get("this");
+ var val = findVariable(e.id).map(Variable::value)
+ .or(() -> imports.findField(e.id, self.value()))
+ .or(() -> imports.findField(e.id));
+
+ if (val.isPresent()) {
+ stack.push(val.get());
+ } else {
+ refname.setLength(0);
+ refname.append(e.id);
+ stack.push(new Value(null, null));
}
-
- stack.push(imports.findStaticField(e.name()).orElseThrow());
}
@Override
public void visit(AST.XBool e) {
- System.out.printf("%s", e.value);
stack.push(Value.constant(e.value));
}
@Override
public void visit(AST.XInteger e) {
- System.out.printf("%d\n", e.value);
stack.push(Value.constant(e.value));
}
@Override
public void visit(AST.XLong e) {
- System.out.printf("%dL\n", e.value);
stack.push(Value.constant(e.value));
}
@Override
public void visit(AST.XFloat e) {
- System.out.printf("%ff", e.value);
stack.push(Value.constant(e.value));
}
@Override
public void visit(AST.XDouble e) {
- System.out.printf("%f", e.value);
stack.push(Value.constant(e.value));
}
@Override
public void visit(AST.XString e) {
- System.out.printf("`%s`", e.value);
stack.push(Value.constant(e.value));
}
+ @Override
+ public void visit(AST.XNull e) {
+ stack.push(Value.NULL);
+ }
+
}
*/
package au.notzed.scripta;
+import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.function.Predicate;
import java.util.stream.IntStream;
import java.util.stream.Stream;
-import org.objectweb.asm.MethodVisitor;
import static org.objectweb.asm.Opcodes.*;
import org.objectweb.asm.Type;
/**
* TODO: might need a Lookup
* TODO: namespaces
+ * <p>
+ * method
+ * invoke args
+ * field
+ * get
+ * set val
+ * constant
+ * get
+ * variable
+ * get
+ * set val
+ * indexed?
+ * get index
+ * set index val
+ * <p>
+ * Value
+ * set(recv, val)
+ * get(recv)
+ * Call
+ * invoke(args)
*/
public class Imports {
/*
Some order of precedence needs to be enforced. e.g. specificity rules.
*/
- record Import(Type recv, String name, Type type, InsertFunction insert) {
- // insert routine also needs arguments
-
- public Generator.Value value() {
- return new Generator.Value(type.getSort() == Type.METHOD ? type.getReturnType() : type, mv -> {
- insert(mv, List.of());
- });
- }
-
- public Generator.Value value(List<Generator.Value> args) {
- return new Generator.Value(type.getSort() == Type.METHOD ? type.getReturnType() : type, mv -> {
- insert(mv, args);
- });
- }
-
- public void insert(MethodVisitor mw, List<Generator.Value> args) {
- insert.insert(this, mw, args);
+ record Import(Type recv, String name, Type type, InsertFunction value) {
+ public Generator.Value value(Generator.Value recv, List<Generator.Value> args) {
+ return value.accept(this, recv, args);
}
}
public interface InsertFunction {
- void insert(Import im, MethodVisitor mv, List<Generator.Value> args);
+ Generator.Value accept(Import im, Generator.Value recv, List<Generator.Value> args);
}
+
// static?
// fields? methods? constructors?
static final int IMPORT_FIELDS = 0;
/**
* Link types to their parents.
*/
- Map<Type, Type> parentClass = new HashMap<>();
+ Map<Type, Type[]> superClass = new HashMap<>();
Map<Type, Map<String, List<Import>>> importInstance[] = IntStream.range(0, 3).mapToObj(i -> new HashMap<>()).toArray(Map[]::new);
Map<Type, Map<String, List<Import>>> importStatic[] = IntStream.range(0, 3).mapToObj(i -> new LinkedHashMap<>()).toArray(Map[]::new);
+ Map<String, List<Import>> importConstructors = new HashMap<>();
public void importInstance(Import im) {
//System.out.println("add new instance " + im);
*
* @param itype
*/
- public void importClass(Class<?> itype) {
- importClass(itype, a -> true, b -> true);
+ public void importClass(Class<?> itype, boolean deep) {
+ importClass(itype, deep, a -> true, b -> true);
}
- // TODO: primitive constants are fully resolved at compile time in java, without the GETSTATIC
- public void importClass(Class<?> itype, Predicate<Field> fields, Predicate<Method> methods) {
- Type irecv = Type.getType(itype);
+ // TODO: final fields shouldn't have rvalue
+ public void importClass(Class<?> itype, boolean deep, Predicate<Field> fields, Predicate<Method> methods) {
+ Class<?> rtype = itype;
+ List<Type> superList = new ArrayList<>();
- Class<?> ptype = itype;
- while (ptype != Object.class) {
- Class<?> ntype = ptype.getSuperclass();
- parentClass.put(Type.getType(ptype), Type.getType(ntype));
- ptype = ntype;
- }
+ superList.add(Type.getType(rtype));
+ while (itype != null) {
+ if (itype == rtype || deep) {
+ for (Field f: itype.getDeclaredFields()) {
+ if (!Modifier.isPublic(f.getModifiers()) || !fields.test(f))
+ continue;
- for (Field f: itype.getFields()) {
- if (fields.test(f)) {
- Type recv = Type.getType(f.getDeclaringClass());
- Type type = Type.getType(f.getType());
- String name = f.getName();
-
- if (Modifier.isStatic(f.getModifiers())) {
- if (type.getSort() == Type.OBJECT) {
- importStatic(new Import(recv, name, type, (im, mv, args) -> {
- mv.visitFieldInsn(GETSTATIC, irecv.getInternalName(), im.name(), im.type().getDescriptor());
- }));
- } else {
- try {
- Object value = f.get(null);
- importStatic(new Import(recv, name, type, (im, mv, args) -> {
- mv.visitLdcInsn(value);
- }));
- } catch (IllegalArgumentException | IllegalAccessException ex) {
- System.out.println("field: " + f + ": " + ex.getMessage());
+ Type recv = Type.getType(f.getDeclaringClass());
+ Type type = Type.getType(f.getType());
+ String name = f.getName();
+
+ if (Modifier.isStatic(f.getModifiers())) {
+ if (type.getSort() == Type.OBJECT || !Modifier.isFinal(f.getModifiers())) {
+ importStatic(new Import(recv, name, type,
+ (im, ro, args) -> new Generator.Value(im.type(),
+ (mv, o) -> {
+ mv.visitFieldInsn(PUTSTATIC, im.recv.getInternalName(), im.name(), im.type().getDescriptor());
+ }, (mv, o) -> {
+ mv.visitFieldInsn(GETSTATIC, im.recv.getInternalName(), im.name(), im.type().getDescriptor());
+ })));
+ } else {
+ try {
+ Object value = f.get(null);
+ importStatic(new Import(recv, name, type,
+ (im, ro, args) -> new Generator.Value(im.type(),
+ (mv, o) -> {
+ mv.visitLdcInsn(value);
+ })));
+ } catch (IllegalArgumentException | IllegalAccessException ex) {
+ System.out.println("field: " + f + ": " + ex.getMessage());
+ }
}
+ } else {
+ // TODO: final primitives?
+ importInstance(new Import(recv, name, type,
+ (im, ro, args) -> new Generator.Value(type,
+ (mv, o) -> {
+ ro.rval().accept(mv);
+ o.rval().accept(mv);
+ mv.visitFieldInsn(PUTFIELD, im.recv.getInternalName(), im.name(), im.type().getDescriptor());
+ },
+ (mv, o) -> {
+ ro.rval().accept(mv);
+ mv.visitFieldInsn(GETFIELD, im.recv.getInternalName(), im.name(), im.type().getDescriptor());
+ })));
}
- } else {
- importInstance(new Import(recv, name, type, (im, mv, args) -> {
- mv.visitFieldInsn(GETFIELD, irecv.getInternalName(), im.name(), im.type().getDescriptor());
- }));
}
- }
- }
- for (Method m: itype.getMethods()) {
- if (methods.test(m)) {
- Type recv = Type.getType(m.getDeclaringClass());
- Type type = Type.getType(m);
- String name = m.getName();
-
- // TODO: interface
- if (Modifier.isStatic(m.getModifiers())) {
- importStatic(new Import(recv, name, type, (im, mv, args) -> {
- Type[] argb = im.type.getArgumentTypes();
- for (int i = 0; i < argb.length; i++) {
- args.get(i).promote(argb[i]).insert().accept(mv);
- }
- mv.visitMethodInsn(INVOKESTATIC, im.recv.getInternalName(), im.name, im.type.getDescriptor(), false);
- }));
- } else {
- // receiver must already be present
- importInstance(new Import(recv, name, type, (im, mv, args) -> {
- Type[] argb = im.type.getArgumentTypes();
- for (int i = 0; i < argb.length; i++) {
- args.get(i).promote(argb[i]).insert().accept(mv);
- }
- mv.visitMethodInsn(INVOKEVIRTUAL, im.recv.getInternalName(), im.name, im.type.getDescriptor(), false);
- }));
+ for (Method m: itype.getDeclaredMethods()) {
+ if (!Modifier.isPublic(m.getModifiers()) || !methods.test(m))
+ continue;
+
+ Type recv = Type.getType(m.getDeclaringClass());
+ Type type = Type.getType(m);
+ String name = m.getName();
+
+ // TODO: interface
+ if (Modifier.isStatic(m.getModifiers())) {
+ importStatic(new Import(recv, name, type,
+ (im, ro, args) -> new Generator.Value(im.type.getReturnType(),
+ (mv, o) -> {
+ Type[] argb = im.type.getArgumentTypes();
+ for (int i = 0; i < argb.length; i++) {
+ args.get(i).promote(argb[i]).rval().accept(mv);
+ }
+ mv.visitMethodInsn(INVOKESTATIC, im.recv.getInternalName(), im.name, im.type.getDescriptor(), false);
+ })));
+ } else {
+ boolean isInterface = itype.isInterface();
+ importInstance(new Import(recv, name, type,
+ (im, ro, args) -> new Generator.Value(type.getReturnType(),
+ (mv, o) -> {
+ Type[] argb = im.type.getArgumentTypes();
+ ro.rval().accept(mv, ro);
+ for (int i = 0; i < argb.length; i++) {
+ args.get(i).promote(argb[i]).rval().accept(mv);
+ }
+ mv.visitMethodInsn(isInterface ? INVOKEINTERFACE : INVOKEVIRTUAL, im.recv.getInternalName(), im.name, im.type.getDescriptor(), isInterface);
+ })));
+ }
+ }
+ if (!Modifier.isAbstract(itype.getModifiers())) {
+ for (Constructor<?> c: itype.getDeclaredConstructors()) {
+ if (!Modifier.isPublic(c.getModifiers()))
+ continue;
+ String name = c.getName();
+ Type recv = Type.getType(c.getDeclaringClass());
+ Type type = Type.getType(c);
+
+ importConstructors.computeIfAbsent(name, k -> new ArrayList<>())
+ .add(new Import(recv, name, type,
+ (im, ro, args) -> new Generator.Value(im.recv,
+ (mv, o) -> {
+ Type[] argb = im.type.getArgumentTypes();
+
+ System.out.println("** " + im.type.getInternalName());
+ mv.visitTypeInsn(NEW, im.name().replace('.', '/'));
+ mv.visitInsn(DUP);
+ for (int i = 0; i < argb.length; i++) {
+ args.get(i).promote(argb[i]).rval().accept(mv);
+ }
+ mv.visitMethodInsn(INVOKESPECIAL, im.recv.getInternalName(), "<init>", im.type.getDescriptor(), false);
+ })));
+ }
}
}
+
+ Class<?> ptype = itype.getSuperclass();
+ if (ptype != null) {
+ superList.add(Type.getType(ptype));
+ }
+ itype = ptype;
}
+
+ superClass.put(superList.get(0), superList.toArray(Type[]::new));
}
static Type[] toArgumentTypes(List<Generator.Value> arga) {
return arga.stream().map(Generator.Value::type).toArray(Type[]::new);
}
- public Optional<Generator.Value> findStaticField(String name) {
+ public Optional<Generator.Value> findField(String name) {
return importStatic[IMPORT_FIELDS].values().stream()
.flatMap(m -> m.getOrDefault(name, List.of()).stream())
.findFirst()
- .map(m -> m.value());
+ .map(im -> im.value(null, null));
+ }
+
+ public Optional<Generator.Value> findField(String name, Generator.Value recv) {
+ return Stream.of(superClass.getOrDefault(recv.type(), new Type[0]))
+ .map(m -> importInstance[IMPORT_FIELDS].getOrDefault(m, Map.of()))
+ .flatMap(m -> m.getOrDefault(name, List.of()).stream())
+ .findFirst()
+ .map(im -> im.value(recv, null));
+ }
+
+ // find a static field
+ public Optional<Import> findField(String name, Type recv) {
+ return Stream.of(superClass.getOrDefault(recv, new Type[0]))
+ .flatMap(m -> Stream.ofNullable(importStatic[IMPORT_FIELDS].get(m)))
+ .flatMap(m -> m.getOrDefault(name, List.of()).stream())
+ .findFirst();
}
record Match(Import im, int score) implements Comparable<Match> {
Type[] argb = im.type.getArgumentTypes();
int score = 0;
- //System.out.printf("match arguments:\n %s\n %s\n",
- // String.join(", ", Stream.of(arga).map(Object::toString).toArray(String[]::new)),
- // String.join(", ", Stream.of(argb).map(Object::toString).toArray(String[]::new)));
-
+ System.out.printf("match arguments:\n %s\n %s\n",
+ String.join(", ", Stream.of(arga).map(Object::toString).toArray(String[]::new)),
+ String.join(", ", Stream.of(argb).map(Object::toString).toArray(String[]::new)));
if (arga.length == argb.length) {
for (int i = 0; i < arga.length; i++) {
Type a = arga[i], b = argb[i];
} else if (b.equals(Generator.STRING_TYPE)) {
// anything can be converted to string
score += 6;
+ } else if (b.equals(Type.getType(Object.class))) {
+ // anything can be converted to object
+ score += 8;
} else {
- //System.out.printf(" -> no match\n");
+ System.out.printf(" -> no match\n");
return new Match(im, -1);
}
}
+ if (arga.length == 0)
+ score = 10;
}
- //System.out.printf(" -> score %d\n", score);
+ System.out.printf(" -> score %d\n", score);
return new Match(im, score);
};
}
// Find a static method.
- public Optional<Generator.Value> findImport(String name, List<Generator.Value> args) {
+ public Optional<Import> findImport(String name, List<Generator.Value> args) {
Type[] arga = toArgumentTypes(args);
System.out.printf("find method: %s", name);
args.stream().forEach(a -> System.out.printf(" %s", a.type()));
System.out.println("> " + m);
return m;
})
- .map(m -> m.im.value(args));
+ .map(m -> m.im);
}
- // find non-static method
- // ?? may have to search fields
- public Optional<Generator.Value> findImport(Type recv, String name, List<Generator.Value> args) {
+ public Optional<Import> findConstructor(String name, List<Generator.Value> args) {
+ Type[] arga = toArgumentTypes(args);
+ System.out.printf("find new: %s", name);
+ args.stream().forEach(a -> System.out.printf(" %s", a.type()));
+ System.out.println();
+
+ return importConstructors.getOrDefault(name, List.of()).stream()
+ .peek(m -> System.out.println("? " + m))
+ .map(match(arga))
+ .filter(m -> m.score >= 0)
+ .sorted()
+ .findFirst()
+ .map(m -> {
+ System.out.println("> " + m);
+ return m;
+ })
+ .map(m -> m.im);
+ }
+
+ public Optional<Import> findImport(String name, Type recv, List<Generator.Value> args) {
Type[] arga = toArgumentTypes(args);
System.out.printf("find method: %s", name);
args.stream().forEach(a -> System.out.printf(" %s", a.type()));
System.out.println();
- // for (var map: importInstance[IMPORT_METHODS].entrySet()) {
- // System.out.println(map.getKey());
- // for (var im: map.getValue().getOrDefault(name, List.of())) {
- // System.out.printf(" ? %s\n", im);
- // }
- // }
-
- var table = importInstance[IMPORT_METHODS];
- List<Type> list = new ArrayList<>();
- Type ptype = recv;
- do {
- list.add(ptype);
- ptype = parentClass.get(ptype);
- } while (ptype != null);
-
- return list.stream()
- .peek(m-> System.out.println("c " + m))
- .map(m -> table.getOrDefault(m, Map.of()))
+
+ return Stream.of(superClass.getOrDefault(recv, new Type[0]))
+ .flatMap(m
+ -> Stream.concat(
+ Stream.ofNullable(importInstance[IMPORT_METHODS].get(m)),
+ Stream.ofNullable(importStatic[IMPORT_METHODS].get(m))))
.flatMap(m -> m.getOrDefault(name, List.of()).stream())
.peek(m -> System.out.println("? " + m))
.map(match(arga))
System.out.println("> " + m);
return m;
})
- .map(m -> m.im.value(args));
- /*
- return importInstance[IMPORT_METHODS].values().stream()
+ .map(m -> m.im);
+ }
+
+ // find non-static method
+ // ?? may have to search fields
+ // TODO: should just take recv and arg *types*
+ public Optional<Import> findImport(String name, Generator.Value recv, List<Generator.Value> args) {
+ Type[] arga = toArgumentTypes(args);
+ System.out.printf("find method: %s", name);
+ args.stream().forEach(a -> System.out.printf(" %s", a.type()));
+ System.out.println();
+
+ return Stream.of(superClass.getOrDefault(recv.type(), new Type[0]))
+ .peek(m -> System.out.println("c " + m))
+ .map(m -> importInstance[IMPORT_METHODS].getOrDefault(m, Map.of()))
.flatMap(m -> m.getOrDefault(name, List.of()).stream())
.peek(m -> System.out.println("? " + m))
.map(match(arga))
System.out.println("> " + m);
return m;
})
- .map(m -> m.im.value(args));
-*/
+ .map(m -> m.im);
}
void dump() {
+ for (var rmap: importConstructors.entrySet()) {
+ for (var im: rmap.getValue()) {
+ System.out.printf(" new %s\n", im);
+ }
+ }
+
for (var rmap: importInstance) {
for (var rme: rmap.entrySet()) {
for (var vme: rme.getValue().entrySet()) {