// Write an annotation attribute.  The VM stores them in raw form, so all we need
// to do is add the attrubute name and fill in the length.
// JSR202|   *Annotations_attribute {
// JSR202|     u2 attribute_name_index;
// JSR202|     u4 attribute_length;
// JSR202|     ...
// JSR202|   }
void JvmtiClassFileReconstituter::write_annotations_attribute(const char* attr_name,
                                                              AnnotationArray* annos) {
  u4 length = annos->length();
  write_attribute_name_index(attr_name);
  write_u4(length);
  memcpy(writeable_address(length), annos->adr_at(0), length);
}
// Write an annotation attribute.  The VM stores them in raw form, so all we need
// to do is add the attrubute name and fill in the length.
// JSR202|   *Annotations_attribute {
// JSR202|     u2 attribute_name_index;
// JSR202|     u4 attribute_length;
// JSR202|     ...
// JSR202|   }
void JvmtiClassFileReconstituter::write_annotations_attribute(const char* attr_name,
                                                              typeArrayHandle annos) {
  u4 length = annos->length();
  write_attribute_name_index(attr_name);
  write_u4(length);
  memcpy(writeable_address(length), annos->byte_at_addr(0), length);
}
// Write stack map table attribute
// JSR-202|   StackMapTable_attribute {
// JSR-202|     u2 attribute_name_index;
// JSR-202|     u4 attribute_length;
// JSR-202|     u2 number_of_entries;
// JSR-202|     stack_map_frame_entries[number_of_entries];
// JSR-202|   }
void JvmtiClassFileReconstituter::write_stackmap_table_attribute(methodHandle method,
                                                                 int stackmap_len) {

  write_attribute_name_index("StackMapTable");
  write_u4(stackmap_len);
  memcpy(
    writeable_address(stackmap_len),
    (void*)(method->stackmap_data()->adr_at(0)),
    stackmap_len);
}
void JvmtiClassFileReconstituter::write_class_file_format() {
  ReallocMark();

  // JVMSpec|   ClassFile {
  // JVMSpec|           u4 magic;
  write_u4(0xCAFEBABE);

  // JVMSpec|           u2 minor_version;
  // JVMSpec|           u2 major_version;
  write_u2(ikh()->minor_version());
  u2 major = ikh()->major_version();
  write_u2(major);

  // JVMSpec|           u2 constant_pool_count;
  // JVMSpec|           cp_info constant_pool[constant_pool_count-1];
  write_u2(cpool()->length());
  copy_cpool_bytes(writeable_address(cpool_size()));

  // JVMSpec|           u2 access_flags;
  write_u2(ikh()->access_flags().get_flags() & JVM_RECOGNIZED_CLASS_MODIFIERS);

  // JVMSpec|           u2 this_class;
  // JVMSpec|           u2 super_class;
  write_u2(class_symbol_to_cpool_index(ikh()->name()));
  Klass* super_class = ikh()->super();
  write_u2(super_class == NULL? 0 :  // zero for java.lang.Object
                class_symbol_to_cpool_index(super_class->name()));

  // JVMSpec|           u2 interfaces_count;
  // JVMSpec|           u2 interfaces[interfaces_count];
  Array<Klass*>* interfaces =  ikh()->local_interfaces();
  int num_interfaces = interfaces->length();
  write_u2(num_interfaces);
  for (int index = 0; index < num_interfaces; index++) {
    HandleMark hm(thread());
    instanceKlassHandle iikh(thread(), interfaces->at(index));
    write_u2(class_symbol_to_cpool_index(iikh->name()));
  }

  // JVMSpec|           u2 fields_count;
  // JVMSpec|           field_info fields[fields_count];
  write_field_infos();

  // JVMSpec|           u2 methods_count;
  // JVMSpec|           method_info methods[methods_count];
  write_method_infos();

  // JVMSpec|           u2 attributes_count;
  // JVMSpec|           attribute_info attributes[attributes_count];
  // JVMSpec|   } /* end ClassFile 8?
  write_class_attributes();
}
void JvmtiClassFileReconstituter::write_u8(u8 x) {
  Bytes::put_Java_u8(writeable_address(8), x);
}
void JvmtiClassFileReconstituter::write_u4(u4 x) {
  Bytes::put_Java_u4(writeable_address(4), x);
}
void JvmtiClassFileReconstituter::write_u2(u2 x) {
  Bytes::put_Java_u2(writeable_address(2), x);
}
void JvmtiClassFileReconstituter::write_u1(u1 x) {
  *writeable_address(1) = x;
}
// Write Code attribute
// JVMSpec|   Code_attribute {
// JVMSpec|     u2 attribute_name_index;
// JVMSpec|     u4 attribute_length;
// JVMSpec|     u2 max_stack;
// JVMSpec|     u2 max_locals;
// JVMSpec|     u4 code_length;
// JVMSpec|     u1 code[code_length];
// JVMSpec|     u2 exception_table_length;
// JVMSpec|     {       u2 start_pc;
// JVMSpec|             u2 end_pc;
// JVMSpec|             u2  handler_pc;
// JVMSpec|             u2  catch_type;
// JVMSpec|     }       exception_table[exception_table_length];
// JVMSpec|     u2 attributes_count;
// JVMSpec|     attribute_info attributes[attributes_count];
// JVMSpec|   }
void JvmtiClassFileReconstituter::write_code_attribute(methodHandle method) {
  ConstMethod* const_method = method->constMethod();
  u2 line_num_cnt = 0;
  int stackmap_len = 0;
  int local_variable_table_length = 0;
  int local_variable_type_table_length = 0;

  // compute number and length of attributes
  int attr_count = 0;
  int attr_size = 0;
  if (const_method->has_linenumber_table()) {
    line_num_cnt = line_number_table_entries(method);
    if (line_num_cnt != 0) {
      ++attr_count;
      // Compute the complete size of the line number table attribute:
      //      LineNumberTable_attribute {
      //        u2 attribute_name_index;
      //        u4 attribute_length;
      //        u2 line_number_table_length;
      //        {  u2 start_pc;
      //           u2 line_number;
      //        } line_number_table[line_number_table_length];
      //      }
      attr_size += 2 + 4 + 2 + line_num_cnt * (2 + 2);
    }
  }
  if (method->has_stackmap_table()) {
    stackmap_len = method->stackmap_data()->length();
    if (stackmap_len != 0) {
      ++attr_count;
      // Compute the  size of the stack map table attribute (VM stores raw):
      //      StackMapTable_attribute {
      //        u2 attribute_name_index;
      //        u4 attribute_length;
      //        u2 number_of_entries;
      //        stack_map_frame_entries[number_of_entries];
      //      }
      attr_size += 2 + 4 + stackmap_len;
    }
  }
  if (method->has_localvariable_table()) {
    local_variable_table_length = method->localvariable_table_length();
    if (local_variable_table_length != 0) {
      ++attr_count;
      // Compute the size of the local variable table attribute (VM stores raw):
      // LocalVariableTable_attribute {
      //   u2 attribute_name_index;
      //   u4 attribute_length;
      //   u2 local_variable_table_length;
      //   {
      //     u2 start_pc;
      //     u2 length;
      //     u2 name_index;
      //     u2 descriptor_index;
      //     u2 index;
      //   }
      attr_size += 2 + 4 + 2 + local_variable_table_length * (2 + 2 + 2 + 2 + 2);

      // Local variables with generic signatures must have LVTT entries
      LocalVariableTableElement *elem = method->localvariable_table_start();
      for (int idx = 0; idx < local_variable_table_length; idx++) {
        if (elem[idx].signature_cp_index != 0) {
          local_variable_type_table_length++;
        }
      }

      if (local_variable_type_table_length != 0) {
        ++attr_count;
        // Compute the size of the local variable type table attribute (VM stores raw):
        // LocalVariableTypeTable_attribute {
        //   u2 attribute_name_index;
        //   u4 attribute_length;
        //   u2 local_variable_type_table_length;
        //   {
        //     u2 start_pc;
        //     u2 length;
        //     u2 name_index;
        //     u2 signature_index;
        //     u2 index;
        //   }
        attr_size += 2 + 4 + 2 + local_variable_type_table_length * (2 + 2 + 2 + 2 + 2);
      }
    }
  }

  ExceptionTable exception_table(method());
  int exception_table_length = exception_table.length();
  int code_size = const_method->code_size();
  int size =
    2+2+4 +                                // max_stack, max_locals, code_length
    code_size +                            // code
    2 +                                    // exception_table_length
    (2+2+2+2) * exception_table_length +   // exception_table
    2 +                                    // attributes_count
    attr_size;                             // attributes

  write_attribute_name_index("Code");
  write_u4(size);
  write_u2(method->verifier_max_stack());
  write_u2(method->max_locals());
  write_u4(code_size);
  copy_bytecodes(method, (unsigned char*)writeable_address(code_size));
  write_u2(exception_table_length);
  for (int index = 0; index < exception_table_length; index++) {
    write_u2(exception_table.start_pc(index));
    write_u2(exception_table.end_pc(index));
    write_u2(exception_table.handler_pc(index));
    write_u2(exception_table.catch_type_index(index));
  }
  write_u2(attr_count);
  if (line_num_cnt != 0) {
    write_line_number_table_attribute(method, line_num_cnt);
  }
  if (stackmap_len != 0) {
    write_stackmap_table_attribute(method, stackmap_len);
  }
  if (local_variable_table_length != 0) {
    write_local_variable_table_attribute(method, local_variable_table_length);
  }
  if (local_variable_type_table_length != 0) {
    write_local_variable_type_table_attribute(method, local_variable_type_table_length);
  }
}
// Write Code attribute
// JVMSpec|   Code_attribute {
// JVMSpec|   	u2 attribute_name_index;
// JVMSpec|   	u4 attribute_length;
// JVMSpec|   	u2 max_stack;
// JVMSpec|   	u2 max_locals;
// JVMSpec|   	u4 code_length;
// JVMSpec|   	u1 code[code_length];
// JVMSpec|   	u2 exception_table_length;
// JVMSpec|   	{    	u2 start_pc;
// JVMSpec|   	      	u2 end_pc;
// JVMSpec|   	      	u2  handler_pc;
// JVMSpec|   	      	u2  catch_type;
// JVMSpec|   	}	exception_table[exception_table_length];
// JVMSpec|   	u2 attributes_count;
// JVMSpec|   	attribute_info attributes[attributes_count];
// JVMSpec|   }
void JvmtiClassFileReconstituter::write_code_attribute(methodHandle method) {
  constMethodHandle const_method(thread(), method->constMethod());
  u2 line_num_cnt = 0;
  int stackmap_len = 0;

  // compute number and length of attributes -- FIXME: for now no LVT
  int attr_count = 0;
  int attr_size = 0;
  if (const_method->has_linenumber_table()) {
    line_num_cnt = line_number_table_entries(method);  
    if (line_num_cnt != 0) {
      ++attr_count;
      // Compute the complete size of the line number table attribute:
      //      LineNumberTable_attribute {
      //      	u2 attribute_name_index;
      //      	u4 attribute_length;
      //      	u2 line_number_table_length;
      //      	{  u2 start_pc;	     
      //      	   u2 line_number;	     
      //      	} line_number_table[line_number_table_length];
      //      }
      attr_size += 2 + 4 + 2 + line_num_cnt * (2 + 2);
    }
  }
  if (method->has_stackmap_table()) {
    stackmap_len = method->stackmap_data()->length();
    if (stackmap_len != 0) {
      ++attr_count;
      // Compute the  size of the stack map table attribute (VM stores raw):
      //      StackMapTable_attribute {
      //      	u2 attribute_name_index;
      //      	u4 attribute_length;
      //      	u2 number_of_entries;
      //      	stack_map_frame_entries[number_of_entries];
      //      }
      attr_size += 2 + 4 + stackmap_len;
    }
  }

  typeArrayHandle exception_table(thread(), const_method->exception_table());
  int exception_table_length = exception_table->length();
  int exception_table_entries = exception_table_length / 4;
  int code_size = const_method->code_size();
  int size = 
    2+2+4 +                                // max_stack, max_locals, code_length
    code_size +                            // code
    2 +                                    // exception_table_length
    (2+2+2+2) * exception_table_entries +  // exception_table
    2 +                                    // attributes_count
    attr_size;                             // attributes

  write_attribute_name_index("Code");
  write_u4(size);
  write_u2(method->max_stack());
  write_u2(method->max_locals());
  write_u4(code_size);
  copy_bytecodes(method, (unsigned char*)writeable_address(code_size));
  write_u2(exception_table_entries);
  for (int index = 0; index < exception_table_length; ) {
    write_u2(exception_table->int_at(index++));
    write_u2(exception_table->int_at(index++));
    write_u2(exception_table->int_at(index++));
    write_u2(exception_table->int_at(index++));
  }
  write_u2(attr_count);
  if (line_num_cnt != 0) {
    write_line_number_table_attribute(method, line_num_cnt);
  }
  if (stackmap_len != 0) {
    write_stackmap_table_attribute(method, stackmap_len);
  }

  // FIXME: write LVT attribute
}