genmsil.scala
来自「JAVA 语言的函数式编程扩展」· SCALA 代码 · 共 1,568 行 · 第 1/5 页
SCALA
1,568 行
/* NSC -- new scala compiler * Copyright 2005-2008 LAMP/EPFL * @author Nikolay Mihaylov */// $Id: GenMSIL.scala 14110 2008-02-22 15:23:15Z michelou $package scala.tools.nsc.backend.msilimport java.io.Fileimport java.nio.{ByteBuffer, ByteOrder}import scala.collection.mutable.{Map, HashMap, HashSet, Stack}import scala.tools.nsc.symtab._import scala.tools.nsc.util.Positionimport ch.epfl.lamp.compiler.msil.{Type => MsilType, _}import ch.epfl.lamp.compiler.msil.emit._/** */abstract class GenMSIL extends SubComponent { import global._ import loaders.clrTypes import clrTypes.{types, constructors, methods, fields} import icodes._ import icodes.opcodes._ /** Create a new phase */ override def newPhase(p: Phase) = new MsilPhase(p) val phaseName = "msil" /** MSIL code generation phase */ class MsilPhase(prev: Phase) extends GlobalPhase(prev) { def name = phaseName override def newFlags = phaseNewFlags override def erasedTypes = true override def run { if (settings.debug.value) inform("[running phase " + name + " on icode]") val codeGenerator = new BytecodeGenerator //classes is ICodes.classes, a HashMap[Symbol, IClass] classes.values foreach codeGenerator.findEntryPoint codeGenerator.initAssembly classes.values foreach codeGenerator.createTypeBuilder classes.values foreach codeGenerator.createClassMembers try { classes.values foreach codeGenerator.genClass } finally { codeGenerator.writeAssembly } } override def apply(unit: CompilationUnit) { abort("MSIL works on icode classes, not on compilation units!") } } /** * MSIL bytecode generator. * */ class BytecodeGenerator { val MODULE_INSTANCE_NAME = "MODULE$" import clrTypes.{VOID => MVOID, BOOLEAN => MBOOL, UBYTE => MBYTE, SHORT => MSHORT, CHAR => MCHAR, INT => MINT, LONG => MLONG, FLOAT => MFLOAT, DOUBLE => MDOUBLE, OBJECT => MOBJECT, STRING => MSTRING, STRING_ARRAY => MSTRING_ARRAY, SCALA_SYMTAB_ATTR => SYMTAB_ATTRIBUTE, SYMTAB_CONSTR => SYMTAB_ATTRIBUTE_CONSTRUCTOR, SYMTAB_DEFAULT_CONSTR => SYMTAB_ATTRIBUTE_EMPTY_CONSTRUCTOR} val EXCEPTION = clrTypes.getType("System.Exception") val MBYTE_ARRAY = clrTypes.mkArrayType(MBYTE) val ICLONEABLE = clrTypes.getType("System.ICloneable") val MEMBERWISE_CLONE = MOBJECT.GetMethod("MemberwiseClone", MsilType.EmptyTypes) val MMONITOR = clrTypes.getType("System.Threading.Monitor") val MMONITOR_ENTER = MMONITOR.GetMethod("Enter", Array(MOBJECT)) val MMONITOR_EXIT = MMONITOR.GetMethod("Exit", Array(MOBJECT)) val MSTRING_BUILDER = clrTypes.getType("System.Text.StringBuilder") val MSTRING_BUILDER_CONSTR = MSTRING_BUILDER.GetConstructor(MsilType.EmptyTypes) val MSTRING_BUILDER_TOSTRING = MSTRING_BUILDER.GetMethod("ToString", MsilType.EmptyTypes) val TYPE_FROM_HANDLE = clrTypes.getType("System.Type").GetMethod("GetTypeFromHandle", Array(clrTypes.getType("System.RuntimeTypeHandle"))) val INT_PTR = clrTypes.getType("System.IntPtr") val JOBJECT = definitions.ObjectClass val JSTRING = definitions.StringClass var JSTRING_SUBSTRING_INT_INT: Symbol = _ val SystemConvert = clrTypes.getType("System.Convert") val objParam = Array(MOBJECT)// val toBool: MethodInfo = SystemConvert.GetMethod("ToBoolean", objParam) val toByte: MethodInfo = SystemConvert.GetMethod("ToByte", objParam) val toShort: MethodInfo = SystemConvert.GetMethod("ToInt16", objParam) val toChar: MethodInfo = SystemConvert.GetMethod("ToChar", objParam) val toInt: MethodInfo = SystemConvert.GetMethod("ToInt32", objParam) val toLong: MethodInfo = SystemConvert.GetMethod("ToInt64", objParam) val toFloat: MethodInfo = SystemConvert.GetMethod("ToSingle", objParam) val toDouble: MethodInfo = SystemConvert.GetMethod("ToDouble", objParam) //val boxedUnit: FieldInfo = msilType(definitions.BoxedUnitModule.info).GetField("UNIT") val boxedUnit: FieldInfo = fields(definitions.BoxedUnit_UNIT.asInstanceOf[clrTypes.global.Symbol]) // Scala attributes // symtab.Definitions -> object (singleton..) val SerializableAttr = definitions.SerializableAttr.tpe val CloneableAttr = definitions.getClass("scala.cloneable").tpe val TransientAtt = definitions.getClass("scala.transient").tpe // remoting: the architectures are too different, no mapping (no portable code // possible) // java instance methods that are mapped to static methods in .net // these will need to be called with OpCodes.Call (not Callvirt) val dynToStatMapped: HashSet[Symbol] = new HashSet() initMappings() // ******************************************************************** // Create the mappings private def initMappings() { mapType(definitions.AnyClass, MOBJECT) mapType(definitions.AnyRefClass, MOBJECT) //mapType(definitions.AllRefClass, clrTypes.getType("scala.AllRef$")) //mapType(definitions.AllClass, clrTypes.getType("scala.All$")) // FIXME: for some reason the upper two lines map to null mapType(definitions.AllRefClass, EXCEPTION) mapType(definitions.AllClass, EXCEPTION) val jEmpty = new Array[Type](0) val jString1 = Array(JSTRING.tpe) val jInt1 = Array(definitions.IntClass.tpe) val jInt2 = Array(definitions.IntClass.tpe, definitions.IntClass.tpe) val jLong1 = Array(definitions.LongClass.tpe) val jStringInt = Array(JSTRING.tpe, definitions.IntClass.tpe) val jChar2 = Array(definitions.CharClass.tpe, definitions.CharClass.tpe) val mObject1 = Array(MOBJECT) val mString1 = Array(MSTRING) val mString2 = Array(MSTRING, MSTRING) val mChar1 = Array(MCHAR) val mCharInt = Array(MCHAR, MINT) JSTRING_SUBSTRING_INT_INT = lookupMethod(JSTRING, "substring", jInt2) mapMethod(JOBJECT, "clone", MOBJECT, "MemberwiseClone") mapMethod(JOBJECT, nme.equals_, MOBJECT, "Equals") mapMethod(JOBJECT, nme.hashCode_, MOBJECT, "GetHashCode") mapMethod(JOBJECT, nme.toString_, MOBJECT, "ToString") mapMethod(JOBJECT, nme.finalize_, MOBJECT, "Finalize") mapMethod(JOBJECT, nme.wait_, jEmpty, MMONITOR, "Wait", mObject1) mapMethod(JOBJECT, nme.wait_, jLong1, MMONITOR, "Wait", Array(MOBJECT, MINT)) mapMethod(JOBJECT, nme.notify_, jEmpty, MMONITOR, "Pulse", mObject1) mapMethod(JOBJECT, nme.notifyAll_, jEmpty, MMONITOR, "PulseAll", mObject1) mapMethod(JSTRING, "compareTo",MSTRING, "CompareTo") mapMethod(JSTRING, "length", MSTRING, "get_Length") mapMethod(JSTRING, "charAt", MSTRING, "get_Chars") mapMethod(JSTRING, "concat", jString1, MSTRING, "Concat", mString2) mapMethod(JSTRING, "indexOf", jInt1, MSTRING, "IndexOf", mChar1) mapMethod(JSTRING, "indexOf", jInt2, MSTRING, "IndexOf", mCharInt) mapMethod(JSTRING, "indexOf", jString1, MSTRING, "IndexOf") mapMethod(JSTRING, "indexOf", jStringInt, MSTRING, "IndexOf") mapMethod(JSTRING, "lastIndexOf", jInt1, MSTRING, "LastIndexOf", mChar1) mapMethod(JSTRING, "lastIndexOf", jInt2, MSTRING, "LastIndexOf", mCharInt) mapMethod(JSTRING, "lastIndexOf", jString1, MSTRING, "LastIndexOf") mapMethod(JSTRING, "lastIndexOf", jStringInt, MSTRING, "LastIndexOf") mapMethod(JSTRING, "toLowerCase", jEmpty, MSTRING, "ToLower") mapMethod(JSTRING, "toUpperCase", jEmpty, MSTRING, "ToUpper") mapMethod(JSTRING, "startsWith", jString1, MSTRING, "StartsWith") mapMethod(JSTRING, "endsWith", jString1, MSTRING, "EndsWith") mapMethod(JSTRING, "substring", jInt1, MSTRING, "Substring") mapMethod(JSTRING, "substring", jInt2, MSTRING, "Substring") mapMethod(JSTRING, "trim", jEmpty, MSTRING, "Trim") mapMethod(JSTRING, "intern", jEmpty, MSTRING, "Intern", mString1) mapMethod(JSTRING, "replace", jChar2, MSTRING, "Replace") mapMethod(JSTRING, "toCharArray", MSTRING, "ToCharArray") mapType(definitions.BooleanClass, MBOOL) mapType(definitions.ByteClass, MBYTE) mapType(definitions.ShortClass, MSHORT) mapType(definitions.CharClass, MCHAR) mapType(definitions.IntClass, MINT) mapType(definitions.LongClass, MLONG) mapType(definitions.FloatClass, MFLOAT) mapType(definitions.DoubleClass, MDOUBLE) } var clasz: IClass = _ var method: IMethod = _ var code: Code = _ var massembly: AssemblyBuilder = _ var mmodule: ModuleBuilder = _ var mcode: ILGenerator = _ var assemName: String = _ var firstSourceName = "" var outDir: File = _ def initAssembly() { assemName = settings.assemname.value if (assemName == "") { if (entryPoint != null) { assemName = msilName(entryPoint.enclClass) // remove the $ at the end (from module-name) assemName = assemName.substring(0, assemName.length() - 1) } else { // assuming filename of first source file assert(firstSourceName.endsWith(".scala"), "Source file doesn't end with .scala") assemName = firstSourceName.substring(0, firstSourceName.length() - 6) } } else { if (assemName.endsWith(".msil")) assemName = assemName.substring(0, assemName.length()-5) if (assemName.endsWith(".il")) assemName = assemName.substring(0, assemName.length()-3) val f: File = new File(assemName) outDir = f.getParentFile() assemName = f.getName() } if (outDir == null) outDir = new File(".") val assemblyName = new AssemblyName() assemblyName.Name = assemName massembly = AssemblyBuilder.DefineDynamicAssembly(assemblyName) val moduleName = assemName + (if (entryPoint == null) ".dll" else ".exe") // filename here: .dll or .exe (in both parameters), second: give absolute-path mmodule = massembly.DefineDynamicModule(moduleName, new File(outDir, moduleName).getAbsolutePath()) assert (mmodule != null) initMappings() } /** * Form of the custom Attribute parameter (Ecma-335.pdf) * - p. 163 for CustomAttrib Form, * - p. 164 for FixedArg Form (Array and Element) (if array or not is known!) * !! least significant *byte* first if values longer than one byte !! * * 1: Prolog (unsigned int16, value 0x0001) -> symtab[0] = 0x01, symtab[1] = 0x00 * 2: FixedArgs (directly the data, get number and types from related constructor) * 2.1: length of the array (unsigned int32, take care on order of the 4 bytes) * 2.2: the byte array data * 3: NumNamed (unsigned int16, number of named fields and properties, 0x0000) * **/ def addSymtabAttribute(sym: Symbol, tBuilder: TypeBuilder) { currentRun.symData.get(sym) match { case Some(pickle) => val symtab: Array[Byte] = new Array[Byte](pickle.writeIndex + 8) symtab(0) = 1.toByte var size:Int = pickle.writeIndex for (i <- 2 until 6) { symtab(i) = (size & 0xff).toByte size = size >> 8 } System.arraycopy(pickle.bytes, 0, symtab, 6, pickle.writeIndex) tBuilder.SetCustomAttribute(SYMTAB_ATTRIBUTE_CONSTRUCTOR, symtab) currentRun.symData -= sym currentRun.symData -= sym.linkedSym //log("Generated ScalaSig Attr for " + sym)//debug case _ => log("Could not find pickle information for " + sym) } } def addAttributes(member: ICustomAttributeSetter, attributes: List[AnnotationInfo]) { return // FIXME if (settings.debug.value) log("creating attributes: " + attributes + " for member : " + member) for (attr@ AnnotationInfo(typ, annArgs, nvPairs) <- attributes ; if attr.isConstant) /* !typ.typeSymbol.hasFlag(Flags.JAVA) */ {// assert(consts.length <= 1,// "too many constant arguments for attribute; "+consts.toString()) // Problem / TODO having the symbol of the attribute type would be nicer // (i hope that type.typeSymbol is the same as the one in types2create) // AND: this will crash if the attribute Type is already compiled (-> not a typeBuilder) // when this is solved, types2create will be the same as icodes.classes, thus superfluous
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?