summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Rytz <lukas.rytz@epfl.ch>2010-01-22 16:55:23 +0000
committerLukas Rytz <lukas.rytz@epfl.ch>2010-01-22 16:55:23 +0000
commitdaf9227e7323c31257fe34bb4f2d38c7c1abbff1 (patch)
treefa7f4e59e132cc707e3d767353247edf936e7a94
parentac9fb6ad28b88ee4e7896c93d26c76e80bd3ece3 (diff)
downloadscala-daf9227e7323c31257fe34bb4f2d38c7c1abbff1.tar.gz
scala-daf9227e7323c31257fe34bb4f2d38c7c1abbff1.tar.bz2
scala-daf9227e7323c31257fe34bb4f2d38c7c1abbff1.zip
fix several issues in .net backend / type parsing.
-rw-r--r--build.xml30
-rw-r--r--lib/msil.jar.desired.sha12
-rw-r--r--src/compiler/scala/tools/nsc/backend/msil/GenMSIL.scala112
-rw-r--r--src/msil/ch/epfl/lamp/compiler/msil/PEFile.java16
-rw-r--r--src/msil/ch/epfl/lamp/compiler/msil/PEModule.java13
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
</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: