summaryrefslogblamecommitdiff
path: root/src/compiler/scala/tools/nsc/backend/jvm/GenJVM.scala
blob: 50a86fdf2703f9e164e8f1b148393d144149d001 (plain) (tree)
1
2
3
4
5
6
7
8
9








                                    

                    

                                               
                                     




                           
                                            








                                                    

                               



                                                         

                                              








                                                                     

                             
     
                           
                                 

                                                          

                                                              
                                                                   
                                                                                        
 
                       
                                                        



                                                                              
                                                                    

                                                                       
                                                                   
 








                                        


















                                                                                    

                                                                                                                                        
     
 
                                        
                                     
 
                                     

                                                                                               

                                          


                                         
                          



                                                         






                                                                          


                                                            


                       
                                         
 






                                                                              
                                       



                                                              


                                                             





                                                                                          

       


                                      
                                 


                                                  
                                                                                                                                       


                                      

                                                        




                                                                                           
                     

                                                          




                                                             


                                                                                                
                 
                  
                               




                                                                 




                                                  

                                                       

                                                                    
 


                                                                                   

















                                                                                              
 
                                  
                                                                   

                                           
                                                                                 
         
 
                                                              
                   
                              


       













                                                                  



















                                                                               





                                         

                                                         




























                                                                                                                                      
                                           

     

                                            
                                              
 
                                    

                                     
                                  



                                              

                               
                                 


                             








                                                                          













                                                               




                                                             


                                            






                                              


                           


                                                                           



                                       
                                                     
                          


                                                                                                  






                                                                     




                                         

                                                                                    
                           
                        

                    
                             


                                       





                                 
                                    
                                                                    









                                                                           






                                            
                                                                   
                
                                                                   

                                             

                                                                         

                                                                                                           
                         
                                        


                                                   
                                       



                                                   
                                                                                          


                                                                        


                                                              





                                             
                                                                    
                
                                                                    

                                              
                                                                                                         
                         
                                        


                                                   
                                       



                                                   
                                               



                                                                    

                                                                                                   


                                                          
                                                  


                                                                                       
                                                




                                                                                      
                                                

                                                                                      



                                                                                      
 



                                                                                      
                                                                                     
                                                                               

                                                                               
                                                   




                                                                 
 
 

             

                                          
                                     
 





                                                                               

























                                                                                          

                                                                                  





                                                                                           

                                                                                     



                                                      







                                                                          

                                             







                                                                          






                                                   







                                                                     




                                                      






                                                                     

                                             






                                                                   






                                                                         






                                                                     








                                             



                                                     













                                                     

                              
                                                                           
                                        
         


                                                                                                                                              
                                   




                                                                 



         
                                                              
































































                                                                          






                                                        

                                                           






                                

                                                           






                                 

                                                           



























                                                         
                                    

                                                           
                            
                                                               
                                                                                                        

                                                        
 

                                  
 





                                                                     
 














                                                                        


       
                                                        









                                                                 








                                                        
                                            
                     

                                            


                                                           

































                                                               

                                                   




                                   

                                                             

                                                                     







                                                                          
       
                                         



                                                                    




                                              
                                                          

                               
                                                   
     





























                                                                                   
                                                                                                             











































                                                                                      





                                                                






























                                                                                          
   
 
/* NSC -- new scala compiler
 * Copyright 2005 LAMP/EPFL
 * @author  Martin Odersky
 */

// $Id$

package scala.tools.nsc.backend.jvm;

import java.io.File;

import scala.collection.mutable.{Map, HashMap};
import scala.tools.nsc.symtab._;
import scala.tools.nsc.util.Position;

import ch.epfl.lamp.fjbg._;

/**
 */
abstract class GenJVM extends SubComponent {
  import global._;
  import icodes._;
  import icodes.opcodes._;

  val phaseName = "jvm";

  /** Create a new phase */
  override def newPhase(p: Phase) = new JvmPhase(p);

  /** JVM code generation phase
   */
  class JvmPhase(prev: Phase) extends GlobalPhase(prev) {
    def name = phaseName;
    override def newFlags = phaseNewFlags;

    override def erasedTypes = true;
    val codeGenerator = new BytecodeGenerator;

    override def run: Unit =
      classes foreach codeGenerator.genClass;

    override def apply(unit: CompilationUnit): Unit =
      abort("JVM works on icode classes, not on compilation units!");
  }

  /**
   * Java bytecode generator.
   *
   */
  class BytecodeGenerator {
    val MIN_SWITCH_DENSITY = 0.7;
    val MODULE_INSTANCE_NAME = "MODULE$";
    val JAVA_LANG_STRINGBUFFER = "java.lang.StringBuffer";
    val JAVA_RMI_REMOTEEXCEPTION = "java.rmi.RemoteException";

    val stringBufferType = new JObjectType(JAVA_LANG_STRINGBUFFER);
    val toStringType = new JMethodType(JObjectType.JAVA_LANG_STRING, JType.EMPTY_ARRAY);

    // Scala attributes
    val SerializableAttr = definitions.SerializableAttr;
    val SerialVersionUID = definitions.getClass("scala.SerialVersionUID").tpe;
    val CloneableAttr    = definitions.getClass("scala.cloneable").tpe;
    val TransientAtt     = definitions.getClass("scala.transient").tpe;
    val VolatileAttr     = definitions.getClass("scala.volatile").tpe;
    val RemoteAttr       = definitions.getClass("scala.remote").tpe;

