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 + -
显示快捷键?