Exemple #1
0
  private void atWhileStmnt(Stmnt st, boolean notDo) throws CompileError {
    ArrayList prevBreakList = breakList;
    ArrayList prevContList = continueList;
    breakList = new ArrayList();
    continueList = new ArrayList();

    ASTree expr = st.head();
    Stmnt body = (Stmnt) st.tail();

    int pc = 0;
    if (notDo) {
      bytecode.addOpcode(Opcode.GOTO);
      pc = bytecode.currentPc();
      bytecode.addIndex(0);
    }

    int pc2 = bytecode.currentPc();
    if (body != null) body.accept(this);

    int pc3 = bytecode.currentPc();
    if (notDo) bytecode.write16bit(pc, pc3 - pc + 1);

    boolean alwaysBranch = compileBooleanExpr(true, expr);
    if (alwaysBranch) {
      bytecode.addOpcode(Opcode.GOTO);
      alwaysBranch = breakList.size() == 0;
    }

    bytecode.addIndex(pc2 - bytecode.currentPc() + 1);
    patchGoto(breakList, bytecode.currentPc());
    patchGoto(continueList, pc3);
    continueList = prevContList;
    breakList = prevBreakList;
    hasReturned = alwaysBranch;
  }
Exemple #2
0
  private static CtMethod delegator0(CtMethod delegate, CtClass declaring)
      throws CannotCompileException, NotFoundException {
    MethodInfo deleInfo = delegate.getMethodInfo2();
    String methodName = deleInfo.getName();
    String desc = deleInfo.getDescriptor();
    ConstPool cp = declaring.getClassFile2().getConstPool();
    MethodInfo minfo = new MethodInfo(cp, methodName, desc);
    minfo.setAccessFlags(deleInfo.getAccessFlags());

    ExceptionsAttribute eattr = deleInfo.getExceptionsAttribute();
    if (eattr != null) minfo.setExceptionsAttribute((ExceptionsAttribute) eattr.copy(cp, null));

    Bytecode code = new Bytecode(cp, 0, 0);
    boolean isStatic = Modifier.isStatic(delegate.getModifiers());
    CtClass deleClass = delegate.getDeclaringClass();
    CtClass[] params = delegate.getParameterTypes();
    int s;
    if (isStatic) {
      s = code.addLoadParameters(params, 0);
      code.addInvokestatic(deleClass, methodName, desc);
    } else {
      code.addLoad(0, deleClass);
      s = code.addLoadParameters(params, 1);
      code.addInvokespecial(deleClass, methodName, desc);
    }

    code.addReturn(delegate.getReturnType());
    code.setMaxLocals(++s);
    code.setMaxStack(s < 2 ? 2 : s); // for a 2-word return value
    minfo.setCodeAttribute(code.toCodeAttribute());
    return new CtMethod(minfo, declaring);
  }
Exemple #3
0
  public void atBinExpr(BinExpr expr) throws CompileError {
    int token = expr.getOperator();

    /* arithmetic operators: +, -, *, /, %, |, ^, &, <<, >>, >>>
     */
    int k = lookupBinOp(token);
    if (k >= 0) {
      expr.oprand1().accept(this);
      ASTree right = expr.oprand2();
      if (right == null) return; // see TypeChecker.atBinExpr().

      int type1 = exprType;
      int dim1 = arrayDim;
      String cname1 = className;
      right.accept(this);
      if (dim1 != arrayDim) throw new CompileError("incompatible array types");

      if (token == '+' && dim1 == 0 && (type1 == CLASS || exprType == CLASS))
        atStringConcatExpr(expr, type1, dim1, cname1);
      else atArithBinExpr(expr, token, k, type1);
    } else {
      /* equation: &&, ||, ==, !=, <=, >=, <, >
       */
      if (!booleanExpr(true, expr)) {
        bytecode.addIndex(7);
        bytecode.addIconst(0); // false
        bytecode.addOpcode(Opcode.GOTO);
        bytecode.addIndex(4);
      }

      bytecode.addIconst(1); // true
    }
  }