    val CloneableClass   = definitions.getClass("java.lang.Cloneable");
    val RemoteInterface  = definitions.getClass("java.rmi.Remote");

    var clasz: IClass = _;
    var method: IMethod = _;
    var code: Code = _;
    var jclass: JClass = _;
    var jmethod: JMethod = _;
    var jcode: JExtendedCode = _;

    val fjbgContext = new FJBGContext();

    def emitClass(jclass: JClass, sym: Symbol): Unit = {
      def addScalaAttr(sym: Symbol): Unit = currentRun.symData.get(sym) match {
        case Some(pickle) =>
          val scalaAttr = fjbgContext.JOtherAttribute(jclass,
                                                  jclass,
                                                  nme.ScalaSignatureATTR.toString(),
                                                  pickle.bytes,
                                                  pickle.writeIndex);
          jclass.addAttribute(scalaAttr);
          currentRun.symData -= sym;
          currentRun.symData -= sym.linkedSym;
          //System.out.println("Generated ScalaSig Attr for " + sym);//debug
        case _ =>
          log("Could not find pickle information for " + sym);
      }
      if (!jclass.getName().endsWith("$"))
        addScalaAttr(if (isTopLevelModule(sym)) sym.sourceModule else sym);
      val outfile = getFile(jclass, ".class");
      jclass.writeTo(outfile);
      val file = scala.tools.util.AbstractFile.getFile(outfile);
      informProgress("wrote " + outfile + " " + (if (file != null) "" + file.getFile() + " " + file.getFile().exists() else "no file"));
    }

    var serialVUID: Option[Long] = None;
    var remoteClass: Boolean = false;

    def genClass(c: IClass): Unit = {
      if (settings.debug.value)
        log("Generating class " + c.symbol + " flags: " + Flags.flagsToString(c.symbol.flags));
      clasz = c;
      var parents = c.symbol.info.parents;
      var ifaces  = JClass.NO_INTERFACES;
      val name    = javaName(c.symbol);
      serialVUID  = None;
      remoteClass = false;

      if (parents.isEmpty)
        parents = definitions.ObjectClass.tpe :: parents;

      c.symbol.attributes foreach { a => a match {
          case Pair(SerializableAttr, _) =>
            parents = parents ::: List(definitions.SerializableClass.tpe);
          case Pair(CloneableAttr, _) =>
            parents = parents ::: List(CloneableClass.tpe);
          case Pair(SerialVersionUID, value :: _) =>
            serialVUID = Some(value.longValue);
          case Pair(RemoteAttr, _) =>
            parents = parents ::: List(RemoteInterface.tpe);
            remoteClass = true;
          case _ => ();
        }
      }
      parents = parents.removeDuplicates;

      if (parents.length > 1 ) {
        ifaces = new Array[String](parents.length - 1);
        parents.drop(1).map((s) => javaName(s.symbol)).copyToArray(ifaces, 0);
        ()
      }

      jclass = fjbgContext.JClass(javaFlags(c.symbol),
                                  name,
                                  javaName(parents(0).symbol),
                                  ifaces,
                                  c.cunit.source.toString());

      if (isTopLevelModule(c.symbol) || serialVUID != None) {
        if (isTopLevelModule(c.symbol))
            addModuleInstanceField;
        addStaticInit(jclass);

        if (c.symbol.linkedClass != NoSymbol)
          log("No mirror class for module with linked class: " + c.symbol.fullNameString);
        else
          dumpMirrorClass;
      }

      clasz.fields foreach genField;
      clasz.methods foreach genMethod;

      emitClass(jclass, c.symbol)
    }

    def isTopLevelModule(sym: Symbol): Boolean = {
      sym.isModuleClass && !sym.isImplClass && !sym.hasFlag(Flags.LIFTED) /* && !atPhase(currentRun.erasurePhase)(sym.isNestedClass) */
    }

    def genField(f: IField): Unit  = {
      if (settings.debug.value)
        log("Adding field: " + f.symbol.fullNameString);
      var attributes = 0;

      f.symbol.attributes foreach { a => a match {
        case Pair(TransientAtt, _) => attributes = attributes | JAccessFlags.ACC_TRANSIENT;
        case Pair(VolatileAttr, _) => attributes = attributes | JAccessFlags.ACC_VOLATILE;
        case _ => ();
      }}
      jclass.addNewField(javaFlags(f.symbol) | attributes,
                         javaName(f.symbol),
                         javaType(toTypeKind(f.symbol.tpe)));
    }

