Some changes for BNA

This commit is contained in:
spaceflint 2020-11-15 11:12:37 +02:00
parent 01a98c2f3f
commit d3426fc442
18 changed files with 487 additions and 16 deletions

View File

@ -142,14 +142,18 @@ System.Runtime.Serialization.StreamingContext
System.Runtime.Serialization.SerializationException
System.IO.BinaryReader
System.IO.BinaryWriter
System.IO.DirectoryNotFoundException
System.IO.EndOfStreamException
System.IO.FileAccess
System.IO.FileAttributes
System.IO.FileMode
System.IO.FileNotFoundException
System.IO.FileShare
System.IO.MemoryStream
System.IO.TextReader
System.IO.TextWriter
System.IO.SeekOrigin
System.IO.StringReader
System.IO.Stream
System.IO.StreamReader

View File

@ -0,0 +1,149 @@
namespace system
{
public static class BitConverter
{
[java.attr.RetainType] private static readonly bool _IsLittleEndian =
java.nio.ByteOrder.nativeOrder() == java.nio.ByteOrder.LITTLE_ENDIAN;
public static readonly bool IsLittleEndian = _IsLittleEndian;
public static int SingleToInt32Bits(float value) => java.lang.Float.floatToRawIntBits(value);
public static float Int32BitsToSingle(int value) => java.lang.Float.intBitsToFloat(value);
public static long DoubleToInt64Bits(double value) => java.lang.Double.doubleToRawLongBits(value);
public static double Int64BitsToDouble(long value) => java.lang.Double.longBitsToDouble(value);
public static byte[] GetBytes(bool value) => new byte[] { value ? (byte) 1 : (byte) 0 };
public static byte[] GetBytes(short value)
=> (byte[]) (object) java.util.Arrays.copyOf(
GetByteBuffer(2).putShort(0, value).array(), 2);
public static byte[] GetBytes(ushort value) => GetBytes((short) value);
public static byte[] GetBytes(char value) => GetBytes((short) value);
public static byte[] GetBytes(int value)
=> (byte[]) (object) java.util.Arrays.copyOf(
GetByteBuffer(4).putInt(0, value).array(), 4);
public static byte[] GetBytes(uint value) => GetBytes((int) value);
public static byte[] GetBytes(long value)
=> (byte[]) (object) java.util.Arrays.copyOf(
GetByteBuffer(8).putLong(0, value).array(), 8);
public static byte[] GetBytes(ulong value) => GetBytes((long) value);
public static byte[] GetBytes(float value)
=> (byte[]) (object) java.util.Arrays.copyOf(
GetByteBuffer(4).putFloat(0, value).array(), 4);
public static byte[] GetBytes(double value)
=> (byte[]) (object) java.util.Arrays.copyOf(
GetByteBuffer(8).putDouble(0, value).array(), 8);
public static short ToInt16(byte[] value, int startIndex)
{
ThrowHelper.ThrowIfNull(value);
if ((uint) startIndex >= value.Length || startIndex > value.Length - 2)
ThrowHelper.ThrowArgumentOutOfRangeException();
byte b0 = value[startIndex];
byte b1 = value[++startIndex];
if (! _IsLittleEndian)
(b0, b1) = (b1, b0);
return (short) (b0 | (b1 << 8));
}
public static ushort ToUInt16(byte[] value, int startIndex)
=> (ushort) ToInt16(value, startIndex);
public static char ToChar(byte[] value, int startIndex)
=> (char) ToInt16(value, startIndex);
public static int ToInt32(byte[] value, int startIndex)
{
ThrowHelper.ThrowIfNull(value);
if ((uint) startIndex >= value.Length || startIndex > value.Length - 4)
ThrowHelper.ThrowArgumentOutOfRangeException();
byte b0 = value[startIndex];
byte b1 = value[++startIndex];
byte b2 = value[++startIndex];
byte b3 = value[++startIndex];
if (! _IsLittleEndian)
(b0, b1, b2, b3) = (b3, b2, b1, b0);
return (b0 | (b1 << 8) | (b2 << 16) | (b3 << 24));
}
public static uint ToUInt32(byte[] value, int startIndex)
=> (uint) ToInt32(value, startIndex);
public static float ToSingle(byte[] value, int startIndex)
=> java.lang.Float.intBitsToFloat(ToInt32(value, startIndex));
public static long ToInt64(byte[] value, int startIndex)
{
ThrowHelper.ThrowIfNull(value);
if ((uint) startIndex >= value.Length || startIndex > value.Length - 8)
ThrowHelper.ThrowArgumentOutOfRangeException();
byte b0 = value[startIndex];
byte b1 = value[++startIndex];
byte b2 = value[++startIndex];
byte b3 = value[++startIndex];
byte b4 = value[++startIndex];
byte b5 = value[++startIndex];
byte b6 = value[++startIndex];
byte b7 = value[++startIndex];
if (! _IsLittleEndian)
(b0, b1, b2, b3, b4, b5, b6, b7) = (b7, b6, b5, b4, b3, b2, b1, b0);
int i1 = (b0 | (b1 << 8) | (b2 << 16) | (b3 << 24));
int i2 = (b4 | (b5 << 8) | (b6 << 16) | (b7 << 24));
return (uint) i1 | ((long) i2 << 32);
}
public static ulong ToUInt64(byte[] value, int startIndex)
=> (ulong) ToInt64(value, startIndex);
public static double ToDouble(byte[] value, int startIndex)
=> java.lang.Double.longBitsToDouble(ToInt64(value, startIndex));
public static bool ToBoolean(byte[] value, int startIndex)
{
ThrowHelper.ThrowIfNull(value);
if (startIndex < 0 || startIndex > value.Length - 1)
ThrowHelper.ThrowArgumentOutOfRangeException();
return (value[startIndex] != 0);
}
public static string ToString(byte[] value, int startIndex, int length)
{
ThrowHelper.ThrowIfNull(value);
int valueLength = value.Length;
if ( startIndex < 0 || length < 0
|| (startIndex >= valueLength && startIndex > 0)
|| (startIndex > valueLength - length))
{
ThrowHelper.ThrowArgumentOutOfRangeException();
}
if (length == 0)
return "";
var output = new char[valueLength * 2];
int outputIndex = 0;
while (valueLength --> 0)
{
byte v = value[startIndex++];
output[outputIndex++] = HexChars[v >> 4];
output[outputIndex++] = HexChars[v & 0x0F];
}
return new string(output);
}
private static java.nio.ByteBuffer GetByteBuffer(int len)
{
var buffer = (java.nio.ByteBuffer) TlsByteBuffer.get();
if (buffer == null || buffer.limit() < len)
{
buffer = java.nio.ByteBuffer.allocate(len)
.order(java.nio.ByteOrder.nativeOrder());
TlsByteBuffer.set(buffer);
}
return buffer;
}
[java.attr.RetainType] static java.lang.ThreadLocal TlsByteBuffer =
new java.lang.ThreadLocal();
[java.attr.RetainType] private static readonly char[] HexChars =
"0123456789ABCDEF".ToCharArray();
}
}

