All Downloads are FREE. Search and download functionalities are using the official Maven repository.

brennus.asm.ASMMethodGenerator Maven / Gradle / Ivy

The newest version!
package brennus.asm;

import static brennus.model.ExistingType.INT;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import brennus.ImmutableList;
import brennus.LocalVarContext;
import brennus.MethodContext;
import brennus.model.BinaryExpression;
import brennus.model.CallConstructorExpression;
import brennus.model.CallConstructorStatement;
import brennus.model.CallMethodExpression;
import brennus.model.CaseBlockStatement;
import brennus.model.CaseStatement;
import brennus.model.CaseStatementVisitor;
import brennus.model.CastExpression;
import brennus.model.DefineVarStatement;
import brennus.model.Expression;
import brennus.model.ExpressionStatement;
import brennus.model.ExpressionVisitor;
import brennus.model.Field;
import brennus.model.FieldAccessType;
import brennus.model.GetExpression;
import brennus.model.GotoCaseStatement;
import brennus.model.GotoStatement;
import brennus.model.IfStatement;
import brennus.model.InstanceOfExpression;
import brennus.model.InstantiationExpression;
import brennus.model.LabelStatement;
import brennus.model.LiteralExpression;
import brennus.model.LocalVariableAccessType;
import brennus.model.NewArrayExpression;
import brennus.model.Parameter;
import brennus.model.ParameterAccessType;
import brennus.model.ReturnStatement;
import brennus.model.SetStatement;
import brennus.model.Statement;
import brennus.model.StatementVisitor;
import brennus.model.SwitchStatement;
import brennus.model.ThrowStatement;
import brennus.model.Type;
import brennus.model.UnaryExpression;
import brennus.model.VarAccessTypeVisitor;

import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LookupSwitchInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TableSwitchInsnNode;
import org.objectweb.asm.tree.TypeInsnNode;

class ASMMethodGenerator implements Opcodes, StatementVisitor {

  private final MethodContext methodContext;
  private final MethodByteCodeContext methodByteCodeContext;

  private LabelNode currentLabel;
  private LabelNode endLabel;

  public ASMMethodGenerator(MethodContext methodContext) {
    this.methodContext = methodContext;
    this.methodByteCodeContext = new MethodByteCodeContext(methodContext);
  }

  private Type visit(Expression expression) {
    methodByteCodeContext.incIndent();
    ASMExpressionVisitor expressionVisitor = new ASMExpressionVisitor(methodContext, methodByteCodeContext);
    expression.accept(expressionVisitor);
    methodByteCodeContext.decIndent();
    return expressionVisitor.getExpressionType();
  }

  @Override
  public void visit(ReturnStatement returnStatement) {
    methodByteCodeContext.incIndent("return exp");
    Type expressionType = visit(returnStatement.getExpression());
    methodByteCodeContext.decIndent();
    Type returnType = methodContext.getReturnType();
    methodByteCodeContext.handleConversion(expressionType, returnType);
    methodByteCodeContext.addReturn(returnType);
  }

  @Override
  public void visit(ExpressionStatement methodCallStatement) {
    visit(methodCallStatement.getExpression());
  }

  @Override
  public void visit(SwitchStatement switchStatement) {
    methodByteCodeContext.incIndent("switch exp");
    Type expressionType = visit(switchStatement.getExpression());
    methodByteCodeContext.handleConversion(expressionType, INT, "switch(exp): convert exp to int");
    methodByteCodeContext.decIndent();
    ImmutableList caseStatements = switchStatement.getCaseStatements();
    int minCase = Integer.MAX_VALUE;
    int maxCase = Integer.MIN_VALUE;
    Map cases = new HashMap();
    int[] values = new int[caseStatements.size()];
    final LabelNode[] labels = new LabelNode[caseStatements.size()];
    for (int i = 0; i < caseStatements.size(); i++) {
      CaseStatement caseStatement = caseStatements.get(i);
      int caseValue = ((Integer)((LiteralExpression)caseStatement.getExpression()).getValue()).intValue();
      values[i] = caseValue;
      minCase = Math.min(minCase, caseValue);
      maxCase = Math.max(caseValue, maxCase);
      cases.put(caseValue, caseStatement);
    }
    Arrays.sort(values);
    for (int i = 0; i < values.length; i++) {
      final int index = i;
      cases.get(values[i]).accept(new CaseStatementVisitor() {
        @Override
        public void visit(GotoCaseStatement gotoCaseStatement) {
          labels[index] = methodByteCodeContext.getLabelForSwitchGotoCase(gotoCaseStatement.getLabel());
        }
        @Override
        public void visit(CaseBlockStatement caseBlockStatement) {
          labels[index] = new LabelNode();
        }
      });
    }
    LabelNode defaultLabel = new LabelNode();
    endLabel = new LabelNode();
    // TODO: more cases
    if ((maxCase - minCase) == values.length - 1) {
      methodByteCodeContext.addInstruction(new TableSwitchInsnNode(minCase, maxCase, defaultLabel, labels), "switch(", switchStatement.getExpression(), ")");
    } else {
      methodByteCodeContext.addInstruction(new LookupSwitchInsnNode(defaultLabel, values, labels), "switch(", switchStatement.getExpression(), ")");
    }
    methodByteCodeContext.incIndent("switch");
    for (int i = 0; i < values.length; i++) {
      currentLabel = labels[i];
      cases.get(values[i]).accept(this);
      currentLabel = null;
    }
    if (switchStatement.getDefaultCaseStatement() != null) {
      currentLabel = defaultLabel;
      switchStatement.getDefaultCaseStatement().accept(this);
      currentLabel = null;
    } else {
      methodByteCodeContext.addInstruction(defaultLabel);
    }
    methodByteCodeContext.decIndent();
    methodByteCodeContext.addInstruction(endLabel, "switch end");
    endLabel = null;
  }

