diff options
-rw-r--r-- | build.xml | 30 | ||||
-rw-r--r-- | lib/msil.jar.desired.sha1 | 2 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala | 112 | ||||
-rw-r--r-- | src/msil/ch/epfl/lamp/compiler/msil/PEFile.java | 16 | ||||
-rw-r--r-- | src/msil/ch/epfl/lamp/compiler/msil/PEModule.java | 13 |
5 files changed, 121 insertions, 52 deletions
@@ -125,10 +125,15 @@ END-USER TARGETS </target> <target name="newlibs" - description="Requires libraries (MSIL, FJBG, FORKJOIN) to be rebuilt. Add this target before any other if class file format is incompatible."> + description="Requires libraries (MSIL, FJBG) to be rebuilt. Add this target before any other if class file format is incompatible."> <property name="libs.outdated" value="yes"/> </target> + <target name="newforkjoin" + description="Requires forkjoin library to be rebuilt. Add this target before any other if class file format is incompatible."> + <property name="forkjoin.outdated" value="yes"/> + </target> + <!-- =========================================================================== PROPERTIES ============================================================================ --> @@ -529,19 +534,27 @@ QUICK BUILD (QUICK) <target name="quick.newlibs" depends="quick.lib" if="libs.outdated"> <antcall target="libs.done"> - <param name="forkjoin.jar" value="${build-libs.dir}/forkjoin.jar"/> <param name="fjbg.jar" value="${build-libs.dir}/fjbg.jar"/> <param name="msil.jar" value="${build-libs.dir}/msil.jar"/> </antcall> </target> <target name="quick.libs" depends="quick.newlibs" unless="libs.outdated"> - <property name="forkjoin.jar" value="${lib.dir}/forkjoin.jar"/> <property name="fjbg.jar" value="${lib.dir}/fjbg.jar"/> <property name="msil.jar" value="${lib.dir}/msil.jar"/> </target> - - <target name="quick.pre-comp" depends="quick.libs"> + + <target name="quick.newforkjoin" depends="quick.libs" if="forkjoin.outdated"> + <antcall target="forkjoin.done"> + <param name="forkjoin.jar" value="${build-libs.dir}/forkjoin.jar"/> + </antcall> + </target> + + <target name="quick.forkjoin" depends="quick.newforkjoin" unless="forkjoin.outdated"> + <property name="forkjoin.jar" value="${lib.dir}/forkjoin.jar"/> + </target> + + <target name="quick.pre-comp" depends="quick.forkjoin"> <uptodate property="quick.comp.available" targetfile="${build-quick.dir}/compiler.complete"> <srcfiles dir="${src.dir}/compiler"/> </uptodate> @@ -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"> <compilerarg line="${javac.args}"/> </javac> @@ -1216,8 +1230,10 @@ LIBRARIES (MSIL, FJBG maybe later) <fileset dir="${build-libs.dir}/classes/fjbg"/> </jar> </target> - - <target name="libs.done" depends="libs.msilpack, libs.fjbgpack, libs.forkjoinpack"/> + + <target name="libs.done" depends="libs.msilpack, libs.fjbgpack"/> + + <target name="forkjoin.done" depends="libs.forkjoinpack"/> <target name="libs.clean" depends="pack.clean"> <delete dir="${build-libs.dir}" includeemptydirs="yes" quiet="yes" failonerror="no"/> 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: |