View File

@ -8,6 +8,13 @@ namespace system
public static void InternalBlockCopy(Array src, int srcOffsetBytes,
Array dst, int dstOffsetBytes, int byteCount)
{
if (src.SyncRoot is sbyte[] srcBytes && dst.SyncRoot is sbyte[] dstBytes)
{
java.lang.System.arraycopy(srcBytes, srcOffsetBytes,
dstBytes, dstOffsetBytes, byteCount);
return;
}
if (src.SyncRoot is char[] srcChars && dst.SyncRoot is char[] dstChars)
{
int srcIndex = srcOffsetBytes >> 1;
@ -21,6 +28,7 @@ namespace system
return;
}
}
throw new System.PlatformNotSupportedException(
"InternalBlockCopy/" + src.GetType() + "/" + dst.GetType());
}

View File

@ -82,7 +82,7 @@ namespace system
// ToString
//
public override string ToString() => JavaCalendar.ToString();
public override string ToString() => ToString(null, null);
public string ToString(IFormatProvider provider) => ToString(null, provider);
@ -207,6 +207,23 @@ namespace system
public static bool operator >= (DateTime d1, DateTime d2)
=> d1.Ticks >= d2.Ticks;
//
// SpecifyKind, ToUniversalTime, ToLocalTime
//
public static DateTime SpecifyKind(DateTime value, DateTimeKind kind)
{
var javaCalendar = java.util.Calendar.getInstance();
if (kind == DateTimeKind.Utc)
javaCalendar.setTimeZone(TimeZoneUTC);
javaCalendar.setTimeInMillis(value.JavaCalendar.getTimeInMillis());
return new DateTime(javaCalendar, kind);
}
public DateTime ToUniversalTime() => SpecifyKind(this, DateTimeKind.Utc);
public DateTime ToLocalTime() => SpecifyKind(this, DateTimeKind.Local);
//
// ISerializable
//

