org.opalj.bc.Assembler.scala Maven / Gradle / Ivy
The newest version!
/* BSD 2-Clause License - see OPAL/LICENSE for details. */
package org.opalj
package bc
import scala.annotation.switch
import java.io.ByteArrayOutputStream
import java.io.DataOutputStream
import org.opalj.bc.MethodFilter.logContext
import org.opalj.bi.ACC_STRICT
import org.opalj.bi.{ConstantPoolTags => CPTags}
import org.opalj.da._
import org.opalj.da.ClassFileReader.LineNumberTable_attribute
import org.opalj.log.OPALLogger
/**
* Factory to create the binary representation (that is, an array of bytes) of a given class file.
*
* @author Michael Eichberg
*/
object Assembler {
private[bc] def as[T](x: AnyRef): T = x.asInstanceOf[T]
implicit object RichCONSTANT_Class_info extends ClassFileElement[CONSTANT_Class_info] {
def write(
ci: CONSTANT_Class_info
)(
implicit
out: DataOutputStream, segmentInformation: (String, Int) => Unit
): Unit = {
import ci._
import out._
writeByte(tag)
writeShort(name_index)
}
}
implicit object RichCONSTANT_Ref extends ClassFileElement[CONSTANT_Ref] {
def write(
cr: CONSTANT_Ref
)(
implicit
out: DataOutputStream, segmentInformation: (String, Int) => Unit
): Unit = {
import cr._
import out._
writeByte(tag)
writeShort(class_index)
writeShort(name_and_type_index)
}
}
implicit object RichCONSTANT_String_info extends ClassFileElement[CONSTANT_String_info] {
def write(
ci: CONSTANT_String_info
)(
implicit
out: DataOutputStream, segmentInformation: (String, Int) => Unit
): Unit = {
import ci._
import out._
writeByte(tag)
writeShort(string_index)
}
}
implicit object RichCONSTANT_Integer_info extends ClassFileElement[CONSTANT_Integer_info] {
def write(
ci: CONSTANT_Integer_info
)(
implicit
out: DataOutputStream, segmentInformation: (String, Int) => Unit
): Unit = {
import ci._
import out._
writeByte(tag)
writeInt(value)
}
}
implicit object RichCONSTANT_Float_info extends ClassFileElement[CONSTANT_Float_info] {
def write(
ci: CONSTANT_Float_info
)(
implicit
out: DataOutputStream, segmentInformation: (String, Int) => Unit
): Unit = {
import ci._
import out._
writeByte(tag)
writeFloat(value)
}
}
implicit object RichCONSTANT_Long_info extends ClassFileElement[CONSTANT_Long_info] {
def write(
ci: CONSTANT_Long_info
)(
implicit
out: DataOutputStream, segmentInformation: (String, Int) => Unit
): Unit = {
import ci._
import out._
writeByte(tag)
writeLong(value)
}
}
implicit object RichCONSTANT_Double_info extends ClassFileElement[CONSTANT_Double_info] {
def write(
ci: CONSTANT_Double_info
)(
implicit
out: DataOutputStream, segmentInformation: (String, Int) => Unit
): Unit = {
import ci._
import out._
writeByte(tag)
writeDouble(value)
}
}
implicit object RichCONSTANT_NameAndType_info
extends ClassFileElement[CONSTANT_NameAndType_info] {
def write(
ci: CONSTANT_NameAndType_info
)(
implicit
out: DataOutputStream, segmentInformation: (String, Int) => Unit
): Unit = {
import ci._
import out._
writeByte(tag)
writeShort(name_index)
writeShort(descriptor_index)
}
}
implicit object RichCONSTANT_Utf8_info
extends ClassFileElement[CONSTANT_Utf8_info] {
def write(
ci: CONSTANT_Utf8_info
)(
implicit
out: DataOutputStream, segmentInformation: (String, Int) => Unit
): Unit = {
import ci._
import out._
writeByte(tag)
writeUTF(value)
}
}
implicit object RichCONSTANT_MethodHandle_info
extends ClassFileElement[CONSTANT_MethodHandle_info] {
def write(
ci: CONSTANT_MethodHandle_info
)(
implicit
out: DataOutputStream, segmentInformation: (String, Int) => Unit
): Unit = {
import ci._
import out._
writeByte(tag)
writeByte(reference_kind)
writeShort(reference_index)
}
}
implicit object RichCONSTANT_MethodType_info
extends ClassFileElement[CONSTANT_MethodType_info] {
def write(
ci: CONSTANT_MethodType_info
)(
implicit
out: DataOutputStream, segmentInformation: (String, Int) => Unit
): Unit = {
import ci._
import out._
writeByte(tag)
writeShort(descriptor_index)
}
}
implicit object RichCONSTANT_InvokeDynamic_info
extends ClassFileElement[CONSTANT_InvokeDynamic_info] {
def write(
ci: CONSTANT_InvokeDynamic_info
)(
implicit
out: DataOutputStream, segmentInformation: (String, Int) => Unit
): Unit = {
import ci._
import out._
writeByte(tag)
writeShort(bootstrap_method_attr_index)
writeShort(name_and_type_index)
}
}
implicit object RichCONSTANT_Module_info extends ClassFileElement[CONSTANT_Module_info] {
def write(
ci: CONSTANT_Module_info
)(
implicit
out: DataOutputStream, segmentInformation: (String, Int) => Unit
): Unit = {
import ci._
import out._
writeByte(tag)
writeShort(name_index)
}
}
implicit object RichCONSTANT_Package_info extends ClassFileElement[CONSTANT_Package_info] {
def write(
ci: CONSTANT_Package_info
)(
implicit
out: DataOutputStream, segmentInformation: (String, Int) => Unit
): Unit = {
import ci._
import out._
writeByte(tag)
writeShort(name_index)
}
}
implicit object RichCONSTANT_Dynamic_info extends ClassFileElement[CONSTANT_Dynamic_info] {
def write(
ci: CONSTANT_Dynamic_info
)(
implicit
out: DataOutputStream, segmentInformation: (String, Int) => Unit
): Unit = {
import ci._
import out._
writeByte(tag)
writeShort(bootstrap_method_attr_index)
writeShort(name_and_type_index)
}
}
implicit object RichConstant_Pool_Entry extends ClassFileElement[Constant_Pool_Entry] {
def write(
cpe: Constant_Pool_Entry
)(
implicit
out: DataOutputStream, segmentInformation: (String, Int) => Unit
): Unit = {
cpe.Constant_Type_Value.id match {
case CPTags.CONSTANT_Utf8_ID => serializeAs[CONSTANT_Utf8_info](cpe)
case CPTags.CONSTANT_Class_ID => serializeAs[CONSTANT_Class_info](cpe)
case CPTags.CONSTANT_String_ID => serializeAs[CONSTANT_String_info](cpe)
case CPTags.CONSTANT_Integer_ID => serializeAs[CONSTANT_Integer_info](cpe)
case CPTags.CONSTANT_Float_ID => serializeAs[CONSTANT_Float_info](cpe)
case CPTags.CONSTANT_Long_ID => serializeAs[CONSTANT_Long_info](cpe)
case CPTags.CONSTANT_Double_ID => serializeAs[CONSTANT_Double_info](cpe)
case CPTags.CONSTANT_NameAndType_ID => serializeAs[CONSTANT_NameAndType_info](cpe)
case CPTags.CONSTANT_Fieldref_ID |
CPTags.CONSTANT_Methodref_ID |
CPTags.CONSTANT_InterfaceMethodref_ID => serializeAs[CONSTANT_Ref](cpe)
// JAVA 7
case CPTags.CONSTANT_MethodHandle_ID =>
serializeAs[CONSTANT_MethodHandle_info](cpe)
case CPTags.CONSTANT_MethodType_ID =>
serializeAs[CONSTANT_MethodType_info](cpe)
case CPTags.CONSTANT_InvokeDynamic_ID =>
serializeAs[CONSTANT_InvokeDynamic_info](cpe)
// JAVA 9
case CPTags.CONSTANT_Module_ID => serializeAs[CONSTANT_Module_info](cpe)
case CPTags.CONSTANT_Package_ID => serializeAs[CONSTANT_Package_info](cpe)
// JAVA 11
case CPTags.CONSTANT_Dynamic_ID => serializeAs[CONSTANT_Dynamic_info](cpe)
}
}
}
implicit object RichElementValue extends ClassFileElement[ElementValue] {
def write(
ev: ElementValue
)(
implicit
out: DataOutputStream,
segmentInformation: (String, Int) => Unit
): Unit = {
import out._
val tag = ev.tag
writeByte(tag)
tag match {
case 'B' | 'C' | 'D' | 'F' | 'I' | 'J' | 'S' | 'Z' =>
writeShort(as[BaseElementValue](ev).const_value_index)
case 's' =>
writeShort(as[StringValue](ev).const_value_index)
case 'e' =>
val e = as[EnumValue](ev)
writeShort(e.type_name_index)
writeShort(e.const_name_index)
case 'c' =>
writeShort(as[ClassValue](ev).class_info_index)
case '@' =>
val av = as[AnnotationValue](ev)
serialize(av.annotation)(RichAnnotation, out, segmentInformation)
case '[' =>
val av = as[ArrayValue](ev)
writeShort(av.values.length)
av.values.foreach { serialize(_) }
}
}
}
implicit object RichAnnotation extends ClassFileElement[Annotation] {
def write(
a: Annotation
)(
implicit
out: DataOutputStream, segmentInformation: (String, Int) => Unit
): Unit = {
import a._
import out._
writeShort(type_index)
writeShort(element_value_pairs.length)
element_value_pairs.foreach { evp =>
writeShort(evp.element_name_index)
serialize(evp.element_value)
}
}
}
implicit object RichTypeAnnotation extends ClassFileElement[TypeAnnotation] {
def write(
ta: TypeAnnotation
)(
implicit
out: DataOutputStream, segmentInformation: (String, Int) => Unit
): Unit = {
import out._
val target_type = ta.target_type
val target_typeTag = target_type.tag
writeByte(target_typeTag)
(target_typeTag: @switch) match {
case 0x00 | 0x01 =>
val tt = as[TATTypeParameter](target_type)
writeByte(tt.type_parameter_index)
case 0x10 =>
val tt = as[TATSupertype](target_type)
writeShort(tt.supertype_index)
case 0x11 | 0x12 =>
val tt = as[TATTypeParameterBound](target_type)
writeByte(tt.type_parameter_index)
writeByte(tt.bound_index)
case 0x16 =>
val tt = as[TATFormalParameter](target_type)
writeByte(tt.formal_parameter_index)
case 0x17 =>
val TATThrows(throws_type_index) = target_type
writeShort(throws_type_index)
case 0x40 | 0x41 =>
val tt = as[TATLocalvar](target_type)
val lvt = tt.localvarTable
writeShort(lvt.length)
lvt.foreach { lvte =>
writeShort(lvte.start_pc)
writeShort(lvte.length)
writeShort(lvte.index)
}
case 0x42 =>
val tt = as[TATCatch](target_type)
writeShort(tt.exception_table_index)
case 0x43 | 0x44 | 0x45 | 0x46 =>
val tt = as[TATWithOffset](target_type)
writeShort(tt.offset)
case 0x47 | 0x48 | 0x49 | 0x4A | 0x4B =>
val tt = as[TATTypeArgument](target_type)
writeShort(tt.offset)
writeByte(tt.type_argument_index)
case 0x13 | 0x14 | 0x15 =>
// EMPTY_TARGET <=> Nothing to do
}
ta.target_path match {
case TypeAnnotationDirectlyOnType =>
writeByte(0)
case TypeAnnotationPathElements(elements) =>
writeByte(elements.length)
elements.foreach { tape =>
writeByte(tape.type_path_kind)
writeByte(tape.type_argument_index)
}
}
writeShort(ta.type_index)
val evps = ta.element_value_pairs
writeShort(evps.length)
evps.foreach { evp =>
writeShort(evp.element_name_index)
serialize(evp.element_value)
}
}
}
implicit object RichVerificationTypeInfo extends ClassFileElement[VerificationTypeInfo] {
def write(
vti: VerificationTypeInfo
)(
implicit
out: DataOutputStream, segmentInformation: (String, Int) => Unit
): Unit = {
import out._
val tag = vti.tag
(tag: @scala.annotation.switch) match {
case VerificationTypeInfo.ITEM_Object =>
val ovi = as[ObjectVariableInfo](vti)
writeByte(tag)
writeShort(ovi.cpool_index)
case VerificationTypeInfo.ITEM_Unitialized =>
val uvi = as[UninitializedVariableInfo](vti)
writeByte(tag)
writeShort(uvi.offset)
case _ =>
writeByte(tag)
}
}
}
implicit object RichAttribute extends ClassFileElement[Attribute] {
def write(
a: Attribute
)(
implicit
out: DataOutputStream, segmentInformation: (String, Int) => Unit
): Unit = {
import a._
import out._
writeShort(attribute_name_index)
writeInt(attribute_length)
a match {
case a: TypeAnnotations_attribute =>
// Handles:
// RuntimeVisibleTypeAnnotations_attribute
// RuntimeInvisibleTypeAnnotations_attribute
writeShort(a.typeAnnotations.size)
a.typeAnnotations.foreach { serialize(_) }
case a: StackMapTable_attribute =>
writeShort(a.stack_map_frames.length)
a.stack_map_frames.foreach { smf =>
val frame_type = smf.frame_type
if (frame_type <= 63) {
writeByte(frame_type)
} else if (frame_type >= 64 && frame_type <= 127) {
val sl1sif = as[SameLocals1StackItemFrame](smf)
writeByte(frame_type)
serialize(sl1sif.verification_type_info_stack)
} else if (frame_type == 247) {
val sl1sife = as[SameLocals1StackItemFrameExtended](smf)
writeByte(frame_type)
writeShort(sl1sife.offset_delta)
serialize(sl1sife.verification_type_info_stack)
} else if (frame_type >= 248 && frame_type <= 250) {
writeByte(frame_type)
writeShort(as[ChopFrame](smf).offset_delta)
} else if (frame_type == 251) {
writeByte(frame_type)
writeShort(as[SameFrameExtended](smf).offset_delta)
} else if (frame_type >= 252 && frame_type <= 254) {
writeByte(frame_type)
val af = as[AppendFrame](smf)
writeShort(af.offset_delta)
af.verification_type_info_locals.foreach { serialize(_) }
} else if (frame_type == 255) {
val ff = as[FullFrame](smf)
writeByte(frame_type)
writeShort(ff.offset_delta)
writeShort(ff.verification_type_info_locals.length)
ff.verification_type_info_locals.foreach { serialize(_) }
writeShort(ff.verification_type_info_stack.length)
ff.verification_type_info_stack.foreach { serialize(_) }
} else {
throw new UnknownError(s"unknown stack map frame: $smf")
}
}
case a: ParametersAnnotations_attribute =>
// Handles:
// RuntimeVisibleParameterAnnotations_attribute
// RuntimeInvisibleParameterAnnotations_attribute
writeByte(a.parameters_annotations.length)
a.parameters_annotations.foreach { pas =>
writeShort(pas.size)
pas.foreach { serialize(_) }
}
case a: MethodParameters_attribute =>
writeByte(a.parameters.length)
a.parameters.foreach { p =>
writeShort(p.name_index)
writeShort(p.access_flags)
}
case a: BootstrapMethods_attribute =>
val length = a.bootstrap_methods.length
writeShort(length)
a.bootstrap_methods.foreach { bm =>
writeShort(bm.method_ref)
writeShort(bm.arguments.length)
bm.arguments.foreach { bma => writeShort(bma.cp_ref) }
}
case as: Annotations_attribute =>
// Handles:
// RuntimeVisibleAnnotations_attribute
// RuntimeInvisibleAnnotations_attribute
writeShort(as.annotations.size)
as.annotations.foreach { serialize(_) }
case a: LocalVariableTypeTable_attribute =>
val length = a.local_variable_type_table.size
writeShort(length)
a.local_variable_type_table.foreach { lvtte =>
writeShort(lvtte.start_pc)
writeShort(lvtte.length)
writeShort(lvtte.name_index)
writeShort(lvtte.signature_index)
writeShort(lvtte.index)
}
case a: LocalVariableTable_attribute =>
val length = a.local_variable_table.size
writeShort(length)
a.local_variable_table.foreach { lvte =>
writeShort(lvte.start_pc)
writeShort(lvte.length)
writeShort(lvte.name_index)
writeShort(lvte.descriptor_index)
writeShort(lvte.index)
}
case a: LineNumberTable_attribute =>
val length = a.line_number_table.size
writeShort(length)
a.line_number_table.foreach { lnte =>
writeShort(lnte.start_pc)
writeShort(lnte.line_number)
}
case e: EnclosingMethod_attribute =>
writeShort(e.class_index)
writeShort(e.method_index)
case c: Code_attribute =>
import c._
writeShort(max_stack)
writeShort(max_locals)
val code_length = code.instructions.length
writeInt(code_length)
out.write(code.instructions, 0, code_length)
writeShort(exceptionTable.length)
exceptionTable.foreach { ex =>
writeShort(ex.start_pc)
writeShort(ex.end_pc)
writeShort(ex.handler_pc)
writeShort(ex.catch_type)
}
writeShort(attributes.length)
attributes foreach { a => serialize(a) }
case e: Exceptions_attribute =>
writeShort(e.exception_index_table.size)
e.exception_index_table.foreach(writeShort)
case i: InnerClasses_attribute =>
import i._
writeShort(classes.size)
classes foreach { c =>
writeShort(c.inner_class_info_index)
writeShort(c.outer_class_info_index)
writeShort(c.inner_name_index)
writeShort(c.inner_class_access_flags)
}
case a: SourceDebugExtension_attribute =>
out.write(a.debug_extension, 0, attribute_length)
case a: AnnotationDefault_attribute => serialize(a.element_value)
case a: SourceFile_attribute => writeShort(a.sourceFile_index)
case a: Signature_attribute => writeShort(a.signature_index)
case a: ConstantValue_attribute => writeShort(a.constantValue_index)
case _: Deprecated_attribute => // nothing more to do
case _: Synthetic_attribute => // nothing more to do
case a: Module_attribute =>
writeShort(a.module_name_index)
writeShort(a.module_flags)
writeShort(a.module_version_index)
writeShort(a.requires.length)
a.requires foreach { r =>
writeShort(r.requires_index)
writeShort(r.requires_flags)
writeShort(r.requires_version_index)
}
writeShort(a.exports.length)
a.exports foreach { e =>
writeShort(e.exports_index)
writeShort(e.exports_flags)
writeShort(e.exports_to_index_table.length)
e.exports_to_index_table.foreach(writeShort)
}
writeShort(a.opens.length)
a.opens foreach { o =>
writeShort(o.opens_index)
writeShort(o.opens_flags)
writeShort(o.opens_to_index_table.length)
o.opens_to_index_table.foreach(writeShort)
}
writeShort(a.uses.length)
a.uses.foreach(writeShort)
writeShort(a.provides.length)
a.provides foreach { p =>
writeShort(p.provides_index)
writeShort(p.provides_with_index_table.length)
p.provides_with_index_table.foreach(writeShort)
}
case a: ModuleMainClass_attribute =>
writeShort(a.main_class_index)
case a: ModulePackages_attribute =>
writeShort(a.package_index_table.length)
a.package_index_table.foreach(writeShort)
case a: NestHost_attribute =>
writeShort(a.host_class_index)
case a: NestMembers_attribute =>
writeShort(a.classes_array.length)
a.classes_array.foreach(writeShort)
case a: Record_attribute =>
writeShort(a.components.length)
a.components foreach { c =>
writeShort(c.name_index)
writeShort(c.descriptor_index)
writeShort(c.attributes.length)
c.attributes foreach RichAttribute.write
}
case a: PermittedSubclasses_attribute =>
writeShort(a.permitted_subclasses.length)
a.permitted_subclasses.foreach(writeShort)
case a: Unknown_attribute => out.write(a.info, 0, a.info.length)
}
}
}
implicit object RichFieldInfo extends ClassFileElement[Field_Info] {
def write(
f: Field_Info
)(
implicit
out: DataOutputStream, segmentInformation: (String, Int) => Unit
): Unit = {
import f._
import out._
writeShort(access_flags)
writeShort(name_index)
writeShort(descriptor_index)
writeShort(attributes.size)
attributes.foreach(serializeAs[Attribute])
}
}
implicit object RichMethodInfo extends ClassFileElement[Method_Info] {
def write(
m: Method_Info
)(
implicit
out: DataOutputStream, segmentInformation: (String, Int) => Unit
): Unit = {
import m._
import out._
writeShort(access_flags)
writeShort(name_index)
writeShort(descriptor_index)
writeShort(attributes.size)
attributes.foreach(serializeAs[Attribute])
}
}
implicit object RichClassFile extends ClassFileElement[ClassFile] {
def write(
classFile: ClassFile
)(
implicit
out: DataOutputStream, segmentInformation: (String, Int) => Unit
): Unit = {
import classFile._
import out._
implicit val cp: Constant_Pool = classFile.constant_pool
writeInt(org.opalj.bi.ClassFileMagic)
writeShort(minor_version)
writeShort(major_version)
segmentInformation("ClassFileMetaInformation", out.size)
writeShort(cp.length)
// OLD cp.tail.filter(_ ne null).foreach(serialize(_))
val cpIt = cp.iterator
cpIt.next()
cpIt.filter(_ ne null).foreach(cpe => serialize(cpe))
segmentInformation("ConstantPool", out.size)
writeShort(access_flags)
segmentInformation("ClassAccessFlags", out.size)
writeShort(this_class)
writeShort(super_class)
writeShort(interfaces.size)
interfaces.foreach { writeShort }
segmentInformation("TypeInformation", out.size)
writeShort(fields.size)
fields.foreach { serializeAs[Field_Info] }
segmentInformation("Fields", out.size)
writeShort(methods.size)
methods foreach { m =>
serialize(m)
if ((ACC_STRICT.mask & m.access_flags) != 0 && (classFile.major_version < 46 || classFile.major_version > 60)) {
OPALLogger.warn("assembler", s"Writing out ACC_STRICT flag for a method in a classfile of version ${classFile.major_version}, which is not interpreted in class files of version < 46 or > 60")
}
segmentInformation("Method: "+cp(m.name_index).toString, out.size)
}
segmentInformation("Methods", out.size)
writeShort(attributes.size)
attributes foreach { serializeAs[Attribute] }
segmentInformation("ClassFileAttributes", out.size)
}
}
/**
* `serializeAs` makes it possible to specify the object type of the given parameter `t` and
* that type will then be used to pick up the implicit class file element value.
*/
def serializeAs[T](
t: AnyRef
)(
implicit
out: DataOutputStream,
segmentInformation: (String, Int) => Unit,
cfe: ClassFileElement[T]
): Unit = {
cfe.write(as[T](t))
}
/**
* @note You should use serialize if the concrete/required type of the given parameter is
* available/can be automatically inferred by the Scala compiler.
*/
def serialize[T: ClassFileElement](
t: T
)(
implicit
out: DataOutputStream,
segmentInformation: (String, Int) => Unit
): Unit = {
implicitly[ClassFileElement[T]].write(t)
}
/**
* @param segmentInformation A function that will be called back to provide information about
* the segment that was just written.
* This is particularly useful when debugging the serializer to determine which
* segments were successfully/completely written.
*/
def apply(
classFile: ClassFile,
segmentInformation: (String, Int) => Unit = (segmentInformation, bytesWritten) => ()
): Array[Byte] = {
val data = new ByteArrayOutputStream(classFile.size)
val out = new DataOutputStream(data)
serialize(classFile)(RichClassFile, out, segmentInformation)
out.flush()
data.toByteArray
}
}