Exemple #4
0
  public void atKeyword(Keyword k) throws CompileError {
    arrayDim = 0;
    int token = k.get();
    switch (token) {
      case TRUE:
        bytecode.addIconst(1);
        exprType = BOOLEAN;
        break;
      case FALSE:
        bytecode.addIconst(0);
        exprType = BOOLEAN;
        break;
      case NULL:
        bytecode.addOpcode(ACONST_NULL);
        exprType = NULL;
        break;
      case THIS:
      case SUPER:
        if (inStaticMethod)
          throw new CompileError("not-available: " + (token == THIS ? "this" : "super"));

        bytecode.addAload(0);
        exprType = CLASS;
        if (token == THIS) className = getThisName();
        else className = getSuperName();
        break;
      default:
        fatal();
    }
  }
Exemple #5
0
  private void atStringConcatExpr(Expr expr, int type1, int dim1, String cname1)
      throws CompileError {
    int type2 = exprType;
    int dim2 = arrayDim;
    boolean type2Is2 = is2word(type2, dim2);
    boolean type2IsString = (type2 == CLASS && jvmJavaLangString.equals(className));

    if (type2Is2) convToString(type2, dim2);

    if (is2word(type1, dim1)) {
      bytecode.addOpcode(DUP_X2);
      bytecode.addOpcode(POP);
    } else bytecode.addOpcode(SWAP);

    // even if type1 is String, the left operand might be null.
    convToString(type1, dim1);
    bytecode.addOpcode(SWAP);

    if (!type2Is2 && !type2IsString) convToString(type2, dim2);

    bytecode.addInvokevirtual(javaLangString, "concat", "(Ljava/lang/String;)Ljava/lang/String;");
    exprType = CLASS;
    arrayDim = 0;
    className = jvmJavaLangString;
  }
Exemple #6
0
  private void atBreakStmnt(Stmnt st, boolean notCont) throws CompileError {
    if (st.head() != null) throw new CompileError("sorry, not support labeled break or continue");

    bytecode.addOpcode(Opcode.GOTO);
    Integer pc = new Integer(bytecode.currentPc());
    bytecode.addIndex(0);
    if (notCont) breakList.add(pc);
    else continueList.add(pc);
  }
Exemple #7
0
 public void atDoubleConst(DoubleConst d) throws CompileError {
   arrayDim = 0;
   if (d.getType() == DoubleConstant) {
     exprType = DOUBLE;
     bytecode.addDconst(d.get());
   } else {
     exprType = FLOAT;
     bytecode.addFconst((float) d.get());
   }
 }
Exemple #8
0
  private void atIfStmnt(Stmnt st) throws CompileError {
    ASTree expr = st.head();
    Stmnt thenp = (Stmnt) st.tail().head();
    Stmnt elsep = (Stmnt) st.tail().tail().head();
    if (compileBooleanExpr(false, expr)) {
      hasReturned = false;
      if (elsep != null) elsep.accept(this);

      return;
    }

    int pc = bytecode.currentPc();
    int pc2 = 0;
    bytecode.addIndex(0); // correct later

    hasReturned = false;
    if (thenp != null) thenp.accept(this);

    boolean thenHasReturned = hasReturned;
    hasReturned = false;

    if (elsep != null && !thenHasReturned) {
      bytecode.addOpcode(Opcode.GOTO);
      pc2 = bytecode.currentPc();
      bytecode.addIndex(0);
    }

    bytecode.write16bit(pc, bytecode.currentPc() - pc + 1);
    if (elsep != null) {
      elsep.accept(this);
      if (!thenHasReturned) bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1);

      hasReturned = thenHasReturned && hasReturned;
    }
  }
Exemple #9
0
 public void atIntConst(IntConst i) throws CompileError {
   arrayDim = 0;
   long value = i.get();
   int type = i.getType();
   if (type == IntConstant || type == CharConstant) {
     exprType = (type == IntConstant ? INT : CHAR);
     bytecode.addIconst((int) value);
   } else {
     exprType = LONG;
     bytecode.addLconst(value);
   }
 }
Exemple #10
0
  public void atArrayPlusPlus(int token, boolean isPost, Expr expr, boolean doDup)
      throws CompileError {
    arrayAccess(expr.oprand1(), expr.oprand2());
    int t = exprType;
    int dim = arrayDim;
    if (dim > 0) badType(expr);

    bytecode.addOpcode(DUP2);
    bytecode.addOpcode(getArrayReadOp(t, arrayDim));
    int dup_code = is2word(t, dim) ? DUP2_X2 : DUP_X2;
    atPlusPlusCore(dup_code, doDup, token, isPost, expr);
    bytecode.addOpcode(getArrayWriteOp(t, dim));
  }