View File

@ -157,6 +157,29 @@ namespace system
//
// CodeNumber.Indirection methods
//
public int Get_U8() => throw new System.NotSupportedException();
public int Get_I8() => throw new System.NotSupportedException();
public void Set_I8(int v) => throw new System.NotSupportedException();
public int Get_U16() => throw new System.NotSupportedException();
public int Get_I16() => throw new System.NotSupportedException();
public void Set_I16(int v) => throw new System.NotSupportedException();
public int Get_I32() => throw new System.NotSupportedException();
public void Set_I32(int v) => throw new System.NotSupportedException();
public long Get_I64() => java.lang.Double.doubleToRawLongBits(v);
public void Set_I64(long v) => Set(java.lang.Double.longBitsToDouble(v));
public double Get_F64() => throw new System.NotSupportedException();
public void Set_F64(double v) => throw new System.NotSupportedException();
//
// IConvertible
//

View File

@ -0,0 +1,32 @@
namespace system.io
{
public static class Directory
{
public static bool Exists(string path)
{
bool exists = false;
if (path != null && path.Length != 0)
{
var file = new java.io.File(path);
exists = file.exists() && file.isDirectory();
}
return exists;
}
public static DirectoryInfo CreateDirectory(string path)
{
ThrowHelper.ThrowIfNull(path);
path = path.Trim();
if (path.Length == 0)
throw new System.ArgumentException();
var file = new java.io.File(path);
file.mkdirs();
return new DirectoryInfo(file);
}
}
}

View File

@ -0,0 +1,12 @@
namespace system.io
{
public sealed class DirectoryInfo : FileSystemInfo
{
public DirectoryInfo(java.io.File javaFile) : base(javaFile) { }
}
}

View File

@ -0,0 +1,22 @@
using FileMode = System.IO.FileMode;
using FileAccess = System.IO.FileAccess;
using FileShare = System.IO.FileShare;
namespace system.io
{
public static class File
{
public static FileStream Open(string path, FileMode mode)
=> new FileStream(path, mode);
public static FileStream Open(string path, FileMode mode, FileAccess access)
=> new FileStream(path, mode, access);
public static FileStream Open(string path, FileMode mode, FileAccess access, FileShare share)
=> new FileStream(path, mode, access);
}
}

View File

@ -27,6 +27,9 @@ namespace system.io
: this(path, mode, (mode == System.IO.FileMode.Append
? System.IO.FileAccess.Write : System.IO.FileAccess.ReadWrite)) { }
public FileStream(string path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share)
: this(path, mode, access) { }
public FileStream(string path, System.IO.FileMode mode, System.IO.FileAccess access)
{
ThrowHelper.ThrowIfNull(path);
@ -131,8 +134,7 @@ namespace system.io
public override bool CanWrite => (Flags & CAN_WRITE) != 0;
public override bool CanSeek => (Flags & CAN_SEEK) != 0;
public override long Length
=> throw new System.PlatformNotSupportedException();
public override long Length => JavaChannel.size();
public override long Position
{
@ -184,10 +186,23 @@ namespace system.io
}
public override void SetLength(long value)
=> throw new System.PlatformNotSupportedException();
{
if (value < 0)
throw new System.ArgumentOutOfRangeException();
JavaChannel.truncate(value);
}
public override long Seek(long offset, System.IO.SeekOrigin origin)
=> throw new System.PlatformNotSupportedException();
{
if (origin == System.IO.SeekOrigin.Current)
offset += JavaChannel.position();
else if (origin == System.IO.SeekOrigin.End)
offset = JavaChannel.size() - offset;
else if (origin != System.IO.SeekOrigin.Begin)
throw new System.ArgumentException();
JavaChannel.position(offset);
return JavaChannel.position();
}
//
// static constructor
@ -199,6 +214,17 @@ namespace system.io
(java.lang.Class) typeof(java.io.FileNotFoundException),
(exc) => new System.IO.FileNotFoundException(exc.getMessage())
);
system.Util.DefineException(
(java.lang.Class) typeof(java.nio.channels.NonWritableChannelException),
(exc) => new System.NotSupportedException(exc.getMessage())
);
system.Util.DefineException(
(java.lang.Class) typeof(java.nio.channels.ClosedChannelException),
(exc) => new System.ObjectDisposedException(exc.getMessage())
);
}
}

View File