    def genMethod(m: IMethod): Unit = {
      if (settings.debug.value)
        log("Generating method " + m.symbol + " flags: " + Flags.flagsToString(m.symbol.flags) +
            " owner: " + m.symbol.owner);
      method = m;
      endPC.clear;
      computeLocalVarsIndex(m);

      var resTpe = javaType(toTypeKind(m.symbol.tpe.resultType));
      if (m.symbol.isClassConstructor)
        resTpe = JType.VOID;

      var flags = javaFlags(m.symbol);
      if (jclass.isInterface())
        flags = flags | JAccessFlags.ACC_ABSTRACT;

      jmethod = jclass.addNewMethod(flags,
                                    javaName(m.symbol),
                                    resTpe,
                                    javaTypes(m.params map (.kind)),
                                    javaNames(m.params map (.sym)));

      if (m.symbol.hasFlag(Flags.BRIDGE))
        jmethod.addAttribute(fjbgContext.JOtherAttribute(jclass, jmethod, "Bridge",
                                                         new Array[Byte](0)));
      if (remoteClass || (m.symbol.attributes contains Pair(RemoteAttr, Nil)))
        if (jmethod.isPublic()) {
          // (see http://java.sun.com/docs/books/vmspec/html/ClassFile.doc.html#3129)
          // Exceptions_attribute {
          // ..
          // u2 number_of_exceptions;
    	  // u2 exception_index_table[number_of_exceptions];
          // }
          val cp = jclass.getConstantPool();
          val reInx = cp.addClass(JAVA_RMI_REMOTEEXCEPTION);
          val contents = java.nio.ByteBuffer.allocate(4); // u2 + u2[1]
          contents.putShort(1.asInstanceOf[Short]);
          contents.putShort(reInx.asInstanceOf[Short]);
          if (settings.debug.value)
            log("adding 'Exceptions_attribute' " + contents + " for remote method " + method);
          jmethod.addAttribute(
            fjbgContext.JOtherAttribute(jclass, jmethod, "Exceptions", contents.array()));
        }

      if (!jmethod.isAbstract()) {
        for (val local <- m.locals; (! m.params.contains(local))) {
          if (settings.debug.value)
            log("add local var: " + local);
          jmethod.addNewLocalVariable(javaType(local.kind), javaName(local.sym));
        }

        jcode = jmethod.getCode().asInstanceOf[JExtendedCode];
        genCode(m);
        genLocalVariableTable;
      }
    }

    def addModuleInstanceField: Unit = {
      import JAccessFlags._;
      jclass.addNewField(ACC_PUBLIC | ACC_FINAL | ACC_STATIC,
                        MODULE_INSTANCE_NAME,
                        jclass.getType());
    }

    def addStaticInit(cls: JClass): Unit = {
      import JAccessFlags._;
      val clinitMethod = cls.addNewMethod(ACC_PUBLIC | ACC_STATIC,
                                          "<clinit>",
                                          JType.VOID,
                                          JType.EMPTY_ARRAY,
                                          new Array[String](0));
      val clinit = clinitMethod.getCode().asInstanceOf[JExtendedCode];
      if (isTopLevelModule(clasz.symbol)) {
        clinit.emitNEW(cls.getName());
        clinit.emitDUP();
        clinit.emitINVOKESPECIAL(cls.getName(),
                                 JMethod.INSTANCE_CONSTRUCTOR_NAME,
                                 JMethodType.ARGLESS_VOID_FUNCTION);
      }

      serialVUID match {
        case Some(value) =>
          val fieldName = "serialVersionUID";
          jclass.addNewField(JAccessFlags.ACC_STATIC | JAccessFlags.ACC_PUBLIC,
                             fieldName,
                             JType.LONG);
          clinit.emitPUSH(value);
          clinit.emitPUTSTATIC(jclass.getName(), fieldName, JType.LONG);
        case None => ();
      }

      clinit.emitRETURN();
    }

    def dumpMirrorClass: Unit = {
      import JAccessFlags._;
      assert(clasz.symbol.isModuleClass);
      if (settings.debug.value)
        log("Dumping mirror class for object: " + clasz);
      val moduleName = javaName(clasz.symbol); // + "$";
      val mirrorName = moduleName.substring(0, moduleName.length() - 1);
      val mirrorClass = fjbgContext.JClass(ACC_SUPER | ACC_PUBLIC | ACC_FINAL,
                                           mirrorName,
                                           "java.lang.Object",
                                           JClass.NO_INTERFACES,
                                           clasz.cunit.source.toString());
      for (val m <- clasz.methods; !(m.symbol.hasFlag(Flags.PRIVATE)) && !m.symbol.isClassConstructor && !isStaticSymbol(m.symbol) ) {
        val mirrorMethod = mirrorClass.addNewMethod(ACC_PUBLIC | ACC_FINAL | ACC_STATIC,
                                               javaName(m.symbol),
                                               javaType(toTypeKind(m.symbol.tpe.resultType)),
                                               javaTypes(m.params map (.kind)),
                                               javaNames(m.params map (.sym)));
        val mirrorCode = mirrorMethod.getCode().asInstanceOf[JExtendedCode];
        mirrorCode.emitGETSTATIC(moduleName,
                                 MODULE_INSTANCE_NAME,
                                 new JObjectType(moduleName));
        var i = 0;
        var index = 0;
        var argTypes = mirrorMethod.getArgumentTypes();
        while (i < argTypes.length) {
          mirrorCode.emitLOAD(index, argTypes(i));
          index = index + argTypes(i).getSize();
          i = i + 1;
        }

        mirrorCode.emitINVOKEVIRTUAL(moduleName, mirrorMethod.getName(), mirrorMethod.getType().asInstanceOf[JMethodType]);
        mirrorCode.emitRETURN(mirrorMethod.getReturnType());
      }
      emitClass(mirrorClass, clasz.symbol);
    }


    val linearizer = new NormalLinearizer();
    var linearization: List[BasicBlock] = Nil;

