+package org.simantics.scl.compiler.constants.generic;
+
+import org.cojen.classfile.TypeDesc;
+import org.simantics.scl.compiler.common.exceptions.InternalCompilerError;
+import org.simantics.scl.compiler.internal.codegen.references.Val;
+import org.simantics.scl.compiler.internal.codegen.utils.ClassBuilder;
+import org.simantics.scl.compiler.internal.codegen.utils.MethodBuilder;
+import org.simantics.scl.compiler.top.SCLCompilerConfiguration;
+
+/**
+ * This interface represents a method for accessing the members of Java classes.
+ * This interface is implemented by <ul>
+ * <li>{@link StaticMethodRef}</li>
+ * <li>{@link ObjectMethodRef}</li>
+ * <li>{@link ConstructorRef}</li>
+ * <li>{@link StaticFieldRef}</li>
+ * <li>{@link FieldRef}</li>
+ * <li>{@link SetStaticFieldRef}</li>
+ * <li>{@link SetFieldRef}</li>
+ * </ul>
+ *
+ * It provides the method {@link #invoke(MethodBuilder, StackItem[], Val[])} for creating the Java byte code
+ * for calling the method or manipulating the field value.
+ *
+ * Parameter and return value type can be accessed using {@link #getParameterTypes()} and {@link #getReturnType()}.
+ */
+public interface MethodRef {
+
+ /**
+ * Build code for invoking the referenced function using a given MethodBuilder.
+ * @param mb a method builder
+ * @param stackItems a set of StackItem objects into which the call parameters are pushed
+ * @param parameters the method call parameters
+ */
+ void invoke(MethodBuilder mb, StackItem[] stackItems, Val[] parameters);
+
+ /**
+ * Get the parameter types for a method. For (non-static) object methods and field accessors, the first
+ * parameter represents the object itself, as passed in the {@code this} variable.
+ */
+ public TypeDesc[] getParameterTypes();
+
+ /**
+ * Get the return type of the method.
+ */
+ public TypeDesc getReturnType();
+
+ /**
+ * This class represents a static Java class method.
+ */
+ public static class StaticMethodRef implements MethodRef {
+
+ String className;
+ String methodName;
+ TypeDesc ret;
+ TypeDesc[] params;
+
+ public StaticMethodRef(String className, String methodName,
+ TypeDesc ret, TypeDesc[] params) {
+ this.className = className;
+ this.methodName = methodName;
+ this.ret = ret;
+ this.params = params;
+ }
+
+ @Override
+ public void invoke(MethodBuilder mb, StackItem[] stackItems, Val[] parameters) {
+ if(SCLCompilerConfiguration.DEBUG)
+ if(stackItems.length != params.length)
+ throw new InternalCompilerError();
+ for(StackItem stackItem : stackItems)
+ stackItem.push(mb, parameters);
+ mb.invokeStatic(className, methodName, ret, params);
+ }
+
+ @Override
+ public TypeDesc[] getParameterTypes() {
+ return params;
+ }
+
+ @Override
+ public TypeDesc getReturnType() {
+ return ret;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder b = new StringBuilder();
+ b.append("public static ");
+ b.append(ret.getFullName());
+ b.append(" ");
+ b.append(methodName);
+ b.append("(");
+ boolean first = true;
+ for(TypeDesc param : params) {
+ if(first)
+ first = false;
+ else
+ b.append(", ");
+ b.append(param.getFullName());
+ }
+ b.append(")");
+ return b.toString();
+ }
+
+ @Override
+ public String getName() {
+ return className + "." + methodName;
+ }
+ }
+
+ /**
+ * This class represents a (non-static) java object method.
+ */
+ public static class ObjectMethodRef implements MethodRef {
+
+ boolean isInterface;
+ String className;
+ String methodName;
+ TypeDesc ret;
+ TypeDesc[] params;
+ TypeDesc[] realParams;
+
+ public ObjectMethodRef(boolean isInterface, String className, String methodName,
+ TypeDesc ret, TypeDesc[] params) {
+ this.isInterface = isInterface;
+ ClassBuilder.checkClassName(className);
+ this.className = className;
+ this.methodName = methodName;
+ this.ret = ret;
+ this.params = params;
+ this.realParams = new TypeDesc[params.length+1];
+ realParams[0] = TypeDesc.forClass(className);
+ for(int i=0;i<params.length;++i)
+ realParams[i+1] = params[i];
+ }
+
+ @Override
+ public void invoke(MethodBuilder mb, StackItem[] stackItems, Val[] parameters) {
+ if(SCLCompilerConfiguration.DEBUG)
+ if(stackItems.length != realParams.length)
+ throw new InternalCompilerError();
+ for(StackItem stackItem : stackItems)
+ stackItem.push(mb, parameters);
+ if(isInterface)
+ mb.invokeInterface(className, methodName, ret, params);
+ else
+ mb.invokeVirtual(className, methodName, ret, params);
+ }
+
+ @Override
+ public TypeDesc[] getParameterTypes() {
+ return realParams;
+ }
+
+ @Override
+ public TypeDesc getReturnType() {
+ return ret;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder b = new StringBuilder();
+ b.append("public ");
+ b.append(ret.getFullName());
+ b.append(" ");
+ b.append(methodName);
+ b.append("(");
+ boolean first = true;
+ for(TypeDesc param : params) {
+ if(first)
+ first = false;
+ else
+ b.append(", ");
+ b.append(param.getFullName());
+ }
+ b.append(")");
+ return b.toString();
+ }
+
+ @Override
+ public String getName() {
+ return className + "." + methodName;
+ }
+ }
+
+ /**
+ * This class represents a Java constructor.
+ */
+ public static class ConstructorRef implements MethodRef {
+
+ String className;
+ TypeDesc[] params;
+ TypeDesc ret;
+
+ public ConstructorRef(String className, TypeDesc[] params) {
+ this.className = className;
+ this.params = params;
+ this.ret = TypeDesc.forClass(className);
+ }
+
+ @Override
+ public void invoke(MethodBuilder mb, StackItem[] stackItems, Val[] parameters) {
+ if(SCLCompilerConfiguration.DEBUG)
+ if(stackItems.length != params.length)
+ throw new InternalCompilerError();
+ mb.newObject(ret);
+ mb.dup();
+ for(StackItem stackItem : stackItems)
+ stackItem.push(mb, parameters);
+ mb.invokeConstructor(className, params);
+ }
+
+ @Override
+ public TypeDesc[] getParameterTypes() {
+ return params;
+ }
+
+ @Override
+ public TypeDesc getReturnType() {
+ return ret;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder b = new StringBuilder();
+ b.append("public ");
+ b.append(className);
+ b.append("(");
+ boolean first = true;
+ for(TypeDesc param : params) {
+ if(first)
+ first = false;
+ else
+ b.append(", ");
+ b.append(param.getFullName());
+ }
+ b.append(")");
+ return b.toString();
+ }
+
+ @Override
+ public String getName() {
+ return className + ".<init>";
+ }
+ }
+
+ /**
+ * This class represents a read access to a static Java field, represented as a zero-arity {@link MethodRef}.
+ */
+ public static class StaticFieldRef implements MethodRef {
+
+ String className;
+ String fieldName;
+ TypeDesc ret;
+
+ public StaticFieldRef(String className, String fieldName, TypeDesc ret) {
+ this.className = className;
+ this.fieldName = fieldName;
+ this.ret = ret;
+ }
+
+ @Override
+ public void invoke(MethodBuilder mb, StackItem[] stackItems, Val[] parameters) {
+ if(SCLCompilerConfiguration.DEBUG)
+ if(stackItems.length != 0)
+ throw new InternalCompilerError();
+ mb.loadStaticField(className, fieldName, ret);
+ }
+
+ @Override
+ public TypeDesc[] getParameterTypes() {
+ return ClassRef.NO_PARAMS;
+ }
+
+ @Override
+ public TypeDesc getReturnType() {
+ return ret;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder b = new StringBuilder();
+ b.append("public static ");
+ b.append(ret.getFullName());
+ b.append(" ");
+ b.append(fieldName);
+ return b.toString();
+ }
+
+ @Override
+ public String getName() {
+ return className + "." + fieldName;
+ }
+ }
+
+ /**
+ * This class represents read access to a Java (non-static) object field as a one-parameter {@link MethodRef}.
+ */
+ public static class FieldRef implements MethodRef {
+
+ String className;
+ String fieldName;
+ TypeDesc[] params;
+ TypeDesc ret;
+
+ public FieldRef(String className, String fieldName, TypeDesc ret) {
+ this.className = className;
+ this.fieldName = fieldName;
+ this.ret = ret;
+ this.params = new TypeDesc[] {TypeDesc.forClass(className)};
+ }
+
+ @Override
+ public void invoke(MethodBuilder mb, StackItem[] stackItems, Val[] parameters) {
+ if(SCLCompilerConfiguration.DEBUG)
+ if(stackItems.length != 1)
+ throw new InternalCompilerError();
+ stackItems[0].push(mb, parameters);
+ mb.loadField(className, fieldName, ret);
+ }
+
+ @Override
+ public TypeDesc[] getParameterTypes() {
+ return params;
+ }
+
+ @Override
+ public TypeDesc getReturnType() {
+ return ret;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder b = new StringBuilder();
+ b.append("public ");
+ b.append(ret.getFullName());
+ b.append(" ");
+ b.append(fieldName);
+ return b.toString();
+ }
+
+ @Override
+ public String getName() {
+ return className + "." + fieldName;
+ }
+ }
+
+ /**
+ * This class represents a method for setting a static Java field value as a one-parameter {@link MethodRef}
+ */
+ public static class SetStaticFieldRef implements MethodRef {
+
+ String className;
+ String fieldName;
+ TypeDesc ret;
+ TypeDesc[] params;
+
+ public SetStaticFieldRef(String className, String fieldName, TypeDesc ret) {
+ this.className = className;
+ this.fieldName = fieldName;
+ this.ret = ret;
+ this.params = new TypeDesc[] {ret};
+ }
+
+ @Override
+ public void invoke(MethodBuilder mb, StackItem[] stackItems, Val[] parameters) {
+ if(SCLCompilerConfiguration.DEBUG)
+ if(stackItems.length != 1)
+ throw new InternalCompilerError();
+ stackItems[0].push(mb, parameters);
+ mb.storeStaticField(className, fieldName, ret);
+ }
+
+ @Override
+ public TypeDesc[] getParameterTypes() {
+ return params;
+ }
+
+ @Override
+ public TypeDesc getReturnType() {
+ return TypeDesc.VOID;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder b = new StringBuilder();
+ b.append("public static ");
+ b.append(ret.getFullName());
+ b.append(" ");
+ b.append(fieldName);
+ return b.toString();
+ }
+
+ @Override
+ public String getName() {
+ return className + ".<set>" + fieldName;
+ }
+ }
+
+ /**
+ * This class represents a method for setting the value of a Java (non-static) object field as a two-parameter
+ * {@link MethodRef}. The first parameter is the object reference and the second parameter is the field value.
+ */
+ public static class SetFieldRef implements MethodRef {
+
+ String className;
+ String fieldName;
+ TypeDesc ret;
+ TypeDesc[] params;
+
+ public SetFieldRef(String className, String fieldName, TypeDesc ret) {
+ this.className = className;
+ this.fieldName = fieldName;
+ this.ret = ret;
+ this.params = new TypeDesc[] {TypeDesc.forClass(className), ret};
+ }
+
+ @Override
+ public void invoke(MethodBuilder mb, StackItem[] stackItems, Val[] parameters) {
+ if(SCLCompilerConfiguration.DEBUG)
+ if(stackItems.length != 2)
+ throw new InternalCompilerError();
+ stackItems[0].push(mb, parameters);
+ stackItems[1].push(mb, parameters);
+ mb.storeField(className, fieldName, ret);
+ }
+
+ @Override
+ public TypeDesc[] getParameterTypes() {
+ return params;
+ }
+
+ @Override
+ public TypeDesc getReturnType() {
+ return TypeDesc.VOID;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder b = new StringBuilder();
+ b.append("public ");
+ b.append(ret.getFullName());
+ b.append(" ");
+ b.append(fieldName);
+ return b.toString();
+ }
+
+ @Override
+ public String getName() {
+ return className + ".<set>" + fieldName;
+ }
+ }
+
+ public abstract String getName();
+}