cleanup.scala

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

SCALA
569
字号
            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("testEqual")), testForNumberOrBoolean)          case nme.NE =>             (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("testNotEqual")), testForNumberOrBoolean)          case nme.LT =>             (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("testLessThan")), testForNumber)          case nme.LE =>             (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("testLessOrEqualThan")), testForNumber)          case nme.GE =>             (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("testGreaterOrEqualThan")), testForNumber)          case nme.GT =>             (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("testGreaterThan")), testForNumber)        /* Conversions */          case nme.toByte =>            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("toByte")), testForNumber)          case nme.toShort =>            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("toShort")), testForNumber)          case nme.toChar =>            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("toCharacter")), testForNumber)          case nme.toInt =>            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("toInteger")), testForNumber)          case nme.toLong =>            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("toLong")), testForNumber)          case nme.toFloat =>            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("toFloat")), testForNumber)          case nme.toDouble =>            (definitions.getMember(definitions.BoxesRunTimeClass, newTermName("toDouble")), testForNumber)        }                /* Transforms the result of a reflective call (always an AnyRef) to         * the actual result value (an AnyRef too). The transformation         * depends on the method's static return type.         * - for units (void), the reflective call will return null: a new         *   boxed unit is generated.         * - for arrays, the reflective call will return an unboxed array:         *   the resulting array is boxed.         * - otherwise, the value is simply casted to the expected type. This         *   is enough even for value (int et al.) values as the result of         *   a dynamic call will box them as a side-effect. */        def fixResult(resType: Type)(tree: Tree): Tree =          localTyper.typed {            if (resType.typeSymbol == UnitClass)              Block (                List(tree),                gen.mkAttributedRef(BoxedUnit_UNIT)              )            else if (resType.typeSymbol == ArrayClass) {              val sym = currentOwner.newValue(tree.pos, newTermName(unit.fresh.newName)) setInfo ObjectClass.tpe              Block(                List(ValDef(sym, tree)),                If(                  Apply(Select(Literal(Constant(null)), Any_==), List(gen.mkAttributedRef(sym))),                  Literal(Constant(null)),                  Apply(                    Select(                      gen.mkAttributedRef(ScalaRunTimeModule),                      ScalaRunTimeModule.tpe.member(nme.boxArray)                    ),                    List(gen.mkAttributedRef(sym))                  )                )              )            }            else if (resType.typeSymbol == ObjectClass) // TODO: remove the cast always when unnecessary.              tree            else              gen.mkAttributedCast(tree, resType)          }                /* Transforms the parameters of a dynamic apply (always AnyRefs) to         * something compatible with reclective calls. The transformation depends         * on the method's static parameter types.         * - for (unboxed) arrays, the (non-null) value is tested for its erased         *   type. If it is a boxed array, the array is unboxed. If it is an         *   unboxed array, it is left alone. */        def fixParams(params: List[Tree], paramTypes: List[Type]): List[Tree] =          (params zip paramTypes) map { case (param, paramType) =>            localTyper.typed {              if (paramType.typeSymbol == ArrayClass) {                val sym = currentOwner.newValue(tree.pos, newTermName(unit.fresh.newName)) setInfo ObjectClass.tpe                val arrayType = {                  assert(paramType.typeArgs.length == 1)                  paramType.typeArgs(0).normalize                }                Block(                  List(ValDef(sym, param)),                  If(                    Apply(Select(Literal(Constant(null)), Any_==), List(gen.mkAttributedRef(sym))),                    Literal(Constant(null)),                    If(                      Apply(                        TypeApply(                          gen.mkAttributedSelect(gen.mkAttributedRef(sym), definitions.Object_isInstanceOf),                          List(TypeTree(BoxedArrayClass.tpe.normalize))                        ),                        List()                      ),                      Apply(                        Select(gen.mkAttributedCast(gen.mkAttributedRef(sym), BoxedArrayClass.tpe), getMember(BoxedArrayClass, nme.unbox)),                        List(Literal(Constant(arrayType)))                      ),                      gen.mkAttributedRef(sym)                    )                  )                )              }              else                param            }          }                  def callAsOperator(paramTypes: List[Type], resType: Type): Tree = localTyper.typed {          if (getPrimitiveReplacementForStructuralCall isDefinedAt ad.symbol.name) {            val (operator, test) = getPrimitiveReplacementForStructuralCall(ad.symbol.name)            If(              test,              Apply(                gen.mkAttributedRef(operator),                qual :: fixParams(params, paramTypes)              ),              callAsMethod(paramTypes, resType)            )          }          else callAsMethod(paramTypes, resType)        }                def callAsMethod(paramTypes: List[Type], resType: Type): Tree = localTyper.typed {          val invokeExc =            currentOwner.newValue(tree.pos, newTermName(unit.fresh.newName)) setInfo InvocationTargetExceptionClass.tpe          Try(            Apply(              Select(                Apply(                  gen.mkAttributedRef(reflectiveMethodCache(tree.pos, ad.symbol.name.toString, paramTypes)),                  List(Apply(Select(qual, ObjectClass.tpe.member(nme.getClass_)), Nil))                ),                MethodClass.tpe.member(nme.invoke_)              ),              List(                qual,                ArrayValue(TypeTree(ObjectClass.tpe), fixParams(params, paramTypes))              )            ),            List(CaseDef(              Bind(invokeExc, Typed(Ident(nme.WILDCARD), TypeTree(InvocationTargetExceptionClass.tpe))),              EmptyTree,              Throw(Apply(Select(Ident(invokeExc), nme.getCause), Nil))            )),            EmptyTree          )        }                def mayRequirePrimitiveReplacement: Boolean = {                  def isBoxed(sym: Symbol): Boolean =            if (forCLDC) {              (sym isNonBottomSubClass ByteClass) ||              (sym isNonBottomSubClass ShortClass) ||              (sym isNonBottomSubClass CharClass) ||              (sym isNonBottomSubClass IntClass) ||              (sym isNonBottomSubClass LongClass)            }            else ((sym isNonBottomSubClass BoxedNumberClass) ||              (!forMSIL && (sym isNonBottomSubClass BoxedCharacterClass)))                    val sym = qual.tpe.typeSymbol          (sym == definitions.ObjectClass) || isBoxed(sym)                  }                /* This creates the tree that does the reflective call (see general comment         * on the apply-dynamic tree for its format). This tree is simply composed         * of three succesive calls, first to getClass on the callee, then to         * getMethod on the classs, then to invoke on the method.         * - getMethod needs an array of classes for choosing one amongst many         *   overloaded versions of the method. This is provided by paramTypeClasses         *   and must be done on the static type as Scala's dispatching is static on         *   the parameters.         * - invoke needs an array of AnyRefs that are the method's arguments. The         *   erasure phase guarantees that any parameter passed to a dynamic apply         *   is compatible (through boxing). Boxed ints et al. is what invoke expects         *   when the applied method expects ints, hence no change needed there.         *   On the other hand, arrays must be dealt with as they must be entered         *   unboxed in the parameter array of invoke. fixParams is responsible for          *   that.         * - in the end, the result of invoke must be fixed, again to deal with arrays.         *   This is provided by fixResult. fixResult will cast the invocation's result         *   to the method's return type, which is generally ok, except when this type         *   is a value type (int et al.) in which case it must cast to the boxed version         *   because invoke only returns object and erasure made sure the result is         *   expected to be an AnyRef. */        val t: Tree = ad.symbol.tpe match {          case MethodType(paramTypes, resType) =>            assert(params.length == paramTypes.length)            atPos(tree.pos)(localTyper.typed {              fixResult(if (isValueClass(resType.typeSymbol)) boxedClass(resType.typeSymbol).tpe else resType) {                if (mayRequirePrimitiveReplacement)                  callAsOperator(paramTypes, resType)                else                  callAsMethod(paramTypes, resType)              }            })        }                /* For testing purposes, the dynamic application's condition         * can be printed-out in great detail. Remove? */        if (settings.debug.value) {          Console.println(            "Dynamically applying '" + qual + "." + ad.symbol.name +            "(" + params.map(_.toString).mkString(", ") + ")' with"          )          ad.symbol.tpe match {            case MethodType(paramTypes, resType) =>              Console.println(                "  - declared parameters' types: " +                (paramTypes.map(_.toString)).mkString("'",", ","'"))              Console.println(                "  - passed arguments' types:    " +                (params.map(_.toString)).mkString("'",", ","'"))              Console.println(                "  - result type:                '" +                resType.toString + "'")          }          Console.println("  - resulting code:    '" + t + "'")        }        /* We return the dynamic call tree, after making sure no other         * clean-up transformation are to be applied on it. */        transform(t)      /* end of dynamic call transformer. */      case Template(parents, self, body) =>        localTyper = typer.atOwner(tree, currentOwner)        if (!forMSIL) {          classConstantMeth.clear          newDefs.clear          val body1 = transformTrees(body)          copy.Template(tree, parents, self, newDefs.toList ::: body1)        }        else super.transform(tree)      case Literal(c) if (c.tag == ClassTag) && !forMSIL=>        val tpe = c.typeValue        atPos(tree.pos) {          localTyper.typed {            if (isValueClass(tpe.typeSymbol) && !forCLDC)              Select(gen.mkAttributedRef(javaBoxClassModule(tpe.typeSymbol)), "TYPE")            else if (settings.target.value != "jvm-1.5" && !forMSIL)              Apply(                gen.mkAttributedRef(classConstantMethod(tree.pos, signature(tpe))),                List())            else tree          }        }      /* MSIL requires that the stack is empty at the end of a try-block.       * Hence, we here rewrite all try blocks with a result != {Unit, All} such that they       * store their result in a local variable. The catch blocks are adjusted as well.       * The try tree is subsituted by a block whose result expression is read of that variable. */      case theTry @ Try(block, catches, finalizer)         if theTry.tpe.typeSymbol != definitions.UnitClass && theTry.tpe.typeSymbol != definitions.AllClass =>        val tpe = theTry.tpe.widen        val tempVar = currentOwner.newValue(theTry.pos, unit.fresh.newName("exceptionResult"))          .setInfo(tpe).setFlag(Flags.MUTABLE)        val newBlock = super.transform(Block(Nil, Assign(Ident(tempVar), transform(block))))        val newCatches = for (CaseDef(pattern, guard, body) <- catches) yield {          CaseDef(            super.transform(pattern),            super.transform(guard),            Block(Nil, Assign(Ident(tempVar), super.transform(body)))          )        }        val newTry = Try(newBlock, newCatches, super.transform(finalizer))        val res = Block(List(ValDef(tempVar, EmptyTree), newTry), Ident(tempVar))        localTyper.typed(res)              case _ =>        super.transform(tree)    }  } // CleanUpTransformer}

⌨️ 快捷键说明

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