    var isModuleInitialized = false;
    def genCode(m: IMethod): Unit = {
      labels.clear;
      isModuleInitialized = false;

      code = m.code;
      linearization = linearizer.linearize(m);
      makeLabels(linearization);
      genBlocks(linearization);

      if (this.method.exh != Nil)
        genExceptionHandlers;
    }

    var nextBlock: BasicBlock = _;

    def genBlocks(l: List[BasicBlock]): Unit = l match {
      case Nil => ();
      case x :: Nil => nextBlock = null; genBlock(x);
      case x :: y :: ys => nextBlock = y; genBlock(x); genBlocks(y :: ys);
    }


    /** Generate exception handlers for the current method. */
    def genExceptionHandlers: Unit = {

      def ranges(e: ExceptionHandler): List[Pair[Int, Int]] = {
        var covered = e.covered;
        var ranges: List[Pair[Int, Int]] = Nil;
        var start = -1;
        var end = -1;

        linearization foreach ((b) => {
          if (! (covered contains b) ) {
            if (start >= 0) { // we're inside a handler range
              end = labels(b).getAnchor();
              ranges = Pair(start, end) :: ranges;
              start = -1;
            }
          } else {
            if (start >= 0) { // we're inside a handler range
              end = endPC(b);
            } else {
              start = labels(b).getAnchor();
              end   = endPC(b);
            }
            covered = covered remove b.==;
          }
        });

        if (start >= 0) {
          ranges = Pair(start, end) :: ranges;
        }

        if (covered != Nil)
          if (settings.debug.value)
            log("Some covered blocks were not found in method: " + method +
                " covered: " + covered + " not in " + linearization);
        ranges
      }

      this.method.exh foreach ((e) => {
        ranges(e).sort({ (p1, p2) => p1._1 < p2._1 })
        .foreach ((p) => {
          if (settings.debug.value)
            log("Adding exception handler " + e + "at block: " + e.startBlock + " for " + method +
                " from: " + p._1 + " to: " + p._2 + " catching: " + e.cls);
          jcode.addExceptionHandler(p._1, p._2,
                                    labels(e.startBlock).getAnchor(),
                                    if (e.cls == NoSymbol)
                                      null
                                    else javaName(e.cls))
        })
      });
    }

