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