зеркало из https://github.com/mono/ikvm-fork.git
421 строка
19 KiB
C#
421 строка
19 KiB
C#
/*
|
|
Copyright (C) 2008, 2009 Jeroen Frijters
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
#if IKVM_REF_EMIT
|
|
using IKVM.Reflection.Emit;
|
|
#else
|
|
using System.Reflection.Emit;
|
|
#endif
|
|
using System.Diagnostics;
|
|
|
|
namespace IKVM.Internal
|
|
{
|
|
static class Intrinsics
|
|
{
|
|
private delegate bool Emitter(DynamicTypeWrapper.FinishContext context, CodeEmitter ilgen, MethodWrapper method, MethodAnalyzer ma, int opcodeIndex, MethodWrapper caller, ClassFile classFile, ClassFile.Method.Instruction[] code);
|
|
private struct IntrinsicKey : IEquatable<IntrinsicKey>
|
|
{
|
|
private readonly string className;
|
|
private readonly string methodName;
|
|
private readonly string methodSignature;
|
|
|
|
internal IntrinsicKey(string className, string methodName, string methodSignature)
|
|
{
|
|
this.className = string.Intern(className);
|
|
this.methodName = string.Intern(methodName);
|
|
this.methodSignature = string.Intern(methodSignature);
|
|
}
|
|
|
|
internal IntrinsicKey(MethodWrapper mw)
|
|
{
|
|
this.className = mw.DeclaringType.Name;
|
|
this.methodName = mw.Name;
|
|
this.methodSignature = mw.Signature;
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
return Equals((IntrinsicKey)obj);
|
|
}
|
|
|
|
public bool Equals(IntrinsicKey other)
|
|
{
|
|
return ReferenceEquals(className, other.className) && ReferenceEquals(methodName, other.methodName) && ReferenceEquals(methodSignature, other.methodSignature);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return methodName.GetHashCode();
|
|
}
|
|
}
|
|
private static readonly Dictionary<IntrinsicKey, Emitter> intrinsics = Register();
|
|
#if STATIC_COMPILER
|
|
private static readonly Type typeofFloatConverter = StaticCompiler.GetRuntimeType("IKVM.Runtime.FloatConverter");
|
|
private static readonly Type typeofDoubleConverter = StaticCompiler.GetRuntimeType("IKVM.Runtime.DoubleConverter");
|
|
#else
|
|
private static readonly Type typeofFloatConverter = typeof(IKVM.Runtime.FloatConverter);
|
|
private static readonly Type typeofDoubleConverter = typeof(IKVM.Runtime.DoubleConverter);
|
|
#endif
|
|
|
|
private static Dictionary<IntrinsicKey, Emitter> Register()
|
|
{
|
|
Dictionary<IntrinsicKey, Emitter> intrinsics = new Dictionary<IntrinsicKey, Emitter>();
|
|
intrinsics.Add(new IntrinsicKey("java.lang.Object", "getClass", "()Ljava.lang.Class;"), Object_getClass);
|
|
intrinsics.Add(new IntrinsicKey("java.lang.Class", "desiredAssertionStatus", "()Z"), Class_desiredAssertionStatus);
|
|
intrinsics.Add(new IntrinsicKey("java.lang.Float", "floatToRawIntBits", "(F)I"), Float_floatToRawIntBits);
|
|
intrinsics.Add(new IntrinsicKey("java.lang.Float", "intBitsToFloat", "(I)F"), Float_intBitsToFloat);
|
|
intrinsics.Add(new IntrinsicKey("java.lang.Double", "doubleToRawLongBits", "(D)J"), Double_doubleToRawLongBits);
|
|
intrinsics.Add(new IntrinsicKey("java.lang.Double", "longBitsToDouble", "(J)D"), Double_longBitsToDouble);
|
|
intrinsics.Add(new IntrinsicKey("java.lang.System", "arraycopy", "(Ljava.lang.Object;ILjava.lang.Object;II)V"), System_arraycopy);
|
|
intrinsics.Add(new IntrinsicKey("java.util.concurrent.atomic.AtomicReferenceFieldUpdater", "newUpdater", "(Ljava.lang.Class;Ljava.lang.Class;Ljava.lang.String;)Ljava.util.concurrent.atomic.AtomicReferenceFieldUpdater;"), AtomicReferenceFieldUpdater_newUpdater);
|
|
#if STATIC_COMPILER
|
|
// String_toCharArray relies on globals, which aren't usable in dynamic mode
|
|
intrinsics.Add(new IntrinsicKey("java.lang.String", "toCharArray", "()[C"), String_toCharArray);
|
|
#endif
|
|
intrinsics.Add(new IntrinsicKey("sun.reflect.Reflection", "getCallerClass", "(I)Ljava.lang.Class;"), Reflection_getCallerClass);
|
|
intrinsics.Add(new IntrinsicKey("java.lang.ClassLoader", "getCallerClassLoader", "()Ljava.lang.ClassLoader;"), ClassLoader_getCallerClassLoader);
|
|
intrinsics.Add(new IntrinsicKey("ikvm.internal.CallerID", "getCallerID", "()Likvm.internal.CallerID;"), CallerID_getCallerID);
|
|
intrinsics.Add(new IntrinsicKey("ikvm.runtime.Util", "getInstanceTypeFromClass", "(Ljava.lang.Class;)Lcli.System.Type;"), Util_getInstanceTypeFromClass);
|
|
#if STATIC_COMPILER
|
|
// this only applies to the core class library, so makes no sense in dynamic mode
|
|
intrinsics.Add(new IntrinsicKey("java.lang.Class", "getPrimitiveClass", "(Ljava.lang.String;)Ljava.lang.Class;"), Class_getPrimitiveClass);
|
|
#endif
|
|
return intrinsics;
|
|
}
|
|
|
|
internal static bool IsIntrinsic(MethodWrapper mw)
|
|
{
|
|
return intrinsics.ContainsKey(new IntrinsicKey(mw)) && mw.DeclaringType.GetClassLoader() == CoreClasses.java.lang.Object.Wrapper.GetClassLoader();
|
|
}
|
|
|
|
internal static bool Emit(DynamicTypeWrapper.FinishContext context, CodeEmitter ilgen, MethodWrapper method, MethodAnalyzer ma, int opcodeIndex, MethodWrapper caller, ClassFile classFile, ClassFile.Method.Instruction[] code)
|
|
{
|
|
// note that intrinsics can always refuse to emit code and the code generator will fall back to a normal method call
|
|
return intrinsics[new IntrinsicKey(method)](context, ilgen, method, ma, opcodeIndex, caller, classFile, code);
|
|
}
|
|
|
|
private static bool Object_getClass(DynamicTypeWrapper.FinishContext context, CodeEmitter ilgen, MethodWrapper method, MethodAnalyzer ma, int opcodeIndex, MethodWrapper caller, ClassFile classFile, ClassFile.Method.Instruction[] code)
|
|
{
|
|
// this is the null-check idiom that javac uses (both in its own source and in the code it generates)
|
|
if (code[opcodeIndex + 1].NormalizedOpCode == NormalizedByteCode.__pop)
|
|
{
|
|
ilgen.Emit(OpCodes.Dup);
|
|
ilgen.EmitNullCheck();
|
|
return true;
|
|
}
|
|
// this optimizes obj1.getClass() ==/!= obj2.getClass()
|
|
else if (opcodeIndex + 3 < code.Length
|
|
&& !code[opcodeIndex + 1].IsBranchTarget && code[opcodeIndex + 1].NormalizedOpCode == NormalizedByteCode.__aload
|
|
&& !code[opcodeIndex + 2].IsBranchTarget && code[opcodeIndex + 2].NormalizedOpCode == NormalizedByteCode.__invokevirtual
|
|
&& !code[opcodeIndex + 3].IsBranchTarget && (code[opcodeIndex + 3].NormalizedOpCode == NormalizedByteCode.__if_acmpeq || code[opcodeIndex + 3].NormalizedOpCode == NormalizedByteCode.__if_acmpne)
|
|
&& (IsSafeForGetClassOptimization(ma.GetStackTypeWrapper(opcodeIndex, 0)) || IsSafeForGetClassOptimization(ma.GetStackTypeWrapper(opcodeIndex + 2, 0))))
|
|
{
|
|
ClassFile.ConstantPoolItemMI cpi = classFile.GetMethodref(code[opcodeIndex + 2].Arg1);
|
|
if (cpi.Class == "java.lang.Object" && cpi.Name == "getClass" && cpi.Signature == "()Ljava.lang.Class;")
|
|
{
|
|
// we can't patch the current opcode, so we have to emit the first call to GetTypeHandle here
|
|
ilgen.Emit(OpCodes.Callvirt, Compiler.getTypeMethod);
|
|
code[opcodeIndex + 2].PatchOpCode(NormalizedByteCode.__intrinsic_gettype);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static bool Class_desiredAssertionStatus(DynamicTypeWrapper.FinishContext context, CodeEmitter ilgen, MethodWrapper method, MethodAnalyzer ma, int opcodeIndex, MethodWrapper caller, ClassFile classFile, ClassFile.Method.Instruction[] code)
|
|
{
|
|
TypeWrapper classLiteral = ilgen.PeekLazyClassLiteral();
|
|
if (classLiteral != null && classLiteral.GetClassLoader().RemoveAsserts)
|
|
{
|
|
ilgen.LazyEmitPop();
|
|
ilgen.LazyEmitLdc_I4(0);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static bool IsSafeForGetClassOptimization(TypeWrapper tw)
|
|
{
|
|
// because of ghost arrays, we don't optimize if both types are either java.lang.Object or an array
|
|
return tw != CoreClasses.java.lang.Object.Wrapper && !tw.IsArray;
|
|
}
|
|
|
|
private static bool Float_floatToRawIntBits(DynamicTypeWrapper.FinishContext context, CodeEmitter ilgen, MethodWrapper method, MethodAnalyzer ma, int opcodeIndex, MethodWrapper caller, ClassFile classFile, ClassFile.Method.Instruction[] code)
|
|
{
|
|
EmitConversion(ilgen, typeofFloatConverter, "ToInt");
|
|
return true;
|
|
}
|
|
|
|
private static bool Float_intBitsToFloat(DynamicTypeWrapper.FinishContext context, CodeEmitter ilgen, MethodWrapper method, MethodAnalyzer ma, int opcodeIndex, MethodWrapper caller, ClassFile classFile, ClassFile.Method.Instruction[] code)
|
|
{
|
|
EmitConversion(ilgen, typeofFloatConverter, "ToFloat");
|
|
return true;
|
|
}
|
|
|
|
private static bool Double_doubleToRawLongBits(DynamicTypeWrapper.FinishContext context, CodeEmitter ilgen, MethodWrapper method, MethodAnalyzer ma, int opcodeIndex, MethodWrapper caller, ClassFile classFile, ClassFile.Method.Instruction[] code)
|
|
{
|
|
EmitConversion(ilgen, typeofDoubleConverter, "ToLong");
|
|
return true;
|
|
}
|
|
|
|
private static bool Double_longBitsToDouble(DynamicTypeWrapper.FinishContext context, CodeEmitter ilgen, MethodWrapper method, MethodAnalyzer ma, int opcodeIndex, MethodWrapper caller, ClassFile classFile, ClassFile.Method.Instruction[] code)
|
|
{
|
|
EmitConversion(ilgen, typeofDoubleConverter, "ToDouble");
|
|
return true;
|
|
}
|
|
|
|
private static void EmitConversion(CodeEmitter ilgen, Type converterType, string method)
|
|
{
|
|
LocalBuilder converter = ilgen.UnsafeAllocTempLocal(converterType);
|
|
ilgen.Emit(OpCodes.Ldloca, converter);
|
|
ilgen.Emit(OpCodes.Call, converterType.GetMethod(method));
|
|
}
|
|
|
|
private static bool System_arraycopy(DynamicTypeWrapper.FinishContext context, CodeEmitter ilgen, MethodWrapper method, MethodAnalyzer ma, int opcodeIndex, MethodWrapper caller, ClassFile classFile, ClassFile.Method.Instruction[] code)
|
|
{
|
|
// 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 on GNU Classpath StringBuffer directly calls
|
|
// this method to avoid prematurely initialising System.
|
|
TypeWrapper dst_type = ma.GetStackTypeWrapper(opcodeIndex, 2);
|
|
TypeWrapper src_type = ma.GetStackTypeWrapper(opcodeIndex, 4);
|
|
if (!dst_type.IsUnloadable && dst_type.IsArray && dst_type == src_type)
|
|
{
|
|
switch (dst_type.Name[1])
|
|
{
|
|
case 'J':
|
|
case 'D':
|
|
ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.arraycopy_primitive_8);
|
|
break;
|
|
case 'I':
|
|
case 'F':
|
|
ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.arraycopy_primitive_4);
|
|
break;
|
|
case 'S':
|
|
case 'C':
|
|
ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.arraycopy_primitive_2);
|
|
break;
|
|
case 'B':
|
|
case 'Z':
|
|
ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.arraycopy_primitive_1);
|
|
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)
|
|
{
|
|
ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.arraycopy_fast);
|
|
}
|
|
else
|
|
{
|
|
ilgen.Emit(OpCodes.Call, ByteCodeHelperMethods.arraycopy);
|
|
}
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static bool AtomicReferenceFieldUpdater_newUpdater(DynamicTypeWrapper.FinishContext context, CodeEmitter ilgen, MethodWrapper method, MethodAnalyzer ma, int opcodeIndex, MethodWrapper caller, ClassFile classFile, ClassFile.Method.Instruction[] code)
|
|
{
|
|
return AtomicReferenceFieldUpdaterEmitter.Emit(context, caller.DeclaringType, ilgen, classFile, opcodeIndex, code);
|
|
}
|
|
|
|
private static bool String_toCharArray(DynamicTypeWrapper.FinishContext context, CodeEmitter ilgen, MethodWrapper method, MethodAnalyzer ma, int opcodeIndex, MethodWrapper caller, ClassFile classFile, ClassFile.Method.Instruction[] code)
|
|
{
|
|
string str = ilgen.PopLazyLdstr();
|
|
if (str != null)
|
|
{
|
|
// arbitrary length for "big" strings
|
|
if (str.Length > 128)
|
|
{
|
|
EmitLoadCharArrayLiteral(ilgen, str, caller.DeclaringType);
|
|
return true;
|
|
}
|
|
ilgen.Emit(OpCodes.Ldstr, str);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static void EmitLoadCharArrayLiteral(CodeEmitter 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, Types.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, Types.String.GetMethod("ToCharArray", Type.EmptyTypes));
|
|
return;
|
|
}
|
|
ilgen.Emit(OpCodes.Ldc_I4, str.Length);
|
|
ilgen.Emit(OpCodes.Newarr, Types.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);
|
|
// MONOBUG Type.Equals(Type) is broken on Mono. We have to use the virtual method that ends up in our implementation
|
|
if (!fb.FieldType.Equals((object)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, JVM.Import(typeof(System.Runtime.CompilerServices.RuntimeHelpers)).GetMethod("InitializeArray", new Type[] { Types.Array, JVM.Import(typeof(RuntimeFieldHandle)) }));
|
|
}
|
|
|
|
private static bool Reflection_getCallerClass(DynamicTypeWrapper.FinishContext context, CodeEmitter ilgen, MethodWrapper method, MethodAnalyzer ma, int opcodeIndex, MethodWrapper caller, ClassFile classFile, ClassFile.Method.Instruction[] code)
|
|
{
|
|
if (caller.HasCallerID
|
|
&& opcodeIndex > 0
|
|
&& !code[opcodeIndex - 1].IsBranchTarget
|
|
&& code[opcodeIndex - 1].NormalizedOpCode == NormalizedByteCode.__iconst
|
|
&& code[opcodeIndex - 1].Arg1 == 2)
|
|
{
|
|
ilgen.LazyEmitPop();
|
|
int arg = caller.GetParametersForDefineMethod().Length - 1;
|
|
if (!caller.IsStatic)
|
|
{
|
|
arg++;
|
|
}
|
|
ilgen.Emit(OpCodes.Ldarg, (short)arg);
|
|
MethodWrapper mw = CoreClasses.ikvm.@internal.CallerID.Wrapper.GetMethodWrapper("getCallerClass", "()Ljava.lang.Class;", false);
|
|
mw.Link();
|
|
mw.EmitCallvirt(ilgen);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static bool ClassLoader_getCallerClassLoader(DynamicTypeWrapper.FinishContext context, CodeEmitter ilgen, MethodWrapper method, MethodAnalyzer ma, int opcodeIndex, MethodWrapper caller, ClassFile classFile, ClassFile.Method.Instruction[] code)
|
|
{
|
|
if (caller.HasCallerID)
|
|
{
|
|
int arg = caller.GetParametersForDefineMethod().Length - 1;
|
|
if (!caller.IsStatic)
|
|
{
|
|
arg++;
|
|
}
|
|
ilgen.Emit(OpCodes.Ldarg, (short)arg);
|
|
MethodWrapper mw = CoreClasses.ikvm.@internal.CallerID.Wrapper.GetMethodWrapper("getCallerClassLoader", "()Ljava.lang.ClassLoader;", false);
|
|
mw.Link();
|
|
mw.EmitCallvirt(ilgen);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static bool CallerID_getCallerID(DynamicTypeWrapper.FinishContext context, CodeEmitter ilgen, MethodWrapper method, MethodAnalyzer ma, int opcodeIndex, MethodWrapper caller, ClassFile classFile, ClassFile.Method.Instruction[] code)
|
|
{
|
|
if (caller.HasCallerID)
|
|
{
|
|
int arg = caller.GetParametersForDefineMethod().Length - 1;
|
|
if (!caller.IsStatic)
|
|
{
|
|
arg++;
|
|
}
|
|
ilgen.Emit(OpCodes.Ldarg, (short)arg);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
JVM.CriticalFailure("CallerID.getCallerID() requires a HasCallerID annotation", null);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static bool Util_getInstanceTypeFromClass(DynamicTypeWrapper.FinishContext context, CodeEmitter ilgen, MethodWrapper method, MethodAnalyzer ma, int opcodeIndex, MethodWrapper caller, ClassFile classFile, ClassFile.Method.Instruction[] code)
|
|
{
|
|
TypeWrapper tw = ilgen.PeekLazyClassLiteral();
|
|
if (tw != null)
|
|
{
|
|
ilgen.LazyEmitPop();
|
|
if (tw.IsRemapped && tw.IsFinal)
|
|
{
|
|
ilgen.Emit(OpCodes.Ldtoken, tw.TypeAsTBD);
|
|
}
|
|
else
|
|
{
|
|
ilgen.Emit(OpCodes.Ldtoken, tw.TypeAsBaseType);
|
|
}
|
|
ilgen.Emit(OpCodes.Call, Compiler.getTypeFromHandleMethod);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#if STATIC_COMPILER
|
|
private static bool Class_getPrimitiveClass(DynamicTypeWrapper.FinishContext context, CodeEmitter ilgen, MethodWrapper method, MethodAnalyzer ma, int opcodeIndex, MethodWrapper caller, ClassFile classFile, ClassFile.Method.Instruction[] code)
|
|
{
|
|
ilgen.LazyEmitPop();
|
|
ilgen.Emit(OpCodes.Ldnull);
|
|
MethodWrapper mw = CoreClasses.java.lang.Class.Wrapper.GetMethodWrapper("<init>", "(Lcli.System.Type;)V", false);
|
|
mw.Link();
|
|
mw.EmitNewobj(ilgen);
|
|
return true;
|
|
}
|
|
#endif
|
|
}
|
|
}
|