    def genBlock(b: BasicBlock): Unit = {
      labels(b).anchorToNext();

      if (settings.debug.value)
        log("Generating code for block: " + b + " at pc: " + labels(b).getAnchor());
      var lastMappedPC = 0;
      var lastLineNr =0;
      var crtPC = 0;

      b traverse ( instr => {
        if (b.lastInstruction == instr)
          endPC(b) = jcode.getPC();

        instr match {
          case THIS(clasz) =>
            jcode.emitALOAD_0();

          case CONSTANT(const) =>
            const.tag match {
              case UnitTag    => ();
              case BooleanTag => jcode.emitPUSH(const.booleanValue);
              case ByteTag    => jcode.emitPUSH(const.byteValue);
              case ShortTag   => jcode.emitPUSH(const.shortValue);
              case CharTag    => jcode.emitPUSH(const.charValue);
              case IntTag     => jcode.emitPUSH(const.intValue);
              case LongTag    => jcode.emitPUSH(const.longValue);
              case FloatTag   => jcode.emitPUSH(const.floatValue);
              case DoubleTag  => jcode.emitPUSH(const.doubleValue);
              case StringTag  => jcode.emitPUSH(const.stringValue);
              case NullTag    => jcode.emitACONST_NULL();
              case _          => abort("Unknown constant value: " + const);
            }

          case LOAD_ARRAY_ITEM(kind) =>
            jcode.emitALOAD(javaType(kind));

          case LOAD_LOCAL(local, isArg) =>
            if (isArg)
              jcode.emitLOAD(indexOf(local), javaType(local.kind));
            else
              jcode.emitLOAD(indexOf(local), javaType(local.kind));

          case LOAD_FIELD(field, isStatic) =>
            var owner = javaName(field.owner);
//            if (field.owner.hasFlag(Flags.MODULE)) owner = owner + "$";
            if (settings.debug.value)
              log("LOAD_FIELD with owner: " + owner + " flags: " + Flags.flagsToString(field.owner.flags));
            if (isStatic)
              jcode.emitGETSTATIC(owner,
                                  javaName(field),
                                  javaType(field));
            else
              jcode.emitGETFIELD(owner,
                                  javaName(field),
                                  javaType(field));

          case LOAD_MODULE(module) =>
            assert(module.isModule || module.isModuleClass, "Expected module: " + module);
            if (settings.debug.value)
              log("genearting LOAD_MODULE for: " + module + " flags: " +
                  Flags.flagsToString(module.flags));
            jcode.emitGETSTATIC(javaName(module) /* + "$" */ ,
                                MODULE_INSTANCE_NAME,
                                javaType(module));

          case STORE_ARRAY_ITEM(kind) =>
            jcode.emitASTORE(javaType(kind));

          case STORE_LOCAL(local, isArg) =>
            if (isArg)
              jcode.emitSTORE(indexOf(local), javaType(local.kind));
            else
              jcode.emitSTORE(indexOf(local), javaType(local.kind));

          case STORE_FIELD(field, isStatic) =>
            val owner = javaName(field.owner); // + (if (field.owner.hasFlag(Flags.MODULE)) "$" else "");
            if (isStatic)
              jcode.emitPUTSTATIC(owner,
                                  javaName(field),
                                  javaType(field));
            else
              jcode.emitPUTFIELD(owner,
                                  javaName(field),
                                  javaType(field));

          case CALL_PRIMITIVE(primitive) =>
            genPrimitive(primitive, instr.pos);

          // TODO: reference the type of the receiver instead of the
          // method owner.
          case CALL_METHOD(method, style) =>
            val owner = javaName(method.owner); // + (if (method.owner.isModuleClass) "$" else "");

            style match {
              case Dynamic =>
                if (method.owner.hasFlag(Flags.INTERFACE))
                  jcode.emitINVOKEINTERFACE(owner,
                                            javaName(method),
                                            javaType(method).asInstanceOf[JMethodType])
                else
                  jcode.emitINVOKEVIRTUAL(owner,
                                          javaName(method),
                                          javaType(method).asInstanceOf[JMethodType]);

              case Static(instance) =>
                if (instance) {
                  jcode.emitINVOKESPECIAL(owner,
                                          javaName(method),
                                          javaType(method).asInstanceOf[JMethodType]);
                } else
                  jcode.emitINVOKESTATIC(owner,
                                          javaName(method),
                                          javaType(method).asInstanceOf[JMethodType]);

              case SuperCall(_) =>
                  jcode.emitINVOKESPECIAL(owner,
                                          javaName(method),
                                          javaType(method).asInstanceOf[JMethodType]);
                  // we initialize the MODULE$ field immediately after the super ctor
                  if (isTopLevelModule(clasz.symbol) && !isModuleInitialized &&
                      jmethod.getName() == JMethod.INSTANCE_CONSTRUCTOR_NAME &&
                      javaName(method) == JMethod.INSTANCE_CONSTRUCTOR_NAME) {
                        isModuleInitialized = true;
                        jcode.emitALOAD_0();
                        jcode.emitPUTSTATIC(jclass.getName(),
                                            MODULE_INSTANCE_NAME,
                                            jclass.getType());
                      }


            }

          case NEW(REFERENCE(cls)) =>
            val className = javaName(cls);
            jcode.emitNEW(className);

          case CREATE_ARRAY(elem) => elem match {
            case REFERENCE(_) | ARRAY(_) =>
              jcode.emitANEWARRAY(javaType(elem).asInstanceOf[JReferenceType]);
            case _ =>
              jcode.emitNEWARRAY(javaType(elem));
          }

          case IS_INSTANCE(tpe) =>
            tpe match {
              case REFERENCE(cls) => jcode.emitINSTANCEOF(new JObjectType(javaName(cls)));
              case ARRAY(elem)    => jcode.emitINSTANCEOF(new JArrayType(javaType(elem)));
              case _ => abort("Unknown reference type in IS_INSTANCE: " + tpe);
            }

          case CHECK_CAST(tpe) =>
            tpe match {
              case REFERENCE(cls) => jcode.emitCHECKCAST(new JObjectType(javaName(cls)));
              case ARRAY(elem)    => jcode.emitCHECKCAST(new JArrayType(javaType(elem)));
              case _ => abort("Unknown reference type in IS_INSTANCE: " + tpe);
            }

          case SWITCH(tags, branches) =>
            val tagArray = new Array[Array[Int]](tags.length);
            var caze = tags;
            var i = 0;
            while (i < tagArray.length) {
              tagArray(i) = new Array[Int](caze.head.length);
              caze.head.copyToArray(tagArray(i), 0);
              i = i + 1;
              caze = caze.tail;
            }
            val branchArray = new Array[JCode$Label](tagArray.length);
            if (settings.debug.value)
              log("Emitting SWITHCH:\ntags: " + tags + "\nbranches: " + branches);
            jcode.emitSWITCH(tagArray,
                             (branches map labels dropRight 1).copyToArray(branchArray, 0),
                             labels(branches.last),
                             MIN_SWITCH_DENSITY);

          case JUMP(where) =>
            if (nextBlock != where)
              jcode.emitGOTO_maybe_W(labels(where), false); // default to short jumps

          case CJUMP(success, failure, cond, kind) =>
            kind match {
              case BOOL | BYTE | CHAR | SHORT | INT =>
                if (nextBlock == success) {
                  jcode.emitIF_ICMP(conds(negate(cond)), labels(failure));
                  // .. and fall through to success label
                } else {
                  jcode.emitIF_ICMP(conds(cond), labels(success));
                  if (nextBlock != failure)
                    jcode.emitGOTO_maybe_W(labels(failure), false);
                }

              case REFERENCE(_) | ARRAY(_) =>
                if (nextBlock == success) {
                  jcode.emitIF_ACMP(conds(negate(cond)), labels(failure));
                  // .. and fall through to success label
                } else {
                  jcode.emitIF_ACMP(conds(cond), labels(success));
                  if (nextBlock != failure)
                    jcode.emitGOTO_maybe_W(labels(failure), false);
                }

              case _ =>
                kind match {
                  case LONG   => jcode.emitLCMP();
                  case FLOAT  => jcode.emitFCMPG();
                  case DOUBLE => jcode.emitDCMPG();
                }
                if (nextBlock == success) {
                  jcode.emitIF(conds(negate(cond)), labels(failure));
                  // .. and fall through to success label
                } else {
                  jcode.emitIF(conds(cond), labels(success));
                  if (nextBlock != failure)
                    jcode.emitGOTO_maybe_W(labels(failure), false);
                }
            }

          case CZJUMP(success, failure, cond, kind) =>
            kind match {
              case BOOL | BYTE | CHAR | SHORT | INT =>
                if (nextBlock == success) {
                  jcode.emitIF(conds(negate(cond)), labels(failure));
                } else {
                  jcode.emitIF(conds(cond), labels(success));
                  if (nextBlock != failure)
                    jcode.emitGOTO_maybe_W(labels(failure), false);
                }

              case REFERENCE(_) | ARRAY(_) =>
                if (nextBlock == success) {
                  jcode.emitIFNONNULL(labels(failure));
                } else {
                  jcode.emitIFNULL(labels(success));
                  if (nextBlock != failure)
                    jcode.emitGOTO_maybe_W(labels(failure), false);
                }

              case _ =>
                kind match {
                  case LONG   => jcode.emitLCONST_0(); jcode.emitLCMP();
                  case FLOAT  => jcode.emitFCONST_0(); jcode.emitFCMPL();
                  case DOUBLE => jcode.emitDCONST_0(); jcode.emitDCMPL();
                }
                if (nextBlock == success) {
                  jcode.emitIF(conds(negate(cond)), labels(failure));
                } else {
                  jcode.emitIF(conds(cond), labels(success));
                  if (nextBlock != failure)
                    jcode.emitGOTO_maybe_W(labels(failure), false);
                }
            }

          case RETURN(kind) =>
            jcode.emitRETURN(javaType(kind));

          case THROW() =>
            jcode.emitATHROW();

          case DROP(kind) =>
            kind match {
              case LONG | DOUBLE => jcode.emitPOP2();
              case _ => jcode.emitPOP();
            }

          case DUP(kind) =>
            kind match {
              case LONG | DOUBLE => jcode.emitDUP2();
              case _ => jcode.emitDUP();
            }


          case MONITOR_ENTER() =>
            jcode.emitMONITORENTER();

          case MONITOR_EXIT() =>
            jcode.emitMONITOREXIT();
        }

        crtPC = jcode.getPC();
        val crtLine = try { clasz.cunit.position(instr.pos).line; } catch {
            case _: Error => lastLineNr;
        }
	//System.err.println("CRTLINE: " + instr.pos + " " +
	//	   /* (if (instr.pos < clasz.cunit.source.content.length) clasz.cunit.source.content(instr.pos) else '*') + */ " " + crtLine);

        if (crtPC > lastMappedPC) {
          jcode.completeLineNumber(lastMappedPC, crtPC, crtLine);
          lastMappedPC = crtPC;
          lastLineNr   = crtLine;
        }

      });
    }


