java-class-tools
Version:
Read and write java class files in node or browser.
706 lines (551 loc) • 23.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _bytebuffer = _interopRequireDefault(require("bytebuffer"));
var _constantType = _interopRequireDefault(require("./constant-type"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
/**
* All objects (structure) MUST follow Jvm8 specification.
*
* @see https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html
*/
var JavaClassFileWriter =
/*#__PURE__*/
function () {
function JavaClassFileWriter() {
_classCallCheck(this, JavaClassFileWriter);
}
_createClass(JavaClassFileWriter, [{
key: "write",
/**
* Write ClassFile object to a ByteBuffer.
* The classFile object must follow this structure
* https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.1
*
* @param {Object} classFile - The classFile object.
* @return {ByteBuffer}
*/
value: function write(classFile) {
this.buf = new _bytebuffer["default"]();
this.classFile = classFile;
this.buf.writeUint8(0xCA);
this.buf.writeUint8(0xFE);
this.buf.writeUint8(0xBA);
this.buf.writeUint8(0xBE);
this.buf.writeUint16(classFile.minor_version);
this.buf.writeUint16(classFile.major_version);
this.buf.writeUint16(classFile.constant_pool_count);
this._writeConstantPool(classFile.constant_pool);
this.buf.writeUint16(classFile.access_flags);
this.buf.writeUint16(classFile.this_class);
this.buf.writeUint16(classFile.super_class);
this.buf.writeUint16(classFile.interfaces_count);
this._writeInterfaces(classFile.interfaces);
this.buf.writeUint16(classFile.fields_count);
this._writeMemberInfoArray(classFile.fields);
this.buf.writeUint16(classFile.methods_count);
this._writeMemberInfoArray(classFile.methods);
this.buf.writeUint16(classFile.attributes_count);
this._writeAttributeInfoArray(classFile.attributes);
this.buf.buffer = this.buf.buffer.slice(0, this.buf.offset);
return this.buf;
}
}, {
key: "_writeMemberInfoArray",
value: function _writeMemberInfoArray(array) {
for (var i = 0; i < array.length; i++) {
var entry = array[i];
this.buf.writeUint16(entry.access_flags);
this.buf.writeUint16(entry.name_index);
this.buf.writeUint16(entry.descriptor_index);
this.buf.writeUint16(entry.attributes_count);
this._writeAttributeInfoArray(entry.attributes);
}
}
}, {
key: "_writeAttributeInfoArray",
value: function _writeAttributeInfoArray(attributes) {
for (var i = 0; i < attributes.length; i++) {
this._writeAttributeInfo(attributes[i]);
}
}
}, {
key: "_writeAttributeAnnotation",
value: function _writeAttributeAnnotation(annotation) {
this.buf.writeUint16(annotation.type_index);
this.buf.writeUint16(annotation.num_element_value_pairs);
for (var i = 0; i < annotation.num_element_value_pairs; i++) {
var element = annotation.element_value_pairs[i];
this.buf.writeUint16(element.element_name_index);
this._writeElementValue(element.element_value);
}
}
}, {
key: "_writeElementValue",
value: function _writeElementValue(element_value) {
this.buf.writeUint8(element_value.tag);
var value = element_value.value;
switch (element_value.tag) {
case 101:
// e
this.buf.writeUint16(value.enum_const_value.type_name_index);
this.buf.writeUint16(value.enum_const_value.const_name_index);
break;
case 99:
// c
this.buf.writeUint16(value.class_info_index);
break;
case 91:
// [
this.buf.writeUint16(value.array_value.num_values);
for (var i = 0; i < value.array_value.num_values; i++) {
this._writeElementValue(value.array_value.values[i]);
}
break;
case 64:
this._writeAttributeAnnotation(value.annotation);
break;
case 66: // B
case 67: // C
case 68: // D
case 70: // F
case 73: // I
case 74: // J
case 83: // S
case 90: // Z
case 115:
// s
this.buf.writeUint16(value.const_value_index);
break;
default:
throw Error("Unexpected tag: ".concat(element_value.tag));
}
}
}, {
key: "_writeTypeAnnotation",
value: function _writeTypeAnnotation(type_annotation) {
this.buf.writeUint8(type_annotation.target_type);
switch (type_annotation.target_type) {
// type_parameter_target
case 0x00:
case 0x01:
this.buf.writeUint8(type_annotation.target_info.type_parameter_index);
break;
// supertype_target
case 0x10:
this.buf.writeUint16(type_annotation.target_info.supertype_index);
break;
// type_parameter_bound_target
case 0x11:
case 0x12:
this.buf.writeUint8(type_annotation.target_info.type_parameter_index);
this.buf.writeUint8(type_annotation.target_info.bound_index);
break;
// empty_target
case 0x13:
case 0x14:
case 0x15:
break;
// formal_parameter_target
case 0x16:
this.buf.writeUint8(type_annotation.target_info.formal_parameter_index);
break;
// throws_target
case 0x17:
this.buf.writeUint16(type_annotation.target_info.throws_type_index);
break;
// localvar_target
case 0x40:
case 0x41:
this.buf.writeUint16(type_annotation.target_info.table_length);
for (var i = 0; i < type_annotation.target_info.table_length; i++) {
var table_entry = type_annotation.target_info.table[i];
this.buf.writeUint16(table_entry.start_pc);
this.buf.writeUint16(table_entry.length);
this.buf.writeUint16(table_entry.index);
}
break;
// catch_target
case 0x42:
this.buf.writeUint16(type_annotation.target_info.exception_table_index);
break;
// offset_target
case 0x43:
case 0x44:
case 0x45:
case 0x46:
this.buf.writeUint16(type_annotation.target_info.offset);
break;
// type_argument_target
case 0x47:
case 0x48:
case 0x49:
case 0x4A:
case 0x4B:
this.buf.writeUint16(type_annotation.target_info.offset);
this.buf.writeUint8(type_annotation.target_info.type_argument_index);
break;
default:
throw Error("Unexpected target_type: ".concat(type_annotation.target_type));
}
this.buf.writeUint8(type_annotation.type_path.path_length);
for (var i = 0; i < type_annotation.type_path.path_length; i++) {
var path_entry = type_annotation.type_path.path[i];
this.buf.writeUint8(path_entry.type_path_kind);
this.buf.writeUint8(path_entry.type_argument_index);
}
this.buf.writeUint16(type_annotation.type_index);
this.buf.writeUint16(type_annotation.num_element_value_pairs);
for (var i = 0; i < type_annotation.num_element_value_pairs; i++) {
var element_value_entry = type_annotation.element_value_pairs[i];
this.buf.writeUint16(element_value_entry.element_name_index);
this._writeElementValue(element_value_entry.element_value);
}
}
}, {
key: "_writeAttributeInfo",
value: function _writeAttributeInfo(attribute) {
this.buf.writeUint16(attribute.attribute_name_index);
this.buf.writeUint32(attribute.attribute_length);
var attributeNameBytes = this.classFile.constant_pool[attribute.attribute_name_index].bytes;
var attributeName = String.fromCharCode.apply(null, attributeNameBytes); // TODO: is this safe?
switch (attributeName) {
case 'RuntimeInvisibleAnnotations':
case 'RuntimeVisibleAnnotations':
this.buf.writeUint16(attribute.num_annotations);
for (var i = 0; i < attribute.num_annotations; i++) {
this._writeAttributeAnnotation(attribute.annotations[i]);
}
break;
// There's no additional information
case 'Deprecated':
case 'Synthetic':
break;
case 'InnerClasses':
this.buf.writeUint16(attribute.number_of_classes);
for (var i = 0; i < attribute.number_of_classes; i++) {
var inner_class = attribute.classes[i];
this.buf.writeUint16(inner_class.inner_class_info_index);
this.buf.writeUint16(inner_class.outer_class_info_index);
this.buf.writeUint16(inner_class.inner_name_index);
this.buf.writeUint16(inner_class.inner_class_access_flags);
}
break;
case 'LocalVariableTable':
this.buf.writeUint16(attribute.local_variable_table_length);
for (var i = 0; i < attribute.local_variable_table_length; i++) {
var local_variable = attribute.local_variable_table[i];
this.buf.writeUint16(local_variable.start_pc);
this.buf.writeUint16(local_variable.length);
this.buf.writeUint16(local_variable.name_index);
this.buf.writeUint16(local_variable.descriptor_index);
this.buf.writeUint16(local_variable.index);
}
break;
case 'LocalVariableTypeTable':
this.buf.writeUint16(attribute.local_variable_type_table_length);
for (var i = 0; i < attribute.local_variable_type_table_length; i++) {
var local_variable_type = attribute.local_variable_type_table[i];
this.buf.writeUint16(local_variable_type.start_pc);
this.buf.writeUint16(local_variable_type.length);
this.buf.writeUint16(local_variable_type.name_index);
this.buf.writeUint16(local_variable_type.signature_index);
this.buf.writeUint16(local_variable_type.index);
}
break;
case 'RuntimeInvisibleParameterAnnotations':
case 'RuntimeVisibleParameterAnnotations':
this.buf.writeUint8(attribute.num_parameters);
for (var i = 0; i < attribute.num_parameters; i++) {
var parameter_annotation = attribute.parameter_annotations[i];
this.buf.writeUint16(parameter_annotation.num_annotations);
for (var j = 0; j < parameter_annotation.num_annotations; j++) {
this._writeAttributeAnnotation(parameter_annotation.annotations[j]);
}
}
break;
case 'BootstrapMethods':
this.buf.writeUint16(attribute.num_bootstrap_methods);
for (var i = 0; i < attribute.num_bootstrap_methods; i++) {
var bootstrap_method = attribute.bootstrap_methods[i];
this.buf.writeUint16(bootstrap_method.bootstrap_method_ref);
this.buf.writeUint16(bootstrap_method.num_bootstrap_arguments);
for (var j = 0; j < bootstrap_method.num_bootstrap_arguments; j++) {
this.buf.writeUint16(bootstrap_method.bootstrap_arguments[j]);
}
}
break;
case 'RuntimeInvisibleTypeAnnotations':
case 'RuntimeVisibleTypeAnnotations':
this.buf.writeUint16(attribute.num_annotations);
for (var i = 0; i < attribute.num_annotations; i++) {
this._writeTypeAnnotation(attribute.annotations[i]);
}
break;
case 'SourceDebugExtension':
for (var i = 0; i < attribute.attribute_length; i++) {
this.buf.writeUint8(attribute.debug_extension[i]);
}
break;
case 'SourceFile':
this.buf.writeUint16(attribute.sourcefile_index);
break;
case 'EnclosingMethod':
this.buf.writeUint16(attribute.class_index);
this.buf.writeUint16(attribute.method_index);
break;
case 'AnnotationDefault':
this._writeElementValue(attribute.default_value);
break;
case 'MethodParameters':
this.buf.writeUint8(attribute.parameters_count);
for (var i = 0; i < attribute.parameters_count; i++) {
var parameter = attribute.parameters[i];
this.buf.writeUint16(parameter.name_index);
this.buf.writeUint16(parameter.access_flags);
}
break;
case 'Exceptions':
this.buf.writeUint16(attribute.number_of_exceptions);
for (var i = 0; i < attribute.number_of_exceptions; i++) {
this.buf.writeUint16(attribute.exception_index_table[i]);
}
break;
case 'ConstantValue':
this.buf.writeUint16(attribute.constantvalue_index);
break;
case 'Signature':
this.buf.writeUint16(attribute.signature_index);
break;
case 'StackMapTable':
{
this.buf.writeUint16(attribute.number_of_entries);
for (var i = 0; i < attribute.number_of_entries; i++) {
var stack_map_frame = attribute.entries[i];
var frame_type = stack_map_frame.frame_type;
this.buf.writeUint8(frame_type); // SAME_LOCALS_1_STACK_ITEM
if (frame_type >= 64 && frame_type <= 127) {
this._writeVerificationTypeInfo(stack_map_frame.stack[0]);
} // SAME_LOCALS_1_STACK_ITEM_EXTENDED
else if (stack_map_frame.frame_type === 247) {
this.buf.writeUint16(stack_map_frame.offset_delta);
this._writeVerificationTypeInfo(stack_map_frame.stack[0]);
} // CHOP = 248-250, SAME_FRAME_EXTENDED = 251
else if (frame_type >= 248 && frame_type <= 251) {
this.buf.writeUint16(stack_map_frame.offset_delta);
} // APPEND
else if (frame_type >= 252 && frame_type <= 254) {
this.buf.writeUint16(stack_map_frame.offset_delta);
var number_of_locals = frame_type - 251;
for (var j = 0; j < number_of_locals; j++) {
this._writeVerificationTypeInfo(stack_map_frame.locals[j]);
}
} // FULL_FRAME
else if (frame_type === 255) {
this.buf.writeUint16(stack_map_frame.offset_delta);
this.buf.writeUint16(stack_map_frame.number_of_locals);
for (var j = 0; j < stack_map_frame.number_of_locals; j++) {
this._writeVerificationTypeInfo(stack_map_frame.locals[j]);
}
this.buf.writeUint16(stack_map_frame.number_of_stack_items);
for (var j = 0; j < stack_map_frame.number_of_stack_items; j++) {
this._writeVerificationTypeInfo(stack_map_frame.stack[j]);
}
}
}
break;
}
case 'Code':
this.buf.writeUint16(attribute.max_stack);
this.buf.writeUint16(attribute.max_locals);
this.buf.writeUint32(attribute.code_length);
for (var i = 0; i < attribute.code_length; i++) {
this.buf.writeUint8(attribute.code[i]);
}
this.buf.writeUint16(attribute.exception_table_length);
for (var i = 0; i < attribute.exception_table_length; i++) {
var exception_entry = attribute.exception_table[i];
this.buf.writeUint16(exception_entry.start_pc);
this.buf.writeUint16(exception_entry.end_pc);
this.buf.writeUint16(exception_entry.handler_pc);
this.buf.writeUint16(exception_entry.catch_type);
}
this.buf.writeUint16(attribute.attributes_count);
this._writeAttributeInfoArray(attribute.attributes);
break;
case 'LineNumberTable':
this.buf.writeUint16(attribute.line_number_table_length);
for (var i = 0; i < attribute.line_number_table_length; i++) {
var line_number = attribute.line_number_table[i];
this.buf.writeUint16(line_number.start_pc);
this.buf.writeUint16(line_number.line_number);
}
break;
case 'Module':
{
this.buf.writeUint16(attribute.module_name_index);
this.buf.writeUint16(attribute.module_flags);
this.buf.writeUint16(attribute.module_version_index);
this.buf.writeUint16(attribute.requires_count);
for (var i = 0; i < attribute.requires_count; i++) {
var entry = attribute.requires[i];
this.buf.writeUint16(entry.requires_index);
this.buf.writeUint16(entry.requires_flags);
this.buf.writeUint16(entry.requires_version_index);
}
this.buf.writeUint16(attribute.exports_count);
for (var i = 0; i < attribute.exports_count; i++) {
var _entry = attribute.exports[i];
this.buf.writeUint16(_entry.exports_index);
this.buf.writeUint16(_entry.exports_flags);
this.buf.writeUint16(_entry.exports_to_count);
for (var j = 0; j < _entry.exports_to_count; j++) {
this.buf.writeUint16(_entry.exports_to_index[j]);
}
}
this.buf.writeUint16(attribute.opens_count);
for (var i = 0; i < attribute.opens_count; i++) {
var _entry2 = attribute.opens[i];
this.buf.writeUint16(_entry2.opens_index);
this.buf.writeUint16(_entry2.opens_flags);
this.buf.writeUint16(_entry2.opens_to_count);
for (var j = 0; j < _entry2.opens_to_count; j++) {
this.buf.writeUint16(_entry2.opens_to_index[j]);
}
}
this.buf.writeUint16(attribute.uses_count);
for (var i = 0; i < attribute.uses_count; i++) {
this.buf.writeUint16(attribute.uses_index[i]);
}
this.buf.writeUint16(attribute.provides_count);
for (var i = 0; i < attribute.provides_count; i++) {
var _entry3 = attribute.provides[i];
this.buf.writeUint16(_entry3.provides_index);
this.buf.writeUint16(_entry3.provides_with_count);
for (var j = 0; j < _entry3.provides_with_count; j++) {
this.buf.writeUint16(_entry3.provides_with_index[j]);
}
}
break;
}
case 'ModulePackages':
this.buf.writeUint16(attribute.package_count);
for (var i = 0; i < attribute.package_count; i++) {
this.buf.writeUint16(attribute.package_index[i]);
}
break;
case 'ModuleMainClass':
this.buf.writeUint16(attribute.main_class_index);
break;
case 'NestHost':
this.buf.writeUint16(attribute.host_class_index);
break;
case 'NestMembers':
this.buf.writeUint16(attribute.number_of_classes);
for (var _i = 0; _i < attribute.number_of_classes; _i++) {
this.buf.writeUint16(attribute.classes[_i]);
}
break;
// Unknown attributes
// See: https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-4.html#jvms-4.7.1
default:
{
for (var i = 0; i < attribute.attribute_length; i++) {
this.buf.writeUint8(attribute.info[i]);
}
}
}
}
}, {
key: "_writeVerificationTypeInfo",
value: function _writeVerificationTypeInfo(type_info) {
this.buf.writeUint8(type_info.tag);
if (type_info.tag === 7) {
this.buf.writeUint16(type_info.cpool_index);
} else if (type_info.tag === 8) {
this.buf.writeUint16(type_info.offset);
}
}
}, {
key: "_writeInterfaces",
value: function _writeInterfaces(interfaces) {
for (var i = 0; i < interfaces.length; i++) {
this.buf.writeUint16(interfaces[i]);
}
}
}, {
key: "_writeConstantPool",
value: function _writeConstantPool(constant_pool) {
for (var i = 1; i < constant_pool.length; i++) {
var entry = constant_pool[i];
if (entry !== undefined) {
this._writeConstantPoolEntry(entry);
}
}
}
}, {
key: "_writeConstantPoolEntry",
value: function _writeConstantPoolEntry(entry) {
this.buf.writeUint8(entry.tag);
switch (entry.tag) {
case _constantType["default"].UTF8:
{
this.buf.writeUint16(entry.length);
for (var i = 0; i < entry.length; i++) {
this.buf.writeUint8(entry.bytes[i]);
}
break;
}
case _constantType["default"].INTEGER:
case _constantType["default"].FLOAT:
this.buf.writeUint32(entry.bytes);
break;
case _constantType["default"].LONG:
case _constantType["default"].DOUBLE:
this.buf.writeUint32(entry.high_bytes);
this.buf.writeUint32(entry.low_bytes);
break;
case _constantType["default"].PACKAGE:
case _constantType["default"].MODULE:
case _constantType["default"].CLASS:
this.buf.writeUint16(entry.name_index);
break;
case _constantType["default"].STRING:
this.buf.writeUint16(entry.string_index);
break;
case _constantType["default"].FIELDREF:
case _constantType["default"].METHODREF:
case _constantType["default"].INTERFACE_METHODREF:
this.buf.writeUint16(entry.class_index);
this.buf.writeUint16(entry.name_and_type_index);
break;
case _constantType["default"].NAME_AND_TYPE:
this.buf.writeUint16(entry.name_index);
this.buf.writeUint16(entry.descriptor_index);
break;
case _constantType["default"].METHOD_HANDLE:
this.buf.writeUint8(entry.reference_kind);
this.buf.writeUint16(entry.reference_index);
break;
case _constantType["default"].METHOD_TYPE:
this.buf.writeUint16(entry.descriptor_index);
break;
case _constantType["default"].DYNAMIC:
case _constantType["default"].INVOKE_DYNAMIC:
this.buf.writeUint16(entry.bootstrap_method_attr_index);
this.buf.writeUint16(entry.name_and_type_index);
break;
default:
throw Error("Unexpected tag: ".concat(entry.tag));
}
}
}]);
return JavaClassFileWriter;
}();
var _default = JavaClassFileWriter;
exports["default"] = _default;