Exemple #11
0
  /* op is either =, %=, &=, *=, /=, +=, -=, ^=, |=, <<=, >>=, or >>>=.
   *
   * expr and var can be null.
   */
  private void atVariableAssign(
      Expr expr, int op, Variable var, Declarator d, ASTree right, boolean doDup)
      throws CompileError {
    int varType = d.getType();
    int varArray = d.getArrayDim();
    String varClass = d.getClassName();
    int varNo = getLocalVar(d);

    if (op != '=') atVariable(var);

    // expr is null if the caller is atDeclarator().
    if (expr == null && right instanceof ArrayInit)
      atArrayVariableAssign((ArrayInit) right, varType, varArray, varClass);
    else atAssignCore(expr, op, right, varType, varArray, varClass);

    if (doDup)
      if (is2word(varType, varArray)) bytecode.addOpcode(DUP2);
      else bytecode.addOpcode(DUP);

    if (varArray > 0) bytecode.addAstore(varNo);
    else if (varType == DOUBLE) bytecode.addDstore(varNo);
    else if (varType == FLOAT) bytecode.addFstore(varNo);
    else if (varType == LONG) bytecode.addLstore(varNo);
    else if (isRefType(varType)) bytecode.addAstore(varNo);
    else bytecode.addIstore(varNo);

    exprType = varType;
    arrayDim = varArray;
    className = varClass;
  }
Exemple #12
0
  /**
   * Creates a public setter method. The setter method assigns the value of the first parameter to
   * the specified field in the class to which this method is added. The created method is not
   * static even if the field is static. You may not change it to be static by <code>setModifiers()
   * </code> in <code>CtBehavior</code>.
   *
   * @param methodName the name of the setter
   * @param field the field accessed.
   */
  public static CtMethod setter(String methodName, CtField field) throws CannotCompileException {
    FieldInfo finfo = field.getFieldInfo2();
    String fieldType = finfo.getDescriptor();
    String desc = "(" + fieldType + ")V";
    ConstPool cp = finfo.getConstPool();
    MethodInfo minfo = new MethodInfo(cp, methodName, desc);
    minfo.setAccessFlags(AccessFlag.PUBLIC);

    Bytecode code = new Bytecode(cp, 3, 3);
    try {
      String fieldName = finfo.getName();
      if ((finfo.getAccessFlags() & AccessFlag.STATIC) == 0) {
        code.addAload(0);
        code.addLoad(1, field.getType());
        code.addPutfield(Bytecode.THIS, fieldName, fieldType);
      } else {
        code.addLoad(1, field.getType());
        code.addPutstatic(Bytecode.THIS, fieldName, fieldType);
      }

      code.addReturn(null);
    } catch (NotFoundException e) {
      throw new CannotCompileException(e);
    }

    minfo.setCodeAttribute(code.toCodeAttribute());
    return new CtMethod(minfo, field.getDeclaringClass());
  }
Exemple #13
0
 protected void patchGoto(ArrayList list, int targetPc) {
   int n = list.size();
   for (int i = 0; i < n; ++i) {
     int pc = ((Integer) list.get(i)).intValue();
     bytecode.write16bit(pc, targetPc - pc + 1);
   }
 }
Exemple #14
0
  protected final void atReturnStmnt2(ASTree result) throws CompileError {
    int op;
    if (result == null) op = Opcode.RETURN;
    else {
      compileExpr(result);
      if (arrayDim > 0) op = ARETURN;
      else {
        int type = exprType;
        if (type == DOUBLE) op = DRETURN;
        else if (type == FLOAT) op = FRETURN;
        else if (type == LONG) op = LRETURN;
        else if (isRefType(type)) op = ARETURN;
        else op = IRETURN;
      }
    }

    for (ReturnHook har = returnHooks; har != null; har = har.next)
      if (har.doit(bytecode, op)) {
        hasReturned = true;
        return;
      }

    bytecode.addOpcode(op);
    hasReturned = true;
  }
Exemple #15
0
 public void atInstanceOfExpr(InstanceOfExpr expr) throws CompileError {
   String cname = resolveClassName(expr.getClassName());
   String toClass = checkCastExpr(expr, cname);
   bytecode.addInstanceof(toClass);
   exprType = BOOLEAN;
   arrayDim = 0;
 }