@ -0,0 +1,101 @@
using FileAttributes = System.IO.FileAttributes;
namespace system.io
{
public abstract class FileSystemInfo
{
[java.attr.RetainType] protected java.io.File JavaFile;
protected FileSystemInfo(java.io.File javaFile)
{
JavaFile = javaFile;
}
public virtual string FullName => JavaFile.getAbsolutePath();
public virtual string Name => JavaFile.getName();
public string Extension
{
get
{
var name = Name;
int dot = Name.LastIndexOf('.');
if (dot != -1)
{
int slash = Name.LastIndexOf('/');
if (dot > slash)
{
return ((java.lang.String) (object) name).substring(dot);
}
}
return "";
}
}
public FileAttributes Attributes
{
get
{
FileAttributes attr = (FileAttributes) 0;
if (JavaFile.isDirectory())
attr |= FileAttributes.Directory;
if (JavaFile.isHidden())
attr |= FileAttributes.Hidden;
if (! JavaFile.canWrite())
attr |= FileAttributes.ReadOnly;
if (attr == (FileAttributes) 0)
attr = FileAttributes.Normal;
return attr;
}
set => throw new System.PlatformNotSupportedException();
}
public virtual void Delete() => JavaFile.delete();
public virtual bool Exists => JavaFile.exists();
public DateTime CreationTimeUtc
{
get => throw new System.PlatformNotSupportedException();
set => throw new System.PlatformNotSupportedException();
}
public DateTime LastAccessTimeUtc
{
get => throw new System.PlatformNotSupportedException();
set => throw new System.PlatformNotSupportedException();
}
public DateTime LastWriteTimeUtc
{
// 10,000 DateTime ticks in a millisecond
get => new DateTime(JavaFile.lastModified() * 10000);
set => JavaFile.setLastModified(value.Ticks / 10000);
}
public DateTime CreationTime
{
get => CreationTimeUtc.ToLocalTime();
set => CreationTimeUtc = value.ToUniversalTime();
}
public DateTime LastAccessTime
{
get => LastAccessTimeUtc.ToLocalTime();
set => LastAccessTimeUtc = value.ToUniversalTime();
}
public DateTime LastWriteTime
{
get => LastWriteTimeUtc.ToLocalTime();
set => LastWriteTimeUtc = value.ToUniversalTime();
}
public void Refresh() { }
}
}

View File

@ -13,7 +13,7 @@ namespace system.io
public static bool IsPathRooted(string path) => false;
public static string Combine (string path1, string path2)
public static string Combine(string path1, string path2)
{
path1 = CheckPath(path1);
path2 = CheckPath(path2);
@ -26,6 +26,12 @@ namespace system.io
return (ch != '/') ? (path1 + "/" + path2) : (path1 + path2);
}
public static string Combine(string path1, string path2, string path3)
=> Combine(Combine(path1, path2), path3);
}
public static class PathInternal
{
}
}

View File

@ -122,6 +122,29 @@ namespace system
//
// CodeNumber.Indirection methods
//
public int Get_U8() => throw new System.NotSupportedException();
public int Get_I8() => throw new System.NotSupportedException();
public void Set_I8(int v) => throw new System.NotSupportedException();
public int Get_U16() => throw new System.NotSupportedException();
public int Get_I16() => throw new System.NotSupportedException();
public void Set_I16(int v) => throw new System.NotSupportedException();
public int Get_I32() => java.lang.Float.floatToRawIntBits(v);
public void Set_I32(int v) => Set(java.lang.Float.intBitsToFloat(v));
public long Get_I64() => throw new System.NotSupportedException();
public void Set_I64(long v) => throw new System.NotSupportedException();
public double Get_F64() => throw new System.NotSupportedException();
public void Set_F64(double v) => throw new System.NotSupportedException();
//
// IConvertible
//

View File