    def genPrimitive(primitive: Primitive, pos: Int): Unit = {
      primitive match {
        case Negation(kind) =>
          kind match {
            case BOOL | BYTE | CHAR | SHORT | INT =>
              jcode.emitINEG();

            case LONG   => jcode.emitLNEG();
            case FLOAT  => jcode.emitFNEG();
            case DOUBLE => jcode.emitDNEG();
            case _ => abort("Impossible to negate a " + kind);
          }

        case Arithmetic(op, kind) =>
          op match {
            case ADD => jcode.emitADD(javaType(kind));
            case SUB =>
              kind match {
                case BOOL | BYTE | CHAR | SHORT | INT =>
                  jcode.emitISUB();
                case LONG   => jcode.emitLSUB();
                case FLOAT  => jcode.emitFSUB();
                case DOUBLE => jcode.emitDSUB();
              }

            case MUL =>
              kind match {
                case BOOL | BYTE | CHAR | SHORT | INT =>
                  jcode.emitIMUL();
                case LONG   => jcode.emitLMUL();
                case FLOAT  => jcode.emitFMUL();
                case DOUBLE => jcode.emitDMUL();
              }

            case DIV =>
              kind match {
                case BOOL | BYTE | CHAR | SHORT | INT =>
                  jcode.emitIDIV();
                case LONG   => jcode.emitLDIV();
                case FLOAT  => jcode.emitFDIV();
                case DOUBLE => jcode.emitDDIV();
              }

            case REM =>
              kind match {
                case BOOL | BYTE | CHAR | SHORT | INT =>
                  jcode.emitIREM();
                case LONG   => jcode.emitLREM();
                case FLOAT  => jcode.emitFREM();
                case DOUBLE => jcode.emitDREM();
              }

            case NOT =>
              kind match {
                case BOOL | BYTE | CHAR | SHORT | INT =>
                  jcode.emitPUSH(-1);
                  jcode.emitIXOR();;
                case LONG   =>
                  jcode.emitPUSH(-1l);
                  jcode.emitLXOR();;
                case _ => abort("Impossible to negate an " + kind);
              }

            case _ => abort("Unknown arithmetic primitive " + primitive );
          }

        case Logical(op, kind) => Pair(op, kind) match {
          case Pair(AND, LONG) =>
            jcode.emitLAND();
          case Pair(AND, INT) =>
            jcode.emitIAND();
          case Pair(AND, _) =>
            jcode.emitIAND();
            if (kind != BOOL)
              jcode.emitT2T(javaType(INT), javaType(kind));

          case Pair(OR, LONG) =>
            jcode.emitLOR();
          case Pair(OR, INT) =>
            jcode.emitIOR();
          case Pair(OR, _) =>
            jcode.emitIOR();
            if (kind != BOOL)
              jcode.emitT2T(javaType(INT), javaType(kind));

          case Pair(XOR, LONG) =>
            jcode.emitLXOR();
          case Pair(XOR, INT) =>
            jcode.emitIXOR();
          case Pair(XOR, _) =>
            jcode.emitIXOR();
            if (kind != BOOL)
              jcode.emitT2T(javaType(INT), javaType(kind));
        }

        case Shift(op, kind) => Pair(op, kind) match {
          case Pair(LSL, LONG) =>
            jcode.emitLSHL();
          case Pair(LSL, INT) =>
            jcode.emitISHL();
          case Pair(LSL, _) =>
            jcode.emitISHL();
            jcode.emitT2T(javaType(INT), javaType(kind));

          case Pair(ASR, LONG) =>
            jcode.emitLSHR();
          case Pair(ASR, INT) =>
            jcode.emitISHR();
          case Pair(ASR, _) =>
            jcode.emitISHR();
            jcode.emitT2T(javaType(INT), javaType(kind));

          case Pair(LSR, LONG) =>
            jcode.emitLUSHR();
          case Pair(LSR, INT) =>
            jcode.emitIUSHR();
          case Pair(LSR, _) =>
            jcode.emitIUSHR();
            jcode.emitT2T(javaType(INT), javaType(kind));
        }

        case Conversion(src, dst) =>
          if (settings.debug.value)
            log("Converting from: " + src + " to: " + dst);
          if (dst == BOOL) {
            Console.println("Illegal conversion at: " + clasz +
                            " at: " + method.sourceFile + ":" + Position.line(clasz.cunit.source, pos));
          } else
            jcode.emitT2T(javaType(src), javaType(dst));

        case ArrayLength(_) =>
          jcode.emitARRAYLENGTH();

        case StartConcat =>
          jcode.emitNEW(JAVA_LANG_STRINGBUFFER);
          jcode.emitDUP();
          jcode.emitINVOKESPECIAL(JAVA_LANG_STRINGBUFFER,
                                  JMethod.INSTANCE_CONSTRUCTOR_NAME,
                                  JMethodType.ARGLESS_VOID_FUNCTION);

        case StringConcat(el) =>
          val jtype = el match {
            case REFERENCE(_) | ARRAY(_)=> JObjectType.JAVA_LANG_OBJECT;
            case _ => javaType(el);
          }
          jcode.emitINVOKEVIRTUAL(JAVA_LANG_STRINGBUFFER,
                                  "append",
                                  new JMethodType(stringBufferType,
                                                  Predef.Array(jtype)));
        case EndConcat =>
          jcode.emitINVOKEVIRTUAL(JAVA_LANG_STRINGBUFFER,
                                  "toString",
                                  toStringType);

        case _ => abort("Unimplemented primitive " + primitive);
      }
    }

