Skip to content

Commit

Permalink
Added a lot of attributes. Added some tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
wasabii committed Jul 28, 2024
1 parent d150bdc commit e4698c9
Show file tree
Hide file tree
Showing 14 changed files with 1,262 additions and 59 deletions.
43 changes: 43 additions & 0 deletions src/IKVM.ByteCode.Tests/Writing/ClassBuilderTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using FluentAssertions;

using IKVM.ByteCode.Buffers;
using IKVM.ByteCode.Parsing;
using IKVM.ByteCode.Reading;
using IKVM.ByteCode.Writing;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace IKVM.ByteCode.Tests.Writing
{

[TestClass]
public class ClassBuilderTests
{

[TestMethod]
public void CanBuildSimpleClass()
{
var b = new ClassBuilder(new ClassFormatVersion(53, 0), AccessFlag.ACC_PUBLIC, "TestClass", "java/lang/Object");
var f = b.AddField(AccessFlag.ACC_PUBLIC, "_field", "Z", new AttributeBuilder(b));
var m = b.AddMethod(AccessFlag.ACC_PUBLIC, "method", "()Z", new AttributeBuilder(b));

var z = new BlobBuilder();
b.Serialize(z);
var a = z.ToArray();

var cls = ClassReader.Read(a);
cls.AccessFlags.Should().Be(AccessFlag.ACC_PUBLIC);
cls.This.Name.Value.Should().Be("TestClass");
cls.Super.Name.Value.Should().Be("java/lang/Object");
cls.Fields.Should().HaveCount(1);
cls.Fields[0].AccessFlags.Should().Be(AccessFlag.ACC_PUBLIC);
cls.Fields[0].Name.Value.Should().Be("_field");
cls.Fields[0].Descriptor.Value.Should().Be("Z");
cls.Methods.Should().HaveCount(1);
cls.Methods[0].Name.Value.Should().Be("method");
cls.Methods[0].Descriptor.Value.Should().Be("()Z");
}

}

}
228 changes: 228 additions & 0 deletions src/IKVM.ByteCode.Tests/Writing/ConstantBuilderTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
using System;
using System.Buffers;
using System.Collections.Generic;

using FluentAssertions;

