Version 0.15
This commit is contained in:
parent
8d9552a154
commit
9c443eefe3
@ -4,6 +4,11 @@ namespace system
|
||||
|
||||
public static class Math
|
||||
{
|
||||
|
||||
public const double E = 2.7182818284590451;
|
||||
public const double PI = 3.1415926535897931;
|
||||
public const double Tau = 6.2831853071795862;
|
||||
|
||||
public static int Sign(sbyte a) => java.lang.Integer.signum(a);
|
||||
public static sbyte Abs(sbyte a) => (sbyte) ((a < 0) ? -a : a);
|
||||
public static sbyte Min(sbyte a, sbyte b) => (a <= b) ? a : b;
|
||||
@ -159,6 +164,11 @@ namespace system
|
||||
|
||||
public static class MathF
|
||||
{
|
||||
|
||||
public const float E = 2.71828175f;
|
||||
public const float PI = 3.14159274f;
|
||||
public const float Tau = 6.28318548f;
|
||||
|
||||
public static int Sign(float a) => (int) java.lang.Math.signum(a);
|
||||
public static float Abs(float a) => (a < 0) ? -a : a;
|
||||
public static float Min(float a, float b) => (a <= b) ? a : b;
|
||||
@ -178,7 +188,7 @@ namespace system
|
||||
public static float Acosh(float a) => (float)
|
||||
java.lang.Math.log(a + java.lang.Math.sqrt((double) a * a - 1.0));
|
||||
public static float Atan(float a) => (float) java.lang.Math.atan(a);
|
||||
public static float Atan2(float x, double y) => (float) java.lang.Math.atan2(x, y);
|
||||
public static float Atan2(float x, float y) => (float) java.lang.Math.atan2(x, y);
|
||||
public static float Atanh(float a) =>
|
||||
(float) java.lang.Math.log((1 + a) / (1 - a)) * 0.5f;
|
||||
|
||||
|
95
Baselib/src/System/Random.cs
Normal file
95
Baselib/src/System/Random.cs
Normal file
@ -0,0 +1,95 @@
|
||||
|
||||
namespace system
|
||||
{
|
||||
|
||||
public class Random
|
||||
{
|
||||
[java.attr.RetainType] private java.util.Random _random;
|
||||
|
||||
// if this is an instance of Random itself, rather than a
|
||||
// derived class, then don't bother with calls to Sample()
|
||||
[java.attr.RetainType] private bool isSystemRandom;
|
||||
|
||||
public Random()
|
||||
{
|
||||
isSystemRandom = (GetType() == typeof(System.Random));
|
||||
_random = new java.util.Random();
|
||||
}
|
||||
|
||||
public Random(int Seed)
|
||||
{
|
||||
isSystemRandom = (GetType() == typeof(System.Random));
|
||||
_random = new java.util.Random(Seed);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// NextBytes(byte[]) does NOT use Sample()
|
||||
public virtual void NextBytes(byte[] buffer)
|
||||
{
|
||||
ThrowHelper.ThrowIfNull(buffer);
|
||||
int n = buffer.Length;
|
||||
for (int i = 0; i < n; i++)
|
||||
buffer[i] = (byte) (_random.nextInt() % 256);
|
||||
}
|
||||
|
||||
// Next() does NOT use Sample()
|
||||
public virtual int Next() => _random.nextInt();
|
||||
|
||||
|
||||
|
||||
// NextBytes(Span<byte>) uses Sample()
|
||||
public virtual void NextBytes(Span<byte> buffer)
|
||||
{
|
||||
throw new System.PlatformNotSupportedException();
|
||||
/*int n = buffer.Length;
|
||||
for (int i = 0; i < n; i++)
|
||||
buffer[i] = (byte) (Sample() * 256);*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Next(int) uses Sample()
|
||||
public virtual int Next(int maxValue) => Next(0, maxValue);
|
||||
|
||||
// Next(int,int) uses Sample()
|
||||
public virtual int Next(int minValue, int maxValue)
|
||||
{
|
||||
if (maxValue < minValue)
|
||||
ThrowHelper.ThrowArgumentOutOfRangeException();
|
||||
var range = (long) maxValue - minValue;
|
||||
if (range <= int.MaxValue && isSystemRandom)
|
||||
return _random.nextInt((int) range) + minValue;
|
||||
return minValue + (int) (Sample() * range);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// NextInt64 uses Sample()
|
||||
public virtual long NextInt64() => NextInt64(0, long.MaxValue);
|
||||
|
||||
// NextInt64(long) uses Sample()
|
||||
public virtual long NextInt64(long maxValue) => NextInt64(0, maxValue);
|
||||
|
||||
// NextInt64(long, long) uses Sample()
|
||||
public virtual long NextInt64(long minValue, long maxValue)
|
||||
{
|
||||
if (maxValue < minValue)
|
||||
ThrowHelper.ThrowArgumentOutOfRangeException();
|
||||
return isSystemRandom ? (_random.nextLong() % maxValue)
|
||||
: (long) (Sample() * long.MaxValue);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// NextSingle uses Sample()
|
||||
public virtual float NextSingle() => (float) Sample();
|
||||
|
||||
// NextDouble uses Sample()
|
||||
public virtual double NextDouble() => Sample();
|
||||
|
||||
|
||||
|
||||
protected virtual double Sample() => _random.nextDouble();
|
||||
}
|
||||
}
|
@ -14,8 +14,8 @@ namespace system
|
||||
//
|
||||
|
||||
[Serializable]
|
||||
public partial class RuntimeType : system.reflection.TypeInfo,
|
||||
ISerializable, ICloneable
|
||||
public sealed partial class RuntimeType : system.reflection.TypeInfo,
|
||||
ISerializable, ICloneable
|
||||
{
|
||||
|
||||
private sealed class GenericData
|
||||
@ -353,7 +353,8 @@ namespace system
|
||||
|
||||
|
||||
// invoked by code generated by GenericUtil::LoadGeneric
|
||||
public Type Argument(int index) => Generic.ArgumentTypes[index];
|
||||
public static Type Argument(RuntimeType runtimeType, int index)
|
||||
=> runtimeType.Generic.ArgumentTypes[index];
|
||||
|
||||
|
||||
|
||||
@ -985,12 +986,48 @@ namespace system
|
||||
public static Type GetType(java.lang.Class cls)
|
||||
=> GetType(cls, (Type[]) null);
|
||||
|
||||
// GetType() for a generic type with one type argument
|
||||
|
||||
public static Type GetType(java.lang.Class cls, Type arg)
|
||||
=> GetType(cls, new Type[] { arg });
|
||||
|
||||
public static Type GetType(java.lang.Class cls, java.lang.Class arg)
|
||||
=> GetType(cls, new Type[] { GetType(arg) });
|
||||
|
||||
// GetType() for a generic type with two type arguments
|
||||
|
||||
public static Type GetType(java.lang.Class cls, Type arg0, Type arg1)
|
||||
=> GetType(cls, new Type[] { arg0, arg1 });
|
||||
|
||||
public static Type GetType(java.lang.Class cls, java.lang.Class arg0, Type arg1)
|
||||
=> GetType(cls, new Type[] { GetType(arg0), arg1 });
|
||||
|
||||
public static Type GetType(java.lang.Class cls, Type arg0, java.lang.Class arg1)
|
||||
=> GetType(cls, new Type[] { arg0, GetType(arg1) });
|
||||
|
||||
public static Type GetType(java.lang.Class cls, java.lang.Class arg0, java.lang.Class arg1)
|
||||
=> GetType(cls, new Type[] { GetType(arg0), GetType(arg1) });
|
||||
|
||||
// GetType() for a generic type with three to eight type arguments
|
||||
|
||||
public static Type GetType(java.lang.Class cls, Type arg0, Type arg1, Type arg2)
|
||||
=> GetType(cls, new Type[] { arg0, arg1, arg2 });
|
||||
|
||||
public static Type GetType(java.lang.Class cls, Type arg0, Type arg1, Type arg2, Type arg3)
|
||||
=> GetType(cls, new Type[] { arg0, arg1, arg2, arg3 });
|
||||
|
||||
public static Type GetType(java.lang.Class cls, Type arg0, Type arg1, Type arg2, Type arg3, Type arg4)
|
||||
=> GetType(cls, new Type[] { arg0, arg1, arg2, arg3, arg4 });
|
||||
|
||||
public static Type GetType(java.lang.Class cls, Type arg0, Type arg1, Type arg2, Type arg3, Type arg4, Type arg5)
|
||||
=> GetType(cls, new Type[] { arg0, arg1, arg2, arg3, arg4, arg5 });
|
||||
|
||||
public static Type GetType(java.lang.Class cls, Type arg0, Type arg1, Type arg2, Type arg3, Type arg4, Type arg5, Type arg6)
|
||||
=> GetType(cls, new Type[] { arg0, arg1, arg2, arg3, arg4, arg5, arg6 });
|
||||
|
||||
public static Type GetType(java.lang.Class cls, Type arg0, Type arg1, Type arg2, Type arg3, Type arg4, Type arg5, Type arg6, Type arg7)
|
||||
=> GetType(cls, new Type[] { arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7 });
|
||||
|
||||
|
||||
|
||||
public override Type MakeGenericType(params Type[] typeArguments)
|
||||
|
@ -135,7 +135,7 @@ namespace SpaceFlint.CilToJava
|
||||
var modifier = (fromType as IModifierType).ModifierType.FullName;
|
||||
if (modifier == "System.Runtime.CompilerServices.IsVolatile")
|
||||
Flags |= VOLATILE;
|
||||
else
|
||||
else if (modifier != "System.Runtime.CompilerServices.IsExternalInit")
|
||||
throw CilMain.Where.Exception($"unknown modifier '{modifier}'");
|
||||
}
|
||||
|
||||
|
@ -99,6 +99,8 @@ namespace SpaceFlint.CilToJava
|
||||
|
||||
(code.MaxLocals, code.MaxStack) = locals.GetMaxLocalsAndStack();
|
||||
locals.TrackUnconditionalBranch(null);
|
||||
|
||||
OptimizeGeneratedCode();
|
||||
}
|
||||
|
||||
|
||||
|
@ -999,7 +999,8 @@ namespace SpaceFlint.CilToJava
|
||||
//
|
||||
// in either case, we want to discard the 'call' instructions
|
||||
// (see also ConvertCallToNop() below). for case (1), we translate
|
||||
// into a call to system.Util.GetType() to obtain a System.Type.
|
||||
// into a call to system.RuntimeType.GetType() to obtain a System.Type,
|
||||
// this is done via a call to LoadMaybeGeneric.
|
||||
// for case (2), we convert to a simple 'ldc' instruction.
|
||||
//
|
||||
// (3) this instruction is used to load a reference to an array
|
||||
|
@ -764,6 +764,86 @@ namespace SpaceFlint.CilToJava
|
||||
theClass.Methods.Add(outerMethod);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void OptimizeGeneratedCode ()
|
||||
{
|
||||
var Instructions = code.Instructions;
|
||||
var StackMap = code.StackMap;
|
||||
int n = Instructions.Count;
|
||||
|
||||
//
|
||||
// nop optimization:
|
||||
//
|
||||
// remove any nop instructions that are not a branch target,
|
||||
// and only if there are no exception tables. note that
|
||||
// removing nops that are a branch target, or nops in a method
|
||||
// with exception tables, would require updating the stack map,
|
||||
// branch instructions and exception tables.
|
||||
//
|
||||
|
||||
var nops = new List<int>();
|
||||
|
||||
bool discardNops =
|
||||
(code.Exceptions == null || code.Exceptions.Count == 0);
|
||||
|
||||
// constants optimization:
|
||||
//
|
||||
// find occurrences of iconst_0 or iconst_1 followed by i2l
|
||||
// (which must not be a branch target), and convert to lconst_xx
|
||||
//
|
||||
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
byte op = Instructions[i].Opcode;
|
||||
|
||||
if (op == 0x00 /* nop */)
|
||||
{
|
||||
// collect this nop only if it is not a branch target
|
||||
if (! StackMap.HasBranchFrame(Instructions[i].Label))
|
||||
{
|
||||
nops.Add(i);
|
||||
}
|
||||
}
|
||||
|
||||
else if (JavaCode.IsBranchOpcode(op)) // jump inst
|
||||
{
|
||||
if (Instructions[i].Data is int)
|
||||
{
|
||||
// if jump instruction has an explicit byte offset,
|
||||
// we can't do nop elimination; see FillJumpTargets
|
||||
// in JavaBinary.JavaCodeWriter
|
||||
discardNops = false;
|
||||
}
|
||||
}
|
||||
|
||||
else if (op == 0x85 /* i2l */ && i > 0)
|
||||
{
|
||||
var prevInst = Instructions[i - 1];
|
||||
if ( prevInst.Opcode == 0x12 /* ldc */
|
||||
&& prevInst.Data is int intValue
|
||||
&& (intValue == 0 || intValue == 1)
|
||||
&& (! StackMap.HasBranchFrame(
|
||||
Instructions[i].Label)))
|
||||
{
|
||||
// convert ldc of 0 or 1 into lconst_0 or lconst_1
|
||||
prevInst.Opcode = (byte) (intValue + 0x09);
|
||||
|
||||
// convert current instruction to nop,
|
||||
// and mark to discard it if discarding nops
|
||||
Instructions[i].Opcode = 0x00; // nop
|
||||
nops.Add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (discardNops)
|
||||
{
|
||||
for (int i = nops.Count; i-- > 0; )
|
||||
Instructions.RemoveAt(nops[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -508,9 +508,10 @@ namespace SpaceFlint.CilToJava
|
||||
code.NewInstruction(0x12 /* ldc */, null, -loadIndex - 1);
|
||||
code.StackMap.PushStack(JavaType.IntegerType);
|
||||
|
||||
// call system.RuntimeType.Argument(int typeArgumentIndex)
|
||||
code.NewInstruction(0xB6 /* invokevirtual */, CilType.SystemRuntimeTypeType,
|
||||
new JavaMethodRef("Argument", CilType.SystemTypeType, JavaType.IntegerType));
|
||||
// call system.RuntimeType.Argument(RuntimeType runtimeType, int typeArgumentIndex)
|
||||
code.NewInstruction(0xB8 /* invokestatic */, CilType.SystemRuntimeTypeType,
|
||||
new JavaMethodRef("Argument", CilType.SystemTypeType,
|
||||
CilType.SystemRuntimeTypeType, JavaType.IntegerType));
|
||||
|
||||
code.StackMap.PopStack(CilMain.Where); // integer
|
||||
code.StackMap.PopStack(CilMain.Where); // generic type field
|
||||
@ -534,58 +535,86 @@ namespace SpaceFlint.CilToJava
|
||||
|
||||
|
||||
|
||||
static void LoadGenericInstance(CilType loadType, List<JavaFieldRef> parameters, JavaCode code)
|
||||
static void LoadGenericInstance_1_2(CilType loadType, int genericCount,
|
||||
List<JavaFieldRef> parameters,
|
||||
JavaCode code)
|
||||
{
|
||||
int count = loadType.GenericParameters.Count;
|
||||
if (count == 1)
|
||||
{
|
||||
// specific handling for the common case of a generic instance
|
||||
// with just one type argument. if this is a concrete argument,
|
||||
// call GetType(class, class). if this is a generic argument,
|
||||
// call GetType(class, type). note that the first class argument
|
||||
// to GetType was alredy inserted by our caller, LoadMaybeGeneric
|
||||
// handling for the case of a generic instance with one or two
|
||||
// type arguments. each argument, if it is a concrete argument,
|
||||
// call GetType(class, class). if it is a generic argument,
|
||||
// call GetType(class, type).
|
||||
// note that the first class argument to GetType was alredy
|
||||
// inserted by our caller, LoadMaybeGeneric().
|
||||
|
||||
for (int i = 0; i < genericCount; i++)
|
||||
{
|
||||
var genericMark = CilMain.GenericStack.Mark();
|
||||
|
||||
var (argType, argIndex) = CilMain.GenericStack.Resolve(
|
||||
loadType.GenericParameters[0].JavaName);
|
||||
loadType.GenericParameters[i].JavaName);
|
||||
|
||||
if (argIndex == 0 && (! argType.HasGenericParameters))
|
||||
{
|
||||
// GetType(java.lang.Class, java.lang.Class)
|
||||
code.NewInstruction(0x12 /* ldc */, argType.AsWritableClass, null);
|
||||
code.StackMap.PushStack(CilType.ClassType);
|
||||
// second parameter of type java.lang.Class
|
||||
// second or third parameter of type java.lang.Class
|
||||
parameters.Add(parameters[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
// GetType(java.lang.Class, system.Type)
|
||||
LoadGeneric(argType, argIndex, code);
|
||||
// second parameter of type system.Type
|
||||
// second or third parameter of type system.Type
|
||||
parameters.Add(new JavaFieldRef("", CilType.SystemTypeType));
|
||||
}
|
||||
|
||||
CilMain.GenericStack.Release(genericMark);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void LoadGenericInstance_3_N(CilType loadType, int genericCount,
|
||||
List<JavaFieldRef> parameters,
|
||||
JavaCode code)
|
||||
{
|
||||
if (genericCount <= 8)
|
||||
{
|
||||
// handling for the less common case of a generic instace with
|
||||
// less than eight arguments. we don't check if the provided
|
||||
// type arguments are concrete or generic, and call a GetType()
|
||||
// override for the appropriate number of parameters.
|
||||
// note that the first class argument to GetType was alredy
|
||||
// inserted by our caller, LoadMaybeGeneric().
|
||||
|
||||
for (int i = 0; i < genericCount; i++)
|
||||
{
|
||||
LoadMaybeGeneric(loadType.GenericParameters[i], code);
|
||||
// next parameter always has type system.Type
|
||||
parameters.Add(new JavaFieldRef("", CilType.SystemTypeType));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// generic handling for the less common case of a generic instace
|
||||
// with more than one argument. we don't check if the provided
|
||||
// with more than eight arguments. we don't check if the provided
|
||||
// type arguments are concrete or generic. we build an array of
|
||||
// system.Type references, and call GetType(class, system.Type[]).
|
||||
// note that the first class argument to GetType was alredy
|
||||
// inserted by our caller, LoadMaybeGeneric().
|
||||
|
||||
var arrayOfType = CilType.SystemTypeType.AdjustRank(1);
|
||||
parameters.Add(new JavaFieldRef("", arrayOfType));
|
||||
|
||||
code.NewInstruction(0x12 /* ldc */, null, count);
|
||||
code.NewInstruction(0x12 /* ldc */, null, genericCount);
|
||||
code.StackMap.PushStack(JavaType.IntegerType);
|
||||
|
||||
code.NewInstruction(0xBD /* anewarray */, CilType.SystemTypeType, null);
|
||||
code.StackMap.PopStack(CilMain.Where);
|
||||
code.StackMap.PushStack(arrayOfType);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
for (int i = 0; i < genericCount; i++)
|
||||
{
|
||||
code.NewInstruction(0x59 /* dup */, null, null);
|
||||
code.StackMap.PushStack(arrayOfType);
|
||||
@ -631,7 +660,17 @@ namespace SpaceFlint.CilToJava
|
||||
|
||||
if (loadType.HasGenericParameters)
|
||||
{
|
||||
LoadGenericInstance(loadType, parameters, code);
|
||||
int genericCount = loadType.GenericParameters.Count;
|
||||
if (genericCount <= 2)
|
||||
{
|
||||
LoadGenericInstance_1_2(loadType, genericCount,
|
||||
parameters, code);
|
||||
}
|
||||
else
|
||||
{
|
||||
LoadGenericInstance_3_N(loadType, genericCount,
|
||||
parameters, code);
|
||||
}
|
||||
}
|
||||
|
||||
code.NewInstruction(0xB8 /* invokestatic */, CilType.SystemRuntimeTypeType,
|
||||
|
@ -10,7 +10,7 @@
|
||||
<!-- Android SDK path -->
|
||||
<AndroidHome Condition="'$(ANDROID_HOME)' != ''">$(ANDROID_HOME)</AndroidHome>
|
||||
<AndroidHome Condition="'$(ANDROID_SDK_ROOT)' != ''">$(ANDROID_SDK_ROOT)</AndroidHome>
|
||||
<AndroidJar>$(AndroidHome)/platforms/android-28/android.jar</AndroidJar>
|
||||
<AndroidJar>$(AndroidHome)/platforms/android-30/android.jar</AndroidJar>
|
||||
<AndroidBin>$(AndroidHome)/build-tools/30.0.2/</AndroidBin>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\Solution.project" />
|
||||
|
@ -11,7 +11,7 @@
|
||||
<!-- Android SDK path -->
|
||||
<AndroidHome Condition="'$(ANDROID_HOME)' != ''">$(ANDROID_HOME)</AndroidHome>
|
||||
<AndroidHome Condition="'$(ANDROID_SDK_ROOT)' != ''">$(ANDROID_SDK_ROOT)</AndroidHome>
|
||||
<AndroidJar>$(AndroidHome)/platforms/android-28/android.jar</AndroidJar>
|
||||
<AndroidJar>$(AndroidHome)/platforms/android-30/android.jar</AndroidJar>
|
||||
<AndroidBin>$(AndroidHome)/build-tools/30.0.2/</AndroidBin>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\..\Solution.project" />
|
||||
|
@ -83,6 +83,11 @@ namespace SpaceFlint.JavaBinary
|
||||
|
||||
|
||||
|
||||
public static bool IsBranchOpcode (byte op)
|
||||
=> (instOperandType[op] & 0x40) == 0x40;
|
||||
|
||||
|
||||
|
||||
static JavaCode()
|
||||
{
|
||||
instOperandType = new byte[256];
|
||||
|
@ -12,9 +12,6 @@ namespace SpaceFlint.JavaBinary
|
||||
{
|
||||
wtr.Where.Push("method body");
|
||||
|
||||
PerformOptimizations();
|
||||
EliminateNops();
|
||||
|
||||
int codeLength = FillInstructions(wtr);
|
||||
if (codeLength > 0xFFFE)
|
||||
throw wtr.Where.Exception("output method is too large");
|
||||
@ -486,7 +483,6 @@ namespace SpaceFlint.JavaBinary
|
||||
if (inst.Data is int intOffset)
|
||||
{
|
||||
// int data is a jump offset that can be calculated immediately
|
||||
// note that this prevents nop elimination; see EliminateNops
|
||||
intOffset -= offset;
|
||||
inst.Bytes[1] = (byte) (offset >> 8);
|
||||
inst.Bytes[2] = (byte) offset;
|
||||
@ -667,76 +663,6 @@ namespace SpaceFlint.JavaBinary
|
||||
return (index <= 3) ? 1 : ((index <= 255) ? 2 : 4);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PerformOptimizations()
|
||||
{
|
||||
int n = Instructions.Count;
|
||||
var prevInst = Instructions[0];
|
||||
for (int i = 1; i < n; i++)
|
||||
{
|
||||
var currInst = Instructions[i];
|
||||
|
||||
// find occurrences of iconst_0 or iconst_1 followed by i2l
|
||||
// (which must not be a branch target), and convert to lconst_xx
|
||||
|
||||
if (currInst.Opcode == 0x85 /* i2l */)
|
||||
{
|
||||
if ( prevInst.Opcode == 0x12 /* ldc */
|
||||
&& prevInst.Data is int intValue
|
||||
&& (intValue == 0 || intValue == 1)
|
||||
&& (! StackMap.HasBranchFrame(currInst.Label)))
|
||||
{
|
||||
// convert ldc of 0 or 1 into lconst_0 or lconst_1
|
||||
prevInst.Opcode = (byte) (intValue + 0x09);
|
||||
currInst.Opcode = 0x00; // nop
|
||||
}
|
||||
}
|
||||
prevInst = currInst;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void EliminateNops()
|
||||
{
|
||||
// remove any nop instructions that are not a branch target,
|
||||
// and only if there are no exception tables. note that
|
||||
// removing nops that are a branch target, or nops in a method
|
||||
// with exception tables, would require updating the stack map,
|
||||
// branch instructions and exception tables.
|
||||
|
||||
if (Exceptions != null && Exceptions.Count != 0)
|
||||
return;
|
||||
var nops = new List<int>();
|
||||
|
||||
int n = Instructions.Count;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
byte op = Instructions[i].Opcode;
|
||||
if (op == 0x00 /* nop */)
|
||||
{
|
||||
// collect this nop only if it is not a branch target
|
||||
if (! StackMap.HasBranchFrame(Instructions[i].Label))
|
||||
{
|
||||
nops.Add(i);
|
||||
}
|
||||
}
|
||||
else if ((instOperandType[op] & 0x40) == 0x40) // jump inst
|
||||
{
|
||||
if (Instructions[i].Data is int)
|
||||
{
|
||||
// if jump instruction has an explicit byte offset,
|
||||
// we can't do nop elimination; see FillJumpTargets
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = nops.Count; i-- > 0; )
|
||||
Instructions.RemoveAt(nops[i]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ There are some additional demos:
|
||||
- Change to the `Demos` directory inside the solution directory.
|
||||
- Restore packages using [nuget](https://www.nuget.org/downloads): `nuget restore`
|
||||
- Build and run each demo: `msbuild -p:Configuration=Release -t:RunDemo`
|
||||
- Note that the Android demos require the `ANDROID_HOME` environment directory, and the project is hard-coded to use Android platform version 28, and build-tools 30.0.2
|
||||
- Note that the Android demos require the `ANDROID_HOME` environment directory, and the project is hard-coded to use Android platform version 30, and build-tools 30.0.2
|
||||
- Note also that the Android demos build an APK file, but do not install it.
|
||||
|
||||
See the [BNA](https://github.com/spaceflint7/bna) and [Unjum](https://github.com/spaceflint7/unjum) repositories for more demos for Android.
|
||||
|
2
USAGE.md
2
USAGE.md
@ -109,7 +109,7 @@ Here are some known differences, deficiencies and incompatibilities of the Blueb
|
||||
|
||||
- Reflection information for generic types depends on the existence of the `Signature` attribute in java class files.
|
||||
|
||||
- `BeforeFieldInit` is not honored; the static initializer for a class will be called at the discretion of the JVM. if it is a generic class, the static initializer is called when the generic type is first referenced.
|
||||
- `BeforeFieldInit` is not honored; the static initializer for a class will be called at the discretion of the JVM. If it is a generic class, the static initializer is called when the generic type is first referenced.
|
||||
|
||||
- The type system is weaker than .NET when it comes to generic types, and in some casts and assignments are permitted between generic objects that differ only in their type arguments.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user