diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/ClassFileConstants.java b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/ClassFileConstants.java
index adb97cb63..397143312 100644
--- a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/ClassFileConstants.java
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/ClassFileConstants.java
@@ -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";
}
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/ClassFileReader.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/ClassFileReader.kt
index ccf14be74..8f887f58c 100644
--- a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/ClassFileReader.kt
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/ClassFileReader.kt
@@ -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
}
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/ClassFileVersion.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/ClassFileVersion.kt
new file mode 100644
index 000000000..6c8bf822e
--- /dev/null
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/ClassFileVersion.kt
@@ -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 .
+ */
+
+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}'")
+ }
+ }
+}
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/IClassFile.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/IClassFile.kt
index c670eaae9..40005d3e8 100644
--- a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/IClassFile.kt
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/IClassFile.kt
@@ -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
@@ -52,6 +50,9 @@ interface IClassFile {
/** The interfaces that this class implements. */
val interfaces: IntArray
+ /** The fields of this class. */
+ val fields: Array
+
/** Get the string representation of this class' name. */
fun getName(): String
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/IFieldInfo.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/IFieldInfo.kt
new file mode 100644
index 000000000..16497a0a0
--- /dev/null
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/IFieldInfo.kt
@@ -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 .
+ */
+
+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
+
+ 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
+ }
+ }
+}
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/Annotation.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/Annotation.kt
new file mode 100644
index 000000000..36dc134ce
--- /dev/null
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/Annotation.kt
@@ -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 .
+ */
+
+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> = 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
+ }
+ }
+}
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/AnnotationAttr.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/AnnotationAttr.kt
new file mode 100644
index 000000000..f193e0f2e
--- /dev/null
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/AnnotationAttr.kt
@@ -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 .
+ */
+
+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 = emptyArray()
+
+ override fun read(attrLength: Int, file: IClassFile, input: DataInputStream) {
+ val count = input.readUnsignedShort()
+ this.annotations = Array(count) { Annotation.read(input) }
+ }
+}
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/BaseAttribute.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/BaseAttribute.kt
new file mode 100644
index 000000000..3350e8700
--- /dev/null
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/BaseAttribute.kt
@@ -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 .
+ */
+
+package com.itsaky.androidide.classfile.attributes
+
+/**
+ * Base class for attrbutes.
+ *
+ * @author Akash Yadav
+ */
+abstract class BaseAttribute(override val nameIndex: Int): IAttribute
\ No newline at end of file
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/ConstantValueAttr.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/ConstantValueAttr.kt
new file mode 100644
index 000000000..d18075f7d
--- /dev/null
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/ConstantValueAttr.kt
@@ -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 .
+ */
+
+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()
+ }
+}
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/DeprecatedAttr.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/DeprecatedAttr.kt
new file mode 100644
index 000000000..e9e67e5bd
--- /dev/null
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/DeprecatedAttr.kt
@@ -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 .
+ */
+
+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)
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/ElementValue.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/ElementValue.kt
new file mode 100644
index 000000000..ebb1d0cff
--- /dev/null
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/ElementValue.kt
@@ -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 .
+ */
+
+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? = 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'")
+ }
+ }
+ }
+}
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/IAttribute.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/IAttribute.kt
new file mode 100644
index 000000000..d3e142ee1
--- /dev/null
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/IAttribute.kt
@@ -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 .
+ */
+
+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")
+ }
+ }
+ }
+}
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/MarkerAttribute.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/MarkerAttribute.kt
new file mode 100644
index 000000000..d331d6ca1
--- /dev/null
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/MarkerAttribute.kt
@@ -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 .
+ */
+
+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) {}
+}
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeInvisibleAnnotationsAttr.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeInvisibleAnnotationsAttr.kt
new file mode 100644
index 000000000..9bb836802
--- /dev/null
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeInvisibleAnnotationsAttr.kt
@@ -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 .
+ */
+
+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
+}
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeInvisibleTypeAnnotations.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeInvisibleTypeAnnotations.kt
new file mode 100644
index 000000000..62cc6419b
--- /dev/null
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeInvisibleTypeAnnotations.kt
@@ -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 .
+ */
+
+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 = emptyArray()
+}
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeVisibleAnnotationsAttr.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeVisibleAnnotationsAttr.kt
new file mode 100644
index 000000000..c283af1c8
--- /dev/null
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeVisibleAnnotationsAttr.kt
@@ -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 .
+ */
+
+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
+}
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeVisibleTypeAnnotations.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeVisibleTypeAnnotations.kt
new file mode 100644
index 000000000..6a93da5eb
--- /dev/null
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/RuntimeVisibleTypeAnnotations.kt
@@ -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 .
+ */
+
+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 = emptyArray()
+}
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/SignatureAttr.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/SignatureAttr.kt
new file mode 100644
index 000000000..005e6a69b
--- /dev/null
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/SignatureAttr.kt
@@ -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 .
+ */
+
+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()
+ }
+}
\ No newline at end of file
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/SyntheticAttr.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/SyntheticAttr.kt
new file mode 100644
index 000000000..947744f1e
--- /dev/null
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/SyntheticAttr.kt
@@ -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 .
+ */
+
+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)
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/TypeAnnotation.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/TypeAnnotation.kt
new file mode 100644
index 000000000..388d470dd
--- /dev/null
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/TypeAnnotation.kt
@@ -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 .
+ */
+
+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> = 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
+ }
+ }
+}
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/TypePath.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/TypePath.kt
new file mode 100644
index 000000000..f65622c02
--- /dev/null
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/TypePath.kt
@@ -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 .
+ */
+
+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 = 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
+ }
+ }
+}
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/targetInfos.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/targetInfos.kt
new file mode 100644
index 000000000..e444b01c4
--- /dev/null
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/attributes/targetInfos.kt
@@ -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 .
+ */
+
+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 = 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()
+ }
+}
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/constants/Utf8Constant.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/constants/Utf8Constant.kt
index e086f2bc2..79e2ccf7f 100644
--- a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/constants/Utf8Constant.kt
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/constants/Utf8Constant.kt
@@ -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()})"
}
}
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/ClassFile.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/ClassFile.kt
index 9d532bbad..f63b83ea2 100644
--- a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/ClassFile.kt
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/ClassFile.kt
@@ -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 = 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 = emptyArray()
override fun getName(): String {
val entry = constantPool[thisClass]
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/FieldInfo.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/FieldInfo.kt
new file mode 100644
index 000000000..c9c52c692
--- /dev/null
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/FieldInfo.kt
@@ -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 .
+ */
+
+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 = emptyArray()
+ internal set
+}
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/NullAttribute.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/NullAttribute.kt
new file mode 100644
index 000000000..73d3c3356
--- /dev/null
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/NullAttribute.kt
@@ -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 .
+ */
+
+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()
+ }
+}
diff --git a/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/NullField.kt b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/NullField.kt
new file mode 100644
index 000000000..6d4510442
--- /dev/null
+++ b/build-tools/classfile/src/main/java/com/itsaky/androidide/classfile/internal/NullField.kt
@@ -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 .
+ */
+
+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
+ get() = throw UnsupportedOperationException()
+}
diff --git a/build-tools/classfile/src/test/java/com/itsaky/androidide/classfile/ClassFileReaderTest.kt b/build-tools/classfile/src/test/java/com/itsaky/androidide/classfile/ClassFileReaderTest.kt
index 95be69804..655cc45e8 100644
--- a/build-tools/classfile/src/test/java/com/itsaky/androidide/classfile/ClassFileReaderTest.kt
+++ b/build-tools/classfile/src/test/java/com/itsaky/androidide/classfile/ClassFileReaderTest.kt
@@ -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")