using IKVM.ByteCode.Buffers;
using IKVM.ByteCode.Parsing;
using IKVM.ByteCode.Text;
using IKVM.ByteCode.Writing;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace IKVM.ByteCode.Tests.Writing
{

[TestClass]
public class ConstantBuilderTests
{

[TestMethod]
public void CanEncodeUtf8ConstantSE10()
{
var cp = new ConstantBuilder(new ClassFormatVersion(0, 1));
cp.AddUtf8Constant("TEST").Value.Should().Be(1);

// output to array
var _blob = new BlobBuilder();
cp.Serialize(_blob);
var blob = _blob.ToArray();

// read constant pool
var rd = new ClassFormatReader(blob);
rd.TryReadU2(out var constant_pool_count).Should().BeTrue();
constant_pool_count.Should().Be(2);
rd.TryReadU1(out var tag).Should().BeTrue();
tag.Should().Be((byte)ConstantTag.Utf8);
rd.TryReadU2(out var length).Should().BeTrue();
length.Should().Be(4);
rd.TryReadManyU1(4, out var text).Should().BeTrue();
text.Length.Should().Be(4);

// contents of string should decode to TEST
var _text = new byte[4];
text.CopyTo(_text);
MUTF8Encoding.GetMUTF8(1).GetString(_text).Should().Be("TEST");
}

[TestMethod]
public void CanEncodeUtf8Constant()
{
var cp = new ConstantBuilder(new ClassFormatVersion(0, 48));
cp.AddUtf8Constant("TEST").Value.Should().Be(1);

// output to array
var _blob = new BlobBuilder();
cp.Serialize(_blob);
var blob = _blob.ToArray();

// read constant pool
var rd = new ClassFormatReader(blob);
rd.TryReadU2(out var constant_pool_count).Should().BeTrue();
constant_pool_count.Should().Be(2);
rd.TryReadU1(out var tag).Should().BeTrue();
tag.Should().Be((byte)ConstantTag.Utf8);
rd.TryReadU2(out var length).Should().BeTrue();
length.Should().Be(4);
rd.TryReadManyU1(4, out var text).Should().BeTrue();
text.Length.Should().Be(4);

// contents of string should decode to TEST
var _text = new byte[4];
text.CopyTo(_text);
MUTF8Encoding.GetMUTF8(48).GetString(_text).Should().Be("TEST");
}

[TestMethod]
public void ShouldNotEncodeDuplicateUtf8Values()
{
var cp = new ConstantBuilder(new ClassFormatVersion(0, 48));
cp.GetOrAddUtf8Constant("TEST").Value.Should().Be(1);
cp.GetOrAddUtf8Constant("TEST").Value.Should().Be(1);

// output to array
var _blob = new BlobBuilder();
cp.Serialize(_blob);
var blob = _blob.ToArray();

// read constant pool
var rd = new ClassFormatReader(blob);
rd.TryReadU2(out var constant_pool_count).Should().BeTrue();
constant_pool_count.Should().Be(2);

// ignore the rest
}

[TestMethod]
public void CanEncodeTwoDistinctUtf8Values()
{
var cp = new ConstantBuilder(new ClassFormatVersion(0, 48));
cp.GetOrAddUtf8Constant("TEST1").Value.Should().Be(1);
cp.GetOrAddUtf8Constant("TEST2").Value.Should().Be(2);

// output to array
var _blob = new BlobBuilder();
cp.Serialize(_blob);
var blob = _blob.ToArray();

// read constant pool
var rd = new ClassFormatReader(blob);
rd.TryReadU2(out var constant_pool_count).Should().BeTrue();
constant_pool_count.Should().Be(3);

for (int i = 1; i < 2; i++)
{
rd.TryReadU1(out var tag).Should().BeTrue();
tag.Should().Be((byte)ConstantTag.Utf8);
rd.TryReadU2(out var length).Should().BeTrue();
length.Should().Be(5);
rd.TryReadManyU1(5, out var text).Should().BeTrue();
text.Length.Should().Be(5);

// contents of string should decode to TEST
var _text = new byte[5];
text.CopyTo(_text);
MUTF8Encoding.GetMUTF8(48).GetString(_text).Should().Be("TEST" + i);
}
}

[TestMethod]
public void CanEncodeIntegerConstant()
{
var cp = new ConstantBuilder(new ClassFormatVersion(0, 48));
cp.AddIntegerConstant(65536).Value.Should().Be(1);

// output to array
var _blob = new BlobBuilder();
cp.Serialize(_blob);
var blob = _blob.ToArray();

// read constant pool
var rd = new ClassFormatReader(blob);
rd.TryReadU2(out var constant_pool_count).Should().BeTrue();
constant_pool_count.Should().Be(2);
rd.TryReadU1(out var tag).Should().BeTrue();
tag.Should().Be((byte)ConstantTag.Integer);
rd.TryReadU4(out var value).Should().BeTrue();
value.Should().Be(65536);
}

[TestMethod]
public unsafe void CanEncodeFloatConstant()
{
var cp = new ConstantBuilder(new ClassFormatVersion(0, 48));
cp.AddFloatConstant(float.MaxValue - 1).Value.Should().Be(1);

// output to array
var _blob = new BlobBuilder();
cp.Serialize(_blob);
var blob = _blob.ToArray();

// read constant pool
var rd = new ClassFormatReader(blob);
rd.TryReadU2(out var constant_pool_count).Should().BeTrue();
constant_pool_count.Should().Be(2);
rd.TryReadU1(out var tag).Should().BeTrue();
tag.Should().Be((byte)ConstantTag.Float);
rd.TryReadU4(out var value).Should().BeTrue();
(*(float*)&value).Should().Be(float.MaxValue - 1);
}

[TestMethod]
public void CanEncodeLongConstant()
{
var v = (long)int.MaxValue + 256;
var h = (uint)(v >> 32);
var l = (uint)v;

var cp = new ConstantBuilder(new ClassFormatVersion(0, 48));
cp.AddLongConstant(v).Value.Should().Be(1);

// output to array
var _blob = new BlobBuilder();
cp.Serialize(_blob);
var blob = _blob.ToArray();

// read constant pool
var rd = new ClassFormatReader(blob);
rd.TryReadU2(out var constant_pool_count).Should().BeTrue();
constant_pool_count.Should().Be(3);
rd.TryReadU1(out var tag).Should().BeTrue();
tag.Should().Be((byte)ConstantTag.Long);
rd.TryReadU4(out var hv).Should().BeTrue();
hv.Should().Be(h);
rd.TryReadU4(out var lv).Should().BeTrue();
lv.Should().Be(l);
}

[TestMethod]
public unsafe void CanEncodeDoubleConstant()
{
var v = (double)float.MaxValue + 256;
var h = (uint)((*(long*)&v) >> 32);
var l = (uint)(*(long*)&v);

var cp = new ConstantBuilder(new ClassFormatVersion(0, 48));
cp.AddDoubleConstant(v).Value.Should().Be(1);

// output to array
var _blob = new BlobBuilder();
cp.Serialize(_blob);
var blob = _blob.ToArray();

// read constant pool
var rd = new ClassFormatReader(blob);
rd.TryReadU2(out var constant_pool_count).Should().BeTrue();
constant_pool_count.Should().Be(3);
rd.TryReadU1(out var tag).Should().BeTrue();
tag.Should().Be((byte)ConstantTag.Double);
rd.TryReadU4(out var hv).Should().BeTrue();
hv.Should().Be(h);
rd.TryReadU4(out var lv).Should().BeTrue();
lv.Should().Be(l);
}

}

}
21 changes: 17 additions & 4 deletions src/IKVM.ByteCode/Buffers/Blob.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,27 @@ namespace IKVM.ByteCode.Buffers
{

/// <summary>
/// Represents a range within a <see cref="BlobBuilder"/>.
/// Represents a single blob.
/// </summary>
public readonly record struct Blob(byte[] Buffer, int Start, int Length)
/// <param name="Buffer"></param>
/// <param name="Offset"></param>
/// <param name="Length"></param>
public readonly record struct Blob(byte[] Buffer, int Offset, int Length)
{

public bool IsDefault => Buffer == null;
/// <summary>
/// Returns <c>true</c> if this is a default instance.
/// </summary>
public readonly bool IsDefault => Buffer == null;

public ArraySegment<byte> GetBytes() => new ArraySegment<byte>(Buffer, Start, Length);
/// <summary>
/// Gets the underlying array segment.
/// </summary>
/// <returns></returns>
public readonly ArraySegment<byte> GetBytes() => new(Buffer, Offset, Length);

/// <inheritdoc />
public readonly override int GetHashCode() => BlobHash.GetFNVHashCode(GetBytes());

}

Expand Down
24 changes: 12 additions & 12 deletions src/IKVM.ByteCode/Buffers/BlobBuilder.Enumerators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace IKVM.ByteCode.Buffers
public partial class BlobBuilder
{

public struct Chunks : IEnumerable<BlobBuilder>, IEnumerator<BlobBuilder>, IEnumerator
public struct BlobBuilderEnumerable : IEnumerable<BlobBuilder>, IEnumerator<BlobBuilder>, IEnumerator
{

readonly BlobBuilder _head;
Expand All @@ -25,7 +25,7 @@ public struct Chunks : IEnumerable<BlobBuilder>, IEnumerator<BlobBuilder>, IEnum
/// Initializes a new instance.
/// </summary>
/// <param name="builder"></param>
internal Chunks(BlobBuilder builder)
internal BlobBuilderEnumerable(BlobBuilder builder)
{
Debug.Assert(builder.IsHead);

Expand Down Expand Up @@ -69,7 +69,7 @@ public void Reset()
readonly void IDisposable.Dispose() { }

/// <inheritdoc />
public Chunks GetEnumerator() => this;
public BlobBuilderEnumerable GetEnumerator() => this;

/// <inheritdoc />
IEnumerator<BlobBuilder> IEnumerable<BlobBuilder>.GetEnumerator() => GetEnumerator();
Expand All @@ -79,18 +79,18 @@ readonly void IDisposable.Dispose() { }

}

public struct Blobs : IEnumerable<Blob>, IEnumerator<Blob>, IEnumerator
public struct BlobEnumerable : IEnumerable<Blob>, IEnumerator<Blob>, IEnumerator
{

Chunks _chunks;
BlobBuilderEnumerable enumerable;

/// <summary>
/// Initializes a new instance.
/// </summary>
/// <param name="builder"></param>
internal Blobs(BlobBuilder builder)
internal BlobEnumerable(BlobBuilder builder)
{
_chunks = new Chunks(builder);
enumerable = new BlobBuilderEnumerable(builder);
}

/// <inheritdoc />
Expand All @@ -100,25 +100,25 @@ public Blob Current
{
get
{
var current = _chunks.Current;
var current = enumerable.Current;
if (current != null)
return new Blob(current._buffer, 0, current.Length);
else
return default(Blob);
return default;
}
}

/// <inheritdoc />
public bool MoveNext() => _chunks.MoveNext();
public bool MoveNext() => enumerable.MoveNext();

/// <inheritdoc />
public void Reset() => _chunks.Reset();
public void Reset() => enumerable.Reset();

/// <inheritdoc />
void IDisposable.Dispose() { }

/// <inheritdoc />
public Blobs GetEnumerator() => this;
public BlobEnumerable GetEnumerator() => this;

/// <inheritdoc />
IEnumerator<Blob> IEnumerable<Blob>.GetEnumerator() => GetEnumerator();
Expand Down
Loading

0 comments on commit e4698c9

Please sign in to comment.