  @Override
  public void visit(CaseBlockStatement caseStatement) {
    Object value = caseStatement.getExpression() == null ? "default" : caseStatement.getliteralExpression().getValue();
    methodByteCodeContext.addLabel(caseStatement.getLine(), currentLabel, "case", value);
    // TODO: understand Frame ... :(
    // I use COMPUTE_FRAME in the generator
    //methodByteCodeContext.addInstruction(new FrameNode(F_SAME, 0, null, 0, null), "case", value);
    methodByteCodeContext.incIndent("case", value);
    for (Statement statement : caseStatement.getStatements()) {
      this.visit(statement);
    }
    if (caseStatement.isBreakCase()) {
      methodByteCodeContext.addInstruction(new JumpInsnNode(GOTO, endLabel), "break case");
    }
    methodByteCodeContext.decIndent();
  }

  @Override
  public void visit(GotoCaseStatement gotoCaseStatement) {
    // nothing to do
  }

  @Override
  public void visit(ThrowStatement throwStatement) {
    methodByteCodeContext.incIndent("throw exp");
    Type expressionType = visit(throwStatement.getExpression());
    methodByteCodeContext.decIndent();
    methodByteCodeContext.addInstruction(new InsnNode(ATHROW));
  }


  @Override
  public void visit(final SetStatement setStatement) {
    methodContext.getVarAccessType(setStatement.getTo()).accept(
    new VarAccessTypeVisitor() {
      private Type evalExp() {
        methodByteCodeContext.incIndent("set exp", setStatement.getTo());
        Type expressionType = ASMMethodGenerator.this.visit(setStatement.getExpression());
        methodByteCodeContext.decIndent();
        return expressionType;
      }
      public void visit(ParameterAccessType parameterAccessType) {
        Type expressionType = evalExp();
        // TODO: type support
        //        System.out.println(getExpression.getFieldName()+" "+param.getIndex());
        Parameter param = parameterAccessType.getParam();
        // TODO: long and double take 2 slots
        // TODO: check boxing
        methodByteCodeContext.store(
            param.getType(),
            // TODO: static methods don't have this
            methodByteCodeContext.getParamByteCodeIndex(param.getIndex()),
            "set param", setStatement.getTo());
      }
      public void visit(FieldAccessType fieldAccessType) {
        Field field = fieldAccessType.getField();
        if (field.isStatic()) {
          Type expressionType = evalExp();
          methodByteCodeContext.handleConversion(expressionType, field.getType());
          methodByteCodeContext.addInstruction(new FieldInsnNode(PUTSTATIC, methodContext.getClassIdentifier(), field.getName(), field.getSignature()), "set static", setStatement.getTo());
        } else {
          methodByteCodeContext.loadThis("set", setStatement.getTo());
          Type expressionType = evalExp();
          methodByteCodeContext.handleConversion(expressionType, field.getType());
          methodByteCodeContext.addInstruction(new FieldInsnNode(PUTFIELD, methodContext.getClassIdentifier(), field.getName(), field.getSignature()), "set", setStatement.getTo());
        }
      }
      public void visit(LocalVariableAccessType localVariableAccessType) {
        Type expressionType = evalExp();
        // TODO: type support
        // TODO: long and double take 2 slots
        methodByteCodeContext.store(
            expressionType,
            methodByteCodeContext.getLocalVariableByteCodeIndex(localVariableAccessType.getVarIndex()),
            "set local var", setStatement.getTo());
      }
    });
  }

  public MethodNode getMethodNode() {
    return methodByteCodeContext.getMethodNode();
  }

  public void visit(Statement statement) {
    methodByteCodeContext.addLineNumber(statement.getLine());
    methodByteCodeContext.incIndent(statement.getClass().getSimpleName());
    statement.accept(this);
    // TODO: statement that return nothing must pop the stack if needed
    // TODO: statements that return something should check there is something on the stack
    methodByteCodeContext.decIndent();
  }

