From b61428d8c54d485e93f1743dc1dc3d86be1baace Mon Sep 17 00:00:00 2001 From: Jerome Haltom Date: Wed, 31 Jul 2024 21:19:13 -0500 Subject: [PATCH] Add some encoders for records straight through. Fix bootstrap method type. --- .../BootstrapMethodsAttributeMethodRecord.cs | 6 +- .../BootstrapMethodsAttributeMethodReader.cs | 4 +- src/IKVM.ByteCode/TypePathKind.cs | 8 +-- .../Writing/AnnotationEncoder.cs | 10 +++- .../Writing/ElementValueEncoder.cs | 55 +++++++++++++++++- ...der.cs => ElementValuePairTableEncoder.cs} | 11 ++++ .../Writing/ElementValueTableEncoder.cs | 56 +++++++++++++++++++ .../Writing/TypeAnnotationEncoder.cs | 43 ++++++++++++++ .../Writing/TypeAnnotationTableEncoder.cs | 15 +++++ src/IKVM.ByteCode/Writing/TypePathEncoder.cs | 38 ++++++++++++- 10 files changed, 230 insertions(+), 16 deletions(-) rename src/IKVM.ByteCode/Writing/{ElementValuePairTableBuilder.cs => ElementValuePairTableEncoder.cs} (78%) create mode 100644 src/IKVM.ByteCode/Writing/ElementValueTableEncoder.cs diff --git a/src/IKVM.ByteCode/Parsing/BootstrapMethodsAttributeMethodRecord.cs b/src/IKVM.ByteCode/Parsing/BootstrapMethodsAttributeMethodRecord.cs index fd3c0be..43d24e3 100644 --- a/src/IKVM.ByteCode/Parsing/BootstrapMethodsAttributeMethodRecord.cs +++ b/src/IKVM.ByteCode/Parsing/BootstrapMethodsAttributeMethodRecord.cs @@ -1,14 +1,14 @@ namespace IKVM.ByteCode.Parsing { - public readonly record struct BootstrapMethodsAttributeMethodRecord(MethodHandleConstantHandle MethodRef, ConstantHandle[] Arguments) + public readonly record struct BootstrapMethodsAttributeMethodRecord(MethodHandleConstantHandle Method, ConstantHandle[] Arguments) { public static bool TryReadBootstrapMethod(ref ClassFormatReader reader, out BootstrapMethodsAttributeMethodRecord method) { method = default; - if (reader.TryReadU2(out ushort methodrefIndex) == false) + if (reader.TryReadU2(out ushort methodIndex) == false) return false; if (reader.TryReadU2(out ushort argumentCount) == false) return false; @@ -22,7 +22,7 @@ public static bool TryReadBootstrapMethod(ref ClassFormatReader reader, out Boot arguments[i] = new(argumentIndex); } - method = new BootstrapMethodsAttributeMethodRecord(new(methodrefIndex), arguments); + method = new BootstrapMethodsAttributeMethodRecord(new(methodIndex), arguments); return true; } diff --git a/src/IKVM.ByteCode/Reading/BootstrapMethodsAttributeMethodReader.cs b/src/IKVM.ByteCode/Reading/BootstrapMethodsAttributeMethodReader.cs index 8a3e583..35a7bab 100644 --- a/src/IKVM.ByteCode/Reading/BootstrapMethodsAttributeMethodReader.cs +++ b/src/IKVM.ByteCode/Reading/BootstrapMethodsAttributeMethodReader.cs @@ -10,7 +10,7 @@ namespace IKVM.ByteCode.Reading public sealed class BootstrapMethodsAttributeMethodReader : ReaderBase { - MethodrefConstantReader methodref; + MethodHandleConstantReader methodref; IReadOnlyList arguments; /// @@ -27,7 +27,7 @@ internal BootstrapMethodsAttributeMethodReader(ClassReader declaringClass, Boots /// /// Gets the method being referenced. /// - public MethodrefConstantReader Methodref => LazyGet(ref methodref, () => DeclaringClass.Constants.Get(Record.Methodref)); + public MethodHandleConstantReader Method => LazyGet(ref methodref, () => DeclaringClass.Constants.Get(Record.MethodRef)); /// /// Gets the arguments bound to the method reference. diff --git a/src/IKVM.ByteCode/TypePathKind.cs b/src/IKVM.ByteCode/TypePathKind.cs index a402a3e..6529780 100644 --- a/src/IKVM.ByteCode/TypePathKind.cs +++ b/src/IKVM.ByteCode/TypePathKind.cs @@ -4,10 +4,10 @@ public enum TypePathKind : byte { - ArrayType = 0, - NestedType = 1, - ParameterizedWildcardTypeArgument = 2, - ParameterizedType = 3, + Array = 0, + InnerType = 1, + Wildcard = 2, + TypeArgument = 3, } diff --git a/src/IKVM.ByteCode/Writing/AnnotationEncoder.cs b/src/IKVM.ByteCode/Writing/AnnotationEncoder.cs index bc6450a..af5201c 100644 --- a/src/IKVM.ByteCode/Writing/AnnotationEncoder.cs +++ b/src/IKVM.ByteCode/Writing/AnnotationEncoder.cs @@ -21,6 +21,15 @@ public AnnotationEncoder(BlobBuilder builder) _builder = builder ?? throw new ArgumentNullException(nameof(builder)); } + /// + /// Encodes an existing annotation. + /// + /// + public void Encode(AnnotationRecord annotation) + { + Encode(annotation.Type, e => e.Encode(annotation.Elements)); + } + /// /// Encodes a new element_value_pair. /// @@ -32,7 +41,6 @@ public void Encode(Utf8ConstantHandle type, Action w.TryWriteU2(type.Index); elementValuePairs(new ElementValuePairTableEncoder(_builder)); } - } } diff --git a/src/IKVM.ByteCode/Writing/ElementValueEncoder.cs b/src/IKVM.ByteCode/Writing/ElementValueEncoder.cs index 1ecd612..96ca097 100644 --- a/src/IKVM.ByteCode/Writing/ElementValueEncoder.cs +++ b/src/IKVM.ByteCode/Writing/ElementValueEncoder.cs @@ -24,6 +24,56 @@ public ElementValueEncoder(BlobBuilder builder) _builder = builder ?? throw new ArgumentNullException(nameof(builder)); } + /// + /// Encodes an existing element value. + /// + /// + public void Encode(ElementValueRecord value) + { + switch (value.Tag) + { + case ElementValueTag.Byte: + Byte((IntegerConstantHandle)((ElementValueConstantValueRecord)value.Value).Handle); + break; + case ElementValueTag.Char: + Char((IntegerConstantHandle)((ElementValueConstantValueRecord)value.Value).Handle); + break; + case ElementValueTag.Integer: + Int((IntegerConstantHandle)((ElementValueConstantValueRecord)value.Value).Handle); + break; + case ElementValueTag.Short: + Short((IntegerConstantHandle)((ElementValueConstantValueRecord)value.Value).Handle); + break; + case ElementValueTag.Boolean: + Boolean((IntegerConstantHandle)((ElementValueConstantValueRecord)value.Value).Handle); + break; + case ElementValueTag.Double: + Double((DoubleConstantHandle)((ElementValueConstantValueRecord)value.Value).Handle); + break; + case ElementValueTag.Float: + Float((FloatConstantHandle)((ElementValueConstantValueRecord)value.Value).Handle); + break; + case ElementValueTag.Long: + Long((LongConstantHandle)((ElementValueConstantValueRecord)value.Value).Handle); + break; + case ElementValueTag.String: + String((Utf8ConstantHandle)((ElementValueConstantValueRecord)value.Value).Handle); + break; + case ElementValueTag.Enum: + Enum(((ElementValueEnumConstantValueRecord)value.Value).TypeName, ((ElementValueEnumConstantValueRecord)value.Value).ConstantName); + break; + case ElementValueTag.Class: + Class(((ElementValueClassValueRecord)value.Value).Class); + break; + case ElementValueTag.Annotation: + Annotation(e => e.Encode(((ElementValueAnnotationValueRecord)value.Value).Annotation)); + break; + case ElementValueTag.Array: + Array(e => e.Encode(((ElementValueArrayValueRecord)value.Value).Values)); + break; + } + } + /// /// Constant of the primitive type byte as the value of this element-value pair. /// @@ -210,17 +260,16 @@ public void Annotation(Action annotationValue) /// Denotes an array as the value of this element-value pair. /// /// - public void Array(Action arrayValue) + public void Array(Action arrayValue) { if (_count > 0) throw new InvalidOperationException("Only a single element value can be encoded by this encoder."); var w = new ClassFormatWriter(_builder.ReserveBytes(ClassFormatWriter.U1).GetBytes()); w.TryWriteU1((byte)ElementValueTag.Array); - arrayValue(new ElementValuePairTableEncoder(_builder)); + arrayValue(new ElementValueTableEncoder(_builder)); _count++; } - } } diff --git a/src/IKVM.ByteCode/Writing/ElementValuePairTableBuilder.cs b/src/IKVM.ByteCode/Writing/ElementValuePairTableEncoder.cs similarity index 78% rename from src/IKVM.ByteCode/Writing/ElementValuePairTableBuilder.cs rename to src/IKVM.ByteCode/Writing/ElementValuePairTableEncoder.cs index 8513b19..fda573c 100644 --- a/src/IKVM.ByteCode/Writing/ElementValuePairTableBuilder.cs +++ b/src/IKVM.ByteCode/Writing/ElementValuePairTableEncoder.cs @@ -27,6 +27,17 @@ public ElementValuePairTableEncoder(BlobBuilder builder) _count = 0; } + /// + /// Encodes an existing set of elements. + /// + /// + /// + public void Encode(ReadOnlySpan elements) + { + foreach (var i in elements) + Element(i.Name, e => e.Encode(i.Value)); + } + /// /// Adds an element value pair. /// diff --git a/src/IKVM.ByteCode/Writing/ElementValueTableEncoder.cs b/src/IKVM.ByteCode/Writing/ElementValueTableEncoder.cs new file mode 100644 index 0000000..2b9d359 --- /dev/null +++ b/src/IKVM.ByteCode/Writing/ElementValueTableEncoder.cs @@ -0,0 +1,56 @@ +using System; + +using IKVM.ByteCode.Buffers; +using IKVM.ByteCode.Parsing; + +namespace IKVM.ByteCode.Writing +{ + + /// + /// Encodes an 'element_value_table' structure. + /// + public struct ElementValueTableEncoder + { + + readonly BlobBuilder _builder; + readonly Blob _countBlob; + ushort _count; + + /// + /// Initializes a new instance. + /// + /// + public ElementValueTableEncoder(BlobBuilder builder) + { + _builder = builder ?? throw new ArgumentNullException(nameof(builder)); + _countBlob = _builder.ReserveBytes(ClassFormatWriter.U2); + _count = 0; + } + + /// + /// Encodes an existing set of elements. + /// + /// + /// + public void Encode(ReadOnlySpan elements) + { + foreach (var i in elements) + Element(e => e.Encode(i)); + } + + /// + /// Adds an element value pair. + /// + /// + public void Element(Action value) + { + if (value is null) + throw new ArgumentNullException(nameof(value)); + + value(new ElementValueEncoder(_builder)); + new ClassFormatWriter(_countBlob.GetBytes()).TryWriteU2(++_count); + } + + } + +} \ No newline at end of file diff --git a/src/IKVM.ByteCode/Writing/TypeAnnotationEncoder.cs b/src/IKVM.ByteCode/Writing/TypeAnnotationEncoder.cs index 50fb1be..66a9463 100644 --- a/src/IKVM.ByteCode/Writing/TypeAnnotationEncoder.cs +++ b/src/IKVM.ByteCode/Writing/TypeAnnotationEncoder.cs @@ -24,6 +24,49 @@ public TypeAnnotationEncoder(BlobBuilder builder) _builder = builder ?? throw new ArgumentNullException(nameof(builder)); } + /// + /// Encodes an existing record. + /// + /// + public void Encode(TypeAnnotationRecord record) + { + switch (record.Target) + { + case TypeParameterTargetRecord target: + TypeParameterTarget(record.TargetType, target.ParameterIndex, e => e.Encode(record.TargetPath), record.Type, e => e.Encode(record.Elements)); + break; + case SuperTypeTargetRecord target: + SuperTypeTarget(record.TargetType, target.SuperTypeIndex, e => e.Encode(record.TargetPath), record.Type, e => e.Encode(record.Elements)); + break; + case TypeParameterBoundTargetRecord target: + TypeParameterBoundTarget(record.TargetType, target.ParameterIndex, target.BoundIndex, e => e.Encode(record.TargetPath), record.Type, e => e.Encode(record.Elements)); + break; + case EmptyTargetRecord target: + EmptyTarget(record.TargetType, e => e.Encode(record.TargetPath), record.Type, e => e.Encode(record.Elements)); + break; + case FormalParameterTargetRecord target: + FormalParameterTarget(record.TargetType, target.ParameterIndex, e => e.Encode(record.TargetPath), record.Type, e => e.Encode(record.Elements)); + break; + case ThrowsTargetRecord target: + ThrowsTarget(record.TargetType, target.ThrowsTypeIndex, e => e.Encode(record.TargetPath), record.Type, e => e.Encode(record.Elements)); + break; + case LocalVariableTargetTableRecord target: + LocalVarTarget(record.TargetType, e => e.Encode(target), record.Type, e => e.Encode(record.Elements)); + break; + case CatchTargetRecord target: + CatchTarget(record.TargetType, target.ExceptionTableIndex, e => e.Encode(record.TargetPath), record.Type, e => e.Encode(record.Elements)); + break; + case OffsetTargetRecord target: + OffsetTarget(record.TargetType, target.Offset, e => e.Encode(record.TargetPath), record.Type, e => e.Encode(record.Elements)); + break; + case TypeArgumentTargetRecord target: + TypeArgumentTarget(record.TargetType, target.Offset, target.TypeArgumentIndex, e => e.Encode(record.TargetPath), record.Type, e => e.Encode(record.Elements)); + break; + default: + throw new InvalidOperationException("Invalid type annotation."); + } + } + /// /// Encodes the footer of the structure. /// diff --git a/src/IKVM.ByteCode/Writing/TypeAnnotationTableEncoder.cs b/src/IKVM.ByteCode/Writing/TypeAnnotationTableEncoder.cs index 45b64eb..2bc58c4 100644 --- a/src/IKVM.ByteCode/Writing/TypeAnnotationTableEncoder.cs +++ b/src/IKVM.ByteCode/Writing/TypeAnnotationTableEncoder.cs @@ -1,4 +1,5 @@ using System; +using System.Security.Policy; using IKVM.ByteCode.Buffers; using IKVM.ByteCode.Parsing; @@ -38,6 +39,20 @@ public TypeAnnotationTableEncoder Add(Action annotation) return this; } + /// + /// Adds an annotation. + /// + /// + /// + public TypeAnnotationTableEncoder Add(TypeAnnotationRecord record) + { + return Add(encoder => Add(encoder, record)); + } + + static void Add(TypeAnnotationEncoder encoder, TypeAnnotationRecord record) + { + } + } } diff --git a/src/IKVM.ByteCode/Writing/TypePathEncoder.cs b/src/IKVM.ByteCode/Writing/TypePathEncoder.cs index 6f246f1..175a7de 100644 --- a/src/IKVM.ByteCode/Writing/TypePathEncoder.cs +++ b/src/IKVM.ByteCode/Writing/TypePathEncoder.cs @@ -24,10 +24,43 @@ public TypePathEncoder(BlobBuilder builder) _count = 0; } + /// + /// Encodes an existing type path. + /// + /// + public void Encode(TypePathRecord targetPath) + { + foreach (var i in targetPath.Path) + Encode(i); + } + + /// + /// Encodes an existing type path item. + /// + /// + public void Encode(TypePathItemRecord item) + { + switch (item.Kind) + { + case TypePathKind.Array: + Array(); + break; + case TypePathKind.InnerType: + InnerType(); + break; + case TypePathKind.Wildcard: + Wildcard(); + break; + case TypePathKind.TypeArgument: + TypeArgument(item.ArgumentIndex); + break; + } + } + /// /// Annotation is deeper in an array type. /// - public void ArrayElement() + public void Array() { var w = new ClassFormatWriter(_builder.ReserveBytes(ClassFormatWriter.U1 + ClassFormatWriter.U1).GetBytes()); w.TryWriteU1(0); @@ -49,7 +82,7 @@ public void InnerType() /// /// Annotation is on the bound of a wildcard type argument of a parameterized type. /// - public void WildcardBound() + public void Wildcard() { var w = new ClassFormatWriter(_builder.ReserveBytes(ClassFormatWriter.U1 + ClassFormatWriter.U1).GetBytes()); w.TryWriteU1(2); @@ -67,7 +100,6 @@ public void TypeArgument(byte index) w.TryWriteU1(index); new ClassFormatWriter(_countBlob.GetBytes()).TryWriteU1(++_count); } - } }