@ -255,6 +255,13 @@ namespace system.text
return bytes;
}
public virtual int GetBytes(string s, int charIndex, int charCount,
byte[] bytes, int byteIndex)
{
ThrowHelper.ThrowIfNull(s);
return GetBytes(s.ToCharArray(), charIndex, charCount, bytes, byteIndex);
}
public virtual string GetString(byte[] bytes)
{
ThrowHelper.ThrowIfNull(bytes);

View File

@ -561,10 +561,10 @@ namespace SpaceFlint.CilToJava
// if we detect such a conflict at a branch target, we assume this
// is the cause, and set the stack elements to a common denominator
// or to the lowest common denominator, java.lang.Object
if ( IsReference && other.IsReference && other is CilType other2
&& (! this.IsValueClass) && (! other2.IsValueClass))
if (IsReference && other.IsReference && other is CilType other2)
{
return FindCommonSuperType(this, other2) ?? CilType.From(JavaType.ObjectType);
return FindCommonSuperType(this, other2)
?? CilType.From(JavaType.ObjectType);
}
return null;

View File

@ -330,7 +330,7 @@ namespace SpaceFlint.CilToJava
public void Store(Code op)
public void Store(Code op, Mono.Cecil.Cil.Instruction inst)
{
TypeCode elemType;
@ -338,7 +338,7 @@ namespace SpaceFlint.CilToJava
{
case Code.Stelem_Ref: case Code.Stelem_Any:
Store(null);
Store(null, null);
return;
case Code.Stelem_I1: elemType = TypeCode.Byte; break;
@ -351,12 +351,12 @@ namespace SpaceFlint.CilToJava
default: throw new InvalidProgramException();
}
Store(CilType.From(new JavaType(elemType, 0, null)));
Store(CilType.From(new JavaType(elemType, 0, null)), inst);
}
void Store(CilType elemType)
void Store(CilType elemType, Mono.Cecil.Cil.Instruction inst)
{
stackMap.PopStack(CilMain.Where); // value
stackMap.PopStack(CilMain.Where); // index
@ -406,6 +406,8 @@ namespace SpaceFlint.CilToJava
elemType = arrayType.AdjustRank(-arrayType.ArrayRank);
}
CheckImmediate(arrayType.PrimitiveType, inst);
if (arrayType.IsValueClass || elemType.IsValueClass)
{
CilMethod.ValueMethod(CilMethod.ValueClone, code);
@ -413,6 +415,32 @@ namespace SpaceFlint.CilToJava
code.NewInstruction(elemType.StoreArrayOpcode, null, null);
}
void CheckImmediate(TypeCode arrayPrimitiveType, Mono.Cecil.Cil.Instruction inst)
{
// Android AOT aborts the compilation with a crash if storing
// an immediate value that does not fit in the target array.
if ( inst != null && inst.Previous != null
&& inst.Previous.OpCode.Code == Code.Ldc_I4)
{
var imm = (int) inst.Previous.Operand;
if ( arrayType.PrimitiveType == TypeCode.Boolean
|| arrayType.PrimitiveType == TypeCode.SByte
|| arrayType.PrimitiveType == TypeCode.Byte)
{
if (imm < -128 || imm >= 128)
code.NewInstruction(0x91 /* i2b */, null, null);
}
else if ( arrayType.PrimitiveType == TypeCode.Char
|| arrayType.PrimitiveType == TypeCode.Int16
|| arrayType.PrimitiveType == TypeCode.UInt16)
{
if (imm < -32768 || imm >= 32768)
code.NewInstruction(0x93 /* i2s */, null, null);
}
}
}
}
@ -505,7 +533,7 @@ namespace SpaceFlint.CilToJava
stackMap.PushStack(valueType);
}
Store(elemType);
Store(elemType, null);
}
else if (method.Name == "Address")

View File

@ -418,7 +418,7 @@ namespace SpaceFlint.CilToJava
case Code.Stelem_I: case Code.Stelem_R4:case Code.Stelem_R8:case Code.Stelem_Any:
case Code.Stelem_Ref:
arrays.Store(cilOp);
arrays.Store(cilOp, cilInst);
break;
case Code.Ldftn: case Code.Ldvirtftn:

View File

@ -121,7 +121,18 @@ namespace SpaceFlint.CilToJava
else if (dstType.IsReference)
{
CodeArrays.CheckCast(dstType, true, code);
if (dstType.IsGenericParameter)
if ( srcType.ArrayRank != 0
&& srcType.ArrayRank == dstType.ArrayRank
&& srcType.PrimitiveType != 0
&& dstType.PrimitiveType != 0
&& srcType.AdjustRank(-srcType.ArrayRank).NewArrayType
== dstType.AdjustRank(-dstType.ArrayRank).NewArrayType)
{
// casting to same java array type, e.g. byte[] to sbyte[]
op = 0x00; // nop
}
else if (dstType.IsGenericParameter)
op = 0x00; // nop
else
op = 0xC0; // checkcast

View File

@ -58,6 +58,8 @@ There are some additional demos:
- 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 also that the Android demos build an APK file, but do not install it.
See the [BNA](https://github.com/spaceflint7/bna) repository for another demo for Android.
## Usage
For more information about using Bluebonnet, please see the [USAGE.md](USAGE.md) file. That document also records any known differences and deficiencies, compared to a proper .NET implementation.