Exemple #16
0
  private void atThrowStmnt(Stmnt st) throws CompileError {
    ASTree e = st.getLeft();
    compileExpr(e);
    if (exprType != CLASS || arrayDim > 0) throw new CompileError("bad throw statement");

    bytecode.addOpcode(ATHROW);
    hasReturned = true;
  }
 @Override
 public void visitEnd() {
   for (Map.Entry<Handle, Handle> entry : lambdaAccessToImplMethods.entrySet()) {
     Handle accessMethod = entry.getKey();
     Handle implMethod = entry.getValue();
     Bytecode.generateDelegateMethod(cv, ACC_STATIC | ACC_SYNTHETIC, accessMethod, implMethod);
   }
   super.visitEnd();
 }
Exemple #18
0
  public void atCondExpr(CondExpr expr) throws CompileError {
    booleanExpr(false, expr.condExpr());
    int pc = bytecode.currentPc();
    bytecode.addIndex(0); // correct later
    expr.thenExpr().accept(this);
    int dim1 = arrayDim;
    bytecode.addOpcode(Opcode.GOTO);
    int pc2 = bytecode.currentPc();
    bytecode.addIndex(0);
    bytecode.write16bit(pc, bytecode.currentPc() - pc + 1);
    expr.elseExpr().accept(this);
    if (dim1 != arrayDim) throw new CompileError("type mismatch in ?:");

    bytecode.write16bit(pc2, bytecode.currentPc() - pc2 + 1);
  }
Exemple #19
0
 public void atCastExpr(CastExpr expr) throws CompileError {
   String cname = resolveClassName(expr.getClassName());
   String toClass = checkCastExpr(expr, cname);
   int srcType = exprType;
   exprType = expr.getType();
   arrayDim = expr.getArrayDim();
   className = cname;
   if (toClass == null) atNumCastExpr(srcType, exprType); // built-in type
   else bytecode.addCheckcast(toClass);
 }
Exemple #20
0
  private void atForStmnt(Stmnt st) throws CompileError {
    ArrayList prevBreakList = breakList;
    ArrayList prevContList = continueList;
    breakList = new ArrayList();
    continueList = new ArrayList();

    Stmnt init = (Stmnt) st.head();
    ASTList p = st.tail();
    ASTree expr = p.head();
    p = p.tail();
    Stmnt update = (Stmnt) p.head();
    Stmnt body = (Stmnt) p.tail();

    if (init != null) init.accept(this);

    int pc = bytecode.currentPc();
    int pc2 = 0;
    if (expr != null) {
      if (compileBooleanExpr(false, expr)) {
        // in case of "for (...; false; ...)"
        continueList = prevContList;
        breakList = prevBreakList;
        hasReturned = false;
        return;
      }

      pc2 = bytecode.currentPc();
      bytecode.addIndex(0);
    }

    if (body != null) body.accept(this);

    int pc3 = bytecode.currentPc();
    if (update != null) update.accept(this);

    bytecode.addOpcode(Opcode.GOTO);
    bytecode.addIndex(pc - bytecode.currentPc() + 1);

    int pc4 = bytecode.currentPc();
    if (expr != null) bytecode.write16bit(pc2, pc4 - pc2 + 1);

    patchGoto(breakList, pc4);
    patchGoto(continueList, pc3);
    continueList = prevContList;
    breakList = prevBreakList;
    hasReturned = false;
  }
 /**
  * Gets the method bridged to by a {@linkplain ResolvedJavaMethod#isBridge() bridge} method. The
  * value returned is the method called by {@code method} that has the same name as {@code bridge}.
  *
  * @param bridge a bridge method
  * @return the method called by {@code bridge} whose name is the same as {@code bridge.getName()}
  */
 public static ResolvedJavaMethod getBridgedMethod(ResolvedJavaMethod bridge) {
   assert bridge.isBridge();
   Bytecode code = new ResolvedJavaMethodBytecode(bridge);
   BytecodeStream stream = new BytecodeStream(code.getCode());
   int opcode = stream.currentBC();
   ResolvedJavaMethod bridged = null;
   while (opcode != Bytecodes.END) {
     switch (opcode) {
       case INVOKEVIRTUAL:
       case INVOKESPECIAL:
       case INVOKESTATIC:
       case INVOKEINTERFACE:
         {
           int cpi = stream.readCPI();
           ConstantPool cp = code.getConstantPool();
           cp.loadReferencedType(cpi, opcode);
           ResolvedJavaMethod method = (ResolvedJavaMethod) cp.lookupMethod(cpi, opcode);
           if (method.getName().equals(bridge.getName())) {
             if (!assertionsEnabled()) {
               return method;
             }
             assert bridged == null || bridged.equals(method)
                 : String.format(
                     "Found calls to different methods named %s in bridge method %s%n  callee 1: %s%n  callee 2: %s",
                     bridge.getName(),
                     bridge.format("%R %H.%n(%P)"),
                     bridged.format("%R %H.%n(%P)"),
                     method.format("%R %H.%n(%P)"));
             bridged = method;
           }
           break;
         }
     }
     stream.next();
     opcode = stream.currentBC();
   }
   if (bridged == null) {
     throw new InternalError("Couldn't find method bridged by " + bridge.format("%R %H.%n(%P)"));
   }
   return bridged;
 }
