ikvm-fork/runtime/compiler.cs

3913 строки
127 KiB
C#
Исходник Обычный вид История

2002-12-18 19:00:25 +03:00
/*
Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Jeroen Frijters
2002-12-18 19:00:25 +03:00
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Jeroen Frijters
jeroen@frijters.net
*/
2005-12-07 12:06:32 +03:00
#if !COMPACT_FRAMEWORK
2002-12-18 19:00:25 +03:00
using System;
using System.Collections;
using System.Reflection;
using System.Reflection.Emit;
2004-03-26 13:19:21 +03:00
using System.Diagnostics;
2002-12-18 19:00:25 +03:00
using System.Diagnostics.SymbolStore;
2004-09-09 15:17:55 +04:00
using IKVM.Attributes;
using IKVM.Internal;
2005-06-01 13:49:30 +04:00
using ILGenerator = IKVM.Internal.CountingILGenerator;
2005-12-29 18:48:32 +03:00
using Label = IKVM.Internal.CountingLabel;
2004-10-19 17:43:55 +04:00
2005-06-01 13:49:30 +04:00
using ExceptionTableEntry = IKVM.Internal.ClassFile.Method.ExceptionTableEntry;
using LocalVariableTableEntry = IKVM.Internal.ClassFile.Method.LocalVariableTableEntry;
using Instruction = IKVM.Internal.ClassFile.Method.Instruction;
2002-12-18 19:00:25 +03:00
2006-04-11 16:05:24 +04:00
class ByteCodeHelperMethods
{
internal static readonly MethodInfo GetClassFromTypeHandle;
internal static readonly MethodInfo multianewarray;
internal static readonly MethodInfo f2i;
internal static readonly MethodInfo d2i;
internal static readonly MethodInfo f2l;
internal static readonly MethodInfo d2l;
internal static readonly MethodInfo arraycopy_fast;
internal static readonly MethodInfo arraycopy_primitive_8;
internal static readonly MethodInfo arraycopy_primitive_4;
internal static readonly MethodInfo arraycopy_primitive_2;
internal static readonly MethodInfo arraycopy_primitive_1;
internal static readonly MethodInfo arraycopy;
internal static readonly MethodInfo DynamicCast;
internal static readonly MethodInfo DynamicGetTypeAsExceptionType;
internal static readonly MethodInfo DynamicAaload;
internal static readonly MethodInfo DynamicAastore;
internal static readonly MethodInfo DynamicClassLiteral;
internal static readonly MethodInfo DynamicGetfield;
internal static readonly MethodInfo DynamicGetstatic;
internal static readonly MethodInfo DynamicInvokeSpecialNew;
internal static readonly MethodInfo DynamicInvokestatic;
internal static readonly MethodInfo DynamicInvokevirtual;
internal static readonly MethodInfo DynamicMultianewarray;
internal static readonly MethodInfo DynamicNewarray;
internal static readonly MethodInfo DynamicNewCheckOnly;
internal static readonly MethodInfo DynamicPutfield;
internal static readonly MethodInfo DynamicPutstatic;
internal static readonly MethodInfo VerboseCastFailure;
internal static readonly MethodInfo SkipFinalizer;
internal static readonly MethodInfo DynamicInstanceOf;
2006-07-26 18:16:52 +04:00
internal static readonly MethodInfo volatileReadDouble;
internal static readonly MethodInfo volatileReadLong;
internal static readonly MethodInfo volatileWriteDouble;
internal static readonly MethodInfo volatileWriteLong;
2006-04-11 16:05:24 +04:00
static ByteCodeHelperMethods()
{
2006-04-11 18:59:43 +04:00
#if STATIC_COMPILER
Type typeofByteCodeHelper = StaticCompiler.GetType("IKVM.Runtime.ByteCodeHelper");
#else
Type typeofByteCodeHelper = typeof(IKVM.Runtime.ByteCodeHelper);
2006-04-11 18:59:43 +04:00
#endif
2006-04-11 16:05:24 +04:00
GetClassFromTypeHandle = typeofByteCodeHelper.GetMethod("GetClassFromTypeHandle");
multianewarray = typeofByteCodeHelper.GetMethod("multianewarray");
f2i = typeofByteCodeHelper.GetMethod("f2i");
d2i = typeofByteCodeHelper.GetMethod("d2i");
f2l = typeofByteCodeHelper.GetMethod("f2l");
d2l = typeofByteCodeHelper.GetMethod("d2l");
arraycopy_fast = typeofByteCodeHelper.GetMethod("arraycopy_fast");
arraycopy_primitive_8 = typeofByteCodeHelper.GetMethod("arraycopy_primitive_8");
arraycopy_primitive_4 = typeofByteCodeHelper.GetMethod("arraycopy_primitive_4");
arraycopy_primitive_2 = typeofByteCodeHelper.GetMethod("arraycopy_primitive_2");
arraycopy_primitive_1 = typeofByteCodeHelper.GetMethod("arraycopy_primitive_1");
arraycopy = typeofByteCodeHelper.GetMethod("arraycopy");
DynamicCast = typeofByteCodeHelper.GetMethod("DynamicCast");
DynamicGetTypeAsExceptionType = typeofByteCodeHelper.GetMethod("DynamicGetTypeAsExceptionType");
DynamicAaload = typeofByteCodeHelper.GetMethod("DynamicAaload");
DynamicAastore = typeofByteCodeHelper.GetMethod("DynamicAastore");
DynamicClassLiteral = typeofByteCodeHelper.GetMethod("DynamicClassLiteral");
DynamicGetfield = typeofByteCodeHelper.GetMethod("DynamicGetfield");
DynamicGetstatic = typeofByteCodeHelper.GetMethod("DynamicGetstatic");
DynamicInvokeSpecialNew = typeofByteCodeHelper.GetMethod("DynamicInvokeSpecialNew");
DynamicInvokestatic = typeofByteCodeHelper.GetMethod("DynamicInvokestatic");
DynamicInvokevirtual = typeofByteCodeHelper.GetMethod("DynamicInvokevirtual");
DynamicMultianewarray = typeofByteCodeHelper.GetMethod("DynamicMultianewarray");
DynamicNewarray = typeofByteCodeHelper.GetMethod("DynamicNewarray");
DynamicNewCheckOnly = typeofByteCodeHelper.GetMethod("DynamicNewCheckOnly");
DynamicPutfield = typeofByteCodeHelper.GetMethod("DynamicPutfield");
DynamicPutstatic = typeofByteCodeHelper.GetMethod("DynamicPutstatic");
VerboseCastFailure = typeofByteCodeHelper.GetMethod("VerboseCastFailure");
SkipFinalizer = typeofByteCodeHelper.GetMethod("SkipFinalizer");
DynamicInstanceOf = typeofByteCodeHelper.GetMethod("DynamicInstanceOf");
2006-07-26 18:16:52 +04:00
volatileReadDouble = typeofByteCodeHelper.GetMethod("VolatileRead", new Type[] { Type.GetType("System.Double&") });
volatileReadLong = typeofByteCodeHelper.GetMethod("VolatileRead", new Type[] { Type.GetType("System.Int64&") });
volatileWriteDouble = typeofByteCodeHelper.GetMethod("VolatileWrite", new Type[] { Type.GetType("System.Double&"), typeof(double) });
volatileWriteLong = typeofByteCodeHelper.GetMethod("VolatileWrite", new Type[] { Type.GetType("System.Int64&"), typeof(long) });
2006-04-11 16:05:24 +04:00
}
}
2002-12-18 19:00:25 +03:00
class Compiler
{
2005-02-23 15:56:15 +03:00
private static MethodInfo mapExceptionMethod;
internal static MethodInfo mapExceptionFastMethod;
2005-05-23 12:24:07 +04:00
private static MethodInfo unmapExceptionMethod;
private static MethodWrapper initCauseMethod;
private static MethodInfo suppressFillInStackTraceMethod;
2004-03-26 13:19:21 +03:00
private static MethodInfo getTypeFromHandleMethod;
private static MethodInfo monitorEnterMethod;
private static MethodInfo monitorExitMethod;
private static MethodInfo keepAliveMethod;
2006-08-26 17:00:50 +04:00
private static MethodWrapper getClassFromTypeHandle;
2006-11-20 12:21:38 +03:00
private static TypeWrapper java_lang_Object;
2004-03-16 20:10:09 +03:00
private static TypeWrapper java_lang_Class;
2003-01-06 16:56:37 +03:00
private static TypeWrapper java_lang_Throwable;
2003-12-20 01:19:18 +03:00
private static TypeWrapper java_lang_ThreadDeath;
2006-11-20 12:21:38 +03:00
private static TypeWrapper cli_System_Object;
2005-02-23 15:56:15 +03:00
private static TypeWrapper cli_System_Exception;
2002-12-18 19:00:25 +03:00
private TypeWrapper clazz;
2004-08-30 19:56:23 +04:00
private MethodWrapper mw;
private ClassFile classFile;
private ClassFile.Method m;
2002-12-18 19:00:25 +03:00
private ILGenerator ilGenerator;
private MethodAnalyzer ma;
2004-03-26 13:19:21 +03:00
private ExceptionTableEntry[] exceptions;
2002-12-18 19:00:25 +03:00
private ISymbolDocumentWriter symboldocument;
2005-06-19 14:44:53 +04:00
private LineNumberTableAttribute.LineNumberWriter lineNumbers;
2005-03-01 11:24:54 +03:00
private bool nonleaf;
2005-07-20 11:26:10 +04:00
private LocalBuilder[] tempLocals = new LocalBuilder[32];
2005-12-07 12:06:32 +03:00
private Hashtable invokespecialstubcache;
2006-08-29 10:28:34 +04:00
private bool debug;
private bool keepAlive;
private bool strictfp;
#if STATIC_COMPILER
private IKVM.Internal.MapXml.ReplaceMethodCall[] replacedMethods;
private MethodWrapper[] replacedMethodWrappers;
#endif
2002-12-18 19:00:25 +03:00
2003-12-20 01:19:18 +03:00
static Compiler()
{
2005-01-03 11:26:21 +03:00
getTypeFromHandleMethod = typeof(Type).GetMethod("GetTypeFromHandle", BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(RuntimeTypeHandle) }, null);
monitorEnterMethod = typeof(System.Threading.Monitor).GetMethod("Enter", BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(object) }, null);
monitorExitMethod = typeof(System.Threading.Monitor).GetMethod("Exit", BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(object) }, null);
keepAliveMethod = typeof(System.GC).GetMethod("KeepAlive", BindingFlags.Static | BindingFlags.Public, null, new Type[] { typeof(object) }, null);
2006-11-20 12:21:38 +03:00
java_lang_Object = CoreClasses.java.lang.Object.Wrapper;
2004-03-20 16:25:08 +03:00
java_lang_Throwable = CoreClasses.java.lang.Throwable.Wrapper;
2006-11-20 12:21:38 +03:00
cli_System_Object = DotNetTypeWrapper.GetWrapperFromDotNetType(typeof(System.Object));
2006-08-12 11:43:34 +04:00
cli_System_Exception = DotNetTypeWrapper.GetWrapperFromDotNetType(typeof(System.Exception));
2004-03-20 16:25:08 +03:00
java_lang_Class = CoreClasses.java.lang.Class.Wrapper;
2003-12-24 14:51:41 +03:00
java_lang_ThreadDeath = ClassLoaderWrapper.LoadClassCritical("java.lang.ThreadDeath");
2005-02-23 15:56:15 +03:00
// HACK we need to special case core compilation, because the __<map> methods are HideFromJava
if(java_lang_Throwable.TypeAsBaseType is TypeBuilder)
{
2005-05-23 12:24:07 +04:00
MethodWrapper mw = java_lang_Throwable.GetMethodWrapper("__<map>", "(Ljava.lang.Throwable;Lcli.System.Type;Z)Ljava.lang.Throwable;", false);
2005-02-23 15:56:15 +03:00
mw.Link();
mapExceptionMethod = (MethodInfo)mw.GetMethod();
2005-05-23 12:24:07 +04:00
mw = java_lang_Throwable.GetMethodWrapper("__<map>", "(Ljava.lang.Throwable;Z)Ljava.lang.Throwable;", false);
2005-02-23 15:56:15 +03:00
mw.Link();
mapExceptionFastMethod = (MethodInfo)mw.GetMethod();
2005-05-23 12:24:07 +04:00
mw = java_lang_Throwable.GetMethodWrapper("__<suppressFillInStackTrace>", "()V", false);
mw.Link();
suppressFillInStackTraceMethod = (MethodInfo)mw.GetMethod();
mw = java_lang_Throwable.GetMethodWrapper("__<unmap>", "(Ljava.lang.Throwable;)Ljava.lang.Throwable;", false);
mw.Link();
unmapExceptionMethod = (MethodInfo)mw.GetMethod();
2005-02-23 15:56:15 +03:00
}
else
{
2005-05-23 12:24:07 +04:00
mapExceptionMethod = java_lang_Throwable.TypeAsBaseType.GetMethod("__<map>", new Type[] { typeof(Exception), typeof(Type), typeof(bool) });
mapExceptionFastMethod = java_lang_Throwable.TypeAsBaseType.GetMethod("__<map>", new Type[] { typeof(Exception), typeof(bool) });
suppressFillInStackTraceMethod = java_lang_Throwable.TypeAsBaseType.GetMethod("__<suppressFillInStackTrace>", Type.EmptyTypes);
unmapExceptionMethod = java_lang_Throwable.TypeAsBaseType.GetMethod("__<unmap>", new Type[] { typeof(Exception) });
2005-02-23 15:56:15 +03:00
}
2005-05-23 12:24:07 +04:00
initCauseMethod = java_lang_Throwable.GetMethodWrapper("initCause", "(Ljava.lang.Throwable;)Ljava.lang.Throwable;", false);
2006-08-26 17:00:50 +04:00
getClassFromTypeHandle = ClassLoaderWrapper.LoadClassCritical("ikvm.runtime.Util").GetMethodWrapper("getClassFromTypeHandle", "(Lcli.System.RuntimeTypeHandle;)Ljava.lang.Class;", false);
getClassFromTypeHandle.Link();
2003-12-20 01:19:18 +03:00
}
2004-03-26 13:19:21 +03:00
private class ExceptionSorter : IComparer
{
public int Compare(object x, object y)
{
ExceptionTableEntry e1 = (ExceptionTableEntry)x;
ExceptionTableEntry e2 = (ExceptionTableEntry)y;
if(e1.start_pc < e2.start_pc)
{
return -1;
}
if(e1.start_pc == e2.start_pc)
{
if(e1.end_pc == e2.end_pc)
{
if(e1.ordinal > e2.ordinal)
{
return -1;
}
return 1;
}
if(e1.end_pc > e2.end_pc)
{
return -1;
}
}
return 1;
}
}
2005-12-07 12:06:32 +03:00
private Compiler(TypeWrapper clazz, MethodWrapper mw, ClassFile classFile, ClassFile.Method m, ILGenerator ilGenerator, ClassLoaderWrapper classLoader, ISymbolDocumentWriter symboldocument, Hashtable invokespecialstubcache)
2002-12-18 19:00:25 +03:00
{
this.clazz = clazz;
2004-08-30 19:56:23 +04:00
this.mw = mw;
this.classFile = classFile;
2002-12-18 19:00:25 +03:00
this.m = m;
this.ilGenerator = ilGenerator;
2004-03-26 13:19:21 +03:00
this.symboldocument = symboldocument;
2005-12-07 12:06:32 +03:00
this.invokespecialstubcache = invokespecialstubcache;
2006-08-29 10:28:34 +04:00
this.debug = classLoader.EmitDebugInfo;
this.strictfp = m.IsStrictfp;
2006-08-29 10:28:34 +04:00
if(m.LineNumberTableAttribute != null && classLoader.EmitStackTraceInfo)
2004-10-19 17:43:55 +04:00
{
2005-06-19 14:44:53 +04:00
this.lineNumbers = new LineNumberTableAttribute.LineNumberWriter(m.LineNumberTableAttribute.Length);
2004-10-19 17:43:55 +04:00
}
if(ReferenceEquals(mw.Name, StringConstants.INIT))
{
MethodWrapper finalize = clazz.GetMethodWrapper(StringConstants.FINALIZE, StringConstants.SIG_VOID, true);
keepAlive = finalize != null && finalize.DeclaringType != java_lang_Object && finalize.DeclaringType != cli_System_Object;
}
#if STATIC_COMPILER
replacedMethods = ((CompilerClassLoader)clazz.GetClassLoader()).GetReplacedMethodsFor(mw);
if(replacedMethods != null)
{
replacedMethodWrappers = new MethodWrapper[replacedMethods.Length];
}
#endif
2004-03-26 13:19:21 +03:00
Profiler.Enter("MethodAnalyzer");
2004-06-25 13:38:07 +04:00
try
{
2004-08-30 19:56:23 +04:00
ma = new MethodAnalyzer(clazz, mw, classFile, m, classLoader);
2004-06-25 13:38:07 +04:00
}
finally
{
Profiler.Leave("MethodAnalyzer");
}
2004-03-26 13:19:21 +03:00
2004-08-30 19:56:23 +04:00
TypeWrapper[] args = mw.GetParameters();
2004-03-26 13:19:21 +03:00
LocalVar[] locals = ma.GetAllLocalVars();
foreach(LocalVar v in locals)
2002-12-18 19:00:25 +03:00
{
2004-03-26 13:19:21 +03:00
if(v.isArg)
2002-12-18 19:00:25 +03:00
{
2004-03-26 13:19:21 +03:00
int arg = m.ArgMap[v.local];
TypeWrapper tw;
2004-08-30 19:56:23 +04:00
if(m.IsStatic)
2004-03-26 13:19:21 +03:00
{
tw = args[arg];
}
else if(arg == 0)
{
tw = clazz;
}
else
{
tw = args[arg - 1];
}
2005-02-02 18:11:26 +03:00
if(!tw.IsUnloadable &&
v.type != VerifierTypeWrapper.UninitializedThis &&
(v.type != tw || tw.TypeAsLocalOrStackType != tw.TypeAsSignatureType))
2004-03-26 13:19:21 +03:00
{
v.builder = ilGenerator.DeclareLocal(v.type.TypeAsLocalOrStackType);
2006-08-29 10:28:34 +04:00
if(debug && v.name != null)
2004-03-26 13:19:21 +03:00
{
v.builder.SetLocalSymInfo(v.name);
}
v.isArg = false;
ilGenerator.Emit(OpCodes.Ldarg_S, (byte)arg);
2005-02-02 18:11:26 +03:00
tw.EmitConvSignatureTypeToStackType(ilGenerator);
2004-03-26 13:19:21 +03:00
ilGenerator.Emit(OpCodes.Stloc, v.builder);
}
2002-12-18 19:00:25 +03:00
}
}
2004-03-26 13:19:21 +03:00
2004-05-14 13:31:54 +04:00
// NOTE we're going to be messing with ExceptionTableEntrys that are owned by the Method, this is very bad practice,
// this code should probably be changed to use our own ETE class (which should also contain the ordinal, instead
2002-12-18 19:00:25 +03:00
// of the one in ClassFile.cs)
2004-05-14 13:31:54 +04:00
ArrayList ar = new ArrayList(m.ExceptionTable);
2005-07-20 11:26:10 +04:00
// This optimization removes the recursive exception handlers that Java compiler place around
// the exit of a synchronization block to be "safe" in the face of asynchronous exceptions.
// (see http://weblog.ikvm.net/PermaLink.aspx?guid=3af9548e-4905-4557-8809-65a205ce2cd6)
// We can safely remove them since the code we generate for this construct isn't async safe anyway,
// but there is another reason why this optimization may be slightly controversial. In some
// pathological cases it can cause observable differences, where the Sun JVM would spin in an
// infinite loop, but we will throw an exception. However, the perf benefit is large enough to
// warrant this "incompatibility".
// Note that there is also code in the exception handler handling code that detects these bytecode
// sequences to try to compile them into a fault block, instead of an exception handler.
for(int i = 0; i < ar.Count; i++)
{
ExceptionTableEntry ei = (ExceptionTableEntry)ar[i];
if(ei.start_pc == ei.handler_pc && ei.catch_type == 0)
{
int index = FindPcIndex(ei.start_pc);
if(index + 2 < m.Instructions.Length
&& FindPcIndex(ei.end_pc) == index + 2
&& m.Instructions[index].NormalizedOpCode == NormalizedByteCode.__aload
&& m.Instructions[index + 1].NormalizedOpCode == NormalizedByteCode.__monitorexit
&& m.Instructions[index + 2].NormalizedOpCode == NormalizedByteCode.__athrow)
{
// this is the async exception guard that Jikes and the Eclipse Java Compiler produce
ar.RemoveAt(i);
i--;
}
else if(index + 4 < m.Instructions.Length
&& FindPcIndex(ei.end_pc) == index + 3
&& m.Instructions[index].NormalizedOpCode == NormalizedByteCode.__astore
&& m.Instructions[index + 1].NormalizedOpCode == NormalizedByteCode.__aload
&& m.Instructions[index + 2].NormalizedOpCode == NormalizedByteCode.__monitorexit
&& m.Instructions[index + 3].NormalizedOpCode == NormalizedByteCode.__aload
&& m.Instructions[index + 4].NormalizedOpCode == NormalizedByteCode.__athrow
&& m.Instructions[index].NormalizedArg1 == m.Instructions[index + 3].NormalizedArg1)
{
// this is the async exception guard that javac produces
ar.RemoveAt(i);
i--;
}
}
}
2003-01-06 19:30:09 +03:00
restart:
for(int i = 0; i < ar.Count; i++)
2002-12-18 19:00:25 +03:00
{
2003-01-06 19:30:09 +03:00
ExceptionTableEntry ei = (ExceptionTableEntry)ar[i];
2004-05-14 13:31:54 +04:00
for(int j = 0; j < ar.Count; j++)
2002-12-18 19:00:25 +03:00
{
2003-01-06 19:30:09 +03:00
ExceptionTableEntry ej = (ExceptionTableEntry)ar[j];
2004-05-14 13:31:54 +04:00
if(ei.start_pc <= ej.start_pc && ej.start_pc < ei.end_pc)
2002-12-18 19:00:25 +03:00
{
2004-05-14 13:31:54 +04:00
// 0006/test.j
2003-01-06 19:30:09 +03:00
if(ej.end_pc > ei.end_pc)
{
ExceptionTableEntry emi = new ExceptionTableEntry();
emi.start_pc = ej.start_pc;
emi.end_pc = ei.end_pc;
emi.catch_type = ei.catch_type;
emi.handler_pc = ei.handler_pc;
ExceptionTableEntry emj = new ExceptionTableEntry();
emj.start_pc = ej.start_pc;
emj.end_pc = ei.end_pc;
emj.catch_type = ej.catch_type;
emj.handler_pc = ej.handler_pc;
ei.end_pc = emi.start_pc;
ej.start_pc = emj.end_pc;
ar.Insert(j, emj);
ar.Insert(i + 1, emi);
goto restart;
}
2004-05-14 13:31:54 +04:00
// 0007/test.j
else if(j > i && ej.end_pc < ei.end_pc)
2003-01-06 19:30:09 +03:00
{
ExceptionTableEntry emi = new ExceptionTableEntry();
emi.start_pc = ej.start_pc;
emi.end_pc = ej.end_pc;
emi.catch_type = ei.catch_type;
emi.handler_pc = ei.handler_pc;
ExceptionTableEntry eei = new ExceptionTableEntry();
eei.start_pc = ej.end_pc;
eei.end_pc = ei.end_pc;
eei.catch_type = ei.catch_type;
eei.handler_pc = ei.handler_pc;
ei.end_pc = emi.start_pc;
ar.Insert(i + 1, eei);
ar.Insert(i + 1, emi);
goto restart;
}
2002-12-18 19:00:25 +03:00
}
}
}
// __jsr inside a try block (to a PC outside the try block) causes the try
// block to be broken into two blocks surrounding the __jsr
// This is actually pretty common. Take, for example, the following code:
// class hello
// {
// public static void main(String[] args)
// {
// try
// {
// for(;;)
// {
// if(args.length == 0) return;
// }
// }
// finally
// {
// System.out.println("Hello, world!");
// }
// }
// }
2003-01-06 19:30:09 +03:00
restart_jsr:
for(int i = 0; i < ar.Count; i++)
2002-12-18 19:00:25 +03:00
{
2003-01-06 19:30:09 +03:00
ExceptionTableEntry ei = (ExceptionTableEntry)ar[i];
for(int j = FindPcIndex(ei.start_pc), e = FindPcIndex(ei.end_pc); j < e; j++)
2002-12-18 19:00:25 +03:00
{
2003-05-24 12:23:25 +04:00
if(m.Instructions[j].NormalizedOpCode == NormalizedByteCode.__jsr)
2002-12-18 19:00:25 +03:00
{
2003-01-06 19:30:09 +03:00
int targetPC = m.Instructions[j].NormalizedArg1 + m.Instructions[j].PC;
if(targetPC < ei.start_pc || targetPC >= ei.end_pc)
{
ExceptionTableEntry en = new ExceptionTableEntry();
en.catch_type = ei.catch_type;
en.handler_pc = ei.handler_pc;
en.start_pc = (ushort)m.Instructions[j + 1].PC;
en.end_pc = ei.end_pc;
ei.end_pc = (ushort)m.Instructions[j].PC;
ar.Insert(i + 1, en);
goto restart_jsr;
}
2002-12-18 19:00:25 +03:00
}
}
}
// Split try blocks at branch targets (branches from outside the try block)
for(int i = 0; i < ar.Count; i++)
{
ExceptionTableEntry ei = (ExceptionTableEntry)ar[i];
int start = FindPcIndex(ei.start_pc);
int end = FindPcIndex(ei.end_pc);
for(int j = 0; j < m.Instructions.Length; j++)
{
if(j < start || j >= end)
{
switch(m.Instructions[j].NormalizedOpCode)
{
2005-07-20 14:47:59 +04:00
case NormalizedByteCode.__tableswitch:
2002-12-18 19:00:25 +03:00
case NormalizedByteCode.__lookupswitch:
2004-03-26 13:19:21 +03:00
// start at -1 to have an opportunity to handle the default offset
2004-08-30 19:56:23 +04:00
for(int k = -1; k < m.Instructions[j].SwitchEntryCount; k++)
2004-03-26 13:19:21 +03:00
{
2004-08-30 19:56:23 +04:00
int targetPC = m.Instructions[j].PC + (k == -1 ? m.Instructions[j].DefaultOffset : m.Instructions[j].GetSwitchTargetOffset(k));
2004-05-14 13:31:54 +04:00
if(ei.start_pc < targetPC && targetPC < ei.end_pc)
2004-03-26 13:19:21 +03:00
{
ExceptionTableEntry en = new ExceptionTableEntry();
en.catch_type = ei.catch_type;
en.handler_pc = ei.handler_pc;
en.start_pc = (ushort)targetPC;
en.end_pc = ei.end_pc;
ei.end_pc = (ushort)targetPC;
ar.Insert(i + 1, en);
goto restart_jsr;
}
}
2002-12-18 19:00:25 +03:00
break;
case NormalizedByteCode.__ifeq:
case NormalizedByteCode.__ifne:
case NormalizedByteCode.__iflt:
case NormalizedByteCode.__ifge:
case NormalizedByteCode.__ifgt:
case NormalizedByteCode.__ifle:
case NormalizedByteCode.__if_icmpeq:
case NormalizedByteCode.__if_icmpne:
case NormalizedByteCode.__if_icmplt:
case NormalizedByteCode.__if_icmpge:
case NormalizedByteCode.__if_icmpgt:
case NormalizedByteCode.__if_icmple:
case NormalizedByteCode.__if_acmpeq:
case NormalizedByteCode.__if_acmpne:
case NormalizedByteCode.__ifnull:
case NormalizedByteCode.__ifnonnull:
case NormalizedByteCode.__goto:
case NormalizedByteCode.__jsr:
{
int targetPC = m.Instructions[j].PC + m.Instructions[j].Arg1;
2004-05-14 13:31:54 +04:00
if(ei.start_pc < targetPC && targetPC < ei.end_pc)
2002-12-18 19:00:25 +03:00
{
ExceptionTableEntry en = new ExceptionTableEntry();
en.catch_type = ei.catch_type;
en.handler_pc = ei.handler_pc;
en.start_pc = (ushort)targetPC;
en.end_pc = ei.end_pc;
ei.end_pc = (ushort)targetPC;
ar.Insert(i + 1, en);
goto restart_jsr;
}
break;
}
}
}
}
}
// exception handlers are also a kind of jump, so we need to split try blocks around handlers as well
for(int i = 0; i < ar.Count; i++)
{
ExceptionTableEntry ei = (ExceptionTableEntry)ar[i];
2003-05-24 12:23:25 +04:00
for(int j = 0; j < ar.Count; j++)
2002-12-18 19:00:25 +03:00
{
ExceptionTableEntry ej = (ExceptionTableEntry)ar[j];
2004-05-14 13:31:54 +04:00
if(ei.start_pc < ej.handler_pc && ej.handler_pc < ei.end_pc)
2002-12-18 19:00:25 +03:00
{
ExceptionTableEntry en = new ExceptionTableEntry();
en.catch_type = ei.catch_type;
en.handler_pc = ei.handler_pc;
2004-05-14 13:31:54 +04:00
en.start_pc = ej.handler_pc;
2002-12-18 19:00:25 +03:00
en.end_pc = ei.end_pc;
2004-05-14 13:31:54 +04:00
ei.end_pc = ej.handler_pc;
2002-12-18 19:00:25 +03:00
ar.Insert(i + 1, en);
goto restart_jsr;
}
}
}
// filter out zero length try blocks
for(int i = 0; i < ar.Count; i++)
{
ExceptionTableEntry ei = (ExceptionTableEntry)ar[i];
if(ei.start_pc == ei.end_pc)
{
ar.RemoveAt(i);
i--;
}
2003-12-20 01:19:18 +03:00
else
{
// exception blocks that only contain harmless instructions (i.e. instructions that will *never* throw an exception)
// are also filtered out (to improve the quality of the generated code)
// NOTE we don't remove exception handlers that could catch ThreadDeath, because that can be thrown
// asynchronously (and thus appear on any instruction). This is particularly important to ensure that
// we run finally blocks when a thread is killed.
2003-12-22 20:06:25 +03:00
if(ei.catch_type != 0)
2003-12-20 01:19:18 +03:00
{
2004-08-30 19:56:23 +04:00
TypeWrapper exceptionType = classFile.GetConstantPoolClassType(ei.catch_type);
2003-12-22 20:06:25 +03:00
if(!exceptionType.IsUnloadable && !java_lang_ThreadDeath.IsAssignableTo(exceptionType))
2003-12-20 01:19:18 +03:00
{
2003-12-22 20:06:25 +03:00
int start = FindPcIndex(ei.start_pc);
int end = FindPcIndex(ei.end_pc);
for(int j = start; j < end; j++)
2003-12-20 01:19:18 +03:00
{
2005-07-20 14:47:59 +04:00
if(ByteCodeMetaData.CanThrowException(m.Instructions[j].NormalizedOpCode))
2003-12-22 20:06:25 +03:00
{
goto next;
}
2003-12-20 01:19:18 +03:00
}
2003-12-22 20:06:25 +03:00
ar.RemoveAt(i);
i--;
2003-12-20 01:19:18 +03:00
}
}
}
next:;
2002-12-18 19:00:25 +03:00
}
2003-01-06 19:30:09 +03:00
// Console.WriteLine("after processing:");
// foreach(ExceptionTableEntry e in ar)
// {
// Console.WriteLine("{0} to {1} handler {2}", e.start_pc, e.end_pc, e.handler_pc);
// }
2002-12-18 19:00:25 +03:00
exceptions = new ExceptionTableEntry[ar.Count];
ar.CopyTo(exceptions, 0);
for(int i = 0; i < exceptions.Length; i++)
{
exceptions[i].ordinal = i;
}
Array.Sort(exceptions, new ExceptionSorter());
// TODO remove these checks, if the above exception untangling is correct, this shouldn't ever
// be triggered
for(int i = 0; i < exceptions.Length; i++)
{
for(int j = i + 1; j < exceptions.Length; j++)
{
// check for partially overlapping try blocks (which is legal for the JVM, but not the CLR)
if(exceptions[i].start_pc < exceptions[j].start_pc &&
exceptions[j].start_pc < exceptions[i].end_pc &&
exceptions[i].end_pc < exceptions[j].end_pc)
{
2003-12-20 01:19:18 +03:00
throw new InvalidOperationException("Partially overlapping try blocks is broken");
2002-12-18 19:00:25 +03:00
}
// check that we didn't destroy the ordering, when sorting
if(exceptions[i].start_pc <= exceptions[j].start_pc &&
exceptions[i].end_pc >= exceptions[j].end_pc &&
exceptions[i].ordinal < exceptions[j].ordinal)
{
2003-12-20 01:19:18 +03:00
throw new InvalidOperationException("Non recursive try blocks is broken");
2002-12-18 19:00:25 +03:00
}
}
// make sure __jsr doesn't jump out of try block
for(int j = FindPcIndex(exceptions[i].start_pc), e = FindPcIndex(exceptions[i].end_pc); j < e; j++)
{
if(m.Instructions[j].NormalizedOpCode == NormalizedByteCode.__jsr)
{
int targetPC = m.Instructions[j].NormalizedArg1 + m.Instructions[j].PC;
if(targetPC < exceptions[i].start_pc || targetPC >= exceptions[i].end_pc)
{
2003-12-20 01:19:18 +03:00
throw new InvalidOperationException("Try block splitting around __jsr is broken");
2002-12-18 19:00:25 +03:00
}
}
}
}
}
2005-07-20 11:26:10 +04:00
private LocalBuilder UnsafeAllocTempLocal(Type type)
{
int free = -1;
for(int i = 0; i < tempLocals.Length; i++)
{
LocalBuilder lb = tempLocals[i];
if(lb == null)
{
if(free == -1)
{
free = i;
}
}
else if(lb.LocalType == type)
{
return lb;
}
}
LocalBuilder lb1 = ilGenerator.DeclareLocal(type);
if(free != -1)
{
tempLocals[free] = lb1;
}
return lb1;
}
private LocalBuilder AllocTempLocal(Type type)
{
for(int i = 0; i < tempLocals.Length; i++)
{
LocalBuilder lb = tempLocals[i];
if(lb != null && lb.LocalType == type)
{
tempLocals[i] = null;
return lb;
}
}
return ilGenerator.DeclareLocal(type);
}
private void ReleaseTempLocal(LocalBuilder lb)
{
for(int i = 0; i < tempLocals.Length; i++)
{
if(tempLocals[i] == null)
{
tempLocals[i] = lb;
break;
}
}
}
2003-12-24 14:51:41 +03:00
private sealed class ReturnCookie
2003-12-20 01:19:18 +03:00
{
2004-03-16 20:10:09 +03:00
private Label stub;
private LocalBuilder local;
internal ReturnCookie(Label stub, LocalBuilder local)
{
this.stub = stub;
this.local = local;
}
internal void EmitRet(ILGenerator ilgen)
{
ilgen.MarkLabel(stub);
if(local != null)
{
ilgen.Emit(OpCodes.Ldloc, local);
}
ilgen.Emit(OpCodes.Ret);
}
2003-12-20 01:19:18 +03:00
}
2003-12-24 14:51:41 +03:00
private sealed class BranchCookie
2003-12-20 01:19:18 +03:00
{
// NOTE Stub gets used for both the push stub (inside the exception block) as well as the pop stub (outside the block)
internal Label Stub;
2004-03-29 14:11:33 +04:00
internal Label TargetLabel;
2003-12-20 01:19:18 +03:00
internal bool ContentOnStack;
internal readonly int TargetPC;
internal DupHelper dh;
2005-07-20 11:26:10 +04:00
internal BranchCookie(Compiler compiler, int stackHeight, int targetPC)
2003-12-20 01:19:18 +03:00
{
2005-07-20 11:26:10 +04:00
this.Stub = compiler.ilGenerator.DefineLabel();
2003-12-20 01:19:18 +03:00
this.TargetPC = targetPC;
2005-07-20 11:26:10 +04:00
this.dh = new DupHelper(compiler, stackHeight);
2003-12-20 01:19:18 +03:00
}
internal BranchCookie(Label label, int targetPC)
{
this.Stub = label;
this.TargetPC = targetPC;
}
}
2002-12-18 19:00:25 +03:00
private struct DupHelper
{
2003-11-17 15:01:50 +03:00
private enum StackType : byte
{
Null,
New,
2005-12-29 12:57:41 +03:00
This,
2003-11-17 15:01:50 +03:00
UnitializedThis,
Other
}
2005-07-20 11:26:10 +04:00
private Compiler compiler;
2003-11-17 15:01:50 +03:00
private StackType[] types;
2002-12-18 19:00:25 +03:00
private LocalBuilder[] locals;
2005-07-20 11:26:10 +04:00
internal DupHelper(Compiler compiler, int count)
2002-12-18 19:00:25 +03:00
{
2005-07-20 11:26:10 +04:00
this.compiler = compiler;
2003-11-17 15:01:50 +03:00
types = new StackType[count];
2002-12-18 19:00:25 +03:00
locals = new LocalBuilder[count];
}
2005-07-20 11:26:10 +04:00
internal void Release()
{
foreach(LocalBuilder lb in locals)
{
if(lb != null)
{
compiler.ReleaseTempLocal(lb);
}
}
}
2003-12-20 01:19:18 +03:00
internal int Count
{
get
{
return types.Length;
}
}
internal void SetType(int i, TypeWrapper type)
2002-12-18 19:00:25 +03:00
{
2003-01-06 16:56:37 +03:00
if(type == VerifierTypeWrapper.Null)
2002-12-18 19:00:25 +03:00
{
2003-11-17 15:01:50 +03:00
types[i] = StackType.Null;
2002-12-18 19:00:25 +03:00
}
2003-04-14 13:41:58 +04:00
else if(VerifierTypeWrapper.IsNew(type))
{
// new objects aren't really there on the stack
2003-11-17 15:01:50 +03:00
types[i] = StackType.New;
}
2005-12-29 12:57:41 +03:00
else if(VerifierTypeWrapper.IsThis(type))
{
types[i] = StackType.This;
}
2003-11-17 15:01:50 +03:00
else if(type == VerifierTypeWrapper.UninitializedThis)
{
// uninitialized references cannot be stored in a local, but we can reload them
types[i] = StackType.UnitializedThis;
2003-04-14 13:41:58 +04:00
}
else
2002-12-18 19:00:25 +03:00
{
2003-11-17 15:01:50 +03:00
types[i] = StackType.Other;
2005-07-20 11:26:10 +04:00
locals[i] = compiler.AllocTempLocal(type.TypeAsLocalOrStackType);
2002-12-18 19:00:25 +03:00
}
}
2003-12-20 01:19:18 +03:00
internal void Load(int i)
2002-12-18 19:00:25 +03:00
{
2003-11-17 15:01:50 +03:00
switch(types[i])
2002-12-18 19:00:25 +03:00
{
2003-11-17 15:01:50 +03:00
case StackType.Null:
2005-07-20 11:26:10 +04:00
compiler.ilGenerator.Emit(OpCodes.Ldnull);
2003-11-17 15:01:50 +03:00
break;
case StackType.New:
// new objects aren't really there on the stack
break;
2005-12-29 12:57:41 +03:00
case StackType.This:
2003-11-17 15:01:50 +03:00
case StackType.UnitializedThis:
2005-07-20 11:26:10 +04:00
compiler.ilGenerator.Emit(OpCodes.Ldarg_0);
2003-11-17 15:01:50 +03:00
break;
case StackType.Other:
2005-07-20 11:26:10 +04:00
compiler.ilGenerator.Emit(OpCodes.Ldloc, locals[i]);
2003-11-17 15:01:50 +03:00
break;
default:
throw new InvalidOperationException();
2002-12-18 19:00:25 +03:00
}
}
2003-12-20 01:19:18 +03:00
internal void Store(int i)
2002-12-18 19:00:25 +03:00
{
2003-11-17 15:01:50 +03:00
switch(types[i])
2002-12-18 19:00:25 +03:00
{
2003-11-17 15:01:50 +03:00
case StackType.Null:
2005-12-29 12:57:41 +03:00
case StackType.This:
2003-11-17 15:01:50 +03:00
case StackType.UnitializedThis:
2005-07-20 11:26:10 +04:00
compiler.ilGenerator.Emit(OpCodes.Pop);
2003-11-17 15:01:50 +03:00
break;
case StackType.New:
// new objects aren't really there on the stack
break;
case StackType.Other:
2005-07-20 11:26:10 +04:00
compiler.ilGenerator.Emit(OpCodes.Stloc, locals[i]);
2003-11-17 15:01:50 +03:00
break;
default:
throw new InvalidOperationException();
2002-12-18 19:00:25 +03:00
}
}
}
2006-05-04 12:09:56 +04:00
internal static void Compile(DynamicTypeWrapper clazz, MethodWrapper mw, ClassFile classFile, ClassFile.Method m, ILGenerator ilGenerator, ref bool nonleaf, Hashtable invokespecialstubcache, ref LineNumberTableAttribute.LineNumberWriter lineNumberTable)
2002-12-18 19:00:25 +03:00
{
2006-07-06 17:53:51 +04:00
ClassLoaderWrapper classLoader = clazz.GetClassLoader();
2004-03-26 13:19:21 +03:00
ISymbolDocumentWriter symboldocument = null;
2006-08-29 10:28:34 +04:00
if(classLoader.EmitDebugInfo)
2004-03-26 13:19:21 +03:00
{
2004-08-30 19:56:23 +04:00
string sourcefile = classFile.SourceFileAttribute;
2004-03-26 13:19:21 +03:00
if(sourcefile != null)
{
2006-08-29 10:28:34 +04:00
if(classLoader.SourcePath != null)
2004-03-26 13:19:21 +03:00
{
2004-10-19 17:43:55 +04:00
string package = clazz.Name;
int index = package.LastIndexOf('.');
package = index == -1 ? "" : package.Substring(0, index).Replace('.', '/');
2006-08-29 10:28:34 +04:00
sourcefile = new System.IO.FileInfo(classLoader.SourcePath + "/" + package + "/" + sourcefile).FullName;
2004-03-26 13:19:21 +03:00
}
2006-07-26 18:16:52 +04:00
symboldocument = classLoader.GetTypeWrapperFactory().ModuleBuilder.DefineDocument(sourcefile, SymLanguageType.Java, Guid.Empty, SymDocumentType.Text);
2004-03-26 13:19:21 +03:00
// the very first instruction in the method must have an associated line number, to be able
// to step into the method in Visual Studio .NET
2004-08-30 19:56:23 +04:00
ClassFile.Method.LineNumberTableEntry[] table = m.LineNumberTableAttribute;
2004-03-26 13:19:21 +03:00
if(table != null)
{
int firstPC = int.MaxValue;
int firstLine = -1;
for(int i = 0; i < table.Length; i++)
{
if(table[i].start_pc < firstPC && table[i].line_number != 0)
{
firstLine = table[i].line_number;
firstPC = table[i].start_pc;
}
}
if(firstLine > 0)
{
ilGenerator.MarkSequencePoint(symboldocument, firstLine, 0, firstLine + 1, 0);
}
}
}
}
2004-08-30 19:56:23 +04:00
TypeWrapper[] args = mw.GetParameters();
2003-08-12 17:09:31 +04:00
for(int i = 0; i < args.Length; i++)
2003-06-10 17:28:47 +04:00
{
2003-08-12 17:09:31 +04:00
if(args[i].IsUnloadable)
2003-06-10 17:28:47 +04:00
{
2004-03-29 14:11:33 +04:00
Profiler.Count("EmitDynamicCast");
2004-07-10 11:19:42 +04:00
ilGenerator.Emit(OpCodes.Ldarg, (short)(i + (m.IsStatic ? 0 : 1)));
2004-01-11 16:14:42 +03:00
ilGenerator.Emit(OpCodes.Ldtoken, clazz.TypeAsTBD);
2003-08-12 17:09:31 +04:00
ilGenerator.Emit(OpCodes.Ldstr, args[i].Name);
2006-04-11 16:05:24 +04:00
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicCast);
2003-08-12 17:09:31 +04:00
ilGenerator.Emit(OpCodes.Pop);
2003-06-10 17:28:47 +04:00
}
}
2002-12-18 19:00:25 +03:00
Compiler c;
try
{
Profiler.Enter("new Compiler");
2004-06-25 13:38:07 +04:00
try
{
2005-12-07 12:06:32 +03:00
c = new Compiler(clazz, mw, classFile, m, ilGenerator, classLoader, symboldocument, invokespecialstubcache);
2004-06-25 13:38:07 +04:00
}
finally
{
Profiler.Leave("new Compiler");
}
2002-12-18 19:00:25 +03:00
}
catch(VerifyError x)
{
2004-12-12 17:36:25 +03:00
Tracer.Error(Tracer.Verifier, x.ToString());
2006-08-21 09:15:51 +04:00
clazz.HasVerifyError = true;
2002-12-18 19:00:25 +03:00
// because in Java the method is only verified if it is actually called,
// we generate code here to throw the VerificationError
2004-08-17 13:05:21 +04:00
EmitHelper.Throw(ilGenerator, "java.lang.VerifyError", x.Message);
2002-12-18 19:00:25 +03:00
return;
}
2006-08-21 09:15:51 +04:00
catch(ClassFormatError x)
{
Tracer.Error(Tracer.Verifier, x.ToString());
clazz.HasClassFormatError = true;
EmitHelper.Throw(ilGenerator, "java.lang.ClassFormatError", x.Message);
return;
}
2002-12-18 19:00:25 +03:00
Profiler.Enter("Compile");
2004-06-25 13:38:07 +04:00
try
2004-03-16 20:10:09 +03:00
{
2005-01-03 11:26:21 +03:00
if(m.IsSynchronized && m.IsStatic)
2004-06-25 13:38:07 +04:00
{
2005-01-03 11:26:21 +03:00
ilGenerator.Emit(OpCodes.Ldsfld, clazz.ClassObjectField);
Label label = ilGenerator.DefineLabel();
ilGenerator.Emit(OpCodes.Brtrue_S, label);
ilGenerator.Emit(OpCodes.Ldtoken, clazz.TypeAsTBD);
2006-08-26 17:00:50 +04:00
getClassFromTypeHandle.EmitCall(ilGenerator);
2005-01-03 11:26:21 +03:00
ilGenerator.Emit(OpCodes.Stsfld, clazz.ClassObjectField);
ilGenerator.MarkLabel(label);
ilGenerator.Emit(OpCodes.Ldsfld, clazz.ClassObjectField);
2004-06-25 13:38:07 +04:00
ilGenerator.Emit(OpCodes.Dup);
LocalBuilder monitor = ilGenerator.DeclareLocal(typeof(object));
ilGenerator.Emit(OpCodes.Stloc, monitor);
ilGenerator.Emit(OpCodes.Call, monitorEnterMethod);
ilGenerator.BeginExceptionBlock();
2005-02-11 17:46:58 +03:00
Block b = new Block(c, 0, int.MaxValue, -1, new ArrayList(), true);
2004-06-25 13:38:07 +04:00
c.Compile(b);
b.Leave();
ilGenerator.BeginFinallyBlock();
ilGenerator.Emit(OpCodes.Ldloc, monitor);
ilGenerator.Emit(OpCodes.Call, monitorExitMethod);
ilGenerator.EndExceptionBlock();
b.LeaveStubs(new Block(c, 0, int.MaxValue, -1, null, false));
}
else
{
Block b = new Block(c, 0, int.MaxValue, -1, null, false);
c.Compile(b);
b.Leave();
}
2004-10-19 17:43:55 +04:00
if(c.lineNumbers != null)
{
2006-04-17 12:33:49 +04:00
for(int i = 0; i < m.Instructions.Length; i++)
{
2006-07-28 15:04:21 +04:00
if(m.Instructions[i].NormalizedOpCode == NormalizedByteCode.__getfield
&& VerifierTypeWrapper.IsThis(c.ma.GetRawStackTypeWrapper(i, 0)))
{
// loading a field from the current object cannot throw
}
else if(m.Instructions[i].NormalizedOpCode == NormalizedByteCode.__putfield
&& VerifierTypeWrapper.IsThis(c.ma.GetRawStackTypeWrapper(i, 1)))
{
// storing a field in the current object cannot throw
}
else if(m.Instructions[i].NormalizedOpCode == NormalizedByteCode.__getstatic
&& classFile.GetFieldref(m.Instructions[i].Arg1).GetClassType() == clazz)
{
// loading a field from the current class cannot throw
}
else if(m.Instructions[i].NormalizedOpCode == NormalizedByteCode.__putstatic
&& classFile.GetFieldref(m.Instructions[i].Arg1).GetClassType() == clazz)
{
// storing a field to the current class cannot throw
}
else if(ByteCodeMetaData.CanThrowException(m.Instructions[i].NormalizedOpCode))
2006-04-17 12:33:49 +04:00
{
2006-05-04 12:09:56 +04:00
lineNumberTable = c.lineNumbers;
2006-04-17 12:33:49 +04:00
break;
}
}
2004-10-19 17:43:55 +04:00
}
2005-12-29 18:48:32 +03:00
if((m.IsSynchronized && m.IsStatic) || c.exceptions.Length > 0)
{
// HACK because of the bogus Leave instruction that Reflection.Emit generates, this location
// sometimes appears reachable (it isn't), so we emit a bogus branch to keep the verifier happy.
//ilGenerator.Emit(OpCodes.Br, - (ilGenerator.GetILOffset() + 5));
ilGenerator.Emit(OpCodes.Br_S, (sbyte)-2);
}
2004-11-04 15:50:28 +03:00
ilGenerator.Finish();
2005-03-01 11:24:54 +03:00
nonleaf = c.nonleaf;
2004-03-16 20:10:09 +03:00
}
2004-06-25 13:38:07 +04:00
finally
2004-03-16 20:10:09 +03:00
{
2004-06-25 13:38:07 +04:00
Profiler.Leave("Compile");
2004-03-16 20:10:09 +03:00
}
2002-12-18 19:00:25 +03:00
}
2004-03-26 13:19:21 +03:00
private class Block
2002-12-18 19:00:25 +03:00
{
2004-03-26 13:19:21 +03:00
private Compiler compiler;
private ILGenerator ilgen;
private int begin;
private int end;
private int exceptionIndex;
2004-03-29 14:11:33 +04:00
private ArrayList exits;
private bool nested;
2004-03-26 13:19:21 +03:00
private object[] labels;
2004-09-27 14:17:34 +04:00
internal Block(Compiler compiler, int beginPC, int endPC, int exceptionIndex, ArrayList exits, bool nested)
2002-12-18 19:00:25 +03:00
{
2004-03-26 13:19:21 +03:00
this.compiler = compiler;
this.ilgen = compiler.ilGenerator;
2004-09-27 14:17:34 +04:00
this.begin = beginPC;
this.end = endPC;
2004-03-26 13:19:21 +03:00
this.exceptionIndex = exceptionIndex;
this.exits = exits;
2004-03-29 14:11:33 +04:00
this.nested = nested;
2004-03-26 13:19:21 +03:00
labels = new object[compiler.m.Instructions.Length];
2002-12-18 19:00:25 +03:00
}
2004-03-26 13:19:21 +03:00
internal int End
2002-12-18 19:00:25 +03:00
{
2004-03-26 13:19:21 +03:00
get
{
return end;
}
2002-12-18 19:00:25 +03:00
}
2004-03-26 13:19:21 +03:00
internal int ExceptionIndex
{
get
{
return exceptionIndex;
}
}
2004-03-29 14:11:33 +04:00
internal void SetBackwardBranchLabel(int instructionIndex, BranchCookie bc)
{
// NOTE we're overwriting the label that is already there
labels[instructionIndex] = bc.Stub;
if(exits == null)
{
exits = new ArrayList();
}
exits.Add(bc);
}
2004-03-26 13:19:21 +03:00
internal Label GetLabel(int targetPC)
2002-12-18 19:00:25 +03:00
{
2004-03-26 13:19:21 +03:00
int targetIndex = compiler.FindPcIndex(targetPC);
if(IsInRange(targetPC))
2002-12-18 19:00:25 +03:00
{
2004-03-26 13:19:21 +03:00
object l = labels[targetIndex];
if(l == null)
2002-12-18 19:00:25 +03:00
{
2004-03-26 13:19:21 +03:00
l = ilgen.DefineLabel();
labels[targetIndex] = l;
2002-12-18 19:00:25 +03:00
}
2004-03-26 13:19:21 +03:00
return (Label)l;
}
else
{
object l = labels[targetIndex];
if(l == null)
2002-12-18 19:00:25 +03:00
{
2004-03-26 13:19:21 +03:00
// if we're branching out of the current exception block, we need to indirect this thru a stub
// that saves the stack and uses leave to leave the exception block (to another stub that recovers
// the stack)
int stackHeight = compiler.ma.GetStackHeight(targetIndex);
2005-07-20 11:26:10 +04:00
BranchCookie bc = new BranchCookie(compiler, stackHeight, targetPC);
2004-03-26 13:19:21 +03:00
bc.ContentOnStack = true;
for(int i = 0; i < stackHeight; i++)
2003-12-20 01:19:18 +03:00
{
2004-03-26 13:19:21 +03:00
bc.dh.SetType(i, compiler.ma.GetRawStackTypeWrapper(targetIndex, i));
2003-12-20 01:19:18 +03:00
}
2004-03-26 13:19:21 +03:00
exits.Add(bc);
l = bc;
labels[targetIndex] = l;
2002-12-18 19:00:25 +03:00
}
2004-03-26 13:19:21 +03:00
return ((BranchCookie)l).Stub;
}
}
2004-03-29 14:11:33 +04:00
internal bool HasLabel(int instructionIndex)
{
return labels[instructionIndex] != null;
}
2004-03-26 13:19:21 +03:00
internal void MarkLabel(int instructionIndex)
{
object label = labels[instructionIndex];
if(label == null)
{
Label l = ilgen.DefineLabel();
ilgen.MarkLabel(l);
labels[instructionIndex] = l;
}
else
{
ilgen.MarkLabel((Label)label);
}
}
internal bool IsInRange(int pc)
{
return begin <= pc && pc < end;
}
internal void Leave()
{
2004-03-29 14:11:33 +04:00
if(exits != null)
2004-03-26 13:19:21 +03:00
{
2004-03-29 14:11:33 +04:00
for(int i = 0; i < exits.Count; i++)
2002-12-18 19:00:25 +03:00
{
2004-03-29 14:11:33 +04:00
object exit = exits[i];
BranchCookie bc = exit as BranchCookie;
if(bc != null && bc.ContentOnStack)
2002-12-18 19:00:25 +03:00
{
2004-03-29 14:11:33 +04:00
bc.ContentOnStack = false;
int stack = bc.dh.Count;
2004-06-07 12:28:57 +04:00
// HACK this is unreachable code, but we make sure that
// forward pass verification always yields a valid stack
// (this is required for unreachable leave stubs that are
// generated for unreachable code that follows an
// embedded exception emitted by the compiler for invalid
// code (e.g. NoSuchFieldError))
for(int n = stack - 1; n >= 0; n--)
{
bc.dh.Load(n);
}
ilgen.MarkLabel(bc.Stub);
2004-03-29 14:11:33 +04:00
for(int n = 0; n < stack; n++)
{
bc.dh.Store(n);
}
if(bc.TargetPC == -1)
{
ilgen.Emit(OpCodes.Br, bc.TargetLabel);
}
else
{
bc.Stub = ilgen.DefineLabel();
ilgen.Emit(OpCodes.Leave, bc.Stub);
}
2002-12-18 19:00:25 +03:00
}
}
2004-03-26 13:19:21 +03:00
}
}
2002-12-18 19:00:25 +03:00
2004-03-26 13:19:21 +03:00
internal void LeaveStubs(Block newBlock)
{
2004-03-29 14:11:33 +04:00
if(exits != null)
2004-03-26 13:19:21 +03:00
{
2004-03-29 14:11:33 +04:00
for(int i = 0; i < exits.Count; i++)
2004-03-26 13:19:21 +03:00
{
2004-03-29 14:11:33 +04:00
object exit = exits[i];
ReturnCookie rc = exit as ReturnCookie;
if(rc != null)
2004-03-26 13:19:21 +03:00
{
2004-12-13 18:30:03 +03:00
if(newBlock.IsNested)
2004-03-29 14:11:33 +04:00
{
2004-12-13 18:30:03 +03:00
newBlock.exits.Add(rc);
2004-03-29 14:11:33 +04:00
}
else
{
2004-12-13 18:30:03 +03:00
rc.EmitRet(ilgen);
2004-03-29 14:11:33 +04:00
}
2004-03-26 13:19:21 +03:00
}
else
{
2004-03-29 14:11:33 +04:00
BranchCookie bc = exit as BranchCookie;
if(bc != null && bc.TargetPC != -1)
{
Debug.Assert(!bc.ContentOnStack);
// if the target is within the new block, we handle it, otherwise we
// defer the cookie to our caller
if(newBlock.IsInRange(bc.TargetPC))
{
bc.ContentOnStack = true;
ilgen.MarkLabel(bc.Stub);
int stack = bc.dh.Count;
for(int n = stack - 1; n >= 0; n--)
{
bc.dh.Load(n);
}
ilgen.Emit(OpCodes.Br, newBlock.GetLabel(bc.TargetPC));
}
else
2002-12-18 19:00:25 +03:00
{
2004-03-29 14:11:33 +04:00
newBlock.exits.Add(bc);
2002-12-18 19:00:25 +03:00
}
}
2004-03-26 13:19:21 +03:00
}
}
}
}
internal void AddExitHack(object bc)
{
exits.Add(bc);
}
internal bool IsNested
{
get
{
2004-03-29 14:11:33 +04:00
return nested;
2004-03-26 13:19:21 +03:00
}
}
}
2005-07-20 11:26:10 +04:00
private bool IsGuardedBlock(Stack blockStack, int instructionIndex, int instructionCount)
{
int start_pc = m.Instructions[instructionIndex].PC;
int end_pc = m.Instructions[instructionIndex + instructionCount].PC;
for(int i = 0; i < exceptions.Length; i++)
{
ExceptionTableEntry e = exceptions[i];
if(e.end_pc > start_pc && e.start_pc < end_pc)
{
foreach(Block block in blockStack)
{
if(block.ExceptionIndex == i)
{
goto next;
}
}
return true;
}
next:;
}
return false;
}
2004-03-26 13:19:21 +03:00
private void Compile(Block block)
{
int[] scope = null;
// if we're emitting debugging information, we need to use scopes for local variables
2006-08-29 10:28:34 +04:00
if(debug)
2004-03-26 13:19:21 +03:00
{
scope = new int[m.Instructions.Length];
LocalVariableTableEntry[] lvt = m.LocalVariableTableAttribute;
if(lvt != null)
{
for(int i = 0; i < lvt.Length; i++)
{
// TODO validate the contents of the LVT entry
int startIndex = SafeFindPcIndex(lvt[i].start_pc);
if(startIndex > 0)
2004-03-26 13:19:21 +03:00
{
// NOTE javac (correctly) sets start_pc of the LVT entry to the instruction
// following the store that first initializes the local, so we have to
// detect that case and adjust our local scope (because we'll be creating
// the local when we encounter the first store).
LocalVar v = ma.GetLocalVar(startIndex - 1);
2004-03-26 13:19:21 +03:00
if(v != null && v.local == lvt[i].index)
2002-12-18 19:00:25 +03:00
{
startIndex--;
2002-12-18 19:00:25 +03:00
}
2004-03-26 13:19:21 +03:00
}
int end = lvt[i].start_pc + lvt[i].length;
int endIndex;
2004-03-26 13:19:21 +03:00
if(end == m.Instructions[m.Instructions.Length - 1].PC)
{
endIndex = m.Instructions.Length - 1;
2004-03-26 13:19:21 +03:00
}
else
{
endIndex = SafeFindPcIndex(end);
}
if(startIndex != -1 && endIndex != -1)
{
scope[startIndex]++;
scope[endIndex]--;
}
else
{
// the LVT range is invalid, but we need to have a scope for the variable,
// so we create an artificial scope that spans the method
scope[0]++;
scope[m.Instructions.Length - 1]--;
2004-03-26 13:19:21 +03:00
}
}
}
}
int exceptionIndex = 0;
Instruction[] code = m.Instructions;
Stack blockStack = new Stack();
2004-03-29 14:11:33 +04:00
bool instructionIsForwardReachable = true;
2004-03-26 13:19:21 +03:00
for(int i = 0; i < code.Length; i++)
{
Instruction instr = code[i];
if(scope != null)
{
for(int j = scope[i]; j < 0; j++)
{
ilGenerator.EndScope();
}
for(int j = scope[i]; j > 0; j--)
{
ilGenerator.BeginScope();
}
}
// if we've left the current exception block, do the exit processing
while(block.End == instr.PC)
{
block.Leave();
ExceptionTableEntry exc = exceptions[block.ExceptionIndex];
Block prevBlock = block;
block = (Block)blockStack.Pop();
exceptionIndex = block.ExceptionIndex + 1;
// skip over exception handlers that are no longer relevant
for(; exceptionIndex < exceptions.Length && exceptions[exceptionIndex].end_pc <= instr.PC; exceptionIndex++)
{
}
2005-07-20 11:26:10 +04:00
int handlerIndex = FindPcIndex(exc.handler_pc);
if(exc.catch_type == 0
&& handlerIndex + 2 < m.Instructions.Length
&& m.Instructions[handlerIndex].NormalizedOpCode == NormalizedByteCode.__aload
&& m.Instructions[handlerIndex + 1].NormalizedOpCode == NormalizedByteCode.__monitorexit
&& m.Instructions[handlerIndex + 2].NormalizedOpCode == NormalizedByteCode.__athrow
&& !IsGuardedBlock(blockStack, handlerIndex, 3))
2004-03-26 13:19:21 +03:00
{
2005-07-20 11:26:10 +04:00
// this is the Jikes & Eclipse Java Compiler synchronization block exit
ilGenerator.BeginFaultBlock();
LoadLocal(m.Instructions[handlerIndex]);
ilGenerator.Emit(OpCodes.Call, monitorExitMethod);
ilGenerator.EndExceptionBlock();
// HACK to keep the verifier happy we need this bogus jump
// (because of the bogus Leave that Ref.Emit ends the try block with)
ilGenerator.Emit(OpCodes.Br_S, (sbyte)-2);
2004-03-26 13:19:21 +03:00
}
2005-07-20 11:26:10 +04:00
else if(exc.catch_type == 0
&& handlerIndex + 3 < m.Instructions.Length
&& m.Instructions[handlerIndex].NormalizedOpCode == NormalizedByteCode.__astore
&& m.Instructions[handlerIndex + 1].NormalizedOpCode == NormalizedByteCode.__aload
&& m.Instructions[handlerIndex + 2].NormalizedOpCode == NormalizedByteCode.__monitorexit
&& m.Instructions[handlerIndex + 3].NormalizedOpCode == NormalizedByteCode.__aload
&& m.Instructions[handlerIndex + 4].NormalizedOpCode == NormalizedByteCode.__athrow
&& !IsGuardedBlock(blockStack, handlerIndex, 5))
2004-03-26 13:19:21 +03:00
{
2005-07-20 11:26:10 +04:00
// this is the javac synchronization block exit
ilGenerator.BeginFaultBlock();
LoadLocal(m.Instructions[handlerIndex + 1]);
ilGenerator.Emit(OpCodes.Call, monitorExitMethod);
ilGenerator.EndExceptionBlock();
// HACK to keep the verifier happy we need this bogus jump
// (because of the bogus Leave that Ref.Emit ends the try block with)
ilGenerator.Emit(OpCodes.Br_S, (sbyte)-2);
2004-09-27 14:17:34 +04:00
}
else
{
2005-07-20 11:26:10 +04:00
TypeWrapper exceptionTypeWrapper;
bool remap;
if(exc.catch_type == 0)
2004-03-26 13:19:21 +03:00
{
2005-07-20 11:26:10 +04:00
exceptionTypeWrapper = java_lang_Throwable;
remap = true;
2004-03-26 13:19:21 +03:00
}
else
{
2005-07-20 11:26:10 +04:00
exceptionTypeWrapper = classFile.GetConstantPoolClassType(exc.catch_type);
2005-08-05 12:40:54 +04:00
remap = exceptionTypeWrapper.IsUnloadable || !exceptionTypeWrapper.IsSubTypeOf(cli_System_Exception);
2004-09-27 14:17:34 +04:00
}
2005-07-20 11:26:10 +04:00
Type excType = exceptionTypeWrapper.TypeAsExceptionType;
bool mapSafe = !exceptionTypeWrapper.IsUnloadable && !exceptionTypeWrapper.IsMapUnsafeException && !exceptionTypeWrapper.IsRemapped;
if(mapSafe)
2004-09-27 14:17:34 +04:00
{
2005-07-20 11:26:10 +04:00
ilGenerator.BeginCatchBlock(excType);
2004-03-26 13:19:21 +03:00
}
else
{
2005-07-20 11:26:10 +04:00
ilGenerator.BeginCatchBlock(typeof(Exception));
2004-03-26 13:19:21 +03:00
}
2005-07-20 11:26:10 +04:00
BranchCookie bc = new BranchCookie(this, 1, exc.handler_pc);
prevBlock.AddExitHack(bc);
Instruction handlerInstr = code[handlerIndex];
2005-07-22 16:26:14 +04:00
bool unusedException = mapSafe && (handlerInstr.NormalizedOpCode == NormalizedByteCode.__pop ||
2005-07-20 11:26:10 +04:00
(handlerInstr.NormalizedOpCode == NormalizedByteCode.__astore &&
2005-07-22 16:26:14 +04:00
ma.GetLocalVar(handlerIndex) == null));
2005-07-20 11:26:10 +04:00
// special case for catch(Throwable) (and finally), that produces less code and
// should be faster
if(mapSafe || exceptionTypeWrapper == java_lang_Throwable)
2004-09-27 14:17:34 +04:00
{
2005-07-20 11:26:10 +04:00
if(unusedException)
{
// we must still have an item on the stack, even though it isn't used!
bc.dh.SetType(0, VerifierTypeWrapper.Null);
}
else
{
if(mapSafe)
{
ilGenerator.Emit(OpCodes.Dup);
}
ilGenerator.Emit(remap ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
ilGenerator.Emit(OpCodes.Call, mapExceptionFastMethod);
if(mapSafe)
{
ilGenerator.Emit(OpCodes.Pop);
}
bc.dh.SetType(0, exceptionTypeWrapper);
bc.dh.Store(0);
}
ilGenerator.Emit(OpCodes.Leave, bc.Stub);
2004-09-27 14:17:34 +04:00
}
else
{
2005-07-20 11:26:10 +04:00
if(exceptionTypeWrapper.IsUnloadable)
{
Profiler.Count("EmitDynamicGetTypeAsExceptionType");
ilGenerator.Emit(OpCodes.Ldtoken, clazz.TypeAsTBD);
ilGenerator.Emit(OpCodes.Ldstr, exceptionTypeWrapper.Name);
2006-04-11 16:05:24 +04:00
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicGetTypeAsExceptionType);
2005-07-20 11:26:10 +04:00
ilGenerator.Emit(remap ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
ilGenerator.Emit(OpCodes.Call, mapExceptionMethod);
}
else
{
ilGenerator.Emit(OpCodes.Ldtoken, excType);
ilGenerator.Emit(OpCodes.Call, getTypeFromHandleMethod);
ilGenerator.Emit(remap ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
ilGenerator.Emit(OpCodes.Call, mapExceptionMethod);
ilGenerator.Emit(OpCodes.Castclass, excType);
}
if(unusedException)
{
// we must still have an item on the stack, even though it isn't used!
bc.dh.SetType(0, VerifierTypeWrapper.Null);
}
else
{
bc.dh.SetType(0, exceptionTypeWrapper);
ilGenerator.Emit(OpCodes.Dup);
bc.dh.Store(0);
}
Label rethrow = ilGenerator.DefineLabel();
ilGenerator.Emit(OpCodes.Brfalse, rethrow);
ilGenerator.Emit(OpCodes.Leave, bc.Stub);
ilGenerator.MarkLabel(rethrow);
ilGenerator.Emit(OpCodes.Rethrow);
2004-09-27 14:17:34 +04:00
}
2005-07-20 11:26:10 +04:00
ilGenerator.EndExceptionBlock();
2002-12-18 19:00:25 +03:00
}
2004-03-26 13:19:21 +03:00
prevBlock.LeaveStubs(block);
}
2005-08-02 11:24:54 +04:00
if(!instr.IsReachable)
2004-04-02 12:13:01 +04:00
{
// skip any unreachable instructions
continue;
}
2004-03-29 14:11:33 +04:00
// if there was a forward branch to this instruction, it is forward reachable
instructionIsForwardReachable |= block.HasLabel(i);
2006-04-20 17:20:57 +04:00
if(block.HasLabel(i) || instr.IsBranchTarget)
2005-12-19 18:12:49 +03:00
{
block.MarkLabel(i);
}
2002-12-18 19:00:25 +03:00
2004-03-29 14:11:33 +04:00
// if the instruction is only backward reachable, ECMA says it must have an empty stack,
// so we move the stack to locals
2004-04-02 12:13:01 +04:00
if(!instructionIsForwardReachable)
2004-03-29 14:11:33 +04:00
{
int stackHeight = ma.GetStackHeight(i);
if(stackHeight != 0)
{
2005-07-20 11:26:10 +04:00
BranchCookie bc = new BranchCookie(this, stackHeight, -1);
2004-03-29 14:11:33 +04:00
bc.ContentOnStack = true;
bc.TargetLabel = ilGenerator.DefineLabel();
ilGenerator.MarkLabel(bc.TargetLabel);
for(int j = 0; j < stackHeight; j++)
{
bc.dh.SetType(j, ma.GetRawStackTypeWrapper(i, j));
}
for(int j = stackHeight - 1; j >= 0; j--)
{
bc.dh.Load(j);
}
block.SetBackwardBranchLabel(i, bc);
}
}
2004-03-26 13:19:21 +03:00
// if we're entering an exception block, we need to setup the exception block and
// transfer the stack into it
2004-04-02 12:13:01 +04:00
// Note that an exception block that *starts* at an unreachable instruction,
// is completely unreachable, because it is impossible to branch into an exception block.
2004-03-26 13:19:21 +03:00
for(; exceptionIndex < exceptions.Length && exceptions[exceptionIndex].start_pc == instr.PC; exceptionIndex++)
{
int stackHeight = ma.GetStackHeight(i);
if(stackHeight != 0)
2002-12-18 19:00:25 +03:00
{
2005-07-20 11:26:10 +04:00
DupHelper dh = new DupHelper(this, stackHeight);
2004-03-26 13:19:21 +03:00
for(int k = 0; k < stackHeight; k++)
2003-01-06 19:30:09 +03:00
{
2004-03-26 13:19:21 +03:00
dh.SetType(k, ma.GetRawStackTypeWrapper(i, k));
dh.Store(k);
}
ilGenerator.BeginExceptionBlock();
for(int k = stackHeight - 1; k >= 0; k--)
{
dh.Load(k);
}
2005-07-20 11:26:10 +04:00
dh.Release();
2004-03-26 13:19:21 +03:00
}
else
{
ilGenerator.BeginExceptionBlock();
}
blockStack.Push(block);
2004-03-29 14:11:33 +04:00
block = new Block(this, exceptions[exceptionIndex].start_pc, exceptions[exceptionIndex].end_pc, exceptionIndex, new ArrayList(), true);
2004-03-26 13:19:21 +03:00
block.MarkLabel(i);
}
2004-10-19 17:43:55 +04:00
ClassFile.Method.LineNumberTableEntry[] table = m.LineNumberTableAttribute;
2004-11-23 20:46:39 +03:00
if(table != null && (symboldocument != null || lineNumbers != null))
2004-03-26 13:19:21 +03:00
{
2004-10-19 17:43:55 +04:00
for(int j = 0; j < table.Length; j++)
2004-03-26 13:19:21 +03:00
{
2004-10-19 17:43:55 +04:00
if(table[j].start_pc == instr.PC && table[j].line_number != 0)
2004-03-26 13:19:21 +03:00
{
2004-10-19 17:43:55 +04:00
if(symboldocument != null)
2004-03-26 13:19:21 +03:00
{
ilGenerator.MarkSequencePoint(symboldocument, table[j].line_number, 0, table[j].line_number + 1, 0);
// we emit a nop to make sure we always have an instruction associated with the sequence point
ilGenerator.Emit(OpCodes.Nop);
}
// we only add a line number mapping if the stack is empty for two reasons:
// 1) the CLR JIT only generates native to IL mappings for locations where the stack is empty
// 2) GetILOffset() flushes the lazy emit stack, so if we don't do this check we miss some optimization opportunities
if(lineNumbers != null && ilGenerator.IsStackEmpty)
2004-10-19 17:43:55 +04:00
{
2005-06-19 14:44:53 +04:00
lineNumbers.AddMapping(ilGenerator.GetILOffset(), table[j].line_number);
2004-10-19 17:43:55 +04:00
}
break;
2004-03-26 13:19:21 +03:00
}
}
}
if(keepAlive)
{
// JSR 133 specifies that a finalizer cannot run while the constructor is still in progress.
// This code attempts to implement that by adding calls to GC.KeepAlive(this) before return,
// backward branches and throw instructions. I don't think it is perfect, you may be able to
// fool it by calling a trivial method that loops forever which the CLR JIT will then inline
// and see that control flow doesn't continue and hence the lifetime of "this" will be
// shorter than the constructor.
switch(instr.NormalizedOpCode)
{
case NormalizedByteCode.__return:
case NormalizedByteCode.__areturn:
case NormalizedByteCode.__ireturn:
case NormalizedByteCode.__lreturn:
case NormalizedByteCode.__freturn:
case NormalizedByteCode.__dreturn:
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Call, keepAliveMethod);
break;
case NormalizedByteCode.__if_icmpeq:
case NormalizedByteCode.__if_icmpne:
case NormalizedByteCode.__if_icmple:
case NormalizedByteCode.__if_icmplt:
case NormalizedByteCode.__if_icmpge:
case NormalizedByteCode.__if_icmpgt:
case NormalizedByteCode.__ifle:
case NormalizedByteCode.__iflt:
case NormalizedByteCode.__ifge:
case NormalizedByteCode.__ifgt:
case NormalizedByteCode.__ifne:
case NormalizedByteCode.__ifeq:
case NormalizedByteCode.__ifnonnull:
case NormalizedByteCode.__ifnull:
case NormalizedByteCode.__if_acmpeq:
case NormalizedByteCode.__if_acmpne:
case NormalizedByteCode.__goto:
if(instr.Arg1 <= 0)
{
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Call, keepAliveMethod);
}
break;
case NormalizedByteCode.__athrow:
case NormalizedByteCode.__athrow_no_unmap:
case NormalizedByteCode.__lookupswitch:
case NormalizedByteCode.__tableswitch:
if(ma.GetLocalTypeWrapper(i, 0) != VerifierTypeWrapper.UninitializedThis)
{
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Call, keepAliveMethod);
}
break;
}
}
2005-08-05 12:40:54 +04:00
switch(instr.NormalizedOpCode)
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
case NormalizedByteCode.__getstatic:
2006-07-28 15:04:21 +04:00
{
ClassFile.ConstantPoolItemFieldref cpi = classFile.GetFieldref(instr.Arg1);
if(cpi.GetClassType() != clazz)
{
// we may trigger a static initializer, which is equivalent to a call
nonleaf = true;
}
FieldWrapper field = cpi.GetField();
field.EmitGet(ilGenerator);
field.FieldTypeWrapper.EmitConvSignatureTypeToStackType(ilGenerator);
break;
}
2005-08-05 12:40:54 +04:00
case NormalizedByteCode.__getfield:
2005-08-05 16:18:35 +04:00
{
ClassFile.ConstantPoolItemFieldref cpi = classFile.GetFieldref(instr.Arg1);
FieldWrapper field = cpi.GetField();
field.EmitGet(ilGenerator);
field.FieldTypeWrapper.EmitConvSignatureTypeToStackType(ilGenerator);
break;
}
case NormalizedByteCode.__putstatic:
2006-07-28 15:04:21 +04:00
{
ClassFile.ConstantPoolItemFieldref cpi = classFile.GetFieldref(instr.Arg1);
if(cpi.GetClassType() != clazz)
{
// we may trigger a static initializer, which is equivalent to a call
nonleaf = true;
}
FieldWrapper field = cpi.GetField();
TypeWrapper tw = field.FieldTypeWrapper;
tw.EmitConvStackTypeToSignatureType(ilGenerator, ma.GetStackTypeWrapper(i, 0));
if(strictfp)
{
// no need to convert
}
else if(tw == PrimitiveTypeWrapper.DOUBLE)
{
ilGenerator.Emit(OpCodes.Conv_R8);
}
else if(tw == PrimitiveTypeWrapper.FLOAT)
{
ilGenerator.Emit(OpCodes.Conv_R4);
}
2006-07-28 15:04:21 +04:00
field.EmitSet(ilGenerator);
break;
}
2005-08-05 12:40:54 +04:00
case NormalizedByteCode.__putfield:
2005-08-05 16:18:35 +04:00
{
ClassFile.ConstantPoolItemFieldref cpi = classFile.GetFieldref(instr.Arg1);
FieldWrapper field = cpi.GetField();
TypeWrapper tw = field.FieldTypeWrapper;
2005-12-29 12:57:41 +03:00
tw.EmitConvStackTypeToSignatureType(ilGenerator, ma.GetStackTypeWrapper(i, 0));
if(strictfp)
{
// no need to convert
}
else if(tw == PrimitiveTypeWrapper.DOUBLE)
{
ilGenerator.Emit(OpCodes.Conv_R8);
}
else if(tw == PrimitiveTypeWrapper.FLOAT)
{
ilGenerator.Emit(OpCodes.Conv_R4);
}
2005-08-05 16:18:35 +04:00
field.EmitSet(ilGenerator);
break;
}
case NormalizedByteCode.__dynamic_getstatic:
case NormalizedByteCode.__dynamic_putstatic:
case NormalizedByteCode.__dynamic_getfield:
case NormalizedByteCode.__dynamic_putfield:
2006-07-28 15:04:21 +04:00
nonleaf = true;
2005-08-05 16:18:35 +04:00
DynamicGetPutField(instr, i);
2005-08-05 12:40:54 +04:00
break;
case NormalizedByteCode.__aconst_null:
ilGenerator.Emit(OpCodes.Ldnull);
break;
case NormalizedByteCode.__iconst:
2006-11-27 10:39:30 +03:00
ilGenerator.LazyEmitLdc_I4(instr.NormalizedArg1);
2005-08-05 12:40:54 +04:00
break;
case NormalizedByteCode.__lconst_0:
2006-11-27 10:39:30 +03:00
ilGenerator.LazyEmitLdc_I8(0);
2005-08-05 12:40:54 +04:00
break;
case NormalizedByteCode.__lconst_1:
2006-11-27 10:39:30 +03:00
ilGenerator.LazyEmitLdc_I8(1);
2005-08-05 12:40:54 +04:00
break;
case NormalizedByteCode.__fconst_0:
case NormalizedByteCode.__dconst_0:
// floats are stored as native size on the stack, so both R4 and R8 are the same
ilGenerator.Emit(OpCodes.Ldc_R4, 0.0f);
break;
case NormalizedByteCode.__fconst_1:
case NormalizedByteCode.__dconst_1:
// floats are stored as native size on the stack, so both R4 and R8 are the same
ilGenerator.Emit(OpCodes.Ldc_R4, 1.0f);
break;
case NormalizedByteCode.__fconst_2:
ilGenerator.Emit(OpCodes.Ldc_R4, 2.0f);
break;
case NormalizedByteCode.__ldc:
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
int constant = instr.Arg1;
switch(classFile.GetConstantPoolConstantType(constant))
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
case ClassFile.ConstantType.Double:
{
double v = classFile.GetConstantPoolConstantDouble(constant);
if(v == 0.0 && BitConverter.DoubleToInt64Bits(v) < 0)
{
// FXBUG the x64 CLR JIT has a bug [1] that causes "cond ? -0:0 : 0.0" to be optimized to 0.0
// This bug causes problems for the sun.misc.FloatingDecimal code, so as a workaround we obfuscate the -0.0 constant.
// [1] https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=276714
ilGenerator.Emit(OpCodes.Ldc_I8, Int64.MinValue);
ilGenerator.Emit(OpCodes.Call, typeof(BitConverter).GetMethod("Int64BitsToDouble"));
}
else
{
ilGenerator.Emit(OpCodes.Ldc_R8, v);
}
2005-08-05 12:40:54 +04:00
break;
}
2005-08-05 12:40:54 +04:00
case ClassFile.ConstantType.Float:
{
float v = classFile.GetConstantPoolConstantFloat(constant);
if(v == 0.0 && BitConverter.DoubleToInt64Bits(v) < 0)
{
// FXBUG the x64 CLR JIT has a bug [1] that causes "cond ? -0:0 : 0.0" to be optimized to 0.0
// This bug causes problems for the sun.misc.FloatingDecimal code, so as a workaround we obfuscate the -0.0 constant.
// [1] https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=276714
ilGenerator.Emit(OpCodes.Ldc_I8, Int64.MinValue);
ilGenerator.Emit(OpCodes.Call, typeof(BitConverter).GetMethod("Int64BitsToDouble"));
}
else
{
ilGenerator.Emit(OpCodes.Ldc_R4, v);
}
2005-08-05 12:40:54 +04:00
break;
}
2005-08-05 12:40:54 +04:00
case ClassFile.ConstantType.Integer:
2006-11-27 10:39:30 +03:00
ilGenerator.LazyEmitLdc_I4(classFile.GetConstantPoolConstantInteger(constant));
2005-08-05 12:40:54 +04:00
break;
case ClassFile.ConstantType.Long:
2006-11-27 10:39:30 +03:00
ilGenerator.LazyEmitLdc_I8(classFile.GetConstantPoolConstantLong(constant));
2005-08-05 12:40:54 +04:00
break;
case ClassFile.ConstantType.String:
ilGenerator.LazyEmitLdstr(classFile.GetConstantPoolConstantString(constant));
2005-08-05 12:40:54 +04:00
break;
case ClassFile.ConstantType.Class:
2003-12-24 14:51:41 +03:00
{
2005-08-05 12:40:54 +04:00
TypeWrapper tw = classFile.GetConstantPoolClassType(constant);
if(tw.IsUnloadable)
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
Profiler.Count("EmitDynamicClassLiteral");
ilGenerator.Emit(OpCodes.Ldtoken, clazz.TypeAsTBD);
ilGenerator.Emit(OpCodes.Ldstr, tw.Name);
2006-04-11 16:05:24 +04:00
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicClassLiteral);
2006-08-26 17:00:50 +04:00
java_lang_Class.EmitCheckcast(clazz, ilGenerator);
2005-08-05 12:40:54 +04:00
}
else
{
ilGenerator.Emit(OpCodes.Ldtoken, tw.IsRemapped ? tw.TypeAsBaseType : tw.TypeAsTBD);
2006-08-26 17:00:50 +04:00
getClassFromTypeHandle.EmitCall(ilGenerator);
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
break;
2003-12-24 14:51:41 +03:00
}
2005-08-05 12:40:54 +04:00
default:
throw new InvalidOperationException();
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
break;
}
case NormalizedByteCode.__dynamic_invokestatic:
case NormalizedByteCode.__invokestatic:
{
ClassFile.ConstantPoolItemMI cpi = classFile.GetMethodref(instr.Arg1);
// HACK special case for calls to System.arraycopy, if the array arguments on the stack
// are of a known array type, we can redirect to an optimized version of arraycopy.
// Note that we also have to handle VMSystem.arraycopy, because StringBuffer directly calls
// this method to avoid prematurely initialising System.
2006-08-06 13:27:20 +04:00
if((ReferenceEquals(cpi.Class, StringConstants.JAVA_LANG_SYSTEM) || ReferenceEquals(cpi.Class, StringConstants.JAVA_LANG_VMSYSTEM))
&& ReferenceEquals(cpi.Name, StringConstants.ARRAYCOPY)
&& ReferenceEquals(cpi.Signature, StringConstants.SIG_ARRAYCOPY)
2006-08-21 10:21:27 +04:00
&& cpi.GetClassType().GetClassLoader() == java_lang_Class.GetClassLoader())
2005-08-05 12:40:54 +04:00
{
2005-12-29 12:57:41 +03:00
TypeWrapper dst_type = ma.GetStackTypeWrapper(i, 2);
TypeWrapper src_type = ma.GetStackTypeWrapper(i, 4);
2005-08-05 12:40:54 +04:00
if(!dst_type.IsUnloadable && dst_type.IsArray && dst_type == src_type)
{
switch(dst_type.Name[1])
2003-01-06 19:30:09 +03:00
{
2005-08-05 12:40:54 +04:00
case 'J':
case 'D':
2006-04-11 16:05:24 +04:00
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.arraycopy_primitive_8);
2005-08-05 12:40:54 +04:00
break;
case 'I':
case 'F':
2006-04-11 16:05:24 +04:00
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.arraycopy_primitive_4);
2005-08-05 12:40:54 +04:00
break;
case 'S':
case 'C':
2006-04-11 16:05:24 +04:00
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.arraycopy_primitive_2);
2005-08-05 12:40:54 +04:00
break;
case 'B':
case 'Z':
2006-04-11 16:05:24 +04:00
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.arraycopy_primitive_1);
2005-08-05 12:40:54 +04:00
break;
default:
// TODO once the verifier tracks actual types (i.e. it knows that
// a particular reference is the result of a "new" opcode) we can
// use the fast version if the exact destination type is known
// (in that case the "dst_type == src_type" above should
// be changed to "src_type.IsAssignableTo(dst_type)".
TypeWrapper elemtw = dst_type.ElementTypeWrapper;
// note that IsFinal returns true for array types, so we have to be careful!
if(!elemtw.IsArray && elemtw.IsFinal)
{
2006-04-11 16:05:24 +04:00
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.arraycopy_fast);
2005-08-05 12:40:54 +04:00
}
else
{
2006-04-11 16:05:24 +04:00
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.arraycopy);
2005-08-05 12:40:54 +04:00
}
break;
2003-01-06 19:30:09 +03:00
}
2005-08-05 12:40:54 +04:00
break;
2002-12-18 19:00:25 +03:00
}
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
MethodWrapper method = GetMethodCallEmitter(cpi, instr.NormalizedOpCode);
// if the stack values don't match the argument types (for interface argument types)
// we must emit code to cast the stack value to the interface type
2005-12-07 12:06:32 +03:00
CastInterfaceArgs(method, cpi.GetArgTypes(), i, false);
2005-08-05 12:40:54 +04:00
method.EmitCall(ilGenerator);
method.ReturnType.EmitConvSignatureTypeToStackType(ilGenerator);
if(!strictfp)
{
// no need to convert
}
else if(method.ReturnType == PrimitiveTypeWrapper.DOUBLE)
{
ilGenerator.Emit(OpCodes.Conv_R8);
}
else if(method.ReturnType == PrimitiveTypeWrapper.FLOAT)
{
ilGenerator.Emit(OpCodes.Conv_R4);
}
2005-08-05 12:40:54 +04:00
nonleaf = true;
break;
}
case NormalizedByteCode.__dynamic_invokeinterface:
case NormalizedByteCode.__dynamic_invokevirtual:
2006-01-31 13:13:12 +03:00
case NormalizedByteCode.__dynamic_invokespecial:
2005-08-05 12:40:54 +04:00
case NormalizedByteCode.__invokevirtual:
case NormalizedByteCode.__invokeinterface:
case NormalizedByteCode.__invokespecial:
{
2006-01-31 13:13:12 +03:00
bool isinvokespecial = instr.NormalizedOpCode == NormalizedByteCode.__invokespecial || instr.NormalizedOpCode == NormalizedByteCode.__dynamic_invokespecial;
2005-08-05 12:40:54 +04:00
nonleaf = true;
ClassFile.ConstantPoolItemMI cpi = classFile.GetMethodref(instr.Arg1);
int argcount = cpi.GetArgTypes().Length;
TypeWrapper type = ma.GetRawStackTypeWrapper(i, argcount);
TypeWrapper thisType = SigTypeToClassName(type, cpi.GetClassType());
2004-03-26 13:19:21 +03:00
2005-08-05 12:40:54 +04:00
MethodWrapper method = GetMethodCallEmitter(cpi, instr.NormalizedOpCode);
2004-06-28 12:24:07 +04:00
#if STATIC_COMPILER
if(method.DeclaringType == CoreClasses.java.lang.String.Wrapper
&& ReferenceEquals(method.Name, StringConstants.TOCHARARRAY)
&& ReferenceEquals(method.Signature, StringConstants.SIG_TOCHARARRAY))
{
string str = ilGenerator.PopLazyLdstr();
if(str != null)
{
// arbitrary length for "big" strings
if(str.Length > 128)
{
EmitLoadCharArrayLiteral(ilGenerator, str, mw.DeclaringType);
break;
}
ilGenerator.Emit(OpCodes.Ldstr, str);
}
}
#endif
2006-11-20 12:21:38 +03:00
if(method.IsProtected && (method.DeclaringType == java_lang_Object || method.DeclaringType == java_lang_Throwable))
{
// HACK we may need to redirect finalize or clone from java.lang.Object/Throwable
// to a more specific base type.
if(thisType.IsAssignableTo(cli_System_Object))
{
method = cli_System_Object.GetMethodWrapper(cpi.Name, cpi.Signature, true);
}
else if(thisType.IsAssignableTo(cli_System_Exception))
{
method = cli_System_Exception.GetMethodWrapper(cpi.Name, cpi.Signature, true);
}
else if(thisType.IsAssignableTo(java_lang_Throwable))
{
method = java_lang_Throwable.GetMethodWrapper(cpi.Name, cpi.Signature, true);
}
}
2005-08-05 12:40:54 +04:00
// if the stack values don't match the argument types (for interface argument types)
// we must emit code to cast the stack value to the interface type
2006-08-06 13:27:20 +04:00
if(isinvokespecial && ReferenceEquals(cpi.Name, StringConstants.INIT) && VerifierTypeWrapper.IsNew(type))
2005-08-05 12:40:54 +04:00
{
TypeWrapper[] args = cpi.GetArgTypes();
2005-12-07 12:06:32 +03:00
CastInterfaceArgs(method, args, i, false);
2005-08-05 12:40:54 +04:00
}
else
{
// the this reference is included in the argument list because it may also need to be cast
TypeWrapper[] methodArgs = cpi.GetArgTypes();
TypeWrapper[] args = new TypeWrapper[methodArgs.Length + 1];
methodArgs.CopyTo(args, 1);
if(instr.NormalizedOpCode == NormalizedByteCode.__invokeinterface)
2003-03-21 16:41:43 +03:00
{
if(method.DeclaringType.IsGhost)
{
// if we're calling a ghost interface method, we need to make sure that CastInterfaceArgs knows
// (cpi.GetClassType() could be an interface that extends the ghost interface)
args[0] = method.DeclaringType;
}
else
{
args[0] = cpi.GetClassType();
}
2002-12-18 19:00:25 +03:00
}
2004-03-26 13:19:21 +03:00
else
2002-12-18 19:00:25 +03:00
{
2005-08-05 12:40:54 +04:00
args[0] = thisType;
2004-03-26 13:19:21 +03:00
}
2005-12-07 12:06:32 +03:00
CastInterfaceArgs(method, args, i, true);
2005-08-05 12:40:54 +04:00
}
2003-02-12 15:28:04 +03:00
2006-08-06 13:27:20 +04:00
if(isinvokespecial && ReferenceEquals(cpi.Name, StringConstants.INIT))
2005-08-05 12:40:54 +04:00
{
if(VerifierTypeWrapper.IsNew(type))
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
// we have to construct a list of all the unitialized references to the object
// we're about to create on the stack, so that we can reconstruct the stack after
// the "newobj" instruction
int trivcount = 0;
bool nontrivial = false;
bool[] stackfix = new bool[ma.GetStackHeight(i) - (argcount + 1)];
for(int j = 0; j < stackfix.Length; j++)
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
if(ma.GetRawStackTypeWrapper(i, argcount + 1 + j) == type)
2003-01-17 17:34:33 +03:00
{
2005-08-05 12:40:54 +04:00
stackfix[j] = true;
if(trivcount == j)
2002-12-18 19:00:25 +03:00
{
2005-08-05 12:40:54 +04:00
trivcount++;
2002-12-18 19:00:25 +03:00
}
2005-08-05 12:40:54 +04:00
else
2002-12-18 19:00:25 +03:00
{
2005-08-05 12:40:54 +04:00
// if there is other stuff on the stack between the new object
// references, we need to do more work to construct the proper stack
// layout after the newobj instruction
2004-03-26 13:19:21 +03:00
nontrivial = true;
2002-12-18 19:00:25 +03:00
}
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
}
for(int j = 0; !nontrivial && j < m.MaxLocals; j++)
{
if(ma.GetLocalTypeWrapper(i, j) == type)
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
nontrivial = true;
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
}
if(!thisType.IsUnloadable && thisType.IsSubTypeOf(java_lang_Throwable))
{
// if the next instruction is an athrow and the exception type
// doesn't override fillInStackTrace, we can suppress the call
// to fillInStackTrace from the constructor (and this is
// a huge perf win)
// NOTE we also can't call suppressFillInStackTrace for non-Java
// exceptions (because then the suppress flag won't be cleared),
// but this case is handled by the "is fillInStackTrace overridden?"
// test, because cli.System.Exception overrides fillInStackTrace.
if(code[i + 1].NormalizedOpCode == NormalizedByteCode.__athrow
&& thisType.GetMethodWrapper("fillInStackTrace", "()Ljava.lang.Throwable;", true).DeclaringType == java_lang_Throwable)
2005-05-23 12:24:07 +04:00
{
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.Call, suppressFillInStackTraceMethod);
2006-04-20 17:20:57 +04:00
if(!code[i + 1].IsBranchTarget)
{
code[i + 1].PatchOpCode(NormalizedByteCode.__athrow_no_unmap);
}
2005-05-23 12:24:07 +04:00
}
2005-08-05 12:40:54 +04:00
}
2006-08-26 17:00:50 +04:00
method.EmitNewobj(ilGenerator, ma, i);
2005-08-05 12:40:54 +04:00
if(!thisType.IsUnloadable && thisType.IsSubTypeOf(cli_System_Exception))
{
// HACK we call Throwable.initCause(null) to force creation of an ExceptionInfoHelper
// (which disables future remapping of the exception) and to prevent others from
// setting the cause.
ilGenerator.Emit(OpCodes.Dup);
ilGenerator.Emit(OpCodes.Ldnull);
initCauseMethod.EmitCallvirt(ilGenerator);
ilGenerator.Emit(OpCodes.Pop);
}
if(nontrivial)
{
// this could be done a little more efficiently, but since in practice this
// code never runs (for code compiled from Java source) it doesn't
// really matter
LocalBuilder newobj = ilGenerator.DeclareLocal(thisType.TypeAsLocalOrStackType);
ilGenerator.Emit(OpCodes.Stloc, newobj);
LocalBuilder[] tempstack = new LocalBuilder[stackfix.Length];
for(int j = 0; j < stackfix.Length; j++)
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
if(!stackfix[j])
2002-12-18 19:00:25 +03:00
{
2005-12-29 12:57:41 +03:00
TypeWrapper stacktype = ma.GetStackTypeWrapper(i, argcount + 1 + j);
2005-08-05 12:40:54 +04:00
// it could be another new object reference (not from current invokespecial <init>
// instruction)
if(stacktype == VerifierTypeWrapper.Null)
2004-03-26 13:19:21 +03:00
{
2005-01-03 11:26:21 +03:00
// NOTE we abuse the newobj local as a cookie to signal null!
2005-08-05 12:40:54 +04:00
tempstack[j] = newobj;
ilGenerator.Emit(OpCodes.Pop);
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
else if(!VerifierTypeWrapper.IsNew(stacktype))
2002-12-18 19:00:25 +03:00
{
2005-08-05 12:40:54 +04:00
LocalBuilder lb = ilGenerator.DeclareLocal(stacktype.TypeAsLocalOrStackType);
ilGenerator.Emit(OpCodes.Stloc, lb);
tempstack[j] = lb;
2003-01-17 17:34:33 +03:00
}
}
2003-12-24 14:51:41 +03:00
}
2005-08-05 12:40:54 +04:00
for(int j = stackfix.Length - 1; j >= 0; j--)
2003-12-24 14:51:41 +03:00
{
2005-08-05 12:40:54 +04:00
if(stackfix[j])
2003-01-17 17:34:33 +03:00
{
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.Ldloc, newobj);
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
else if(tempstack[j] != null)
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
// NOTE we abuse the newobj local as a cookie to signal null!
if(tempstack[j] == newobj)
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.Ldnull);
}
else
{
ilGenerator.Emit(OpCodes.Ldloc, tempstack[j]);
2004-03-26 13:19:21 +03:00
}
2002-12-18 19:00:25 +03:00
}
}
2004-03-26 13:19:21 +03:00
LocalVar[] locals = ma.GetLocalVarsForInvokeSpecial(i);
for(int j = 0; j < locals.Length; j++)
2003-12-24 14:51:41 +03:00
{
2004-03-26 13:19:21 +03:00
if(locals[j] != null)
2002-12-18 19:00:25 +03:00
{
2004-03-26 13:19:21 +03:00
if(locals[j].builder == null)
{
// for invokespecial the resulting type can never be null
locals[j].builder = ilGenerator.DeclareLocal(locals[j].type.TypeAsLocalOrStackType);
}
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.Ldloc, newobj);
2004-03-26 13:19:21 +03:00
ilGenerator.Emit(OpCodes.Stloc, locals[j].builder);
2002-12-18 19:00:25 +03:00
}
}
}
else
{
2005-08-05 12:40:54 +04:00
if(trivcount == 0)
2002-12-18 19:00:25 +03:00
{
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.Pop);
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
else
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
for(int j = 1; j < trivcount; j++)
{
ilGenerator.Emit(OpCodes.Dup);
}
2002-12-18 19:00:25 +03:00
}
}
}
2004-03-26 13:19:21 +03:00
else
{
2005-08-05 12:40:54 +04:00
Debug.Assert(type == VerifierTypeWrapper.UninitializedThis);
method.EmitCall(ilGenerator);
LocalVar[] locals = ma.GetLocalVarsForInvokeSpecial(i);
for(int j = 0; j < locals.Length; j++)
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
if(locals[j] != null)
{
if(locals[j].builder == null)
{
// for invokespecial the resulting type can never be null
locals[j].builder = ilGenerator.DeclareLocal(locals[j].type.TypeAsLocalOrStackType);
}
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Stloc, locals[j].builder);
}
2004-03-26 13:19:21 +03:00
}
}
}
2005-08-05 12:40:54 +04:00
else
2004-03-26 13:19:21 +03:00
{
2006-01-31 13:13:12 +03:00
if(isinvokespecial)
2004-03-26 13:19:21 +03:00
{
2005-12-29 12:57:41 +03:00
if(VerifierTypeWrapper.IsThis(type))
{
method.EmitCall(ilGenerator);
}
else if(method.IsPrivate)
{
// if the method is private, we can get away with a callvirt (and not generate the stub)
method.EmitCallvirt(ilGenerator);
}
else
{
ilGenerator.Emit(OpCodes.Callvirt, GetInvokeSpecialStub(method));
}
2004-03-26 13:19:21 +03:00
}
else
{
2006-04-05 12:18:58 +04:00
// NOTE this check is written somewhat pessimistically, because we need to
// take remapped types into account. For example, Throwable.getClass() "overrides"
// the final Object.getClass() method and we don't want to call Object.getClass()
// on a Throwable instance, because that would yield unverifiable code (java.lang.Throwable
// extends System.Exception instead of java.lang.Object in the .NET type system).
if(VerifierTypeWrapper.IsThis(type)
&& (method.IsFinal || clazz.IsFinal)
&& clazz.GetMethodWrapper(method.Name, method.Signature, true) == method)
{
// we're calling a method on our own instance that can't possibly be overriden,
// so we don't need to use callvirt
method.EmitCall(ilGenerator);
}
else
{
method.EmitCallvirt(ilGenerator);
}
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
method.ReturnType.EmitConvSignatureTypeToStackType(ilGenerator);
if(!strictfp)
{
// no need to convert
}
else if(method.ReturnType == PrimitiveTypeWrapper.DOUBLE)
{
ilGenerator.Emit(OpCodes.Conv_R8);
}
else if(method.ReturnType == PrimitiveTypeWrapper.FLOAT)
{
ilGenerator.Emit(OpCodes.Conv_R4);
}
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
break;
}
case NormalizedByteCode.__clone_array:
ilGenerator.Emit(OpCodes.Callvirt, ArrayTypeWrapper.CloneMethod);
break;
case NormalizedByteCode.__return:
case NormalizedByteCode.__areturn:
case NormalizedByteCode.__ireturn:
case NormalizedByteCode.__lreturn:
case NormalizedByteCode.__freturn:
case NormalizedByteCode.__dreturn:
{
if(block.IsNested)
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
// if we're inside an exception block, copy TOS to local, emit "leave" and push item onto our "todo" list
LocalBuilder local = null;
if(instr.NormalizedOpCode != NormalizedByteCode.__return)
2002-12-18 19:00:25 +03:00
{
2005-08-05 12:40:54 +04:00
TypeWrapper retTypeWrapper = mw.ReturnType;
2005-12-29 12:57:41 +03:00
retTypeWrapper.EmitConvStackTypeToSignatureType(ilGenerator, ma.GetStackTypeWrapper(i, 0));
2005-08-05 12:40:54 +04:00
local = UnsafeAllocTempLocal(retTypeWrapper.TypeAsSignatureType);
ilGenerator.Emit(OpCodes.Stloc, local);
2003-08-12 17:09:31 +04:00
}
2005-08-05 12:40:54 +04:00
Label label = ilGenerator.DefineLabel();
// NOTE leave automatically discards any junk that may be on the stack
ilGenerator.Emit(OpCodes.Leave, label);
block.AddExitHack(new ReturnCookie(label, local));
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
else
2004-03-26 13:19:21 +03:00
{
2006-07-18 11:38:24 +04:00
// HACK the x64 JIT is lame and optimizes calls before ret into a tail call
// and this makes the method disappear from the call stack, so we try to thwart that
// by inserting some bogus instructions between the call and the return.
// Note that this optimization doesn't appear to happen if the method has exception handlers,
// so in that case we don't do anything.
bool x64hack = false;
if(exceptions.Length == 0 && i > 0)
{
int k = i - 1;
while(k > 0 && m.Instructions[k].NormalizedOpCode == NormalizedByteCode.__nop)
{
k--;
}
switch(m.Instructions[k].NormalizedOpCode)
{
case NormalizedByteCode.__invokeinterface:
case NormalizedByteCode.__invokespecial:
case NormalizedByteCode.__invokestatic:
case NormalizedByteCode.__invokevirtual:
x64hack = true;
break;
}
}
2005-08-05 12:40:54 +04:00
// if there is junk on the stack (other than the return value), we must pop it off
// because in .NET this is invalid (unlike in Java)
int stackHeight = ma.GetStackHeight(i);
if(instr.NormalizedOpCode == NormalizedByteCode.__return)
2003-01-06 16:56:37 +03:00
{
2006-07-18 11:38:24 +04:00
if(stackHeight != 0 || x64hack)
2005-02-23 15:56:15 +03:00
{
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.Leave_S, (byte)0);
2005-02-23 15:56:15 +03:00
}
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.Ret);
2002-12-18 19:00:25 +03:00
}
2004-03-26 13:19:21 +03:00
else
2003-01-06 16:56:37 +03:00
{
2005-08-05 12:40:54 +04:00
TypeWrapper retTypeWrapper = mw.ReturnType;
2005-12-29 12:57:41 +03:00
retTypeWrapper.EmitConvStackTypeToSignatureType(ilGenerator, ma.GetStackTypeWrapper(i, 0));
2005-08-05 12:40:54 +04:00
if(stackHeight != 1)
2005-02-23 15:56:15 +03:00
{
2005-08-05 12:40:54 +04:00
LocalBuilder local = AllocTempLocal(retTypeWrapper.TypeAsSignatureType);
ilGenerator.Emit(OpCodes.Stloc, local);
ilGenerator.Emit(OpCodes.Leave_S, (byte)0);
ilGenerator.Emit(OpCodes.Ldloc, local);
ReleaseTempLocal(local);
2005-02-23 15:56:15 +03:00
}
2006-07-18 11:38:24 +04:00
else if(x64hack)
{
ilGenerator.Emit(OpCodes.Ldnull);
ilGenerator.Emit(OpCodes.Pop);
}
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.Ret);
2004-03-26 13:19:21 +03:00
}
}
2005-08-05 12:40:54 +04:00
break;
}
case NormalizedByteCode.__aload:
{
TypeWrapper type = ma.GetLocalTypeWrapper(i, instr.NormalizedArg1);
if(type == VerifierTypeWrapper.Null)
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
// if the local is known to be null, we just emit a null
ilGenerator.Emit(OpCodes.Ldnull);
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
else if(VerifierTypeWrapper.IsNew(type))
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
// since new objects aren't represented on the stack, we don't need to do anything here
2004-03-26 13:19:21 +03:00
}
2005-12-29 12:57:41 +03:00
else if(VerifierTypeWrapper.IsThis(type))
{
ilGenerator.Emit(OpCodes.Ldarg_0);
}
2005-08-05 12:40:54 +04:00
else if(type == VerifierTypeWrapper.UninitializedThis)
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
// any unitialized this reference has to be loaded from arg 0
// NOTE if the method overwrites the this references, it will always end up in
// a different local (due to the way the local variable liveness analysis works),
// so we don't have to worry about that.
ilGenerator.Emit(OpCodes.Ldarg_0);
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
else
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
LocalVar v = LoadLocal(instr);
2005-08-05 16:18:35 +04:00
if(!type.IsUnloadable && (v.type.IsUnloadable || !v.type.IsAssignableTo(type)))
2003-09-10 16:21:01 +04:00
{
2005-08-05 12:40:54 +04:00
type.EmitCheckcast(type, ilGenerator);
2003-09-10 16:21:01 +04:00
}
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
break;
}
case NormalizedByteCode.__astore:
{
TypeWrapper type = ma.GetRawStackTypeWrapper(i, 0);
// NOTE we use "int" to track the return address of a jsr
if(VerifierTypeWrapper.IsRet(type))
{
StoreLocal(instr);
}
else if(VerifierTypeWrapper.IsNew(type))
{
// new objects aren't really on the stack, so we can't copy them into the local
// (and the local doesn't exist anyway)
}
else if(type == VerifierTypeWrapper.UninitializedThis)
{
// any unitialized reference is always the this reference, we don't store anything
// here (because CLR won't allow unitialized references in locals) and then when
// the unitialized ref is loaded we redirect to the this reference
ilGenerator.Emit(OpCodes.Pop);
}
else
{
StoreLocal(instr);
}
break;
}
case NormalizedByteCode.__iload:
case NormalizedByteCode.__lload:
case NormalizedByteCode.__fload:
case NormalizedByteCode.__dload:
LoadLocal(instr);
break;
case NormalizedByteCode.__istore:
case NormalizedByteCode.__lstore:
StoreLocal(instr);
break;
case NormalizedByteCode.__fstore_conv:
ilGenerator.Emit(OpCodes.Conv_R4);
StoreLocal(instr);
break;
2005-08-05 12:40:54 +04:00
case NormalizedByteCode.__fstore:
StoreLocal(instr);
break;
case NormalizedByteCode.__dstore_conv:
{
LocalVar v = StoreLocal(instr);
if(v != null && !v.isArg)
{
// HACK this appears to be the fastest way to do the equivalent of
// an explicit conv.r8. Simply doing a conv.r8 can result in the
// CLR JIT using an unaligned stack address for the conversion
// and that is ridiculously expensive. Doing this indirect load
// triggers the stack alignment and according to my reading
// of the ECMA CLI spec is guaranteed to result in a converted
// value (and in practice it actually works and seems to
// perform reasonably well). The downside is that it affects
// performance negatively on x64 where a conv.r8 is free.
ilGenerator.Emit(OpCodes.Ldloca, v.builder);
ilGenerator.Emit(OpCodes.Volatile);
ilGenerator.Emit(OpCodes.Ldind_R8);
ilGenerator.Emit(OpCodes.Pop);
}
break;
}
2005-08-05 12:40:54 +04:00
case NormalizedByteCode.__dstore:
StoreLocal(instr);
break;
case NormalizedByteCode.__new:
{
TypeWrapper wrapper = classFile.GetConstantPoolClassType(instr.Arg1);
if(wrapper.IsUnloadable)
{
Profiler.Count("EmitDynamicNewCheckOnly");
// this is here to make sure we throw the exception in the right location (before
// evaluating the constructor arguments)
ilGenerator.Emit(OpCodes.Ldtoken, clazz.TypeAsTBD);
ilGenerator.Emit(OpCodes.Ldstr, wrapper.Name);
2006-04-11 16:05:24 +04:00
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicNewCheckOnly);
2005-08-05 12:40:54 +04:00
}
2005-08-24 12:14:23 +04:00
else if(wrapper != clazz)
{
// trigger cctor (as the spec requires)
wrapper.EmitRunClassConstructor(ilGenerator);
}
// we don't actually allocate the object here, the call to <init> will be converted into a newobj instruction
2005-08-05 12:40:54 +04:00
break;
}
case NormalizedByteCode.__multianewarray:
{
LocalBuilder localArray = UnsafeAllocTempLocal(typeof(int[]));
LocalBuilder localInt = UnsafeAllocTempLocal(typeof(int));
2006-11-27 10:39:30 +03:00
ilGenerator.LazyEmitLdc_I4(instr.Arg2);
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.Newarr, typeof(int));
ilGenerator.Emit(OpCodes.Stloc, localArray);
for(int j = 1; j <= instr.Arg2; j++)
{
ilGenerator.Emit(OpCodes.Stloc, localInt);
ilGenerator.Emit(OpCodes.Ldloc, localArray);
2006-11-27 10:39:30 +03:00
ilGenerator.LazyEmitLdc_I4(instr.Arg2 - j);
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.Ldloc, localInt);
2004-03-26 13:19:21 +03:00
ilGenerator.Emit(OpCodes.Stelem_I4);
2005-08-05 12:40:54 +04:00
}
TypeWrapper wrapper = classFile.GetConstantPoolClassType(instr.Arg1);
if(wrapper.IsUnloadable)
{
Profiler.Count("EmitDynamicMultianewarray");
ilGenerator.Emit(OpCodes.Ldtoken, clazz.TypeAsTBD);
ilGenerator.Emit(OpCodes.Ldstr, wrapper.Name);
ilGenerator.Emit(OpCodes.Ldloc, localArray);
2006-04-11 16:05:24 +04:00
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicMultianewarray);
2005-08-05 12:40:54 +04:00
}
else
{
Type type = wrapper.TypeAsArrayType;
ilGenerator.Emit(OpCodes.Ldtoken, type);
ilGenerator.Emit(OpCodes.Ldloc, localArray);
2006-04-11 16:05:24 +04:00
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.multianewarray);
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.Castclass, type);
}
break;
}
case NormalizedByteCode.__anewarray:
{
TypeWrapper wrapper = classFile.GetConstantPoolClassType(instr.Arg1);
if(wrapper.IsUnloadable)
{
Profiler.Count("EmitDynamicNewarray");
ilGenerator.Emit(OpCodes.Ldtoken, clazz.TypeAsTBD);
ilGenerator.Emit(OpCodes.Ldstr, wrapper.Name);
2006-04-11 16:05:24 +04:00
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicNewarray);
2005-08-05 12:40:54 +04:00
}
else
{
// NOTE for ghost types we create object arrays to make sure that Ghost implementers can be
// stored in ghost arrays, but this has the unintended consequence that ghost arrays can
// contain *any* reference type (because they are compiled as Object arrays). We could
// modify aastore to emit code to check for this, but this would have an huge performance
// cost for all object arrays.
// Oddly, while the JVM accepts any reference for any other interface typed references, in the
// case of aastore it does check that the object actually implements the interface. This
// is unfortunate, but I think we can live with this minor incompatibility.
// Note that this does not break type safety, because when the incorrect object is eventually
// used as the ghost interface type it will generate a ClassCastException.
ilGenerator.Emit(OpCodes.Newarr, wrapper.TypeAsArrayType);
}
break;
}
case NormalizedByteCode.__newarray:
switch(instr.Arg1)
{
case 4:
ilGenerator.Emit(OpCodes.Newarr, PrimitiveTypeWrapper.BOOLEAN.TypeAsArrayType);
2004-03-26 13:19:21 +03:00
break;
2005-08-05 12:40:54 +04:00
case 5:
ilGenerator.Emit(OpCodes.Newarr, PrimitiveTypeWrapper.CHAR.TypeAsArrayType);
2004-03-26 13:19:21 +03:00
break;
2005-08-05 12:40:54 +04:00
case 6:
ilGenerator.Emit(OpCodes.Newarr, PrimitiveTypeWrapper.FLOAT.TypeAsArrayType);
2004-03-26 13:19:21 +03:00
break;
2005-08-05 12:40:54 +04:00
case 7:
ilGenerator.Emit(OpCodes.Newarr, PrimitiveTypeWrapper.DOUBLE.TypeAsArrayType);
2004-03-26 13:19:21 +03:00
break;
2005-08-05 12:40:54 +04:00
case 8:
ilGenerator.Emit(OpCodes.Newarr, PrimitiveTypeWrapper.BYTE.TypeAsArrayType);
2004-03-26 13:19:21 +03:00
break;
2005-08-05 12:40:54 +04:00
case 9:
ilGenerator.Emit(OpCodes.Newarr, PrimitiveTypeWrapper.SHORT.TypeAsArrayType);
2004-03-26 13:19:21 +03:00
break;
2005-08-05 12:40:54 +04:00
case 10:
ilGenerator.Emit(OpCodes.Newarr, PrimitiveTypeWrapper.INT.TypeAsArrayType);
2004-03-26 13:19:21 +03:00
break;
2005-08-05 12:40:54 +04:00
case 11:
ilGenerator.Emit(OpCodes.Newarr, PrimitiveTypeWrapper.LONG.TypeAsArrayType);
break;
default:
// this can't happen, the verifier would have caught it
throw new InvalidOperationException();
}
break;
case NormalizedByteCode.__checkcast:
{
TypeWrapper wrapper = classFile.GetConstantPoolClassType(instr.Arg1);
wrapper.EmitCheckcast(clazz, ilGenerator);
break;
}
case NormalizedByteCode.__instanceof:
{
TypeWrapper wrapper = classFile.GetConstantPoolClassType(instr.Arg1);
wrapper.EmitInstanceOf(clazz, ilGenerator);
break;
}
case NormalizedByteCode.__aaload:
{
TypeWrapper tw = ma.GetRawStackTypeWrapper(i, 1);
if(tw.IsUnloadable)
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
Profiler.Count("EmitDynamicAaload");
ilGenerator.Emit(OpCodes.Ldtoken, clazz.TypeAsTBD);
ilGenerator.Emit(OpCodes.Ldstr, tw.Name);
2006-04-11 16:05:24 +04:00
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicAaload);
2005-08-05 12:40:54 +04:00
}
else
{
TypeWrapper elem = tw.ElementTypeWrapper;
if(elem.IsNonPrimitiveValueType)
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
Type t = elem.TypeAsTBD;
ilGenerator.Emit(OpCodes.Ldelema, t);
ilGenerator.Emit(OpCodes.Ldobj, t);
elem.EmitBox(ilGenerator);
2004-03-26 13:19:21 +03:00
}
else
{
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.Ldelem_Ref);
2002-12-18 19:00:25 +03:00
}
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
break;
}
case NormalizedByteCode.__baload:
// NOTE both the JVM and the CLR use signed bytes for boolean arrays (how convenient!)
ilGenerator.Emit(OpCodes.Ldelem_I1);
break;
case NormalizedByteCode.__bastore:
ilGenerator.Emit(OpCodes.Stelem_I1);
break;
case NormalizedByteCode.__caload:
ilGenerator.Emit(OpCodes.Ldelem_U2);
break;
case NormalizedByteCode.__castore:
ilGenerator.Emit(OpCodes.Stelem_I2);
break;
case NormalizedByteCode.__saload:
ilGenerator.Emit(OpCodes.Ldelem_I2);
break;
case NormalizedByteCode.__sastore:
ilGenerator.Emit(OpCodes.Stelem_I2);
break;
case NormalizedByteCode.__iaload:
ilGenerator.Emit(OpCodes.Ldelem_I4);
break;
case NormalizedByteCode.__iastore:
ilGenerator.Emit(OpCodes.Stelem_I4);
break;
case NormalizedByteCode.__laload:
ilGenerator.Emit(OpCodes.Ldelem_I8);
break;
case NormalizedByteCode.__lastore:
ilGenerator.Emit(OpCodes.Stelem_I8);
break;
case NormalizedByteCode.__faload:
ilGenerator.Emit(OpCodes.Ldelem_R4);
break;
case NormalizedByteCode.__fastore:
ilGenerator.Emit(OpCodes.Stelem_R4);
break;
case NormalizedByteCode.__fastore_conv:
{
// see dastore_conv comment
LocalBuilder local = UnsafeAllocTempLocal(typeof(float));
ilGenerator.Emit(OpCodes.Stloc, local);
ilGenerator.Emit(OpCodes.Ldloc, local);
ilGenerator.Emit(OpCodes.Conv_R4);
ilGenerator.Emit(OpCodes.Stelem_R4);
break;
}
2005-08-05 12:40:54 +04:00
case NormalizedByteCode.__daload:
ilGenerator.Emit(OpCodes.Ldelem_R8);
break;
case NormalizedByteCode.__dastore_conv:
{
// HACK the intermediate local is to work around a CLR JIT flaw,
// without the intermediate local it will actually do an explicit
// fstp/fld for the conv.r8, but with intermediate it notices
// that the explicit conversion isn't needed since the array store
// will already result in the conversion. Note that we still need
// do the conv.r8, because otherwise it would be legal
// (per ECMA CLI spec) for the JIT to reuse the unconverted value
// on the FPU stack.
LocalBuilder local = UnsafeAllocTempLocal(typeof(double));
ilGenerator.Emit(OpCodes.Stloc, local);
ilGenerator.Emit(OpCodes.Ldloc, local);
ilGenerator.Emit(OpCodes.Conv_R8);
ilGenerator.Emit(OpCodes.Stelem_R8);
break;
}
2005-08-05 12:40:54 +04:00
case NormalizedByteCode.__dastore:
ilGenerator.Emit(OpCodes.Stelem_R8);
break;
case NormalizedByteCode.__aastore:
{
TypeWrapper tw = ma.GetRawStackTypeWrapper(i, 2);
if(tw.IsUnloadable)
{
Profiler.Count("EmitDynamicAastore");
ilGenerator.Emit(OpCodes.Ldtoken, clazz.TypeAsTBD);
ilGenerator.Emit(OpCodes.Ldstr, tw.Name);
2006-04-11 16:05:24 +04:00
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicAastore);
2005-08-05 12:40:54 +04:00
}
else
{
TypeWrapper elem = tw.ElementTypeWrapper;
if(elem.IsNonPrimitiveValueType)
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
Type t = elem.TypeAsTBD;
LocalBuilder local = UnsafeAllocTempLocal(typeof(object));
ilGenerator.Emit(OpCodes.Stloc, local);
ilGenerator.Emit(OpCodes.Ldelema, t);
ilGenerator.Emit(OpCodes.Ldloc, local);
elem.EmitUnbox(ilGenerator);
ilGenerator.Emit(OpCodes.Stobj, t);
2003-08-12 17:09:31 +04:00
}
2004-03-26 13:19:21 +03:00
else
{
2005-08-05 12:40:54 +04:00
// NOTE for verifiability it is expressly *not* required that the
// value matches the array type, so we don't need to handle interface
// references here.
ilGenerator.Emit(OpCodes.Stelem_Ref);
2003-12-24 14:51:41 +03:00
}
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
break;
}
case NormalizedByteCode.__arraylength:
if(ma.GetRawStackTypeWrapper(i, 0).IsUnloadable)
{
ilGenerator.Emit(OpCodes.Castclass, typeof(Array));
ilGenerator.Emit(OpCodes.Callvirt, typeof(Array).GetMethod("get_Length"));
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
else
{
ilGenerator.Emit(OpCodes.Ldlen);
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
break;
case NormalizedByteCode.__lcmp:
{
LocalBuilder value1 = AllocTempLocal(typeof(long));
LocalBuilder value2 = AllocTempLocal(typeof(long));
ilGenerator.Emit(OpCodes.Stloc, value2);
ilGenerator.Emit(OpCodes.Stloc, value1);
ilGenerator.Emit(OpCodes.Ldloc, value1);
ilGenerator.Emit(OpCodes.Ldloc, value2);
ilGenerator.Emit(OpCodes.Cgt);
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.Ldloc, value1);
ilGenerator.Emit(OpCodes.Ldloc, value2);
ilGenerator.Emit(OpCodes.Clt);
ilGenerator.Emit(OpCodes.Sub);
2005-08-05 12:40:54 +04:00
ReleaseTempLocal(value1);
ReleaseTempLocal(value2);
break;
}
case NormalizedByteCode.__fcmpl:
{
LocalBuilder value1 = AllocTempLocal(typeof(float));
LocalBuilder value2 = AllocTempLocal(typeof(float));
ilGenerator.Emit(OpCodes.Stloc, value2);
ilGenerator.Emit(OpCodes.Stloc, value1);
ilGenerator.Emit(OpCodes.Ldloc, value1);
ilGenerator.Emit(OpCodes.Ldloc, value2);
ilGenerator.Emit(OpCodes.Cgt);
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.Ldloc, value1);
ilGenerator.Emit(OpCodes.Ldloc, value2);
ilGenerator.Emit(OpCodes.Clt_Un);
ilGenerator.Emit(OpCodes.Sub);
2005-08-05 12:40:54 +04:00
ReleaseTempLocal(value1);
ReleaseTempLocal(value2);
break;
}
case NormalizedByteCode.__fcmpg:
{
LocalBuilder value1 = AllocTempLocal(typeof(float));
LocalBuilder value2 = AllocTempLocal(typeof(float));
ilGenerator.Emit(OpCodes.Stloc, value2);
ilGenerator.Emit(OpCodes.Stloc, value1);
ilGenerator.Emit(OpCodes.Ldloc, value1);
ilGenerator.Emit(OpCodes.Ldloc, value2);
ilGenerator.Emit(OpCodes.Cgt_Un);
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.Ldloc, value1);
ilGenerator.Emit(OpCodes.Ldloc, value2);
ilGenerator.Emit(OpCodes.Clt);
ilGenerator.Emit(OpCodes.Sub);
2005-08-05 12:40:54 +04:00
ReleaseTempLocal(value1);
ReleaseTempLocal(value2);
break;
}
case NormalizedByteCode.__dcmpl:
{
LocalBuilder value1 = AllocTempLocal(typeof(double));
LocalBuilder value2 = AllocTempLocal(typeof(double));
ilGenerator.Emit(OpCodes.Stloc, value2);
ilGenerator.Emit(OpCodes.Stloc, value1);
ilGenerator.Emit(OpCodes.Ldloc, value1);
ilGenerator.Emit(OpCodes.Ldloc, value2);
ilGenerator.Emit(OpCodes.Cgt);
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.Ldloc, value1);
ilGenerator.Emit(OpCodes.Ldloc, value2);
ilGenerator.Emit(OpCodes.Clt_Un);
ilGenerator.Emit(OpCodes.Sub);
2005-08-05 12:40:54 +04:00
ReleaseTempLocal(value1);
ReleaseTempLocal(value2);
break;
}
case NormalizedByteCode.__dcmpg:
{
LocalBuilder value1 = AllocTempLocal(typeof(double));
LocalBuilder value2 = AllocTempLocal(typeof(double));
ilGenerator.Emit(OpCodes.Stloc, value2);
ilGenerator.Emit(OpCodes.Stloc, value1);
ilGenerator.Emit(OpCodes.Ldloc, value1);
ilGenerator.Emit(OpCodes.Ldloc, value2);
ilGenerator.Emit(OpCodes.Cgt_Un);
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.Ldloc, value1);
ilGenerator.Emit(OpCodes.Ldloc, value2);
ilGenerator.Emit(OpCodes.Clt);
ilGenerator.Emit(OpCodes.Sub);
2005-08-05 12:40:54 +04:00
ReleaseTempLocal(value1);
ReleaseTempLocal(value2);
break;
}
case NormalizedByteCode.__if_icmpeq:
ilGenerator.Emit(OpCodes.Beq, block.GetLabel(instr.PC + instr.Arg1));
break;
case NormalizedByteCode.__if_icmpne:
ilGenerator.Emit(OpCodes.Bne_Un, block.GetLabel(instr.PC + instr.Arg1));
break;
case NormalizedByteCode.__if_icmple:
ilGenerator.Emit(OpCodes.Ble, block.GetLabel(instr.PC + instr.Arg1));
break;
case NormalizedByteCode.__if_icmplt:
ilGenerator.Emit(OpCodes.Blt, block.GetLabel(instr.PC + instr.Arg1));
break;
case NormalizedByteCode.__if_icmpge:
ilGenerator.Emit(OpCodes.Bge, block.GetLabel(instr.PC + instr.Arg1));
break;
case NormalizedByteCode.__if_icmpgt:
ilGenerator.Emit(OpCodes.Bgt, block.GetLabel(instr.PC + instr.Arg1));
break;
case NormalizedByteCode.__ifle:
ilGenerator.Emit(OpCodes.Ldc_I4_0);
ilGenerator.Emit(OpCodes.Ble, block.GetLabel(instr.PC + instr.Arg1));
break;
case NormalizedByteCode.__iflt:
ilGenerator.Emit(OpCodes.Ldc_I4_0);
ilGenerator.Emit(OpCodes.Blt, block.GetLabel(instr.PC + instr.Arg1));
break;
case NormalizedByteCode.__ifge:
ilGenerator.Emit(OpCodes.Ldc_I4_0);
ilGenerator.Emit(OpCodes.Bge, block.GetLabel(instr.PC + instr.Arg1));
break;
case NormalizedByteCode.__ifgt:
ilGenerator.Emit(OpCodes.Ldc_I4_0);
ilGenerator.Emit(OpCodes.Bgt, block.GetLabel(instr.PC + instr.Arg1));
break;
case NormalizedByteCode.__ifne:
2006-11-27 10:39:30 +03:00
ilGenerator.LazyEmit_ifne(block.GetLabel(instr.PC + instr.Arg1));
2005-08-05 12:40:54 +04:00
break;
case NormalizedByteCode.__ifeq:
2006-11-27 10:39:30 +03:00
ilGenerator.LazyEmit_ifeq(block.GetLabel(instr.PC + instr.Arg1));
2005-08-05 12:40:54 +04:00
break;
case NormalizedByteCode.__ifnonnull:
ilGenerator.Emit(OpCodes.Brtrue, block.GetLabel(instr.PC + instr.Arg1));
break;
case NormalizedByteCode.__ifnull:
ilGenerator.Emit(OpCodes.Brfalse, block.GetLabel(instr.PC + instr.Arg1));
break;
case NormalizedByteCode.__if_acmpeq:
ilGenerator.Emit(OpCodes.Beq, block.GetLabel(instr.PC + instr.Arg1));
break;
case NormalizedByteCode.__if_acmpne:
ilGenerator.Emit(OpCodes.Bne_Un, block.GetLabel(instr.PC + instr.Arg1));
break;
case NormalizedByteCode.__goto:
ilGenerator.Emit(OpCodes.Br, block.GetLabel(instr.PC + instr.Arg1));
break;
case NormalizedByteCode.__ineg:
case NormalizedByteCode.__lneg:
case NormalizedByteCode.__fneg:
case NormalizedByteCode.__dneg:
ilGenerator.Emit(OpCodes.Neg);
break;
case NormalizedByteCode.__iadd:
case NormalizedByteCode.__ladd:
ilGenerator.Emit(OpCodes.Add);
break;
2005-08-05 12:40:54 +04:00
case NormalizedByteCode.__fadd:
ilGenerator.Emit(OpCodes.Add);
if(strictfp)
{
ilGenerator.Emit(OpCodes.Conv_R4);
}
break;
2005-08-05 12:40:54 +04:00
case NormalizedByteCode.__dadd:
ilGenerator.Emit(OpCodes.Add);
if(strictfp)
{
ilGenerator.Emit(OpCodes.Conv_R8);
}
2005-08-05 12:40:54 +04:00
break;
case NormalizedByteCode.__isub:
case NormalizedByteCode.__lsub:
ilGenerator.Emit(OpCodes.Sub);
break;
2005-08-05 12:40:54 +04:00
case NormalizedByteCode.__fsub:
ilGenerator.Emit(OpCodes.Sub);
if(strictfp)
{
ilGenerator.Emit(OpCodes.Conv_R4);
}
break;
2005-08-05 12:40:54 +04:00
case NormalizedByteCode.__dsub:
ilGenerator.Emit(OpCodes.Sub);
if(strictfp)
{
ilGenerator.Emit(OpCodes.Conv_R8);
}
2005-08-05 12:40:54 +04:00
break;
case NormalizedByteCode.__ixor:
case NormalizedByteCode.__lxor:
ilGenerator.Emit(OpCodes.Xor);
break;
case NormalizedByteCode.__ior:
case NormalizedByteCode.__lor:
ilGenerator.Emit(OpCodes.Or);
break;
case NormalizedByteCode.__iand:
case NormalizedByteCode.__land:
ilGenerator.Emit(OpCodes.And);
break;
case NormalizedByteCode.__imul:
case NormalizedByteCode.__lmul:
ilGenerator.Emit(OpCodes.Mul);
break;
2005-08-05 12:40:54 +04:00
case NormalizedByteCode.__fmul:
ilGenerator.Emit(OpCodes.Mul);
if(strictfp)
{
ilGenerator.Emit(OpCodes.Conv_R4);
}
break;
2005-08-05 12:40:54 +04:00
case NormalizedByteCode.__dmul:
ilGenerator.Emit(OpCodes.Mul);
if(strictfp)
{
ilGenerator.Emit(OpCodes.Conv_R8);
}
2005-08-05 12:40:54 +04:00
break;
case NormalizedByteCode.__idiv:
2006-11-27 10:39:30 +03:00
ilGenerator.LazyEmit_idiv();
break;
2005-08-05 12:40:54 +04:00
case NormalizedByteCode.__ldiv:
2006-11-27 10:39:30 +03:00
ilGenerator.LazyEmit_ldiv();
2005-08-05 12:40:54 +04:00
break;
case NormalizedByteCode.__fdiv:
ilGenerator.Emit(OpCodes.Div);
if(strictfp)
{
ilGenerator.Emit(OpCodes.Conv_R4);
}
break;
2005-08-05 12:40:54 +04:00
case NormalizedByteCode.__ddiv:
ilGenerator.Emit(OpCodes.Div);
if(strictfp)
{
ilGenerator.Emit(OpCodes.Conv_R8);
}
2005-08-05 12:40:54 +04:00
break;
case NormalizedByteCode.__irem:
case NormalizedByteCode.__lrem:
{
// we need to special case taking the remainder of dividing by -1,
// because the CLR rem instruction throws an OverflowException when
// taking the remainder of dividing Int32.MinValue by -1, and
// Java just silently overflows
ilGenerator.Emit(OpCodes.Dup);
ilGenerator.Emit(OpCodes.Ldc_I4_M1);
if(instr.NormalizedOpCode == NormalizedByteCode.__lrem)
{
ilGenerator.Emit(OpCodes.Conv_I8);
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
Label label = ilGenerator.DefineLabel();
ilGenerator.Emit(OpCodes.Bne_Un_S, label);
ilGenerator.Emit(OpCodes.Pop);
ilGenerator.Emit(OpCodes.Pop);
ilGenerator.Emit(OpCodes.Ldc_I4_0);
if(instr.NormalizedOpCode == NormalizedByteCode.__lrem)
{
ilGenerator.Emit(OpCodes.Conv_I8);
}
Label label2 = ilGenerator.DefineLabel();
ilGenerator.Emit(OpCodes.Br_S, label2);
ilGenerator.MarkLabel(label);
ilGenerator.Emit(OpCodes.Rem);
ilGenerator.MarkLabel(label2);
break;
}
case NormalizedByteCode.__frem:
ilGenerator.Emit(OpCodes.Rem);
if(strictfp)
{
ilGenerator.Emit(OpCodes.Conv_R4);
}
break;
2005-08-05 12:40:54 +04:00
case NormalizedByteCode.__drem:
ilGenerator.Emit(OpCodes.Rem);
if(strictfp)
{
ilGenerator.Emit(OpCodes.Conv_R8);
}
2005-08-05 12:40:54 +04:00
break;
case NormalizedByteCode.__ishl:
2006-11-27 10:39:30 +03:00
ilGenerator.LazyEmitLdc_I4(31);
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.And);
ilGenerator.Emit(OpCodes.Shl);
break;
case NormalizedByteCode.__lshl:
2006-11-27 10:39:30 +03:00
ilGenerator.LazyEmitLdc_I4(63);
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.And);
ilGenerator.Emit(OpCodes.Shl);
break;
case NormalizedByteCode.__iushr:
2006-11-27 10:39:30 +03:00
ilGenerator.LazyEmitLdc_I4(31);
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.And);
ilGenerator.Emit(OpCodes.Shr_Un);
break;
case NormalizedByteCode.__lushr:
2006-11-27 10:39:30 +03:00
ilGenerator.LazyEmitLdc_I4(63);
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.And);
ilGenerator.Emit(OpCodes.Shr_Un);
break;
case NormalizedByteCode.__ishr:
2006-11-27 10:39:30 +03:00
ilGenerator.LazyEmitLdc_I4(31);
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.And);
ilGenerator.Emit(OpCodes.Shr);
break;
case NormalizedByteCode.__lshr:
2006-11-27 10:39:30 +03:00
ilGenerator.LazyEmitLdc_I4(63);
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.And);
ilGenerator.Emit(OpCodes.Shr);
break;
case NormalizedByteCode.__swap:
{
DupHelper dh = new DupHelper(this, 2);
dh.SetType(0, ma.GetRawStackTypeWrapper(i, 0));
dh.SetType(1, ma.GetRawStackTypeWrapper(i, 1));
dh.Store(0);
dh.Store(1);
dh.Load(0);
dh.Load(1);
dh.Release();
break;
}
case NormalizedByteCode.__dup:
// if the TOS contains a "new" object, it isn't really there, so we don't dup it
if(!VerifierTypeWrapper.IsNew(ma.GetRawStackTypeWrapper(i, 0)))
2004-03-26 13:19:21 +03:00
{
ilGenerator.Emit(OpCodes.Dup);
}
2005-08-05 12:40:54 +04:00
break;
case NormalizedByteCode.__dup2:
{
TypeWrapper type1 = ma.GetRawStackTypeWrapper(i, 0);
if(type1.IsWidePrimitive)
2004-03-26 13:19:21 +03:00
{
ilGenerator.Emit(OpCodes.Dup);
}
2005-08-05 12:40:54 +04:00
else
2004-03-26 13:19:21 +03:00
{
2005-07-20 11:26:10 +04:00
DupHelper dh = new DupHelper(this, 2);
2005-08-05 12:40:54 +04:00
dh.SetType(0, type1);
2004-03-26 13:19:21 +03:00
dh.SetType(1, ma.GetRawStackTypeWrapper(i, 1));
dh.Store(0);
dh.Store(1);
2005-08-05 12:40:54 +04:00
dh.Load(1);
2004-03-26 13:19:21 +03:00
dh.Load(0);
dh.Load(1);
2005-08-05 12:40:54 +04:00
dh.Load(0);
2005-07-20 11:26:10 +04:00
dh.Release();
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
break;
}
case NormalizedByteCode.__dup_x1:
{
DupHelper dh = new DupHelper(this, 2);
dh.SetType(0, ma.GetRawStackTypeWrapper(i, 0));
dh.SetType(1, ma.GetRawStackTypeWrapper(i, 1));
dh.Store(0);
dh.Store(1);
dh.Load(0);
dh.Load(1);
dh.Load(0);
dh.Release();
break;
}
case NormalizedByteCode.__dup2_x1:
{
TypeWrapper type1 = ma.GetRawStackTypeWrapper(i, 0);
if(type1.IsWidePrimitive)
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
DupHelper dh = new DupHelper(this, 2);
dh.SetType(0, type1);
dh.SetType(1, ma.GetRawStackTypeWrapper(i, 1));
dh.Store(0);
dh.Store(1);
dh.Load(0);
dh.Load(1);
dh.Load(0);
dh.Release();
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
else
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
DupHelper dh = new DupHelper(this, 3);
dh.SetType(0, type1);
2004-03-26 13:19:21 +03:00
dh.SetType(1, ma.GetRawStackTypeWrapper(i, 1));
2005-08-05 12:40:54 +04:00
dh.SetType(2, ma.GetRawStackTypeWrapper(i, 2));
2004-03-26 13:19:21 +03:00
dh.Store(0);
dh.Store(1);
2005-08-05 12:40:54 +04:00
dh.Store(2);
dh.Load(1);
2004-03-26 13:19:21 +03:00
dh.Load(0);
2005-08-05 12:40:54 +04:00
dh.Load(2);
2004-03-26 13:19:21 +03:00
dh.Load(1);
dh.Load(0);
2005-07-20 11:26:10 +04:00
dh.Release();
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
break;
}
case NormalizedByteCode.__dup2_x2:
{
TypeWrapper type1 = ma.GetRawStackTypeWrapper(i, 0);
TypeWrapper type2 = ma.GetRawStackTypeWrapper(i, 1);
if(type1.IsWidePrimitive)
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
if(type2.IsWidePrimitive)
2003-12-24 14:51:41 +03:00
{
2005-08-05 12:40:54 +04:00
// Form 4
2005-07-20 11:26:10 +04:00
DupHelper dh = new DupHelper(this, 2);
2004-03-26 13:19:21 +03:00
dh.SetType(0, type1);
2005-08-05 12:40:54 +04:00
dh.SetType(1, type2);
2004-03-26 13:19:21 +03:00
dh.Store(0);
dh.Store(1);
dh.Load(0);
dh.Load(1);
dh.Load(0);
2005-07-20 11:26:10 +04:00
dh.Release();
2002-12-18 19:00:25 +03:00
}
2004-03-26 13:19:21 +03:00
else
2002-12-18 19:00:25 +03:00
{
2005-08-05 12:40:54 +04:00
// Form 2
2005-07-20 11:26:10 +04:00
DupHelper dh = new DupHelper(this, 3);
2004-03-26 13:19:21 +03:00
dh.SetType(0, type1);
2005-08-05 12:40:54 +04:00
dh.SetType(1, type2);
2004-03-26 13:19:21 +03:00
dh.SetType(2, ma.GetRawStackTypeWrapper(i, 2));
2003-12-20 01:19:18 +03:00
dh.Store(0);
dh.Store(1);
2004-03-26 13:19:21 +03:00
dh.Store(2);
2003-12-20 01:19:18 +03:00
dh.Load(0);
2004-03-26 13:19:21 +03:00
dh.Load(2);
2003-12-20 01:19:18 +03:00
dh.Load(1);
dh.Load(0);
2005-07-20 11:26:10 +04:00
dh.Release();
2002-12-18 19:00:25 +03:00
}
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
else
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
TypeWrapper type3 = ma.GetRawStackTypeWrapper(i, 2);
if(type3.IsWidePrimitive)
2002-12-18 19:00:25 +03:00
{
2005-08-05 12:40:54 +04:00
// Form 3
DupHelper dh = new DupHelper(this, 3);
dh.SetType(0, type1);
dh.SetType(1, type2);
dh.SetType(2, type3);
dh.Store(0);
dh.Store(1);
dh.Store(2);
dh.Load(1);
dh.Load(0);
dh.Load(2);
dh.Load(1);
dh.Load(0);
dh.Release();
2002-12-18 19:00:25 +03:00
}
2004-03-26 13:19:21 +03:00
else
2002-12-18 19:00:25 +03:00
{
2005-08-05 12:40:54 +04:00
// Form 1
DupHelper dh = new DupHelper(this, 4);
dh.SetType(0, type1);
dh.SetType(1, type2);
dh.SetType(2, type3);
dh.SetType(3, ma.GetRawStackTypeWrapper(i, 3));
dh.Store(0);
dh.Store(1);
dh.Store(2);
dh.Store(3);
dh.Load(1);
dh.Load(0);
dh.Load(3);
dh.Load(2);
dh.Load(1);
dh.Load(0);
dh.Release();
2002-12-18 19:00:25 +03:00
}
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
break;
}
case NormalizedByteCode.__dup_x2:
{
TypeWrapper type2 = ma.GetRawStackTypeWrapper(i, 1);
if(type2.IsWidePrimitive)
{
// Form 2
DupHelper dh = new DupHelper(this, 2);
dh.SetType(0, ma.GetRawStackTypeWrapper(i, 0));
dh.SetType(1, type2);
dh.Store(0);
dh.Store(1);
dh.Load(0);
dh.Load(1);
dh.Load(0);
dh.Release();
}
else
{
// Form 1
DupHelper dh = new DupHelper(this, 3);
dh.SetType(0, ma.GetRawStackTypeWrapper(i, 0));
dh.SetType(1, type2);
dh.SetType(2, ma.GetRawStackTypeWrapper(i, 2));
dh.Store(0);
dh.Store(1);
dh.Store(2);
dh.Load(0);
dh.Load(2);
dh.Load(1);
dh.Load(0);
dh.Release();
}
2005-08-05 12:40:54 +04:00
break;
}
case NormalizedByteCode.__pop2:
{
TypeWrapper type1 = ma.GetRawStackTypeWrapper(i, 0);
if(type1.IsWidePrimitive)
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.Pop);
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
else
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
if(!VerifierTypeWrapper.IsNew(type1))
2002-12-18 19:00:25 +03:00
{
2004-03-26 13:19:21 +03:00
ilGenerator.Emit(OpCodes.Pop);
2002-12-18 19:00:25 +03:00
}
2005-08-05 12:40:54 +04:00
if(!VerifierTypeWrapper.IsNew(ma.GetRawStackTypeWrapper(i, 1)))
2004-03-26 13:19:21 +03:00
{
2002-12-18 19:00:25 +03:00
ilGenerator.Emit(OpCodes.Pop);
2004-03-26 13:19:21 +03:00
}
2005-08-05 12:40:54 +04:00
}
break;
}
case NormalizedByteCode.__pop:
// if the TOS is a new object, it isn't really there, so we don't need to pop it
if(!VerifierTypeWrapper.IsNew(ma.GetRawStackTypeWrapper(i, 0)))
2005-07-20 14:47:59 +04:00
{
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.Pop);
2005-07-20 14:47:59 +04:00
}
2005-08-05 12:40:54 +04:00
break;
case NormalizedByteCode.__monitorenter:
ilGenerator.Emit(OpCodes.Call, monitorEnterMethod);
break;
case NormalizedByteCode.__monitorexit:
ilGenerator.Emit(OpCodes.Call, monitorExitMethod);
break;
2006-04-20 17:20:57 +04:00
case NormalizedByteCode.__athrow_no_unmap:
if(ma.GetRawStackTypeWrapper(i, 0).IsUnloadable)
{
ilGenerator.Emit(OpCodes.Castclass, typeof(Exception));
}
ilGenerator.Emit(OpCodes.Throw);
break;
2005-08-05 12:40:54 +04:00
case NormalizedByteCode.__athrow:
if(ma.GetRawStackTypeWrapper(i, 0).IsUnloadable)
{
ilGenerator.Emit(OpCodes.Castclass, typeof(Exception));
}
ilGenerator.Emit(OpCodes.Call, unmapExceptionMethod);
ilGenerator.Emit(OpCodes.Throw);
break;
case NormalizedByteCode.__tableswitch:
{
// note that a tableswitch always has at least one entry
// (otherwise it would have failed verification)
Label[] labels = new Label[instr.SwitchEntryCount];
for(int j = 0; j < labels.Length; j++)
{
labels[j] = block.GetLabel(instr.PC + instr.GetSwitchTargetOffset(j));
}
if(instr.GetSwitchValue(0) != 0)
{
2006-11-27 10:39:30 +03:00
ilGenerator.LazyEmitLdc_I4(instr.GetSwitchValue(0));
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.Sub);
}
ilGenerator.Emit(OpCodes.Switch, labels);
ilGenerator.Emit(OpCodes.Br, block.GetLabel(instr.PC + instr.DefaultOffset));
break;
}
case NormalizedByteCode.__lookupswitch:
for(int j = 0; j < instr.SwitchEntryCount; j++)
{
ilGenerator.Emit(OpCodes.Dup);
2006-11-27 10:39:30 +03:00
ilGenerator.LazyEmitLdc_I4(instr.GetSwitchValue(j));
2005-08-05 12:40:54 +04:00
Label label = ilGenerator.DefineLabel();
ilGenerator.Emit(OpCodes.Bne_Un_S, label);
2005-07-20 14:47:59 +04:00
ilGenerator.Emit(OpCodes.Pop);
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.Br, block.GetLabel(instr.PC + instr.GetSwitchTargetOffset(j)));
ilGenerator.MarkLabel(label);
}
ilGenerator.Emit(OpCodes.Pop);
ilGenerator.Emit(OpCodes.Br, block.GetLabel(instr.PC + instr.DefaultOffset));
break;
case NormalizedByteCode.__iinc:
LoadLocal(instr);
2006-11-27 10:39:30 +03:00
ilGenerator.LazyEmitLdc_I4(instr.Arg2);
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.Add);
StoreLocal(instr);
break;
case NormalizedByteCode.__i2b:
ilGenerator.Emit(OpCodes.Conv_I1);
break;
case NormalizedByteCode.__i2c:
ilGenerator.Emit(OpCodes.Conv_U2);
break;
case NormalizedByteCode.__i2s:
ilGenerator.Emit(OpCodes.Conv_I2);
break;
case NormalizedByteCode.__l2i:
ilGenerator.Emit(OpCodes.Conv_I4);
break;
case NormalizedByteCode.__f2i:
2006-04-11 16:05:24 +04:00
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.f2i);
2005-08-05 12:40:54 +04:00
break;
case NormalizedByteCode.__d2i:
2006-04-11 16:05:24 +04:00
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.d2i);
2005-08-05 12:40:54 +04:00
break;
case NormalizedByteCode.__f2l:
2006-04-11 16:05:24 +04:00
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.f2l);
2005-08-05 12:40:54 +04:00
break;
case NormalizedByteCode.__d2l:
2006-04-11 16:05:24 +04:00
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.d2l);
2005-08-05 12:40:54 +04:00
break;
case NormalizedByteCode.__i2l:
ilGenerator.Emit(OpCodes.Conv_I8);
break;
case NormalizedByteCode.__i2f:
case NormalizedByteCode.__l2f:
case NormalizedByteCode.__d2f:
ilGenerator.Emit(OpCodes.Conv_R4);
break;
case NormalizedByteCode.__i2d:
case NormalizedByteCode.__l2d:
case NormalizedByteCode.__f2d:
ilGenerator.Emit(OpCodes.Conv_R8);
break;
case NormalizedByteCode.__jsr:
{
int index = FindPcIndex(instr.PC + instr.Arg1);
int[] callsites = ma.GetCallSites(index);
for(int j = 0; j < callsites.Length; j++)
2004-03-26 13:19:21 +03:00
{
2005-08-05 12:40:54 +04:00
if(callsites[j] == i)
2002-12-18 19:00:25 +03:00
{
2006-11-27 10:39:30 +03:00
ilGenerator.LazyEmitLdc_I4(j);
2005-08-05 12:40:54 +04:00
break;
2002-12-18 19:00:25 +03:00
}
}
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.Br, block.GetLabel(instr.PC + instr.Arg1));
break;
}
case NormalizedByteCode.__ret:
{
// NOTE using a OpCodes.Switch here is not efficient, because 99 out of a 100 cases
// there are either one or two call sites.
int subid = ((VerifierTypeWrapper)ma.GetLocalTypeWrapper(i, instr.Arg1)).Index;
int[] callsites = ma.GetCallSites(subid);
for(int j = 0; j < callsites.Length - 1; j++)
2002-12-18 19:00:25 +03:00
{
2005-08-05 12:40:54 +04:00
if(m.Instructions[callsites[j]].IsReachable)
2005-08-02 11:24:54 +04:00
{
2005-08-05 12:40:54 +04:00
LoadLocal(instr);
2006-11-27 10:39:30 +03:00
ilGenerator.LazyEmitLdc_I4(j);
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.Beq, block.GetLabel(m.Instructions[callsites[j] + 1].PC));
2004-03-26 13:19:21 +03:00
}
2003-12-24 14:51:41 +03:00
}
2005-08-05 12:40:54 +04:00
if(m.Instructions[callsites[callsites.Length - 1]].IsReachable)
2005-08-02 11:24:54 +04:00
{
2005-08-05 12:40:54 +04:00
ilGenerator.Emit(OpCodes.Br, block.GetLabel(m.Instructions[callsites[callsites.Length - 1] + 1].PC));
2005-08-02 11:24:54 +04:00
}
2005-08-05 12:40:54 +04:00
break;
2002-12-18 19:00:25 +03:00
}
2005-08-05 12:40:54 +04:00
case NormalizedByteCode.__nop:
ilGenerator.Emit(OpCodes.Nop);
break;
case NormalizedByteCode.__static_error:
{
TypeWrapper exceptionType;
switch(instr.HardError)
{
case HardError.AbstractMethodError:
exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.AbstractMethodError");
break;
case HardError.IllegalAccessError:
exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.IllegalAccessError");
break;
case HardError.IncompatibleClassChangeError:
exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.IncompatibleClassChangeError");
break;
case HardError.InstantiationError:
exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.InstantiationError");
break;
case HardError.LinkageError:
exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.LinkageError");
break;
case HardError.NoClassDefFoundError:
exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.NoClassDefFoundError");
break;
case HardError.NoSuchFieldError:
exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.NoSuchFieldError");
break;
case HardError.NoSuchMethodError:
exceptionType = ClassLoaderWrapper.LoadClassCritical("java.lang.NoSuchMethodError");
break;
default:
throw new InvalidOperationException();
}
string message = ma.GetErrorMessage(instr.HardErrorMessageId);
Tracer.Error(Tracer.Compiler, "{0}: {1}\n\tat {2}.{3}{4}", exceptionType.Name, message, classFile.Name, m.Name, m.Signature);
ilGenerator.Emit(OpCodes.Ldstr, message);
MethodWrapper method = exceptionType.GetMethodWrapper("<init>", "(Ljava.lang.String;)V", false);
method.Link();
method.EmitNewobj(ilGenerator);
ilGenerator.Emit(OpCodes.Throw);
break;
}
default:
throw new NotImplementedException(instr.NormalizedOpCode.ToString());
}
// mark next instruction as inuse
switch(instr.NormalizedOpCode)
{
case NormalizedByteCode.__tableswitch:
case NormalizedByteCode.__lookupswitch:
case NormalizedByteCode.__goto:
case NormalizedByteCode.__jsr:
case NormalizedByteCode.__ret:
case NormalizedByteCode.__ireturn:
case NormalizedByteCode.__lreturn:
case NormalizedByteCode.__freturn:
case NormalizedByteCode.__dreturn:
case NormalizedByteCode.__areturn:
case NormalizedByteCode.__return:
case NormalizedByteCode.__athrow:
2006-04-20 17:20:57 +04:00
case NormalizedByteCode.__athrow_no_unmap:
2005-08-05 12:40:54 +04:00
case NormalizedByteCode.__static_error:
instructionIsForwardReachable = false;
break;
default:
instructionIsForwardReachable = true;
Debug.Assert(m.Instructions[i + 1].IsReachable);
// don't fall through end of try block
if(m.Instructions[i + 1].PC == block.End)
{
// TODO instead of emitting a branch to the leave stub, it would be more efficient to put the leave stub here
ilGenerator.Emit(OpCodes.Br, block.GetLabel(m.Instructions[i + 1].PC));
}
break;
2004-03-26 13:19:21 +03:00
}
}
}
private static void EmitLoadCharArrayLiteral(ILGenerator ilgen, string str, TypeWrapper tw)
{
ModuleBuilder mod = tw.GetClassLoader().GetTypeWrapperFactory().ModuleBuilder;
// FXBUG on .NET 1.1 & 2.0 the value type that Ref.Emit automatically generates is public,
// so we pre-create a non-public type with the right name here and it will "magically" use
// that instead.
// If we're running on Mono this isn't necessary, but for simplicitly we'll simply create
// the type as well (it is useless, but all it does is waste a little space).
int length = str.Length * 2;
string typename = "$ArrayType$" + length;
Type type = mod.GetType(typename, false, false);
if(type == null)
{
if(tw.GetClassLoader().GetTypeWrapperFactory().ReserveName(typename))
{
TypeBuilder tb = mod.DefineType(typename, TypeAttributes.Sealed | TypeAttributes.Class | TypeAttributes.ExplicitLayout | TypeAttributes.NotPublic, typeof(ValueType), PackingSize.Size1, length);
AttributeHelper.HideFromJava(tb);
type = tb.CreateType();
}
}
if(type == null
|| !type.IsValueType
|| type.StructLayoutAttribute.Pack != 1 || type.StructLayoutAttribute.Size != length)
{
// the type that we found doesn't match (must mean we've compiled a Java type with that name),
// so we fall back to the string approach
ilgen.Emit(OpCodes.Ldstr, str);
ilgen.Emit(OpCodes.Call, typeof(string).GetMethod("ToCharArray", Type.EmptyTypes));
return;
}
ilgen.Emit(OpCodes.Ldc_I4, str.Length);
ilgen.Emit(OpCodes.Newarr, typeof(char));
ilgen.Emit(OpCodes.Dup);
byte[] data = new byte[length];
for (int j = 0; j < str.Length; j++)
{
data[j * 2 + 0] = (byte)(str[j] >> 0);
data[j * 2 + 1] = (byte)(str[j] >> 8);
}
// NOTE we define a module field, because type fields on Mono don't use the global $ArrayType$<n> type.
// NOTE this also means that this will only work during static compilation, because ModuleBuilder.CreateGlobalFunctions() must
// be called before the field can be used.
FieldBuilder fb = mod.DefineInitializedData("__<str>", data, FieldAttributes.Static | FieldAttributes.PrivateScope);
if(!fb.FieldType.Equals(type))
{
// this is actually relatively harmless, but I would like to know about it, so we abort and hope that users report this when they encounter it
JVM.CriticalFailure("Unsupported runtime: ModuleBuilder.DefineInitializedData() field type mispredicted", null);
}
ilgen.Emit(OpCodes.Ldtoken, fb);
ilgen.Emit(OpCodes.Call, typeof(System.Runtime.CompilerServices.RuntimeHelpers).GetMethod("InitializeArray", new Type[] { typeof(Array), typeof(RuntimeFieldHandle) }));
}
2005-12-07 12:06:32 +03:00
private MethodInfo GetInvokeSpecialStub(MethodWrapper method)
{
string key = method.DeclaringType.Name + ":" + method.Name + method.Signature;
MethodInfo mi = (MethodInfo)invokespecialstubcache[key];
if(mi == null)
{
MethodBuilder stub = clazz.TypeAsBuilder.DefineMethod("<>", MethodAttributes.PrivateScope, method.ReturnTypeForDefineMethod, method.GetParametersForDefineMethod());
ILGenerator ilgen = stub.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_0);
int argc = method.GetParametersForDefineMethod().Length;
for(int i = 1; i <= argc; i++)
{
ilgen.Emit(OpCodes.Ldarg_S, (byte)i);
}
method.EmitCall(ilgen);
ilgen.Emit(OpCodes.Ret);
invokespecialstubcache[key] = stub;
mi = stub;
}
return mi;
}
2003-04-14 13:41:58 +04:00
// NOTE despite its name this also handles value type args
2005-12-07 12:06:32 +03:00
private void CastInterfaceArgs(MethodWrapper method, TypeWrapper[] args, int instructionIndex, bool instanceMethod)
2003-02-12 15:28:04 +03:00
{
2005-12-07 12:06:32 +03:00
bool needsCast = false;
2005-08-05 16:18:35 +04:00
bool dynamic;
switch(m.Instructions[instructionIndex].NormalizedOpCode)
{
case NormalizedByteCode.__dynamic_invokeinterface:
case NormalizedByteCode.__dynamic_invokestatic:
case NormalizedByteCode.__dynamic_invokevirtual:
dynamic = true;
break;
default:
dynamic = false;
break;
}
2003-02-12 15:28:04 +03:00
2005-12-19 18:12:49 +03:00
int firstCastArg = -1;
2003-04-18 14:27:33 +04:00
if(!needsCast)
2003-02-12 15:28:04 +03:00
{
2003-04-18 14:27:33 +04:00
for(int i = 0; i < args.Length; i++)
2003-02-12 15:28:04 +03:00
{
2003-08-07 12:58:08 +04:00
if(args[i].IsUnloadable)
{
// nothing to do, callee will (eventually) do the cast
}
2003-10-17 12:08:31 +04:00
else if(args[i].IsGhost)
{
needsCast = true;
2005-12-19 18:12:49 +03:00
firstCastArg = i;
2003-10-17 12:08:31 +04:00
break;
}
2003-08-29 14:14:08 +04:00
else if(args[i].IsInterfaceOrInterfaceArray)
2003-04-18 14:27:33 +04:00
{
2005-12-29 12:57:41 +03:00
TypeWrapper tw = ma.GetStackTypeWrapper(instructionIndex, args.Length - 1 - i);
2003-08-28 13:55:04 +04:00
if(!tw.IsUnloadable && !tw.IsAssignableTo(args[i]))
2003-04-18 14:27:33 +04:00
{
needsCast = true;
2005-12-19 18:12:49 +03:00
firstCastArg = i;
2003-04-18 14:27:33 +04:00
break;
}
}
2003-08-07 12:58:08 +04:00
else if(args[i].IsNonPrimitiveValueType)
2003-02-12 15:28:04 +03:00
{
2004-09-09 15:17:55 +04:00
if(i == 0 && instanceMethod && method.DeclaringType != args[i])
{
// no cast needed because we're calling an inherited method
}
else
{
needsCast = true;
2005-12-19 18:12:49 +03:00
firstCastArg = i;
break;
2004-09-09 15:17:55 +04:00
}
2003-02-12 15:28:04 +03:00
}
2003-12-24 14:51:41 +03:00
// if the stack contains an unloadable, we might need to cast it
// (e.g. if the argument type is a base class that is loadable)
if(ma.GetRawStackTypeWrapper(instructionIndex, i).IsUnloadable)
{
needsCast = true;
2005-12-19 18:12:49 +03:00
firstCastArg = i;
2003-12-24 14:51:41 +03:00
break;
}
2003-02-12 15:28:04 +03:00
}
}
if(needsCast)
{
2005-07-20 11:26:10 +04:00
DupHelper dh = new DupHelper(this, args.Length);
2005-12-19 18:12:49 +03:00
for(int i = firstCastArg + 1; i < args.Length; i++)
2003-02-12 15:28:04 +03:00
{
2003-12-24 14:51:41 +03:00
TypeWrapper tw = ma.GetRawStackTypeWrapper(instructionIndex, args.Length - 1 - i);
2005-12-29 12:57:41 +03:00
if(tw != VerifierTypeWrapper.UninitializedThis
&& !VerifierTypeWrapper.IsThis(tw))
2003-12-24 14:51:41 +03:00
{
tw = args[i];
}
dh.SetType(i, tw);
2003-02-12 15:28:04 +03:00
}
2005-12-19 18:12:49 +03:00
for(int i = args.Length - 1; i >= firstCastArg; i--)
2003-02-12 15:28:04 +03:00
{
2004-09-27 14:17:34 +04:00
if(!args[i].IsUnloadable && !args[i].IsGhost)
2003-02-12 15:28:04 +03:00
{
2005-12-29 12:57:41 +03:00
TypeWrapper tw = ma.GetStackTypeWrapper(instructionIndex, args.Length - 1 - i);
2003-12-24 14:51:41 +03:00
if(tw.IsUnloadable || (args[i].IsInterfaceOrInterfaceArray && !tw.IsAssignableTo(args[i])))
2003-08-29 14:14:08 +04:00
{
2005-02-16 14:20:43 +03:00
EmitHelper.EmitAssertType(ilGenerator, args[i].TypeAsTBD);
2004-06-25 13:38:07 +04:00
Profiler.Count("InterfaceDownCast");
2003-08-29 14:14:08 +04:00
}
2003-02-12 15:28:04 +03:00
}
2005-12-19 18:12:49 +03:00
if(i != firstCastArg)
{
dh.Store(i);
}
2003-02-12 15:28:04 +03:00
}
2005-12-19 18:12:49 +03:00
for(int i = firstCastArg; i < args.Length; i++)
2003-02-12 15:28:04 +03:00
{
2005-12-19 18:12:49 +03:00
if(i != firstCastArg)
{
dh.Load(i);
}
2005-08-05 16:18:35 +04:00
if(!args[i].IsUnloadable && args[i].IsGhost && !dynamic)
2003-04-14 13:41:58 +04:00
{
2004-06-28 12:24:07 +04:00
if(i == 0 && instanceMethod && !method.DeclaringType.IsInterface)
{
// we're calling a java.lang.Object method through a ghost interface reference,
// no ghost handling is needed
}
else
2003-04-14 13:41:58 +04:00
{
2005-12-19 18:12:49 +03:00
LocalBuilder ghost = AllocTempLocal(typeof(object));
ilGenerator.Emit(OpCodes.Stloc, ghost);
2005-07-20 11:26:10 +04:00
LocalBuilder local = AllocTempLocal(args[i].TypeAsSignatureType);
2004-06-28 12:24:07 +04:00
ilGenerator.Emit(OpCodes.Ldloca, local);
2005-12-19 18:12:49 +03:00
ilGenerator.Emit(OpCodes.Ldloc, ghost);
2004-06-28 12:24:07 +04:00
ilGenerator.Emit(OpCodes.Stfld, args[i].GhostRefField);
ilGenerator.Emit(OpCodes.Ldloca, local);
2005-07-20 11:26:10 +04:00
ReleaseTempLocal(local);
2004-06-28 12:24:07 +04:00
// NOTE when the this argument is a value type, we need the address on the stack instead of the value
if(i != 0 || !instanceMethod)
{
2005-02-02 18:11:26 +03:00
ilGenerator.Emit(OpCodes.Ldobj, args[i].TypeAsSignatureType);
2004-06-28 12:24:07 +04:00
}
2003-10-17 12:08:31 +04:00
}
}
else
{
2005-08-05 16:18:35 +04:00
if(!args[i].IsUnloadable && !dynamic)
2003-10-17 12:08:31 +04:00
{
2003-12-24 14:51:41 +03:00
if(args[i].IsNonPrimitiveValueType)
2003-10-17 12:08:31 +04:00
{
2004-09-09 15:17:55 +04:00
if(i == 0 && instanceMethod)
2003-12-24 14:51:41 +03:00
{
2004-09-09 15:17:55 +04:00
// we only need to unbox if the method was actually declared on the value type
if(method.DeclaringType == args[i])
{
2005-12-20 15:44:29 +03:00
ilGenerator.LazyEmitUnbox(args[i].TypeAsTBD);
2004-09-09 15:17:55 +04:00
}
2003-12-24 14:51:41 +03:00
}
else
{
2004-09-09 15:17:55 +04:00
args[i].EmitUnbox(ilGenerator);
2003-12-24 14:51:41 +03:00
}
2003-11-17 15:01:50 +03:00
}
2003-12-24 14:51:41 +03:00
else if(ma.GetRawStackTypeWrapper(instructionIndex, args.Length - 1 - i).IsUnloadable)
2003-11-17 15:01:50 +03:00
{
2005-02-02 18:11:26 +03:00
ilGenerator.Emit(OpCodes.Castclass, args[i].TypeAsSignatureType);
2003-10-17 12:08:31 +04:00
}
2003-04-14 13:41:58 +04:00
}
}
2003-02-12 15:28:04 +03:00
}
2005-07-20 11:26:10 +04:00
dh.Release();
2003-02-12 15:28:04 +03:00
}
}
2005-08-05 16:18:35 +04:00
private void DynamicGetPutField(Instruction instr, int i)
2002-12-18 19:00:25 +03:00
{
2003-08-12 17:09:31 +04:00
NormalizedByteCode bytecode = instr.NormalizedOpCode;
2004-08-30 19:56:23 +04:00
ClassFile.ConstantPoolItemFieldref cpi = classFile.GetFieldref(instr.Arg1);
2005-08-05 16:18:35 +04:00
bool write = (bytecode == NormalizedByteCode.__dynamic_putfield || bytecode == NormalizedByteCode.__dynamic_putstatic);
2004-08-30 19:56:23 +04:00
TypeWrapper wrapper = cpi.GetClassType();
2005-08-05 16:18:35 +04:00
TypeWrapper fieldTypeWrapper = cpi.GetFieldType();
if(write && !fieldTypeWrapper.IsUnloadable && fieldTypeWrapper.IsPrimitive)
2003-01-06 16:56:37 +03:00
{
2005-08-05 16:18:35 +04:00
ilGenerator.Emit(OpCodes.Box, fieldTypeWrapper.TypeAsTBD);
2003-01-06 16:56:37 +03:00
}
2005-08-05 16:18:35 +04:00
ilGenerator.Emit(OpCodes.Ldstr, cpi.Name);
ilGenerator.Emit(OpCodes.Ldstr, cpi.Signature);
ilGenerator.Emit(OpCodes.Ldtoken, clazz.TypeAsTBD);
ilGenerator.Emit(OpCodes.Ldstr, wrapper.Name);
switch(bytecode)
2002-12-18 19:00:25 +03:00
{
2005-08-05 16:18:35 +04:00
case NormalizedByteCode.__dynamic_getfield:
Profiler.Count("EmitDynamicGetfield");
2006-04-11 16:05:24 +04:00
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicGetfield);
2005-08-05 16:18:35 +04:00
EmitReturnTypeConversion(ilGenerator, fieldTypeWrapper);
break;
case NormalizedByteCode.__dynamic_putfield:
Profiler.Count("EmitDynamicPutfield");
2006-04-11 16:05:24 +04:00
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicPutfield);
2005-08-05 16:18:35 +04:00
break;
case NormalizedByteCode.__dynamic_getstatic:
Profiler.Count("EmitDynamicGetstatic");
2006-04-11 16:05:24 +04:00
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicGetstatic);
2005-08-05 16:18:35 +04:00
EmitReturnTypeConversion(ilGenerator, fieldTypeWrapper);
break;
case NormalizedByteCode.__dynamic_putstatic:
Profiler.Count("EmitDynamicPutstatic");
2006-04-11 16:05:24 +04:00
ilGenerator.Emit(OpCodes.Call, ByteCodeHelperMethods.DynamicPutstatic);
2005-08-05 16:18:35 +04:00
break;
default:
throw new InvalidOperationException();
2002-12-18 19:00:25 +03:00
}
}
2004-08-17 13:05:21 +04:00
private static void EmitReturnTypeConversion(ILGenerator ilgen, TypeWrapper typeWrapper)
2002-12-18 19:00:25 +03:00
{
2004-08-17 13:05:21 +04:00
if(typeWrapper.IsUnloadable)
2002-12-18 19:00:25 +03:00
{
2004-08-17 13:05:21 +04:00
// nothing to do for unloadables
2002-12-18 19:00:25 +03:00
}
2004-08-17 13:05:21 +04:00
else if(typeWrapper == PrimitiveTypeWrapper.VOID)
2002-12-18 19:00:25 +03:00
{
2004-08-17 13:05:21 +04:00
ilgen.Emit(OpCodes.Pop);
}
else if(typeWrapper.IsPrimitive)
{
// NOTE we don't need to use TypeWrapper.EmitUnbox, because the return value cannot be null
ilgen.Emit(OpCodes.Unbox, typeWrapper.TypeAsTBD);
ilgen.Emit(OpCodes.Ldobj, typeWrapper.TypeAsTBD);
2005-02-02 18:11:26 +03:00
if(typeWrapper == PrimitiveTypeWrapper.BYTE)
{
ilgen.Emit(OpCodes.Conv_I1);
}
2004-08-17 13:05:21 +04:00
}
else
{
ilgen.Emit(OpCodes.Castclass, typeWrapper.TypeAsTBD);
2002-12-18 19:00:25 +03:00
}
}
2004-08-17 13:05:21 +04:00
private class DynamicMethodWrapper : MethodWrapper
2003-01-06 19:30:09 +03:00
{
private TypeWrapper wrapper;
2004-08-17 13:05:21 +04:00
private ClassFile.ConstantPoolItemMI cpi;
2003-01-06 19:30:09 +03:00
2005-05-31 19:30:36 +04:00
internal DynamicMethodWrapper(TypeWrapper wrapper, ClassFile.ConstantPoolItemMI cpi)
2005-02-02 18:11:26 +03:00
: base(wrapper, cpi.Name, cpi.Signature, null, cpi.GetRetType(), cpi.GetArgTypes(), Modifiers.Public, MemberFlags.None)
2003-01-06 19:30:09 +03:00
{
this.wrapper = wrapper;
this.cpi = cpi;
}
2004-08-17 13:05:21 +04:00
internal override void EmitCall(ILGenerator ilgen)
{
2006-04-11 16:05:24 +04:00
Emit(ByteCodeHelperMethods.DynamicInvokestatic, ilgen, cpi.GetRetType());
2004-08-17 13:05:21 +04:00
}
internal override void EmitCallvirt(ILGenerator ilgen)
{
2006-04-11 16:05:24 +04:00
Emit(ByteCodeHelperMethods.DynamicInvokevirtual, ilgen, cpi.GetRetType());
2004-08-17 13:05:21 +04:00
}
2006-08-26 17:00:50 +04:00
internal override void EmitNewobj(ILGenerator ilgen, MethodAnalyzer ma, int opcodeIndex)
2004-08-17 13:05:21 +04:00
{
2006-04-11 16:05:24 +04:00
Emit(ByteCodeHelperMethods.DynamicInvokeSpecialNew, ilgen, cpi.GetClassType());
2004-08-17 13:05:21 +04:00
}
private void Emit(MethodInfo helperMethod, ILGenerator ilGenerator, TypeWrapper retTypeWrapper)
2003-01-06 19:30:09 +03:00
{
2004-03-29 14:11:33 +04:00
Profiler.Count("EmitDynamicInvokeEmitter");
2004-08-30 19:56:23 +04:00
TypeWrapper[] args = cpi.GetArgTypes();
2003-01-06 19:30:09 +03:00
LocalBuilder argarray = ilGenerator.DeclareLocal(typeof(object[]));
2004-08-17 13:05:21 +04:00
LocalBuilder val = ilGenerator.DeclareLocal(typeof(object));
2003-01-06 19:30:09 +03:00
ilGenerator.Emit(OpCodes.Ldc_I4, args.Length);
ilGenerator.Emit(OpCodes.Newarr, typeof(object));
ilGenerator.Emit(OpCodes.Stloc, argarray);
for(int i = args.Length - 1; i >= 0; i--)
{
if(args[i].IsPrimitive)
{
2004-01-11 16:14:42 +03:00
ilGenerator.Emit(OpCodes.Box, args[i].TypeAsTBD);
2003-01-06 19:30:09 +03:00
}
ilGenerator.Emit(OpCodes.Stloc, val);
ilGenerator.Emit(OpCodes.Ldloc, argarray);
ilGenerator.Emit(OpCodes.Ldc_I4, i);
ilGenerator.Emit(OpCodes.Ldloc, val);
ilGenerator.Emit(OpCodes.Stelem_Ref);
}
2004-01-11 16:14:42 +03:00
ilGenerator.Emit(OpCodes.Ldtoken, wrapper.TypeAsTBD);
2003-01-06 19:30:09 +03:00
ilGenerator.Emit(OpCodes.Ldstr, cpi.Class);
ilGenerator.Emit(OpCodes.Ldstr, cpi.Name);
ilGenerator.Emit(OpCodes.Ldstr, cpi.Signature);
ilGenerator.Emit(OpCodes.Ldloc, argarray);
2003-08-12 17:09:31 +04:00
ilGenerator.Emit(OpCodes.Call, helperMethod);
EmitReturnTypeConversion(ilGenerator, retTypeWrapper);
}
}
#if STATIC_COMPILER
private class ReplacedMethodWrapper : MethodWrapper
{
private CodeEmitter code;
internal ReplacedMethodWrapper(ClassFile.ConstantPoolItemMI cpi, CodeEmitter code)
: base(cpi.GetClassType(), cpi.Name, cpi.Signature, null, cpi.GetRetType(), cpi.GetArgTypes(), Modifiers.Public, MemberFlags.None)
{
this.code = code;
}
internal override void EmitCall(ILGenerator ilgen)
{
code.Emit(ilgen);
}
internal override void EmitCallvirt(ILGenerator ilgen)
{
code.Emit(ilgen);
}
internal override void EmitNewobj(ILGenerator ilgen, MethodAnalyzer ma, int opcodeIndex)
{
code.Emit(ilgen);
}
}
#endif
2005-08-05 12:40:54 +04:00
private MethodWrapper GetMethodCallEmitter(ClassFile.ConstantPoolItemMI cpi, NormalizedByteCode invoke)
2002-12-18 19:00:25 +03:00
{
#if STATIC_COMPILER
if(replacedMethods != null)
{
for(int i = 0; i < replacedMethods.Length; i++)
{
if(replacedMethods[i].Class == cpi.Class
&& replacedMethods[i].Name == cpi.Name
&& replacedMethods[i].Sig == cpi.Signature)
{
if(replacedMethodWrappers[i] == null)
{
replacedMethodWrappers[i] = new ReplacedMethodWrapper(cpi, replacedMethods[i].code);
}
return replacedMethodWrappers[i];
}
}
}
#endif
2007-01-04 15:54:10 +03:00
MethodWrapper mw = null;
switch (invoke)
2005-08-05 12:40:54 +04:00
{
case NormalizedByteCode.__invokespecial:
2007-01-04 15:54:10 +03:00
mw = cpi.GetMethodForInvokespecial();
break;
2005-08-05 12:40:54 +04:00
case NormalizedByteCode.__invokeinterface:
2007-01-04 15:54:10 +03:00
mw = cpi.GetMethod();
break;
2005-08-05 12:40:54 +04:00
case NormalizedByteCode.__invokestatic:
case NormalizedByteCode.__invokevirtual:
2007-01-04 15:54:10 +03:00
mw = cpi.GetMethod();
break;
2005-08-05 12:40:54 +04:00
case NormalizedByteCode.__dynamic_invokeinterface:
case NormalizedByteCode.__dynamic_invokestatic:
case NormalizedByteCode.__dynamic_invokevirtual:
2006-01-31 13:13:12 +03:00
case NormalizedByteCode.__dynamic_invokespecial:
2005-08-05 12:40:54 +04:00
return new DynamicMethodWrapper(clazz, cpi);
default:
throw new InvalidOperationException();
2002-12-18 19:00:25 +03:00
}
2007-01-04 15:54:10 +03:00
if(mw.DeclaringType.IsDynamicOnly)
{
return new DynamicMethodWrapper(clazz, cpi);
}
return mw;
2002-12-18 19:00:25 +03:00
}
2003-01-06 16:56:37 +03:00
// TODO this method should have a better name
private TypeWrapper SigTypeToClassName(TypeWrapper type, TypeWrapper nullType)
2002-12-18 19:00:25 +03:00
{
2005-12-29 12:57:41 +03:00
if(type == VerifierTypeWrapper.UninitializedThis
|| VerifierTypeWrapper.IsThis(type))
2002-12-18 19:00:25 +03:00
{
2003-01-06 16:56:37 +03:00
return clazz;
2002-12-18 19:00:25 +03:00
}
2003-01-06 16:56:37 +03:00
else if(VerifierTypeWrapper.IsNew(type))
2002-12-18 19:00:25 +03:00
{
2003-01-06 16:56:37 +03:00
return ((VerifierTypeWrapper)type).UnderlyingType;
2002-12-18 19:00:25 +03:00
}
2003-01-06 16:56:37 +03:00
else if(type == VerifierTypeWrapper.Null)
2002-12-18 19:00:25 +03:00
{
2003-01-06 16:56:37 +03:00
return nullType;
}
else
{
return type;
2002-12-18 19:00:25 +03:00
}
}
private int FindPcIndex(int target)
{
return m.PcIndexMap[target];
}
private int SafeFindPcIndex(int target)
{
if(target < 0 || target >= m.PcIndexMap.Length)
{
return -1;
}
return m.PcIndexMap[target];
}
2004-03-26 13:19:21 +03:00
private LocalVar LoadLocal(ClassFile.Method.Instruction instr)
2002-12-18 19:00:25 +03:00
{
2004-03-26 13:19:21 +03:00
LocalVar v = ma.GetLocalVar(FindPcIndex(instr.PC));
if(v.isArg)
2002-12-18 19:00:25 +03:00
{
int i = m.ArgMap[instr.NormalizedArg1];
if(v.type == PrimitiveTypeWrapper.DOUBLE)
{
ilGenerator.Emit(OpCodes.Ldarga, (short)i);
ilGenerator.Emit(OpCodes.Volatile);
ilGenerator.Emit(OpCodes.Ldind_R8);
return v;
}
if(v.type == PrimitiveTypeWrapper.FLOAT)
{
ilGenerator.Emit(OpCodes.Ldarga, (short)i);
ilGenerator.Emit(OpCodes.Volatile);
ilGenerator.Emit(OpCodes.Ldind_R4);
return v;
}
2002-12-18 19:00:25 +03:00
switch(i)
{
case 0:
ilGenerator.Emit(OpCodes.Ldarg_0);
break;
case 1:
ilGenerator.Emit(OpCodes.Ldarg_1);
break;
case 2:
ilGenerator.Emit(OpCodes.Ldarg_2);
break;
case 3:
ilGenerator.Emit(OpCodes.Ldarg_3);
break;
default:
if(i < 256)
{
ilGenerator.Emit(OpCodes.Ldarg_S, (byte)i);
}
else
{
2004-07-10 11:19:42 +04:00
ilGenerator.Emit(OpCodes.Ldarg, (short)i);
2002-12-18 19:00:25 +03:00
}
break;
}
}
2004-03-26 13:19:21 +03:00
else if(v.type == VerifierTypeWrapper.Null)
2002-12-18 19:00:25 +03:00
{
2004-03-26 13:19:21 +03:00
ilGenerator.Emit(OpCodes.Ldnull);
2002-12-18 19:00:25 +03:00
}
else
{
2004-03-26 13:19:21 +03:00
if(v.builder == null)
2002-12-18 19:00:25 +03:00
{
2004-03-26 13:19:21 +03:00
v.builder = ilGenerator.DeclareLocal(v.type.TypeAsLocalOrStackType);
2006-08-29 10:28:34 +04:00
if(debug && v.name != null)
2004-03-26 13:19:21 +03:00
{
v.builder.SetLocalSymInfo(v.name);
}
2002-12-18 19:00:25 +03:00
}
2004-03-26 13:19:21 +03:00
ilGenerator.Emit(OpCodes.Ldloc, v.builder);
2002-12-18 19:00:25 +03:00
}
2004-03-26 13:19:21 +03:00
return v;
2002-12-18 19:00:25 +03:00
}
private LocalVar StoreLocal(ClassFile.Method.Instruction instr)
2002-12-18 19:00:25 +03:00
{
2004-03-26 13:19:21 +03:00
LocalVar v = ma.GetLocalVar(FindPcIndex(instr.PC));
2004-03-20 16:25:08 +03:00
if(v == null)
2002-12-18 19:00:25 +03:00
{
2004-03-26 13:19:21 +03:00
// dead store
ilGenerator.Emit(OpCodes.Pop);
2002-12-18 19:00:25 +03:00
}
2004-03-26 13:19:21 +03:00
else if(v.isArg)
2002-12-18 19:00:25 +03:00
{
2004-03-26 13:19:21 +03:00
int i = m.ArgMap[instr.NormalizedArg1];
if(i < 256)
2002-12-18 19:00:25 +03:00
{
2004-03-26 13:19:21 +03:00
ilGenerator.Emit(OpCodes.Starg_S, (byte)i);
2002-12-18 19:00:25 +03:00
}
2004-03-26 13:19:21 +03:00
else
2002-12-18 19:00:25 +03:00
{
2004-07-10 11:19:42 +04:00
ilGenerator.Emit(OpCodes.Starg, (short)i);
2002-12-18 19:00:25 +03:00
}
2004-03-26 13:19:21 +03:00
}
else if(v.type == VerifierTypeWrapper.Null)
{
ilGenerator.Emit(OpCodes.Pop);
2002-12-18 19:00:25 +03:00
}
else
{
2004-03-26 13:19:21 +03:00
if(v.builder == null)
2003-12-20 01:19:18 +03:00
{
2004-03-26 13:19:21 +03:00
v.builder = ilGenerator.DeclareLocal(v.type.TypeAsLocalOrStackType);
2006-08-29 10:28:34 +04:00
if(debug && v.name != null)
2003-12-20 01:19:18 +03:00
{
2004-03-26 13:19:21 +03:00
v.builder.SetLocalSymInfo(v.name);
2003-12-20 01:19:18 +03:00
}
}
2004-03-26 13:19:21 +03:00
ilGenerator.Emit(OpCodes.Stloc, v.builder);
2002-12-18 19:00:25 +03:00
}
return v;
2002-12-18 19:00:25 +03:00
}
}
2005-12-07 12:06:32 +03:00
#endif