genicode.scala

来自「JAVA 语言的函数式编程扩展」· SCALA 代码 · 共 1,653 行 · 第 1/5 页

SCALA
1,653
字号
/* NSC -- new Scala compiler * Copyright 2005-2008 LAMP/EPFL * @author  Martin Odersky */// $Id: GenICode.scala 14481 2008-04-02 14:22:37Z dragos $package scala.tools.nsc.backend.icodeimport scala.collection.mutable.{Map, HashMap, ListBuffer, Buffer, HashSet}import scala.tools.nsc.symtab._import scala.tools.nsc.util.Position/** This class ... * *  @author  Iulian Dragos *  @version 1.0 */// TODO:// - switches with alternativesabstract class GenICode extends SubComponent  {  import global._  import icodes._  import icodes.opcodes._  val phaseName = "icode"  override def newPhase(prev: Phase) = new ICodePhase(prev)  class ICodePhase(prev: Phase) extends StdPhase(prev) {    override def description = "Generate ICode from the AST"    var unit: CompilationUnit = _    // We assume definitions are alread initialized    val STRING = REFERENCE(definitions.StringClass)    // this depends on the backend! should be changed.    val ANY_REF_CLASS = REFERENCE(definitions.ObjectClass)    val SCALA_ALL    = REFERENCE(definitions.AllClass)    val SCALA_ALLREF = REFERENCE(definitions.AllRefClass)    val THROWABLE    = REFERENCE(definitions.ThrowableClass)        val BoxesRunTime_equals =       if (!forMSIL)        definitions.getMember(definitions.BoxesRunTimeClass, nme.equals_)      else         definitions.getMember(definitions.getClass("scala.runtime.Comparator").linkedModuleOfClass, nme.equals_)    override def run {      scalaPrimitives.init      classes.clear      super.run    }    override def apply(unit: CompilationUnit): Unit = {      this.unit = unit      unit.icode.clear      log("Generating icode for " + unit)      gen(unit.body)      this.unit = null    }    def gen(tree: Tree): Context = gen(tree, new Context())    def gen(trees: List[Tree], ctx: Context): Context = {      var ctx1 = ctx      for (t <- trees) ctx1 = gen(t, ctx1)      ctx1    }    /////////////////// Code generation ///////////////////////    def gen(tree: Tree, ctx: Context): Context = tree match {      case EmptyTree => ctx      case PackageDef(name, stats) =>        gen(stats, ctx setPackage name)      case ClassDef(mods, name, _, impl) =>        log("Generating class: " + tree.symbol.fullNameString)        ctx setClass (new IClass(tree.symbol) setCompilationUnit unit)        addClassFields(ctx, tree.symbol);        classes += (tree.symbol -> ctx.clazz)        unit.icode += ctx.clazz          gen(impl, ctx)        ctx setClass null      // !! modules should be eliminated by refcheck... or not?      case ModuleDef(mods, name, impl) =>        abort("Modules should not reach backend!")      case ValDef(mods, name, tpt, rhs) =>        ctx // we use the symbol to add fields      case DefDef(mods, name, tparams, vparamss, tpt, rhs) =>        if (settings.debug.value)          log("Entering method " + name)        val m = new IMethod(tree.symbol)        m.sourceFile = unit.source.toString()        m.returnType = if (tree.symbol.isConstructor) UNIT                       else toTypeKind(tree.symbol.info.resultType)        ctx.clazz.addMethod(m)        var ctx1 = ctx.enterMethod(m, tree.asInstanceOf[DefDef])        addMethodParams(ctx1, vparamss)        m.native = m.symbol.hasAttribute(definitions.NativeAttr)        if (!m.isDeferred && !m.native) {          ctx1 = genLoad(rhs, ctx1, m.returnType);          // reverse the order of the local variables, to match the source-order          m.locals = m.locals.reverse          rhs match {            case Block(_, Return(_)) => ()            case Return(_) => ()            case EmptyTree =>               error("Concrete method has no definition: " + tree)            case _ => if (ctx1.bb.isEmpty)              ctx1.bb.emit(RETURN(m.returnType), rhs.pos)            else              ctx1.bb.emit(RETURN(m.returnType))          }          ctx1.bb.close          prune(ctx1.method)        } else          ctx1.method.setCode(null)        ctx1      case Template(_, _, body) =>        gen(body, ctx)      case _ =>        abort("Illegal tree in gen: " + tree)    }    private def genStat(trees: List[Tree], ctx: Context): Context = {      var currentCtx = ctx      for (t <- trees)        currentCtx = genStat(t, currentCtx)      currentCtx    }    /**     * Generate code for the given tree. The trees should contain statements     * and not produce any value. Use genLoad for expressions which leave     * a value on top of the stack.     *     * @param tree ...     * @param ctx  ...     * @return a new context. This is necessary for control flow instructions     *         which may change the current basic block.     */    private def genStat(tree: Tree, ctx: Context): Context = {      tree match {        case Assign(lhs @ Select(_, _), rhs) =>          if (isStaticSymbol(lhs.symbol)) {            val ctx1 = genLoad(rhs, ctx, toTypeKind(lhs.symbol.info))            ctx1.bb.emit(STORE_FIELD(lhs.symbol, true), tree.pos)            ctx1          } else {            var ctx1 = genLoadQualifier(lhs, ctx)            ctx1 = genLoad(rhs, ctx1, toTypeKind(lhs.symbol.info))            ctx1.bb.emit(STORE_FIELD(lhs.symbol, false), tree.pos)            ctx1          }        case Assign(lhs, rhs) =>          val ctx1 = genLoad(rhs, ctx, toTypeKind(lhs.symbol.info))          val Some(l) = ctx.method.lookupLocal(lhs.symbol)          ctx1.bb.emit(STORE_LOCAL(l), tree.pos)          ctx1        case _ =>          genLoad(tree, ctx, UNIT)      }    }    /**     * Generate code for trees that produce values on the stack     *     * @param tree The tree to be translated     * @param ctx  The current context     * @param expectedType The type of the value to be generated on top of the     *                     stack.     * @return The new context. The only thing that may change is the current      *         basic block (as the labels map is mutable).     */    private def genLoad(tree: Tree, ctx: Context, expectedType: TypeKind): Context = {      var generatedType = expectedType      if (settings.debug.value)        log("at line: " + (tree.pos).line.map(_.toString).getOrElse(tree.pos.toString))      /**       * Generate code for primitive arithmetic operations.       */      def genArithmeticOp(tree: Tree, ctx: Context, code: Int): Context = {        val Apply(fun @ Select(larg, _), args) = tree        var ctx1 = ctx        var resKind = toTypeKind(larg.tpe)        if (settings.debug.value) {          assert(args.length <= 1,                 "Too many arguments for primitive function: " + fun.symbol)          assert(resKind.isNumericType | resKind == BOOL,                 resKind.toString() + " is not a numeric or boolean type " +                 "[operation: " + fun.symbol + "]")        }        args match {          // unary operation          case Nil =>            ctx1 = genLoad(larg, ctx1, resKind)            code match {              case scalaPrimitives.POS =>                () // nothing               case scalaPrimitives.NEG =>                ctx1.bb.emit(CALL_PRIMITIVE(Negation(resKind)), larg.pos)              case scalaPrimitives.NOT =>                ctx1.bb.emit(CALL_PRIMITIVE(Arithmetic(NOT, resKind)), larg.pos)              case _ =>                abort("Unknown unary operation: " + fun.symbol.fullNameString +                      " code: " + code)            }            generatedType = resKind          // binary operation          case rarg :: Nil =>            resKind = getMaxType(larg.tpe :: rarg.tpe :: Nil);            if (scalaPrimitives.isShiftOp(code) || scalaPrimitives.isBitwiseOp(code))              assert(resKind.isIntType | resKind == BOOL,                   resKind.toString() + " incompatible with arithmetic modulo operation: " + ctx1);            ctx1 = genLoad(larg, ctx1, resKind);            ctx1 = genLoad(rarg,                           ctx1,  // check .NET size of shift arguments!                           if (scalaPrimitives.isShiftOp(code)) INT else resKind)            generatedType = resKind            code match {              case scalaPrimitives.ADD =>                ctx1.bb.emit(CALL_PRIMITIVE(Arithmetic(ADD, resKind)), tree.pos)              case scalaPrimitives.SUB =>                ctx1.bb.emit(CALL_PRIMITIVE(Arithmetic(SUB, resKind)), tree.pos)              case scalaPrimitives.MUL =>                ctx1.bb.emit(CALL_PRIMITIVE(Arithmetic(MUL, resKind)), tree.pos)              case scalaPrimitives.DIV =>                ctx1.bb.emit(CALL_PRIMITIVE(Arithmetic(DIV, resKind)), tree.pos)              case scalaPrimitives.MOD =>                ctx1.bb.emit(CALL_PRIMITIVE(Arithmetic(REM, resKind)), tree.pos)              case scalaPrimitives.OR  =>                ctx1.bb.emit(CALL_PRIMITIVE(Logical(OR, resKind)), tree.pos)              case scalaPrimitives.XOR =>                ctx1.bb.emit(CALL_PRIMITIVE(Logical(XOR, resKind)), tree.pos)              case scalaPrimitives.AND =>                ctx1.bb.emit(CALL_PRIMITIVE(Logical(AND, resKind)), tree.pos)              case scalaPrimitives.LSL =>                ctx1.bb.emit(CALL_PRIMITIVE(Shift(LSL, resKind)), tree.pos)                generatedType = resKind              case scalaPrimitives.LSR =>                ctx1.bb.emit(CALL_PRIMITIVE(Shift(LSR, resKind)), tree.pos)                generatedType = resKind              case scalaPrimitives.ASR =>                ctx1.bb.emit(CALL_PRIMITIVE(Shift(ASR, resKind)), tree.pos)                generatedType = resKind              case _ =>                abort("Unknown primitive: " + fun.symbol + "[" + code + "]")            }          case _ =>            abort("Too many arguments for primitive function: " + tree)        }        ctx1      }      /** Generate primitive array operations.       *       *  @param tree ...       *  @param ctx  ...       *  @param code ...       *  @return     ...       */      def genArrayOp(tree: Tree, ctx: Context, code: Int): Context = {        import scalaPrimitives._        val Apply(Select(arrayObj, _), args) = tree        val k = toTypeKind(arrayObj.tpe)        val ARRAY(elem) = k        var ctx1 = genLoad(arrayObj, ctx, k)        if (scalaPrimitives.isArrayGet(code)) {          // load argument on stack          if (settings.debug.value)            assert(args.length == 1,                   "Too many arguments for array get operation: " + tree);          ctx1 = genLoad(args.head, ctx1, INT)          generatedType = elem        } else if (scalaPrimitives.isArraySet(code)) {          if (settings.debug.value)            assert(args.length == 2,                   "Too many arguments for array set operation: " + tree);          ctx1 = genLoad(args.head, ctx1, INT)          ctx1 = genLoad(args.tail.head, ctx1, toTypeKind(args.tail.head.tpe))          // the following line should really be here, but because of bugs in erasure          // we pretend we generate whatever type is expected from us.          //generatedType = UNIT        } else          generatedType = INT        code match {          case ZARRAY_LENGTH =>            ctx1.bb.emit(CALL_PRIMITIVE(ArrayLength(BOOL)), tree.pos)          case BARRAY_LENGTH =>            ctx1.bb.emit(CALL_PRIMITIVE(ArrayLength(BYTE)), tree.pos)          case SARRAY_LENGTH =>            ctx1.bb.emit(CALL_PRIMITIVE(ArrayLength(SHORT)), tree.pos)          case CARRAY_LENGTH =>            ctx1.bb.emit(CALL_PRIMITIVE(ArrayLength(CHAR)), tree.pos)          case IARRAY_LENGTH =>            ctx1.bb.emit(CALL_PRIMITIVE(ArrayLength(INT)), tree.pos)          case LARRAY_LENGTH =>            ctx1.bb.emit(CALL_PRIMITIVE(ArrayLength(LONG)), tree.pos)          case FARRAY_LENGTH =>            ctx1.bb.emit(CALL_PRIMITIVE(ArrayLength(FLOAT)), tree.pos)          case DARRAY_LENGTH =>

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?