Exemple #22
0
  void atNumCastExpr(int srcType, int destType) throws CompileError {
    if (srcType == destType) return;

    int op, op2;
    int stype = typePrecedence(srcType);
    int dtype = typePrecedence(destType);
    if (0 <= stype && stype < 3) op = castOp[stype * 4 + dtype];
    else op = NOP;

    if (destType == DOUBLE) op2 = I2D;
    else if (destType == FLOAT) op2 = I2F;
    else if (destType == LONG) op2 = I2L;
    else if (destType == SHORT) op2 = I2S;
    else if (destType == CHAR) op2 = I2C;
    else if (destType == BYTE) op2 = I2B;
    else op2 = NOP;

    if (op != NOP) bytecode.addOpcode(op);

    if (op == NOP || op == L2I || op == F2I || op == D2I) if (op2 != NOP) bytecode.addOpcode(op2);
  }
Exemple #23
0
  private void atStringPlusEq(Expr expr, int type, int dim, String cname, ASTree right)
      throws CompileError {
    if (!jvmJavaLangString.equals(cname)) badAssign(expr);

    convToString(type, dim); // the value might be null.
    right.accept(this);
    convToString(exprType, arrayDim);
    bytecode.addInvokevirtual(javaLangString, "concat", "(Ljava/lang/String;)Ljava/lang/String;");
    exprType = CLASS;
    arrayDim = 0;
    className = jvmJavaLangString;
  }
  @Override
  public String toString() {
    String s = head.toString();
    if (s.length() > 100) s = s.substring(0, 100) + "...";

    if (tail != null)
      // se il bytecode contiene la stringa vuota, non stampiamo un newline.
      // Utile per il bytecode che scompare dal print-out.
      if (s.length() > 0) return s + "\n" + tail.toString();
      else return tail.toString();
    else return s;
  }
Exemple #25
0
  public void atVariable(Variable v) throws CompileError {
    Declarator d = v.getDeclarator();
    exprType = d.getType();
    arrayDim = d.getArrayDim();
    className = d.getClassName();
    int var = getLocalVar(d);

    if (arrayDim > 0) bytecode.addAload(var);
    else
      switch (exprType) {
        case CLASS:
          bytecode.addAload(var);
          break;
        case LONG:
          bytecode.addLload(var);
          break;
        case FLOAT:
          bytecode.addFload(var);
          break;
        case DOUBLE:
          bytecode.addDload(var);
          break;
        default: // BOOLEAN, BYTE, CHAR, SHORT, INT
          bytecode.addIload(var);
          break;
      }
  }
  /**
   * Replace a method body with a new method body wrapping the given method.
   *
   * @param mbody the wrapped method
   * @param constParam the constant parameter given to the wrapped method (maybe <code>null</code>).
   * @see
   *     CtNewMethod#wrapped(CtClass,String,CtClass[],CtClass[],CtMethod,CtMethod.ConstParameter,CtClass)
   */
  public void setWrappedBody(CtMethod mbody, ConstParameter constParam)
      throws CannotCompileException {
    declaringClass.checkModify();

    CtClass clazz = getDeclaringClass();
    CtClass[] params;
    CtClass retType;
    try {
      params = getParameterTypes();
      retType = getReturnType();
    } catch (NotFoundException e) {
      throw new CannotCompileException(e);
    }

    Bytecode code =
        CtNewWrappedMethod.makeBody(
            clazz, clazz.getClassFile2(), mbody, params, retType, constParam);
    CodeAttribute cattr = code.toCodeAttribute();
    methodInfo.setCodeAttribute(cattr);
    methodInfo.setAccessFlags(methodInfo.getAccessFlags() & ~AccessFlag.ABSTRACT);
    // rebuilding a stack map table is not needed.
  }
