From daf9227e7323c31257fe34bb4f2d38c7c1abbff1 Mon Sep 17 00:00:00 2001 From: Lukas Rytz Date: Fri, 22 Jan 2010 16:55:23 +0000 Subject: fix several issues in .net backend / type parsing. --- build.xml | 30 ++++-- lib/msil.jar.desired.sha1 | 2 +- .../scala/tools/nsc/backend/msil/GenMSIL.scala | 112 ++++++++++++++------- src/msil/ch/epfl/lamp/compiler/msil/PEFile.java | 16 ++- src/msil/ch/epfl/lamp/compiler/msil/PEModule.java | 13 ++- 5 files changed, 121 insertions(+), 52 deletions(-) diff --git a/build.xml b/build.xml index 2abc7a8c3f..ee747fc98c 100644 --- a/build.xml +++ b/build.xml @@ -125,10 +125,15 @@ END-USER TARGETS + description="Requires libraries (MSIL, FJBG) to be rebuilt. Add this target before any other if class file format is incompatible."> + + + + @@ -529,19 +534,27 @@ QUICK BUILD (QUICK) - - - - + + + + + + + + + + + + @@ -1158,6 +1171,7 @@ LIBRARIES (MSIL, FJBG maybe later) classpath="${build-libs.dir}/classes/msil" includes="**/*.java" excludes="**/tests/**" + debug="true" target="1.5" source="1.4"> @@ -1216,8 +1230,10 @@ LIBRARIES (MSIL, FJBG maybe later) - - + + + + diff --git a/lib/msil.jar.desired.sha1 b/lib/msil.jar.desired.sha1 index 20dd30e75c..457da38de3 100644 --- a/lib/msil.jar.desired.sha1 +++ b/lib/msil.jar.desired.sha1 @@ -1 +1 @@ -07c906973c7082c3a958e4e56793c005d6404e50 ?msil.jar +92c69c96c171b36e7ad36fb9c77fec822d766876 ?msil.jar diff --git a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala index d9261a3006..794f05ce17 100644 --- a/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala +++ b/src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala @@ -248,7 +248,7 @@ abstract class GenMSIL extends SubComponent { assemblyName.Name = assemName massembly = AssemblyBuilderFactory.DefineDynamicAssembly(assemblyName) - moduleName = assemName + (if (entryPoint == null) ".dll" else ".exe") + 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()) @@ -613,6 +613,7 @@ abstract class GenMSIL extends SubComponent { beginExBlock.clear() beginCatchBlock.clear() endExBlock.clear() + endFinallyLabels.clear() } def genBlocks(blocks: List[BasicBlock], previous: BasicBlock = null) { @@ -630,10 +631,13 @@ abstract class GenMSIL extends SubComponent { val beginCatchBlock = new HashMap[BasicBlock, ExceptionHandler]() val endExBlock = new HashMap[BasicBlock, List[ExceptionHandler]]() - // when emitting the code (genBlock), the number of currently active try / catch - // blocks. When seeing a `RETURN' inside a try / catch, we need to replace - // it with Leave - var currentHandlers = 0 + /** When emitting the code (genBlock), the number of currently active try / catch + * blocks. When seeing a `RETURN' inside a try / catch, we need to + * - store the result in a local (if it's not UNIT) + * - emit `Leave handlerReturnLabel` instead of the Return + * - emit code at the end: load the local and return its value + */ + var currentHandlers = new Stack[ExceptionHandler] // The IMethod the Local/Label/Kind below belong to var handlerReturnMethod: IMethod = _ // Stores the result when returning inside an exception block @@ -655,6 +659,13 @@ abstract class GenMSIL extends SubComponent { (handlerReturnLocal, handlerReturnLabel) } + /** For try/catch nested inside a finally, we can't use `Leave OutsideFinally`, the + * Leave target has to be inside the finally (and it has to be the `endfinally` instruction). + * So for every finalizer, we have a label which marks the place of the `endfinally`, + * nested try/catch blocks will leave there. + */ + val endFinallyLabels = new HashMap[ExceptionHandler, Label]() + /** Computes which blocks are the beginning / end of a try or catch block */ private def computeExceptionMaps(blocks: List[BasicBlock], m: IMethod): List[BasicBlock] = { val visitedBlocks = new HashSet[BasicBlock]() @@ -826,11 +837,14 @@ abstract class GenMSIL extends SubComponent { var lastLineNr: Int = 0 + // EndExceptionBlock must happen before MarkLabel because it adds the // Leave instruction. Otherwise, labels(block) points to the Leave // (inside the catch) instead of the instruction afterwards. for (handlers <- endExBlock.get(block); exh <- handlers) { - currentHandlers -= 1 + currentHandlers.pop() + for (l <- endFinallyLabels.get(exh)) + mcode.MarkLabel(l) mcode.EndExceptionBlock() } @@ -848,7 +862,7 @@ abstract class GenMSIL extends SubComponent { } } for (handlers <- beginExBlock.get(block); exh <- handlers) { - currentHandlers += 1 + currentHandlers.push(exh) mcode.BeginExceptionBlock() } @@ -904,9 +918,7 @@ abstract class GenMSIL extends SubComponent { case FLOAT => mcode.Emit(OpCodes.Ldelem_R4) case DOUBLE => mcode.Emit(OpCodes.Ldelem_R8) case REFERENCE(cls) => mcode.Emit(OpCodes.Ldelem_Ref) - - // case ARRAY(elem) is not possible, for Array[Array[Int]], the - // load will be case REFERENCE(java.lang.Object) + case ARRAY(elem) => mcode.Emit(OpCodes.Ldelem_Ref) // case UNIT is not possible: an Array[Unit] will be an // Array[scala.runtime.BoxedUnit] (-> case REFERENCE) @@ -951,8 +963,9 @@ abstract class GenMSIL extends SubComponent { case FLOAT => mcode.Emit(OpCodes.Stelem_R4) case DOUBLE => mcode.Emit(OpCodes.Stelem_R8) case REFERENCE(cls) => mcode.Emit(OpCodes.Stelem_Ref) + case ARRAY(elem) => mcode.Emit(OpCodes.Stelem_Ref) // @TODO: test this! (occurs when calling a Array[Object]* vararg param method) - // case UNIT / ARRRAY are not possible (see comment at LOAD_ARRAY_ITEM) + // case UNIT not possible (see comment at LOAD_ARRAY_ITEM) } case STORE_LOCAL(local) => @@ -1183,11 +1196,12 @@ abstract class GenMSIL extends SubComponent { mcode.Emit(OpCodes.Br, defaultTarget) case JUMP(whereto) => - val (leaveHandler, leaveFinally) = leavesHandler(block, whereto, method.exh) + val (leaveHandler, leaveFinally, lfTarget) = leavesHandler(block, whereto) if (leaveHandler) { - if (leaveFinally) - mcode.Emit(OpCodes.Endfinally) - else + if (leaveFinally) { + if (lfTarget.isDefined) mcode.Emit(OpCodes.Leave, lfTarget.get) + else mcode.Emit(OpCodes.Endfinally) + } else mcode.Emit(OpCodes.Leave, labels(whereto)) } else if (next != whereto) mcode.Emit(OpCodes.Br, labels(whereto)) @@ -1198,13 +1212,13 @@ abstract class GenMSIL extends SubComponent { // kind is TypeKind val isFloat = kind == FLOAT || kind == DOUBLE val emit = (c: TestOp, l: Label) => emitBr(c, l, isFloat) - emitCondBr(block, cond, success, failure, next, method.exh, emit) + emitCondBr(block, cond, success, failure, next, emit) case CZJUMP(success, failure, cond, kind) => - emitCondBr(block, cond, success, failure, next, method.exh, emitBrBool(_, _)) + emitCondBr(block, cond, success, failure, next, emitBrBool(_, _)) case RETURN(kind) => - if (currentHandlers == 0) + if (currentHandlers.isEmpty) mcode.Emit(OpCodes.Ret) else { val (local, label) = returnFromHandler(kind) @@ -1362,24 +1376,44 @@ abstract class GenMSIL extends SubComponent { ////////////////////// branches /////////////////////// - def leavesHandler(from: BasicBlock, to: BasicBlock, handlers: List[ExceptionHandler]) = - if (currentHandlers == 0) (false, false) + /** Returns a Triple (Boolean, Boolean, Option[Label]) + * - wether the jump leaves some exception block (try / catch / finally) + * - wether the it leaves a finally handler (finally block, but not it's try / catch) + * - a label where to jump for leaving the finally handler + * . None to leave directly using `endfinally` + * . Some(label) to emit `leave label` (for try / catch inside a finally handler) + */ + def leavesHandler(from: BasicBlock, to: BasicBlock): (Boolean, Boolean, Option[Label]) = + if (currentHandlers.isEmpty) (false, false, None) else { - val h = handlers.find(e => { - e.covers(from) != e.covers(to) || - e.blocks.contains(from) != e.blocks.contains(to) - }) - if (h.isDefined) { - val leavesFinalizerHandler = - h.get.cls == NoSymbol && h.get.blocks.contains(from) != h.get.blocks.contains(to) - (true, leavesFinalizerHandler) - } else (false, false) + val h = currentHandlers.head + val leaveHead = { h.covers(from) != h.covers(to) || + h.blocks.contains(from) != h.blocks.contains(to) } + if (leaveHead) { + // we leave the innermost exception block. + // find out if we also leave som e `finally` handler + currentHandlers.find(e => { + e.cls == NoSymbol && e.blocks.contains(from) != e.blocks.contains(to) + }) match { + case Some(finallyHandler) => + if (h == finallyHandler) { + // the finally handler is the innermost, so we can emit `endfinally` directly + (true, true, None) + } else { + // we need to `Leave` to the `endfinally` of the next outer finally handler + val l = endFinallyLabels.getOrElseUpdate(finallyHandler, mcode.DefineLabel()) + (true, true, Some(l)) + } + case None => + (true, false, None) + } + } else (false, false, None) } def emitCondBr(block: BasicBlock, cond: TestOp, success: BasicBlock, failure: BasicBlock, - next: BasicBlock, handlers: List[ExceptionHandler], emitBrFun: (TestOp, Label) => Unit) { - val (sLeaveHandler, sLeaveFinally) = leavesHandler(block, success, handlers) - val (fLeaveHandler, fLeaveFinally) = leavesHandler(block, failure, handlers) + next: BasicBlock, emitBrFun: (TestOp, Label) => Unit) { + val (sLeaveHandler, sLeaveFinally, slfTarget) = leavesHandler(block, success) + val (fLeaveHandler, fLeaveFinally, flfTarget) = leavesHandler(block, failure) if (sLeaveHandler || fLeaveHandler) { val sLabelOpt = if (sLeaveHandler) { @@ -1392,18 +1426,20 @@ abstract class GenMSIL extends SubComponent { } if (fLeaveHandler) { - if (fLeaveFinally) - mcode.Emit(OpCodes.Endfinally) - else + if (fLeaveFinally) { + if (flfTarget.isDefined) mcode.Emit(OpCodes.Leave, flfTarget.get) + else mcode.Emit(OpCodes.Endfinally) + } else mcode.Emit(OpCodes.Leave, labels(failure)) } else mcode.Emit(OpCodes.Br, labels(failure)) sLabelOpt.map(l => { mcode.MarkLabel(l) - if (sLeaveFinally) - mcode.Emit(OpCodes.Endfinally) - else + if (sLeaveFinally) { + if (slfTarget.isDefined) mcode.Emit(OpCodes.Leave, slfTarget.get) + else mcode.Emit(OpCodes.Endfinally) + } else mcode.Emit(OpCodes.Leave, labels(success)) }) } else { diff --git a/src/msil/ch/epfl/lamp/compiler/msil/PEFile.java b/src/msil/ch/epfl/lamp/compiler/msil/PEFile.java index 459bb39a20..457a1d8c2b 100644 --- a/src/msil/ch/epfl/lamp/compiler/msil/PEFile.java +++ b/src/msil/ch/epfl/lamp/compiler/msil/PEFile.java @@ -854,10 +854,18 @@ public class PEFile { while (getByte() == ELEMENT_TYPE_CMOD_OPT || getByte() == ELEMENT_TYPE_CMOD_REQD) { - Type t = decodeType(); - System.err.println("CMOD: " + t); - if (getByte() == ELEMENT_TYPE_CMOD_REQD) - throw new RuntimeException("Reqired CMOD: " + t); + // skip the tag 23.2.7 + readByte(); + // skip the TypeDefOrRefEncoded (23.2.8) + readByte(); + readByte(); + + // @FIXME: could be 4 bytes, not always 2... + + //Type t = decodeType(); + //System.err.println("CMOD: " + t); + //if (getByte() == ELEMENT_TYPE_CMOD_REQD) + //throw new RuntimeException("Reqired CMOD: " + t); } } diff --git a/src/msil/ch/epfl/lamp/compiler/msil/PEModule.java b/src/msil/ch/epfl/lamp/compiler/msil/PEModule.java index a6e7bb31b2..78c17038cb 100644 --- a/src/msil/ch/epfl/lamp/compiler/msil/PEModule.java +++ b/src/msil/ch/epfl/lamp/compiler/msil/PEModule.java @@ -143,8 +143,17 @@ final class PEModule extends Module { Assembly assem = getAssembly(name); type = assem.GetType(typeName); if (type == null) { - throw new RuntimeException("Failed to locate type " + - typeName + " in assembly " + assem); + // HACK: the IKVM.OpenJDK.Core assembly is compiled against mscorlib.dll v2.0 + // The MSIL library cannot parse the v2.0 mscorlib because of generics, so we + // use the v1.0 + // However, the java.io.FileDescriptor.FlushFileBuffers method uses a type + // Microsoft.Win32.SafeHandles.SafeFileHandle, which only exists in mscorlib + // v2.0 + // For now, jsut return Object (fine as long as we don't use that method). + Assembly asmb = getAssembly("mscorlib"); + type = asmb.GetType("System.Object"); + //throw new RuntimeException("Failed to locate type " + + //typeName + " in assembly " + assem); } break; case ModuleDef.ID: -- cgit v1.2.3