001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * https://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 020package org.apache.bcel.classfile; 021 022import java.io.DataInput; 023import java.io.DataOutputStream; 024import java.io.IOException; 025import java.util.Arrays; 026 027import org.apache.bcel.Const; 028import org.apache.commons.lang3.SystemProperties; 029 030/** 031 * This class represents a reference to an unknown (that is, application-specific) attribute of a class. It is instantiated 032 * from the {@link Attribute#readAttribute(java.io.DataInput, ConstantPool)} method. Applications that need to read in 033 * application-specific attributes should create an {@link UnknownAttributeReader} implementation and attach it via 034 * {@link Attribute#addAttributeReader(String, UnknownAttributeReader)}. 035 * 036 * @see Attribute 037 * @see UnknownAttributeReader 038 */ 039public final class Unknown extends Attribute { 040 041 /** 042 * Arbitrary to limit the maximum length of unknown attributes to avoid OOM errors. 043 */ 044 static final int MAX_LEN = 1_000_000; 045 046 private static final String MAX_LEN_PROP = "BCEL.Attribute.Unknown.max_attribute_length"; 047 048 private byte[] bytes; 049 050 private final String name; 051 052 /** 053 * Constructs a new instance for a non-standard attribute. 054 * 055 * @param nameIndex Index in constant pool. 056 * @param length Content length in bytes. 057 * @param bytes Attribute contents. 058 * @param constantPool Array of constants. 059 */ 060 public Unknown(final int nameIndex, final int length, final byte[] bytes, final ConstantPool constantPool) { 061 super(Const.ATTR_UNKNOWN, nameIndex, length, constantPool); 062 this.bytes = bytes; 063 this.name = constantPool.getConstantUtf8(nameIndex).getBytes(); 064 } 065 066 /** 067 * Constructs a new instance from a DataInput. 068 * <p> 069 * The size of an <a href="https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-4.html#jvms-4.7">Attribute</a> unknown to the JVM specification is 070 * limited to 1 MB and is overridden with the system property {@code BCEL.Attribute.Unknown.max_attribute_length}. 071 * </p> 072 * 073 * @param nameIndex Index in constant pool. 074 * @param length Content length in bytes. 075 * @param input Input stream. 076 * @param constantPool Array of constants. 077 * @throws IOException if an I/O error occurs. 078 * @see <a href="https://docs.oracle.com/javase/specs/jvms/se25/html/jvms-4.html#jvms-4.7">Attribute</a> 079 */ 080 Unknown(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool) throws IOException { 081 this(nameIndex, length, (byte[]) null, constantPool); 082 if (length > 0) { 083 if (length > SystemProperties.getInt(MAX_LEN_PROP, () -> MAX_LEN)) { 084 throw new IOException( 085 String.format("Unknown attribute length %,d > %,d; use the %s system property to increase the limit.", length, MAX_LEN, MAX_LEN_PROP)); 086 } 087 bytes = new byte[length]; 088 input.readFully(bytes); 089 } 090 } 091 092 /** 093 * Constructs a new instance from another instance. Note that both objects use the same references (shallow copy). Use clone() for a physical copy. 094 * 095 * @param unknown Source. 096 */ 097 public Unknown(final Unknown unknown) { 098 this(unknown.getNameIndex(), unknown.getLength(), unknown.getBytes(), unknown.getConstantPool()); 099 } 100 101 /** 102 * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class. 103 * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects. 104 * 105 * @param v Visitor object. 106 */ 107 @Override 108 public void accept(final Visitor v) { 109 v.visitUnknown(this); 110 } 111 112 /** 113 * @return deep copy of this attribute. 114 */ 115 @Override 116 public Attribute copy(final ConstantPool constantPool) { 117 final Unknown c = (Unknown) clone(); 118 if (bytes != null) { 119 c.bytes = bytes.clone(); 120 } 121 c.setConstantPool(constantPool); 122 return c; 123 } 124 125 /** 126 * Dumps unknown bytes to file stream. 127 * 128 * @param file Output file stream. 129 * @throws IOException if an I/O error occurs. 130 */ 131 @Override 132 public void dump(final DataOutputStream file) throws IOException { 133 super.dump(file); 134 if (super.getLength() > 0) { 135 file.write(bytes, 0, super.getLength()); 136 } 137 } 138 139 /** 140 * @return data bytes. 141 */ 142 public byte[] getBytes() { 143 return bytes; 144 } 145 146 /** 147 * @return name of attribute. 148 */ 149 @Override 150 public String getName() { 151 return name; 152 } 153 154 /** 155 * @param bytes the bytes to set. 156 */ 157 public void setBytes(final byte[] bytes) { 158 this.bytes = bytes; 159 } 160 161 /** 162 * @return String representation. 163 */ 164 @Override 165 public String toString() { 166 if (super.getLength() == 0 || bytes == null) { 167 return "(Unknown attribute " + name + ")"; 168 } 169 final String hex; 170 final int limit = 10; 171 if (super.getLength() > limit) { 172 final byte[] tmp = Arrays.copyOf(bytes, limit); 173 hex = Utility.toHexString(tmp) + "... (truncated)"; 174 } else { 175 hex = Utility.toHexString(bytes); 176 } 177 return "(Unknown attribute " + name + ": " + hex + ")"; 178 } 179}