checkers.scala

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

SCALA
601
字号
/* NSC -- new Scala compiler * Copyright 2005-2008 LAMP/EPFL * @author  Martin Odersky */// $Id: Checkers.scala 13819 2008-01-28 18:43:24Z odersky $package scala.tools.nsc.backend.icodeimport scala.collection.mutable.{Buffer, ListBuffer, Map, HashMap}import scala.tools.nsc.symtab._abstract class Checkers {  val global: Global  import global._  /** <p>   *    This class performs a set of checks similar to what the bytecode   *    verifier does. For each basic block, it checks that:   *  </p>   *  <ul>   *    <li>   *      for primitive operations: the type and numer of operands match   *      the type of the operation   *    </li>   *    <li>   *      for method calls: the method exists in the type of the receiver   *      and the number and type of arguments match the declared type of    *      the method.   *    </li>   *    <li>   *      for object creation: the constructor can be called.   *    </li>   *    <li>   *      for load/stores: the field/local/param exists and the type    *      of the value matches that of the target.   *    </li>   *  </ul>   *  <p>   *    For a control flow graph it checks that type stacks at entry to   *    each basic block 'agree':    *  </p>   *  <ul>   *    <li>they have the same length</li>   *    <li>there exists a lub for all types at the same position in stacks.</li>   *  </ul>   *   *  @author  Iulian Dragos   *  @version 1.0, 06/09/2005   *   *  @todo Better checks for <code>MONITOR_ENTER/EXIT</code>   *        Better checks for local var initializations   */  class ICodeChecker {    import icodes._    import opcodes._    var clasz: IClass = _    var method: IMethod = _    var code: Code = _    val in: Map[BasicBlock, TypeStack] = new HashMap()    val out: Map[BasicBlock, TypeStack] = new HashMap()    val emptyStack = new TypeStack()    val STRING        = REFERENCE(definitions.StringClass)    val SCALA_ALL     = REFERENCE(definitions.AllClass)    val SCALA_ALL_REF = REFERENCE(definitions.AllRefClass)//    val CASE_CLASS    = REFERENCE(definitions.getClass("scala.CaseClass"))    def checkICodes: Unit = {      Console.println("[[consistency check at beginning of phase " + globalPhase.name + "]]")      classes.values foreach check    }    def check(cls: IClass) {      log("Checking class " + cls)      clasz = cls      for (f1 <- cls.fields; f2 <- cls.fields if f1 ne f2)        if (f1.symbol.name == f2.symbol.name)          Checkers.this.global.error("Repetitive field name: " +                                     f1.symbol.fullNameString);      for (m1 <- cls.methods; m2 <- cls.methods if m1 ne m2)        if (m1.symbol.name == m2.symbol.name &&            m1.symbol.tpe =:= m2.symbol.tpe)          Checkers.this.global.error("Repetitive method: " +                                     m1.symbol.fullNameString);      clasz.methods.foreach(check)    }    /** Apply the give funtion to each pair of the cartesian product of     * l1 x l2.     */    def pairwise[a](l1: List[a], l2: List[a])(f: (a, a) => Unit) =      l1 foreach { x =>        l2 foreach { y => f(x, y) }      }    def check(m: IMethod) {      log("Checking method " + m)      method = m      if (!m.isDeferred)        check(m.code)    }    def check(c: Code) {      var worklist: Buffer[BasicBlock] = new ListBuffer()      def append(elems: List[BasicBlock]) = elems foreach appendBlock;      def appendBlock(bl: BasicBlock) =        if ( !worklist.exists(bl.==) )          worklist + bl;      in.clear;  out.clear;      code = c;      worklist + c.startBlock;      for (bl <- c.blocks) {        in  += (bl -> emptyStack)        out += (bl -> emptyStack)      }      while (worklist.length > 0) {        val block = worklist(0); worklist.trimStart(1);        val output = check(block, in(block));        if (output != out(block) ||            (out(block) eq emptyStack)) {          log("Output changed for block: " + block.fullString);          out(block) = output;          append(block.successors);          block.successors foreach meet;        }      }    }    /**      * Apply the meet operator of the stack lattice on bl's predecessors.     * :-). Compute the input to bl by checking that all stacks have the     * same length, and taking the lub of types at the same positions.     */    def meet(bl: BasicBlock) {      val preds = bl.predecessors      def meet2(s1: TypeStack, s2: TypeStack): TypeStack = {        if (s1 eq emptyStack) s2        else if (s2 eq emptyStack) s1         else {          if (s1.length != s2.length)            throw new CheckerError("Incompatible stacks: " + s1 + " and " + s2 + " in " + method + " at entry to block: " + bl);          new TypeStack(List.map2(s1.types, s2.types) (lub))        }      }      if (preds != Nil) {        in(bl) = (preds map out.apply) reduceLeft meet2;              log("Input changed for block: " + bl +" to: " + in(bl));      }    }    private var typeStack: TypeStack = null    private var instruction: Instruction = null    private var basicBlock: BasicBlock = null    /**     * Check the basic block to be type correct and return the     * produced type stack.     */    def check(b: BasicBlock, initial: TypeStack): TypeStack = {      log("** Checking block:\n" + b.fullString + " with initial stack:\n" + initial)      var stack = new TypeStack(initial)      this.typeStack = stack      this.basicBlock = b      def typeError(k1: TypeKind, k2: TypeKind) {        error(" expected: " + k1 + " but " + k2 + " found")      }      b traverse (instr => {        def checkStack(len: Int) {          if (stack.length < len)            ICodeChecker.this.error("Expected at least " + len + " elements on the stack", stack);          else            ()        }        def checkLocal(local: Local) {          method.lookupLocal(local.sym.name) match {            case None => error(" " + local + " is not defined in method " + method);            case _ => ()          }        }        def checkField(obj: TypeKind, field: Symbol) {          obj match {            case REFERENCE(sym) =>              if (sym.info.member(field.name) == NoSymbol)                 error(" " + field + " is not defined in class " + clasz);            case _ =>              error(" expected reference type, but " + obj + " found");          }        }        /** Checks that tpe is a subtype of one of the allowed types */        def checkType(tpe: TypeKind, allowed: TypeKind*) {          if (isOneOf(tpe, allowed: _*))            ()          else            error(tpe.toString() + " is not one of: " + allowed.toList.mkString("{", ", ", "}"));        }        /** Checks that the 2 topmost elements on stack are of the         *  kind TypeKind.         */        def checkBinop(kind: TypeKind) {          val (a, b) = stack.pop2          checkType(a, kind)          checkType(b, kind)        }        /** Check that arguments on the stack match method params. */        def checkMethodArgs(method: Symbol) {          val params = method.info.paramTypes          checkStack(params.length)          params.reverse.foreach( (tpe) => checkType(stack.pop, toTypeKind(tpe)))        }        /** Checks that the object passed as receiver has a method         *  <code>method</code> and that it is callable from the current method.         *         *  @param receiver ...         *  @param method   ...         */        def checkMethod(receiver: TypeKind, method: Symbol) =           receiver match {            case REFERENCE(sym) =>              checkBool(sym.info.member(method.name) != NoSymbol,                        "Method " + method + " does not exist in " + sym.fullNameString);              if (method hasFlag Flags.PRIVATE)                checkBool(method.owner == clasz.symbol,                          "Cannot call private method of " + method.owner.fullNameString                           + " from " + clasz.symbol.fullNameString);              else if (method hasFlag Flags.PROTECTED)                checkBool(clasz.symbol isSubClass method.owner,                          "Cannot call protected method of " + method.owner.fullNameString                          + " from " + clasz.symbol.fullNameString);            case ARRAY(_) =>              checkBool(receiver.toType.member(method.name) != NoSymbol,                        "Method " + method + " does not exist in " + receiver)            case t =>              error("Not a reference type: " + t)          }        def checkBool(cond: Boolean, msg: String) =          if (cond) () else error(msg)        this.instruction = instr        if (settings.debug.value) {          log("PC: " + instr)          log("stack: " + stack)          log("================")        }        instr match {          case THIS(clasz) =>            stack push toTypeKind(clasz.tpe)          case CONSTANT(const) =>            stack push toTypeKind(const.tpe)          case LOAD_ARRAY_ITEM(kind) =>            checkStack(2)            (stack.pop2: @unchecked) match {              case (INT, ARRAY(elem)) =>                 if (!(elem <:< kind))                  typeError(kind, elem);                stack.push(elem);              case (a, b) =>                error(" expected and INT and a array reference, but " +                    a + ", " + b + " found");            }         case LOAD_LOCAL(local) =>           checkLocal(local)           stack.push(local.kind)         case LOAD_FIELD(field, isStatic) =>           if (isStatic) {             // the symbol's owner should contain it's field, but             // this is already checked by the type checker, no need             // to redo that here           } else {             checkStack(1)             val obj = stack.pop             checkField(obj, field)           }

⌨️ 快捷键说明

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