Exemple #27
0
  /** @param isCons true if super() must be called. false if the method is a class initializer. */
  public void atMethodBody(Stmnt s, boolean isCons, boolean isVoid) throws CompileError {
    if (s == null) return;

    if (isCons && needsSuperCall(s)) insertDefaultSuperCall();

    hasReturned = false;
    s.accept(this);
    if (!hasReturned)
      if (isVoid) {
        bytecode.addOpcode(Opcode.RETURN);
        hasReturned = true;
      } else throw new CompileError("no return statement");
  }
Exemple #28
0
  public void atStmnt(Stmnt st) throws CompileError {
    if (st == null) return; // empty

    int op = st.getOperator();
    if (op == EXPR) {
      ASTree expr = st.getLeft();
      doTypeCheck(expr);
      if (expr instanceof AssignExpr) atAssignExpr((AssignExpr) expr, false);
      else if (isPlusPlusExpr(expr)) {
        Expr e = (Expr) expr;
        atPlusPlus(e.getOperator(), e.oprand1(), e, false);
      } else {
        expr.accept(this);
        if (is2word(exprType, arrayDim)) bytecode.addOpcode(POP2);
        else if (exprType != VOID) bytecode.addOpcode(POP);
      }
    } else if (op == DECL || op == BLOCK) {
      ASTList list = st;
      while (list != null) {
        ASTree h = list.head();
        list = list.tail();
        if (h != null) h.accept(this);
      }
    } else if (op == IF) atIfStmnt(st);
    else if (op == WHILE || op == DO) atWhileStmnt(st, op == WHILE);
    else if (op == FOR) atForStmnt(st);
    else if (op == BREAK || op == CONTINUE) atBreakStmnt(st, op == BREAK);
    else if (op == TokenId.RETURN) atReturnStmnt(st);
    else if (op == THROW) atThrowStmnt(st);
    else if (op == TRY) atTryStmnt(st);
    else if (op == SWITCH) atSwitchStmnt(st);
    else if (op == SYNCHRONIZED) atSyncStmnt(st);
    else {
      // LABEL, SWITCH label stament might be null?.
      hasReturned = false;
      throw new CompileError("sorry, not supported statement: TokenId " + op);
    }
  }
Exemple #29
0
  protected void atPlusPlusCore(int dup_code, boolean doDup, int token, boolean isPost, Expr expr)
      throws CompileError {
    int t = exprType;

    if (doDup && isPost) bytecode.addOpcode(dup_code);

    if (t == INT || t == BYTE || t == CHAR || t == SHORT) {
      bytecode.addIconst(1);
      bytecode.addOpcode(token == PLUSPLUS ? IADD : ISUB);
      exprType = INT;
    } else if (t == LONG) {
      bytecode.addLconst((long) 1);
      bytecode.addOpcode(token == PLUSPLUS ? LADD : LSUB);
    } else if (t == FLOAT) {
      bytecode.addFconst(1.0f);
      bytecode.addOpcode(token == PLUSPLUS ? FADD : FSUB);
    } else if (t == DOUBLE) {
      bytecode.addDconst(1.0);
      bytecode.addOpcode(token == PLUSPLUS ? DADD : DSUB);
    } else badType(expr);

    if (doDup && !isPost) bytecode.addOpcode(dup_code);
  }
Exemple #30
0
  private void atArrayAssign(Expr expr, int op, Expr array, ASTree right, boolean doDup)
      throws CompileError {
    arrayAccess(array.oprand1(), array.oprand2());

    if (op != '=') {
      bytecode.addOpcode(DUP2);
      bytecode.addOpcode(getArrayReadOp(exprType, arrayDim));
    }

    int aType = exprType;
    int aDim = arrayDim;
    String cname = className;

    atAssignCore(expr, op, right, aType, aDim, cname);

    if (doDup)
      if (is2word(aType, aDim)) bytecode.addOpcode(DUP2_X2);
      else bytecode.addOpcode(DUP_X2);

    bytecode.addOpcode(getArrayWriteOp(aType, aDim));
    exprType = aType;
    arrayDim = aDim;
    className = cname;
  }