    val endPC: HashMap[BasicBlock, Int] = new HashMap();
    val labels: HashMap[BasicBlock, JCode$Label] = new HashMap();
    val conds: HashMap[TestOp, Int] = new HashMap();

    conds += EQ -> JExtendedCode.COND_EQ;
    conds += NE -> JExtendedCode.COND_NE;
    conds += LT -> JExtendedCode.COND_LT;
    conds += GT -> JExtendedCode.COND_GT;
    conds += LE -> JExtendedCode.COND_LE;
    conds += GE -> JExtendedCode.COND_GE;

    val negate: HashMap[TestOp, TestOp] = new HashMap();

    negate += EQ -> NE;
    negate += NE -> EQ;
    negate += LT -> GE;
    negate += GT -> LE;
    negate += LE -> GT;
    negate += GE -> LT;

    def makeLabels(bs: List[BasicBlock]) = {
      //labels.clear;
      if (settings.debug.value)
        log("Making labels for: " + method);
      bs foreach (bb => labels += bb -> jcode.newLabel() );
    }


    ////////////////////// local vars ///////////////////////

    def sizeOf(sym: Symbol): Int = sizeOf(toTypeKind(sym.tpe));


    def sizeOf(k: TypeKind): Int = k match {
      case DOUBLE | LONG => 2;
      case _ => 1;
    }

    def indexOf(m: IMethod, sym: Symbol): Int = {
      val Some(local) = m.lookupLocal(sym);
      assert (local.index >= 0,
              "Invalid index for: " + local);
      local.index
    }

    def indexOf(local: Local): Int = {
      assert (local.index >= 0,
              "Invalid index for: " + local);
      local.index
    }

    /**
     * Compute the indexes of each local variable of the given
     * method.
     */
    def computeLocalVarsIndex(m: IMethod): Unit = {
      var idx = 1;
      if (isStaticSymbol(m.symbol))
        idx = 0;

      for (val l <- m.locals) {
        if (settings.debug.value)
          log("Index value for " + l + ": " + idx);
        l.index = idx;
        idx = idx + sizeOf(l.kind);
      }
    }

    ////////////////////// Utilities ////////////////////////

