ClassFileReader: Implemented field reader

This commit is contained in:
Akash Yadav
2022-09-03 18:28:48 +05:30
parent a978941ce4
commit 94b01441d3
28 changed files with 1302 additions and 37 deletions
@@ -53,4 +53,35 @@ public class ClassFileConstants {
/** Is a module, not a class or interface. */
public static final int ACC_MODULE = 0x8000;
public static final String ATTR_CONSTANT_VALUE = "ConstantValue";
public static final String ATTR_CODE = "Code";
public static final String ATTR_EXCEPTIONS = "Exceptions";
public static final String ATTR_SOURCE_FILE = "SourceFile";
public static final String ATTR_LINE_NUMBER_TABLE = "LineNumberTable";
public static final String ATTR_LOCAL_VARIABLE_TABLE = "LocalVariableTable";
public static final String ATTR_INNER_CLASSES = "InnerClasses";
public static final String ATTR_SYNTHETIC = "Synthetic";
public static final String ATTR_DEPRECATED = "Deprecated";
public static final String ATTR_ENCLOSING_METHOD = "EnclosingMethod";
public static final String ATTR_SIGNATURE = "Signature";
public static final String ATTR_SOURCE_DEBUG_EXTENSION = "SourceDebugExtension";
public static final String ATTR_LOCAL_VARIABLE_TYPE_TABLE = "LocalVariableTypeTable";
public static final String ATTR_RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations";
public static final String ATTR_RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations";
public static final String ATTR_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = "RuntimeVisibleParameterAnnotations";
public static final String ATTR_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = "RuntimeInvisibleParameterAnnotations";
public static final String ATTR_ANNOTATION_DEFAULT = "AnnotationDefault";
public static final String ATTR_STACK_MAP_TABLE = "StackMapTable";
public static final String ATTR_BOOTSTRAP_METHODS = "BootstrapMethods";
public static final String ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS = "RuntimeVisibleTypeAnnotations";
public static final String ATTR_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS = "RuntimeInvisibleTypeAnnotations";
public static final String ATTR_METHOD_PARAMETERS = "MethodParameters";
public static final String ATTR_MODULE = "Module";
public static final String ATTR_MODULE_PACKAGES = "ModulePackages";
public static final String ATTR_MAIN_CLASS = "ModuleMainClass";
public static final String ATTR_NEST_HOST = "NestHost";
public static final String ATTR_NEST_MEMBERS = "NestMembers";
public static final String ATTR_RECORD = "Record";
public static final String ATTR_PERMITTED_SUBCLASSES = "PermittedSubclasses";
}
@@ -22,6 +22,7 @@ import com.itsaky.androidide.classfile.constants.IConstant.Type.DOUBLE
import com.itsaky.androidide.classfile.constants.IConstant.Type.LONG
import com.itsaky.androidide.classfile.internal.ClassFile
import com.itsaky.androidide.classfile.internal.NullConstant
import com.itsaky.androidide.classfile.internal.NullField
import java.io.DataInputStream
import java.io.InputStream
@@ -31,7 +32,7 @@ import java.io.InputStream
*
* @author Akash Yadav
*/
class ClassFileReader(val stream: InputStream) {
class ClassFileReader(private val stream: InputStream) {
/**
* Read the class file.
@@ -48,13 +49,15 @@ class ClassFileReader(val stream: InputStream) {
}
val file = ClassFile()
file.minorVersion = input.readUnsignedShort()
file.majorVersion = input.readUnsignedShort()
if (file.majorVersion >= 56 && file.minorVersion != 0 && file.minorVersion != 65535) {
val minorVersion = input.readUnsignedShort()
val majorVersion = input.readUnsignedShort()
if (majorVersion >= 56 && minorVersion != 0 && minorVersion != 65535) {
// When major version is >= 56, minor version must be 0 or 65535
throw IllegalStateException("Invalid class file minor version: '${file.minorVersion}'")
throw IllegalStateException("Invalid class file minor version: '${minorVersion}'")
}
file.version = ClassFileVersion.get(majorVersion, minorVersion)
val poolCount = input.readUnsignedShort()
file.constantPool = Array(poolCount) { NullConstant }
@@ -63,27 +66,36 @@ class ClassFileReader(val stream: InputStream) {
while (index < poolCount) {
val constant = readPoolConstant(input)
file.constantPool[index] = constant
if (constant.type == LONG || constant.type == DOUBLE) {
++index
}
++index
}
file.accessFlags = input.readUnsignedShort()
file.thisClass = input.readUnsignedShort()
file.superClass = input.readUnsignedShort()
val interfaceCount = input.readUnsignedShort()
file.interfaces = IntArray(interfaceCount)
index = 0
while (index < interfaceCount) {
file.interfaces[index] = input.readUnsignedShort()
++index
}
val fieldCount = input.readUnsignedShort()
file.fields = Array(fieldCount) { NullField }
index = 0
while (index < fieldCount) {
file.fields[index] = IFieldInfo.read(input, file)
++index
}
return file
}
@@ -0,0 +1,51 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.classfile
/**
* Version info for the class file.
*
* @author Akash Yadav
*/
enum class ClassFileVersion(val major: Int, val minor: Int) {
JAVA_1_0(45, 0),
JAVA_1_1(45, 3),
JAVA_1_2(46, 0),
JAVA_1_3(47, 0),
JAVA_1_4(48, 0),
JAVA_5(49, 0),
JAVA_6(50, 0),
JAVA_7(51, 0),
JAVA_8(52, 0),
JAVA_9(53, 0),
JAVA_10(54, 0),
JAVA_11(55, 0),
JAVA_12(56, 0),
JAVA_13(57, 0),
JAVA_14(58, 0),
JAVA_15(59, 0),
JAVA_16(60, 0),
JAVA_17(61, 0);
companion object {
internal fun get(major: Int, minor: Int): ClassFileVersion {
return values().firstOrNull { it.major == major && it.minor == minor }
?: throw IllegalArgumentException("Unknown class file version '${major}.${minor}'")
}
}
}
@@ -25,11 +25,9 @@ import com.itsaky.androidide.classfile.constants.IConstant
* @author Akash Yadav
*/
interface IClassFile {
/** Major version of the class file. */
val majorVersion: Int
/** Minor version of the class file. */
val minorVersion: Int
/** The class file version. */
val version: ClassFileVersion
/** The constant pool. */
val constantPool: Array<IConstant>
@@ -52,6 +50,9 @@ interface IClassFile {
/** The interfaces that this class implements. */
val interfaces: IntArray
/** The fields of this class. */
val fields: Array<IFieldInfo>
/** Get the string representation of this class' name. */
fun getName(): String
@@ -0,0 +1,66 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.classfile
import com.itsaky.androidide.classfile.attributes.IAttribute
import com.itsaky.androidide.classfile.internal.ClassFile
import com.itsaky.androidide.classfile.internal.FieldInfo
import com.itsaky.androidide.classfile.internal.NullAttribute
import java.io.DataInputStream
/**
* Information about a field.
*
* @author Akash Yadav
*/
interface IFieldInfo {
/** Access flags for this field. */
val accessFlags: Int
/** The index in the constant pool which corresponds to the name of this field. */
val nameIndex: Int
/**
* The index in the constant pool which corresponds to the field descriptor string for this field.
*/
val descriptorIndex: Int
/** The attributes of this field. */
val attributes: Array<IAttribute>
companion object {
internal fun read(input: DataInputStream, file: ClassFile): IFieldInfo {
val field = FieldInfo()
field.accessFlags = input.readUnsignedShort()
field.nameIndex = input.readUnsignedShort()
field.descriptorIndex = input.readUnsignedShort()
val attrCount = input.readUnsignedShort()
field.attributes = Array(attrCount) { NullAttribute }
var index = 0
while (index < attrCount) {
field.attributes[index] = IAttribute.read(input, file)
++index
}
return field
}
}
}
@@ -0,0 +1,52 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.classfile.attributes
import java.io.DataInputStream
/**
* Annotation model for annotation based attributes in class files.
*
* @author Akash Yadav
*/
open class Annotation {
/**
* Index of the entry in the constant pool which corresponds to the type of the the annotation.
* The entry at this index is always an UTF-8 constant.
*/
var typeIndex: Int = -1
internal set
/** The element value pairs. */
open var elementValues: Array<Pair<Int, ElementValue>> = emptyArray()
internal set
companion object {
internal fun read(input: DataInputStream): Annotation {
val annotation = Annotation()
annotation.typeIndex = input.readUnsignedShort()
val valueCount = input.readUnsignedShort()
annotation.elementValues =
Array(valueCount) { input.readUnsignedShort() to ElementValue.read(input) }
return annotation
}
}
}
@@ -0,0 +1,40 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.classfile.attributes
import com.itsaky.androidide.classfile.ClassFileVersion
import com.itsaky.androidide.classfile.ClassFileVersion.JAVA_5
import com.itsaky.androidide.classfile.IClassFile
import java.io.DataInputStream
/**
* An attribute which stores information about annotations.
*
* @author Akash Yadav
*/
abstract class AnnotationAttr(nameIndex: Int) : BaseAttribute(nameIndex) {
override val version: ClassFileVersion = JAVA_5
/** The annotations. */
var annotations: Array<Annotation> = emptyArray()
override fun read(attrLength: Int, file: IClassFile, input: DataInputStream) {
val count = input.readUnsignedShort()
this.annotations = Array(count) { Annotation.read(input) }
}
}
@@ -0,0 +1,25 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.classfile.attributes
/**
* Base class for attrbutes.
*
* @author Akash Yadav
*/
abstract class BaseAttribute(override val nameIndex: Int): IAttribute
@@ -0,0 +1,43 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.classfile.attributes
import com.itsaky.androidide.classfile.ClassFileVersion
import com.itsaky.androidide.classfile.ClassFileVersion.JAVA_1_2
import com.itsaky.androidide.classfile.IClassFile
import com.itsaky.androidide.classfile.attributes.IAttribute.Type
import com.itsaky.androidide.classfile.attributes.IAttribute.Type.CONSTANT_VALUE
import java.io.DataInputStream
/**
* Constant value for a field.
*
* @author Akash Yadav
*/
class ConstantValueAttr(nameIndex: Int) : BaseAttribute(nameIndex) {
override val version: ClassFileVersion = JAVA_1_2
override val type: Type = CONSTANT_VALUE
/** The index in the constant pool which corresponds to the value of this constant. */
var valueIndex: Int = -1
internal set
override fun read(attrLength: Int, file: IClassFile, input: DataInputStream) {
this.valueIndex = input.readUnsignedShort()
}
}
@@ -0,0 +1,28 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.classfile.attributes
import com.itsaky.androidide.classfile.ClassFileVersion.JAVA_1_1
import com.itsaky.androidide.classfile.attributes.IAttribute.Type.DEPRECATED
/**
* Indicates that a class, field or method is deprecated.
*
* @author Akash Yadav
*/
class DeprecatedAttr(nameIndex: Int) : MarkerAttribute(nameIndex, DEPRECATED, JAVA_1_1)
@@ -0,0 +1,140 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.classfile.attributes
import com.itsaky.androidide.classfile.attributes.ElementValue.Type.ANNOTATION_INTERFACE
import com.itsaky.androidide.classfile.attributes.ElementValue.Type.ARRAY_TYPE
import com.itsaky.androidide.classfile.attributes.ElementValue.Type.CLASS
import com.itsaky.androidide.classfile.attributes.ElementValue.Type.ENUM_CLASS
import com.itsaky.androidide.classfile.attributes.ElementValue.Type.UNKNOWN
import java.io.DataInputStream
/**
* The element value in an annotation.
*
* @author Akash Yadav
*/
class ElementValue {
/** The element tag. */
var type: Type = UNKNOWN
internal set
/**
* The constant value if the [type] is one of the following :
*
* - [Type.BYTE]
* - [Type.CHAR]
* - [Type.DOUBLE]
* - [Type.FLOAT]
* - [Type.INT]
* - [Type.LONG]
* - [Type.SHORT]
* - [Type.BOOLEAN]
* - [Type.STRING]
*/
var constValIndex: Int = -1
internal set
/** The element constant value if the [type] is [Type.ENUM_CLASS]. */
var enumValue: EnumConstValue? = null
internal set
/** The class name index if the [type] is [Type.CLASS]. */
var classNameIndex: Int = -1
internal set
/** The annotation constant value if the [type] is [Type.ANNOTATION_INTERFACE]. */
var annotationValue: Annotation? = null
internal set
/** The array constant value if the [type] is [Type.ARRAY_TYPE]. */
var arrayValue: Array<ElementValue>? = null
internal set
companion object {
internal fun read(input: DataInputStream): ElementValue {
val value = ElementValue()
value.type = Type.forTag(input.readUnsignedByte())
when (value.type) {
ENUM_CLASS -> value.enumValue = EnumConstValue.read(input)
CLASS -> value.classNameIndex = input.readUnsignedShort()
ANNOTATION_INTERFACE -> value.annotationValue = Annotation.read(input)
ARRAY_TYPE -> {
val count = input.readUnsignedShort()
val nullElem = ElementValue()
value.arrayValue = Array(count) { nullElem }
var index = 0
while (index < count) {
value.arrayValue!![index] = read(input)
++index
}
}
else -> value.constValIndex = input.readUnsignedShort()
}
return value
}
}
/**
* The enum constant value.
*
* @property typeNameIndex The index of the entry in the constant pool which corresponds to the
* name of the type of this constant.
* @property constNameIndex The index of the entry in the constant pool which corresponds to the
* name of this constant.
*/
data class EnumConstValue(val typeNameIndex: Int, val constNameIndex: Int) {
companion object {
internal fun read(input: DataInputStream): EnumConstValue {
return EnumConstValue(input.readUnsignedShort(), input.readUnsignedShort())
}
}
}
/** The type of [ElementValue]. */
enum class Type(val tag: Int) {
BYTE('B'.code),
CHAR('C'.code),
DOUBLE('D'.code),
FLOAT('F'.code),
INT('I'.code),
LONG('J'.code),
SHORT('S'.code),
BOOLEAN('Z'.code),
STRING('s'.code),
ENUM_CLASS('e'.code),
CLASS('c'.code),
ANNOTATION_INTERFACE('@'.code),
ARRAY_TYPE('['.code),
/** **FOR INTERNAL USE ONLY** */
UNKNOWN('-'.code);
companion object {
/** Get the element type for the given tag value. */
internal fun forTag(tag: Int): Type {
return values().firstOrNull { it.tag == tag }
?: throw IllegalArgumentException("Invalid tag for element value type tag '$tag'")
}
}
}
}
@@ -0,0 +1,106 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.classfile.attributes
import com.itsaky.androidide.classfile.ClassFileConstants.ATTR_CONSTANT_VALUE
import com.itsaky.androidide.classfile.ClassFileConstants.ATTR_DEPRECATED
import com.itsaky.androidide.classfile.ClassFileConstants.ATTR_RUNTIME_INVISIBLE_ANNOTATIONS
import com.itsaky.androidide.classfile.ClassFileConstants.ATTR_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS
import com.itsaky.androidide.classfile.ClassFileConstants.ATTR_RUNTIME_VISIBLE_ANNOTATIONS
import com.itsaky.androidide.classfile.ClassFileConstants.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS
import com.itsaky.androidide.classfile.ClassFileConstants.ATTR_SIGNATURE
import com.itsaky.androidide.classfile.ClassFileConstants.ATTR_SYNTHETIC
import com.itsaky.androidide.classfile.ClassFileVersion
import com.itsaky.androidide.classfile.IClassFile
import com.itsaky.androidide.classfile.attributes.IAttribute.Type.CONSTANT_VALUE
import com.itsaky.androidide.classfile.attributes.IAttribute.Type.DEPRECATED
import com.itsaky.androidide.classfile.attributes.IAttribute.Type.RUNTIME_INVISIBLE_ANNOTATIONS
import com.itsaky.androidide.classfile.attributes.IAttribute.Type.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS
import com.itsaky.androidide.classfile.attributes.IAttribute.Type.RUNTIME_VISIBLE_ANNOTATIONS
import com.itsaky.androidide.classfile.attributes.IAttribute.Type.RUNTIME_VISIBLE_TYPE_ANNOTATIONS
import com.itsaky.androidide.classfile.attributes.IAttribute.Type.SIGNATURE
import com.itsaky.androidide.classfile.attributes.IAttribute.Type.SYNTHETIC
import com.itsaky.androidide.classfile.constants.Utf8Constant
import com.itsaky.androidide.classfile.internal.ClassFile
import java.io.DataInputStream
/**
* Base class for all attributes in a class, field or method.
*
* @author Akash Yadav
*/
interface IAttribute {
/** The version of the class file in which this attribute appears. */
val version: ClassFileVersion
/** The index of the entry in the constant pool which represents the name of this attrbute. */
val nameIndex: Int
/** The type of attribute. */
val type: Type
/**
* **FOR INTERNAL USE ONLY**
*
* Read further information from the given input.
*/
fun read(attrLength: Int, file: IClassFile, input: DataInputStream)
enum class Type(val attrName: String) {
CONSTANT_VALUE(ATTR_CONSTANT_VALUE),
SYNTHETIC(ATTR_SYNTHETIC),
SIGNATURE(ATTR_SIGNATURE),
DEPRECATED(ATTR_DEPRECATED),
RUNTIME_VISIBLE_ANNOTATIONS(ATTR_RUNTIME_VISIBLE_ANNOTATIONS),
RUNTIME_INVISIBLE_ANNOTATIONS(ATTR_RUNTIME_INVISIBLE_ANNOTATIONS),
RUNTIME_VISIBLE_TYPE_ANNOTATIONS(ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS),
RUNTIME_INVISIBLE_TYPE_ANNOTATIONS(ATTR_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS);
companion object {
internal fun forName(name: String): Type {
return values().firstOrNull { it.attrName == name }
?: throw IllegalArgumentException("Unknown attribute '$name'")
}
}
}
companion object {
internal fun read(input: DataInputStream, file: ClassFile): IAttribute {
val nameIndex = input.readUnsignedShort()
val attrLength = input.readInt()
val name = (file.constantPool[nameIndex] as Utf8Constant).content()
val type = Type.forName(name)
return createForType(type, nameIndex).also { it.read(attrLength, file, input) }
}
private fun createForType(type: Type, nameIndex: Int): IAttribute {
return when (type) {
CONSTANT_VALUE -> ConstantValueAttr(nameIndex)
SYNTHETIC -> SyntheticAttr(nameIndex)
SIGNATURE -> SignatureAttr(nameIndex)
DEPRECATED -> DeprecatedAttr(nameIndex)
RUNTIME_VISIBLE_ANNOTATIONS -> RuntimeVisibleAnnotationsAttr(nameIndex)
RUNTIME_INVISIBLE_ANNOTATIONS -> RuntimeVisibleAnnotationsAttr(nameIndex)
RUNTIME_VISIBLE_TYPE_ANNOTATIONS -> RuntimeVisibleTypeAnnotations(nameIndex)
RUNTIME_INVISIBLE_TYPE_ANNOTATIONS -> RuntimeInvisibleTypeAnnotations(nameIndex)
else -> throw IllegalArgumentException("Unknown attribute type $type")
}
}
}
}
@@ -0,0 +1,36 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.classfile.attributes
import com.itsaky.androidide.classfile.ClassFileVersion
import com.itsaky.androidide.classfile.IClassFile
import com.itsaky.androidide.classfile.attributes.IAttribute.Type
import java.io.DataInputStream
/**
* An attribute which is simply a marker and does not contain any further information.
*
* @author Akash Yadav
*/
abstract class MarkerAttribute(
nameIndex: Int,
override val type: Type,
override val version: ClassFileVersion
) : BaseAttribute(nameIndex) {
override fun read(attrLength: Int, file: IClassFile, input: DataInputStream) {}
}
@@ -0,0 +1,31 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.classfile.attributes
import com.itsaky.androidide.classfile.attributes.IAttribute.Type
import com.itsaky.androidide.classfile.attributes.IAttribute.Type.RUNTIME_INVISIBLE_ANNOTATIONS
/**
* Stores run-time invisible annotations on the declaration of the corresponding class, method,
* field, or record component.
*
* @author Akash Yadav
*/
class RuntimeInvisibleAnnotationsAttr(nameIndex: Int) : AnnotationAttr(nameIndex) {
override val type: Type = RUNTIME_INVISIBLE_ANNOTATIONS
}
@@ -0,0 +1,37 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.classfile.attributes
import com.itsaky.androidide.classfile.attributes.IAttribute.Type
import com.itsaky.androidide.classfile.attributes.IAttribute.Type.RUNTIME_INVISIBLE_TYPE_ANNOTATIONS
import com.itsaky.androidide.classfile.attributes.IAttribute.Type.RUNTIME_VISIBLE_TYPE_ANNOTATIONS
/**
* Stores run-time invisible annotations on type parameter declarations of generic classes,
* interfaces, methods, and constructors.
*
* @author Akash Yadav
*/
class RuntimeInvisibleTypeAnnotations(nameIndex: Int) : AnnotationAttr(nameIndex) {
override val type: Type = RUNTIME_INVISIBLE_TYPE_ANNOTATIONS
/**
* The annotations always of type [TypeAnnotation]
*/
var typeAnnotations: Array<TypeAnnotation> = emptyArray()
}
@@ -0,0 +1,30 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.classfile.attributes
import com.itsaky.androidide.classfile.attributes.IAttribute.Type
import com.itsaky.androidide.classfile.attributes.IAttribute.Type.RUNTIME_VISIBLE_ANNOTATIONS
/**
* Stores run-time visible annotations on the declaration of the corresponding class, field, method,
* or record component.
* @author Akash Yadav
*/
class RuntimeVisibleAnnotationsAttr(nameIndex: Int) : AnnotationAttr(nameIndex) {
override val type: Type = RUNTIME_VISIBLE_ANNOTATIONS
}
@@ -0,0 +1,36 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.classfile.attributes
import com.itsaky.androidide.classfile.attributes.IAttribute.Type
import com.itsaky.androidide.classfile.attributes.IAttribute.Type.RUNTIME_VISIBLE_TYPE_ANNOTATIONS
/**
* Stores run-time visible annotations on type parameter declarations of generic classes,
* interfaces, methods, and constructors.
*
* @author Akash Yadav
*/
class RuntimeVisibleTypeAnnotations(nameIndex: Int) : AnnotationAttr(nameIndex) {
override val type: Type = RUNTIME_VISIBLE_TYPE_ANNOTATIONS
/**
* The annotations always of type [TypeAnnotation]
*/
var typeAnnotations: Array<TypeAnnotation> = emptyArray()
}
@@ -0,0 +1,42 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.classfile.attributes
import com.itsaky.androidide.classfile.ClassFileVersion
import com.itsaky.androidide.classfile.ClassFileVersion.JAVA_5
import com.itsaky.androidide.classfile.IClassFile
import com.itsaky.androidide.classfile.attributes.IAttribute.Type
import com.itsaky.androidide.classfile.attributes.IAttribute.Type.SIGNATURE
import java.io.DataInputStream
/**
* Signature of a class, field or method.
*
* @author Akash Yadav
*/
class SignatureAttr(nameIndex: Int) : BaseAttribute(nameIndex) {
override val version: ClassFileVersion = JAVA_5
override val type: Type = SIGNATURE
var signatureIndex: Int = -1
internal set
override fun read(attrLength: Int, file: IClassFile, input: DataInputStream) {
this.signatureIndex = input.readUnsignedShort()
}
}
@@ -0,0 +1,28 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.classfile.attributes
import com.itsaky.androidide.classfile.ClassFileVersion.JAVA_1_1
import com.itsaky.androidide.classfile.attributes.IAttribute.Type.SYNTHETIC
/**
* Indicates that a class, field or method is synthetic.
*
* @author Akash Yadav
*/
class SyntheticAttr(nameIndex: Int) : MarkerAttribute(nameIndex, SYNTHETIC, JAVA_1_1)
@@ -0,0 +1,59 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.classfile.attributes
import java.io.DataInputStream
/**
* Annotation model for (in)visible type annotations.
*
* @author Akash Yadav
*/
class TypeAnnotation {
lateinit var targetInfo: TargetInfo
internal set
lateinit var typePath: TypePath
internal set
/**
* Index of the entry in the constant pool which corresponds to the type of the the annotation.
* The entry at this index is always an UTF-8 constant.
*/
var typeIndex: Int = -1
internal set
/** The element value pairs. */
var elementValues: Array<Pair<Int, ElementValue>> = emptyArray()
internal set
companion object {
internal fun read(input: DataInputStream): TypeAnnotation {
val annotation = TypeAnnotation()
val targetType = input.readUnsignedByte()
annotation.targetInfo = TargetInfo.forType(targetType).apply { read(input) }
annotation.typePath = TypePath().apply { read(input) }
annotation.typeIndex = input.readUnsignedShort()
val valueCount = input.readUnsignedShort()
annotation.elementValues =
Array(valueCount) { input.readUnsignedShort() to ElementValue.read(input) }
return annotation
}
}
}
@@ -0,0 +1,54 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.classfile.attributes
import java.io.DataInputStream
/**
* Wherever a type is used in a declaration or expression, the type_path structure identifies which
* part of the type is annotated. An annotation may appear on the type itself, but if the type is a
* reference type, then there are additional locations where an annotation may appear.
*
* @author Akash Yadav
*/
class TypePath {
var paths: Array<PathEntry> = emptyArray()
internal set
class PathEntry {
var typePathKind: Int = -1
internal set
var typeArgumentIndex: Int = -1
internal set
internal fun read(input: DataInputStream) {
this.typePathKind = input.readUnsignedByte()
this.typeArgumentIndex = input.readUnsignedByte()
}
}
internal fun read(input: DataInputStream) {
this.paths =
Array(input.readUnsignedByte()) {
val entry = PathEntry()
entry.read(input)
entry
}
}
}
@@ -0,0 +1,210 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.classfile.attributes
import java.io.DataInputStream
/**
* Target information in a type annotation.
*
* @author Akash Yadav
*/
abstract class TargetInfo(val currentType: Int, val types: IntArray) {
/** The read the target info. */
internal abstract fun read(input: DataInputStream)
companion object {
internal fun forType(type: Int): TargetInfo {
return when (type) {
0x00,
0x01 -> TypeParameterTarget(type)
0x10 -> SupertypeTarget(type)
0x11,
0x12 -> TypeParameterBoundTarget(type)
0x13,
0x14,
0x15 -> EmptyTarget(type)
0x16 -> FormalParameterTarget(type)
0x17 -> ThrowsTarget(type)
0x40,
0x41 -> LocalVarTarget(type)
0x42 -> CatchTarget(type)
0x43,
0x44,
0x45,
0x46 -> OffsetTarget(type)
0x47,
0x48,
0x49,
0x4A,
0x4B -> TypeArgumentTarget(type)
else -> throw IllegalArgumentException("Unknown type parameter target")
}
}
}
}
open class TypeParameterTarget
@JvmOverloads
constructor(currentType: Int, types: IntArray = intArrayOf(0x00, 0x01)) :
TargetInfo(currentType, types) {
/**
* The value of the type_parameter_index item specifies which type parameter declaration is
* annotated. A type_parameter_index value of 0 specifies the first type parameter declaration.
*/
var typeParameterIndex: Int = -1
internal set
override fun read(input: DataInputStream) {
this.typeParameterIndex = input.readUnsignedByte()
}
}
class SupertypeTarget(currentType: Int) : TargetInfo(currentType, intArrayOf(0x10)) {
/**
* A supertype_index value of 65535 specifies that the annotation appears on the superclass in an
* extends clause of a class declaration.
*
* Any other supertype_index value is an index into the interfaces array of the enclosing
* ClassFile structure, and specifies that the annotation appears on that superinterface in either
* the implements clause of a class declaration or the extends clause of an interface declaration.
*/
var supertypeIndex: Int = -1
internal set
override fun read(input: DataInputStream) {
this.supertypeIndex = input.readUnsignedShort()
}
}
class TypeParameterBoundTarget(currentType: Int) :
TypeParameterTarget(currentType, intArrayOf(0x11, 0x12)) {
/**
* The value of the bound_index item specifies which bound of the type parameter declaration
* indicated by type_parameter_index is annotated. A bound_index value of 0 specifies the first
* bound of a type parameter declaration.
*/
var boundIndex: Int = -1
internal set
override fun read(input: DataInputStream) {
super.read(input)
this.boundIndex = input.readUnsignedShort()
}
}
class EmptyTarget(currentType: Int) : TargetInfo(currentType, intArrayOf(0x13, 0x14, 0x15)) {
override fun read(input: DataInputStream) {}
}
class FormalParameterTarget(currentType: Int) : TargetInfo(currentType, intArrayOf(0x16)) {
/**
* The value of the formal_parameter_index item specifies which formal parameter declaration has
* an annotated type. A formal_parameter_index value of i may, but is not required to, correspond
* to the i'th parameter descriptor in the method descriptor.
*/
var formalParameterIndex: Int = -1
internal set
override fun read(input: DataInputStream) {
this.formalParameterIndex = input.readUnsignedByte()
}
}
class ThrowsTarget(currentType: Int) : TargetInfo(currentType, intArrayOf(0x17)) {
/**
* The value of the throws_type_index item is an index into the exception_index_table array of the
* Exceptions attribute of the method_info structure enclosing the RuntimeVisibleTypeAnnotations
* attribute.
*/
var throwsTypeIndex: Int = -1
internal set
override fun read(input: DataInputStream) {
this.throwsTypeIndex = input.readUnsignedShort()
}
}
class LocalVarTarget(currentType: Int) : TargetInfo(currentType, intArrayOf(0x40, 0x41)) {
var table: Array<LocalVarTagetTableEntry> = emptyArray()
class LocalVarTagetTableEntry {
var startPc: Int = -1
internal set
var length: Int = -1
internal set
var index: Int = -1
internal set
}
override fun read(input: DataInputStream) {
val length = input.readUnsignedShort()
this.table =
Array(length) {
val entry = LocalVarTagetTableEntry()
entry.startPc = input.readUnsignedShort()
entry.length = input.readUnsignedShort()
entry.index = input.readUnsignedShort()
entry
}
}
}
class CatchTarget(currentType: Int) : TargetInfo(currentType, intArrayOf(0x42)) {
var exceptionTableIndex: Int = -1
internal set
override fun read(input: DataInputStream) {
this.exceptionTableIndex = input.readUnsignedShort()
}
}
class OffsetTarget(currentType: Int) : TargetInfo(currentType, intArrayOf(0x43, 0x44, 0x45, 0x46)) {
var offset: Int = -1
internal set
override fun read(input: DataInputStream) {
this.offset = input.readUnsignedShort()
}
}
class TypeArgumentTarget(currentType: Int) :
TargetInfo(currentType, intArrayOf(0x47, 0x48, 0x49, 0x4A, 0x4B)) {
var offset: Int = -1
internal set
var typeArgumentIndex: Int = -1
internal set
override fun read(input: DataInputStream) {
this.offset = input.readUnsignedShort()
this.typeArgumentIndex = input.readUnsignedByte()
}
}
@@ -33,6 +33,9 @@ class Utf8Constant : IConstant {
var bytes: ByteArray = ByteArray(0)
private set
/** Get the string represented by this constant. */
fun content() = this.bytes.decodeToString()
override fun read(input: DataInputStream) {
val length = input.readUnsignedShort()
this.bytes = ByteArray(length)
@@ -56,6 +59,6 @@ class Utf8Constant : IConstant {
}
override fun toString(): String {
return "Utf8Constant(type=$type, bytes=${bytes.contentToString()}, bytesAsString=${bytes.decodeToString()})"
return "Utf8Constant(type=$type, bytes=${bytes.contentToString()}, bytesAsString=${content()})"
}
}
@@ -17,8 +17,11 @@
package com.itsaky.androidide.classfile.internal
import com.itsaky.androidide.classfile.ClassFileVersion
import com.itsaky.androidide.classfile.ClassFileVersion.JAVA_1_0
import com.itsaky.androidide.classfile.constants.IConstant
import com.itsaky.androidide.classfile.IClassFile
import com.itsaky.androidide.classfile.IFieldInfo
import com.itsaky.androidide.classfile.constants.ClassConstant
import com.itsaky.androidide.classfile.constants.Utf8Constant
@@ -29,31 +32,13 @@ import com.itsaky.androidide.classfile.constants.Utf8Constant
*/
internal class ClassFile : IClassFile {
/** Major version of the class file. */
override var majorVersion: Int = 0
/** Minor version of the class file. */
override var minorVersion: Int = 0
/** The constant pool. */
override var version: ClassFileVersion = JAVA_1_0
override var constantPool: Array<IConstant> = emptyArray()
/** Access flags for the class. */
override var accessFlags: Int = 0
/**
* The index in the constant pool which corresponds to the internal representation of the name of
* this class.
*/
override var thisClass: Int = -1
/**
* The index in the constant pool which corresponds to the internal representation of the name of
* the super class of this class.
*/
override var superClass: Int = -1
override var interfaces: IntArray = IntArray(0)
override var fields: Array<IFieldInfo> = emptyArray()
override fun getName(): String {
val entry = constantPool[thisClass]
@@ -0,0 +1,37 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.classfile.internal
import com.itsaky.androidide.classfile.IFieldInfo
import com.itsaky.androidide.classfile.attributes.IAttribute
/**
* Default internal implementation of [IFieldInfo].
*
* @author Akash Yadav
*/
internal class FieldInfo : IFieldInfo {
override var accessFlags: Int = 0
internal set
override var nameIndex: Int = -1
internal set
override var descriptorIndex: Int = -1
internal set
override var attributes: Array<IAttribute> = emptyArray()
internal set
}
@@ -0,0 +1,44 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.classfile.internal
import com.itsaky.androidide.classfile.ClassFileVersion
import com.itsaky.androidide.classfile.IClassFile
import com.itsaky.androidide.classfile.attributes.IAttribute
import com.itsaky.androidide.classfile.attributes.IAttribute.Type
import java.io.DataInputStream
/**
* **FOR INTERNAL USE ONLY**
*
* Represents a null attribute. Not present in a class file.
*
* @author Akash Yadav
*/
internal object NullAttribute : IAttribute {
override val version: ClassFileVersion
get() = throw UnsupportedOperationException()
override val nameIndex: Int
get() = throw UnsupportedOperationException()
override val type: Type
get() = throw UnsupportedOperationException()
override fun read(attrLength: Int, file: IClassFile, input: DataInputStream) {
throw UnsupportedOperationException()
}
}
@@ -0,0 +1,39 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.classfile.internal
import com.itsaky.androidide.classfile.IFieldInfo
import com.itsaky.androidide.classfile.attributes.IAttribute
/**
* **FOR INTERNAL USE ONLY**
*
* Represents a null field. Not available in class file.
*
* @author Akash Yadav
*/
internal object NullField : IFieldInfo {
override val accessFlags: Int
get() = throw UnsupportedOperationException()
override val nameIndex: Int
get() = throw UnsupportedOperationException()
override val descriptorIndex: Int
get() = throw UnsupportedOperationException()
override val attributes: Array<IAttribute>
get() = throw UnsupportedOperationException()
}
@@ -39,8 +39,7 @@ class ClassFileReaderTest {
val file = reader.read()
assertThat(file).isNotNull()
assertThat(file.majorVersion).isEqualTo(55)
assertThat(file.minorVersion).isEqualTo(0)
assertThat(file.version).isEqualTo(ClassFileVersion.JAVA_11)
assertThat(file.getName()).isEqualTo("io/github/rosemoe/sora/widget/CodeEditor")
assertThat(file.getSuperClassName()).isEqualTo("android/view/View")