258 lines
7.6 KiB
C#
Executable File
258 lines
7.6 KiB
C#
Executable File
using System.Runtime.InteropServices;
|
|
|
|
namespace JCIL.Java.Class;
|
|
|
|
public abstract class Attribute
|
|
{
|
|
public static Attribute Read(Class parent, Reader reader)
|
|
{
|
|
var attrName = (string)parent.GetConstant(reader.ReadUInt16())!;
|
|
var length = reader.ReadUInt32();
|
|
|
|
switch (attrName)
|
|
{
|
|
case "MethodParameters":
|
|
{
|
|
var count = reader.ReadUInt8();
|
|
var args = new (string, ParameterAccessFlags)[count];
|
|
foreach (ref var tuple in args.AsSpan())
|
|
{
|
|
tuple.Item1 = (string)parent.GetConstant(reader.ReadUInt16())!;
|
|
tuple.Item2 = (ParameterAccessFlags)reader.ReadUInt16();
|
|
}
|
|
|
|
return new MethodParameters(args);
|
|
}
|
|
|
|
case "Code": return Code.Read(parent, reader);
|
|
case "StackMapTable": return StackMapTable.Read(parent, reader);
|
|
case "LocalVariableTable": return LocalVariableTable.Read(parent, reader);
|
|
|
|
default:
|
|
{
|
|
var data = new byte[length];
|
|
reader.Stream.ReadExactly(data);
|
|
return new GenericAttribute(attrName, data);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public sealed class GenericAttribute(string name, ReadOnlyMemory<byte> data) : Attribute
|
|
{
|
|
public readonly ReadOnlyMemory<byte> Data = data;
|
|
public readonly string Name = name;
|
|
}
|
|
|
|
[Flags]
|
|
public enum ParameterAccessFlags : ushort
|
|
{
|
|
// Indicates that the formal parameter was declared final.
|
|
Final = 0x0010,
|
|
|
|
// Indicates that the formal parameter was not explicitly or implicitly declared in source code, according to the specification of the language in which the source code was written (JLS §13.1). (The formal parameter is an implementation artifact of the compiler which produced this class file.)
|
|
Synthetic = 0x1000,
|
|
|
|
// Indicates that the formal parameter was implicitly declared in source code, according to the specification of the language in which the source code was written (JLS §13.1). (The formal parameter is mandated by a language specification, so all compilers for the language must emit it.)
|
|
Mandated = 0x8000,
|
|
}
|
|
|
|
public sealed class MethodParameters(IReadOnlyList<(string, ParameterAccessFlags)> parameters) : Attribute
|
|
{
|
|
public readonly IReadOnlyList<(string, ParameterAccessFlags)> Parameters = parameters;
|
|
}
|
|
|
|
public sealed class Code : Attribute
|
|
{
|
|
public ushort MaxStack { get; private init; }
|
|
public ushort MaxLocals { get; private init; }
|
|
public ReadOnlyMemory<byte> Bytecode { get; private init; }
|
|
public IReadOnlyList<ExceptionTableEntry> ExceptionTable { get; private init; } = [];
|
|
public IReadOnlyList<Attribute> Attributes { get; private init; } = [];
|
|
|
|
internal new static Code Read(Class parent, Reader reader)
|
|
{
|
|
var maxStack = reader.ReadUInt16();
|
|
var maxLocals = reader.ReadUInt16();
|
|
var codeLength = reader.ReadUInt32();
|
|
|
|
var code = new byte[codeLength];
|
|
reader.Stream.ReadExactly(code);
|
|
|
|
var exceptions = new ExceptionTableEntry[reader.ReadUInt16()];
|
|
foreach (ref var entry in exceptions.AsSpan())
|
|
{
|
|
entry.Start = reader.ReadUInt16();
|
|
entry.End = reader.ReadUInt16();
|
|
entry.Handler = reader.ReadUInt16();
|
|
entry.CatchType = (Class)parent.GetConstant(reader.ReadUInt16())!;
|
|
}
|
|
|
|
var attributes = new Attribute[reader.ReadUInt16()];
|
|
foreach (ref var entry in attributes.AsSpan())
|
|
entry = Attribute.Read(parent, reader);
|
|
|
|
return new Code
|
|
{
|
|
MaxStack = maxStack,
|
|
MaxLocals = maxLocals,
|
|
Bytecode = code,
|
|
ExceptionTable = exceptions,
|
|
Attributes = attributes,
|
|
};
|
|
}
|
|
|
|
public OpCodeReader CreateOpCodeReader()
|
|
{
|
|
MemoryMarshal.TryGetArray(Bytecode, out var array);
|
|
return new OpCodeReader(new MemoryStream(array.Array!, array.Offset, array.Count));
|
|
}
|
|
|
|
public record struct ExceptionTableEntry(ushort Start, ushort End, ushort Handler, Class CatchType);
|
|
}
|
|
|
|
public sealed class LocalVariableTable : Attribute
|
|
{
|
|
public IReadOnlyList<VariableEntry> Variables { get; private init; } = [];
|
|
|
|
internal new static LocalVariableTable Read(Class parent, Reader reader)
|
|
{
|
|
var variables = new VariableEntry[reader.ReadUInt16()];
|
|
foreach (ref var variable in variables.AsSpan())
|
|
{
|
|
variable.Start = reader.ReadUInt16();
|
|
variable.Length = reader.ReadUInt16();
|
|
variable.Name = (string)parent.GetConstant(reader.ReadUInt16())!;
|
|
variable.Signature = (string)parent.GetConstant(reader.ReadUInt16())!;
|
|
variable.Index = reader.ReadUInt16();
|
|
}
|
|
|
|
return new LocalVariableTable { Variables = variables };
|
|
}
|
|
|
|
public record struct VariableEntry(ushort Start, ushort Length, string Name, string Signature, ushort Index);
|
|
}
|
|
|
|
public sealed class StackMapTable : Attribute
|
|
{
|
|
public IReadOnlyList<Frame> Frames { get; private init; } = [];
|
|
|
|
public enum VerificationTypeInfoTag : byte
|
|
{
|
|
Top = 0,
|
|
Integer = 1,
|
|
Float = 2,
|
|
Double = 3,
|
|
Long = 4,
|
|
Null = 5,
|
|
UninitializedThis = 6,
|
|
Object = 7,
|
|
Uninitialized = 8,
|
|
}
|
|
|
|
internal new static StackMapTable Read(Class parent, Reader reader)
|
|
{
|
|
var one = 0;
|
|
var offset = 0;
|
|
var frames = new Frame[reader.ReadUInt16()];
|
|
foreach (ref var frame in frames.AsSpan())
|
|
{
|
|
var delta = 0;
|
|
var frameType = reader.ReadUInt8();
|
|
switch (frameType)
|
|
{
|
|
case < 64:
|
|
delta = frameType + one;
|
|
offset += delta;
|
|
frame = new SameFrame(offset);
|
|
break;
|
|
|
|
case < 128:
|
|
delta = frameType - 64 + one;
|
|
offset += delta;
|
|
frame = new OneTempFrame(offset, VerificationTypeInfo.Read(reader));
|
|
break;
|
|
|
|
case 247:
|
|
delta = reader.ReadUInt16() + one;
|
|
offset += delta;
|
|
frame = new OneTempFrameExtended(offset, VerificationTypeInfo.Read(reader));
|
|
break;
|
|
|
|
case 248 or 249 or 250:
|
|
delta = reader.ReadUInt16() + one;
|
|
offset += delta;
|
|
frame = new ChopFrame(offset, (ushort)(251 - frameType));
|
|
break;
|
|
|
|
case 251:
|
|
delta = reader.ReadUInt16() + one;
|
|
offset += delta;
|
|
frame = new SameFrameExtended(offset);
|
|
break;
|
|
|
|
case 252 or 253 or 254:
|
|
delta = reader.ReadUInt16() + one;
|
|
offset += delta;
|
|
frame = new AppendFrame(offset, VerificationTypeInfo.Read(reader, frameType - 251));
|
|
break;
|
|
|
|
case 255:
|
|
delta = reader.ReadUInt16() + one;
|
|
offset += delta;
|
|
frame = new FullFrame(
|
|
offset,
|
|
VerificationTypeInfo.Read(reader, reader.ReadUInt16()),
|
|
VerificationTypeInfo.Read(reader, reader.ReadUInt16())
|
|
);
|
|
break;
|
|
|
|
default: throw new NotImplementedException($"Unsupported frame type {frameType}");
|
|
}
|
|
one = 1;
|
|
}
|
|
|
|
return new StackMapTable { Frames = frames };
|
|
}
|
|
|
|
public struct VerificationTypeInfo
|
|
{
|
|
public VerificationTypeInfoTag Tag;
|
|
public ushort Parameter;
|
|
|
|
public static VerificationTypeInfo Read(Reader reader)
|
|
{
|
|
var info = new VerificationTypeInfo { Tag = (VerificationTypeInfoTag)reader.ReadUInt8() };
|
|
if (info.Tag is VerificationTypeInfoTag.Uninitialized or VerificationTypeInfoTag.Object)
|
|
info.Parameter = reader.ReadUInt16();
|
|
return info;
|
|
}
|
|
|
|
public static VerificationTypeInfo[] Read(Reader reader, int count)
|
|
{
|
|
var infos = new VerificationTypeInfo[count];
|
|
foreach (ref var info in infos.AsSpan()) info = Read(reader);
|
|
return infos;
|
|
}
|
|
}
|
|
|
|
public abstract record Frame(int StartOffset);
|
|
|
|
public sealed record ChopFrame(int StartOffset, ushort Count) : Frame(StartOffset);
|
|
|
|
public sealed record SameFrame(int StartOffset) : Frame(StartOffset);
|
|
|
|
public sealed record SameFrameExtended(int StartOffset) : Frame(StartOffset);
|
|
|
|
public sealed record OneTempFrame(int StartOffset, VerificationTypeInfo Entry) : Frame(StartOffset);
|
|
|
|
public sealed record OneTempFrameExtended(int StartOffset, VerificationTypeInfo Entry) : Frame(StartOffset);
|
|
|
|
public sealed record AppendFrame(int StartOffset, VerificationTypeInfo[] Locals) : Frame(StartOffset);
|
|
|
|
public sealed record FullFrame(
|
|
int StartOffset,
|
|
VerificationTypeInfo[] Locals,
|
|
VerificationTypeInfo[] StackItems
|
|
) : Frame(StartOffset);
|
|
} |