    /** Return the a name of this symbol that can be used on the Java
     * platform. It removes spaces from names.
     *
     * Special handling: scala.All and scala.AllRef are 'erased' to
     * scala.All$ and scala.AllRef$. This is needed because they are
     * not real classes, and they mean 'abrupt termination upon evaluation
     * of that expression' or 'null' respectively. This handling is
     * done already in GenICode, but here we need to remove references
     * from method signatures to these types, because such classes can
     * not exist in the classpath: the type checker will be very confused.
     */
    def javaName(sym: Symbol): String = {
      val suffix = if (sym.hasFlag(Flags.MODULE) && !sym.isMethod &&
                        !sym.isImplClass &&
                        !sym.hasFlag(Flags.JAVA)) "$" else "";

      if (sym == definitions.AllClass)
        return "scala.All$"
      else if (sym == definitions.AllRefClass)
        return "scala.AllRef$";

      (if (sym.isClass || (sym.isModule && !sym.isMethod))
        sym.fullNameString('/')
      else
        sym.simpleName.toString().trim()) + suffix;
    }

    def javaNames(syms: List[Symbol]): Array[String] = {
      val res = new Array[String](syms.length);
      var i = 0;
      syms foreach ( s => { res(i) = javaName(s); i = i + 1; } );
      res
    }

    /**
     * Return the Java modifiers for the given symbol.
     * Java modifiers for classes:
     *  - public, abstract, final, strictfp (not used)
     * for interfaces:
     *  - the same as for classes, without 'final'
     * for fields:
     *  - public, protected, private
     *  - static, final
     * for methods:
     *  - the same as for fields, plus:
     *  - abstract, synchronized (not used), strictfp (not used), native (not used)
     */
    def javaFlags(sym: Symbol): Int = {
      import JAccessFlags._;

      var jf: Int = 0;
      val f = sym.flags;
      jf = jf | (if (sym hasFlag Flags.PRIVATE) ACC_PRIVATE else ACC_PUBLIC);
      jf = jf | (if ((sym hasFlag Flags.ABSTRACT) ||
                     (sym hasFlag Flags.DEFERRED)) ACC_ABSTRACT else 0);
      jf = jf | (if (sym hasFlag Flags.INTERFACE) ACC_INTERFACE else 0);
      jf = jf | (if ((sym hasFlag Flags.FINAL) && !sym.enclClass.hasFlag(Flags.INTERFACE)) ACC_FINAL else 0);
      jf = jf | (if (isStaticSymbol(sym)) ACC_STATIC else 0);
      jf
    }

    def isStaticSymbol(s: Symbol): Boolean =
      s.hasFlag(Flags.STATIC) || s.hasFlag(Flags.STATICMEMBER) || s.owner.isImplClass;

    def javaType(t: TypeKind): JType = t match {
      case UNIT            => JType.VOID;

      case BOOL            => JType.BOOLEAN;
      case BYTE            => JType.BYTE;
      case SHORT           => JType.SHORT;
      case CHAR            => JType.CHAR;
      case INT             => JType.INT;
      case LONG            => JType.LONG;
      case FLOAT           => JType.FLOAT;
      case DOUBLE          => JType.DOUBLE;
      case REFERENCE(cls)  => new JObjectType(javaName(cls));
      case ARRAY(elem)     => new JArrayType(javaType(elem));
    }

    def javaType(s: Symbol): JType =
      if (s.isMethod)
        new JMethodType(
          if (s.isClassConstructor)
            JType.VOID else javaType(toTypeKind(s.tpe.resultType)),
          javaTypes(s.tpe.paramTypes map toTypeKind))
      else
        javaType(toTypeKind(s.tpe));

    def javaTypes(ts: List[TypeKind]): Array[JType] = {
      val res = new Array[JType](ts.length);
      var i = 0;
      ts foreach ( t => { res(i) = javaType(t); i = i + 1; } );
      res
    }

//     def javaTypes(syms: List[Symbol]): Array[JType] = {
//       val res = new Array[JType](syms.length);
//       var i = 0;
//       syms foreach ( s => { res(i) = javaType(toTypeKind(s.tpe)); i = i + 1; } );
//       res
//     }

    def getFile(cls: JClass, suffix: String): String = {
      val path = cls.getName().replace('.', File.separatorChar);
      settings.outdir.value + File.separatorChar + path + suffix
    }

    private def genLocalVariableTable: Unit = {
        val vars: Array[JLocalVariable] = jmethod.getLocalVariables();

        if (!settings.debuginfo.value || vars.length == 0)
            return;

        val pool = jclass.getConstantPool();
        val pc = jcode.getPC();
        var anonCounter = 0;

        val lvTab = java.nio.ByteBuffer.allocate(2 + 10 * vars.length);
        lvTab.putShort(vars.length.asInstanceOf[Short]);
        for (val lv <- vars) {
            val name = if (lv.getName() == null) {
              anonCounter = anonCounter + 1;
              "<anon" + anonCounter + ">"
            } else lv.getName();

            lvTab.putShort(0.asInstanceOf[Short]);
            lvTab.putShort(pc.asInstanceOf[Short]);
            lvTab.putShort(pool.addUtf8(name).asInstanceOf[Short]);
            lvTab.putShort(pool.addUtf8(lv.getType().getSignature()).asInstanceOf[Short]);
            lvTab.putShort(lv.getIndex().asInstanceOf[Short]);
        }
        val attr =
            fjbgContext.JOtherAttribute(jclass,
                                        jmethod,
                                        "LocalVariableTable",
                                        lvTab.array());
        jcode.addAttribute(attr);
    }
  }
}