  @Override
  public void visit(final IfStatement ifStatement) {
    methodByteCodeContext.incIndent("if");
    ifStatement.getExpression().accept(new ExpressionVisitor() {
      public void visit(BinaryExpression binaryExpression) {
        methodByteCodeContext.incIndent("if left");
        ASMMethodGenerator.this.visit(binaryExpression.getLeftExpression());
        methodByteCodeContext.decIndent();
        methodByteCodeContext.incIndent("if right");
        ASMMethodGenerator.this.visit(binaryExpression.getRightExpression());
        methodByteCodeContext.decIndent();
        methodByteCodeContext.incIndent(binaryExpression.getOperator().getRepresentation());
        // TODO: combine expression ! and =  etc
        switch (binaryExpression.getOperator()) {
        case EQUALS:
          // TODO handle types
          generateThenElse(IF_ICMPNE, ifStatement.getElseStatements(), ifStatement.getThenStatements());
          break;
        case GREATER_THAN:
          generateThenElse(IF_ICMPGT, ifStatement.getThenStatements(), ifStatement.getElseStatements());
          break;
        default:
          throw new UnsupportedOperationException("op: "+binaryExpression.getOperator());
        }
        methodByteCodeContext.decIndent();
      }

      public void visit(LiteralExpression literalExpression) {
        throw new UnsupportedOperationException();
      }

      public void visit(CallMethodExpression callMethodExpression) {
        methodByteCodeContext.incIndent("if call", callMethodExpression.getMethodName());
        ASMMethodGenerator.this.visit(callMethodExpression);
        // if (false) else then
        generateThenElse(IFEQ, ifStatement.getElseStatements(), ifStatement.getThenStatements());
        methodByteCodeContext.decIndent();
      }

      public void visit(GetExpression getFieldExpression) {
        methodByteCodeContext.incIndent("if get", getFieldExpression.getFieldName());
        ASMMethodGenerator.this.visit(getFieldExpression);
        // if (false) else then
        generateThenElse(IFEQ, ifStatement.getElseStatements(), ifStatement.getThenStatements());
        methodByteCodeContext.decIndent();
      }

      @Override
      public void visit(UnaryExpression unaryExpression) {
        throw new UnsupportedOperationException();
      }

      @Override
      public void visit(InstanceOfExpression instanceOfExpression) {
        methodByteCodeContext.incIndent("if instanceof", instanceOfExpression.getType());
        ASMMethodGenerator.this.visit(instanceOfExpression.getExpression());
        methodByteCodeContext.addInstruction(new TypeInsnNode(INSTANCEOF, instanceOfExpression.getType().getClassIdentifier()), "if");
        generateThenElse(IFEQ, ifStatement.getElseStatements(), ifStatement.getThenStatements());
        methodByteCodeContext.decIndent();
      }

      @Override
      public void visit(CastExpression castExpression) {
        throw new UnsupportedOperationException();
      }

      @Override
      public void visit(CallConstructorExpression callConstructorExpression) {
        throw new UnsupportedOperationException();
      }

      @Override
      public void visit(InstantiationExpression instantiationExpression) {
        throw new UnsupportedOperationException("NYI");
      }

      @Override
      public void visit(NewArrayExpression e) {
        throw new UnsupportedOperationException("NYI");
      }

    });
    methodByteCodeContext.decIndent();
  }

  private void generateThenElse(int jumpInst, ImmutableList thenStatements, ImmutableList elseStatements) {
    LabelNode thenNode = new LabelNode();
    LabelNode endNode = new LabelNode();
    methodByteCodeContext.addInstruction(new JumpInsnNode(jumpInst, thenNode), "IF exp GOTO label else keep going");
    methodByteCodeContext.incIndent();
    // else
    for (Statement statement : elseStatements) {
      visit(statement);
    }
    methodByteCodeContext.decIndent();
    methodByteCodeContext.addInstruction(new JumpInsnNode(GOTO, endNode), "endElse now Then");
    // then
    methodByteCodeContext.addLabel(thenNode, "then");
    methodByteCodeContext.incIndent();
    for (Statement statement : thenStatements) {
      visit(statement);
    }
    methodByteCodeContext.decIndent();
    methodByteCodeContext.addLabel(endNode, "endThen");
  }

  @Override
  public void visit(LabelStatement labelStatement) {
    methodByteCodeContext.addNamedLabel(labelStatement.getLine(), labelStatement.getName());
  }

  @Override
  public void visit(GotoStatement gotoStatement) {
    methodByteCodeContext.gotoLabel(gotoStatement.getName());
  }

  @Override
  public void visit(CallConstructorStatement callConstructorStatement) {
    visit(callConstructorStatement.getExpression());
  }

  @Override
  public void visit(DefineVarStatement defineVarStatement) {
    LocalVarContext context = methodContext.defineLocalVar(defineVarStatement.getType(), defineVarStatement.getVarName());
    methodByteCodeContext.defineLocalVar(context.getType(), context.getName(), context.getIndex());
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy