Commit 4561c26f authored by Eric Bruneton's avatar Eric Bruneton
Browse files

Change the API and/or use defensive copy to prevent the user from modifying...

Change the API and/or use defensive copy to prevent the user from modifying internal data structures. This requires one backward incompatible change, namely the removal of the ClassReader.b field. I think this is acceptable, as this should normally be rarely used. The changes to ConstantDynamic are not backward incompatible, because this API was experimental up to now. The (unrelated) change of TypePath to a final class is also not an issue, because its unique constructor was package private. There is an inevitable performance impact, especially on ClassReader, but only when using the constructors using a byte array (the stream constructors don't use defensive copy).
parent 19735cbf
Pipeline #2916 passed with stage
in 7 minutes and 26 seconds
......@@ -172,10 +172,11 @@ public abstract class Remapper {
}
if (value instanceof ConstantDynamic) {
ConstantDynamic constantDynamic = (ConstantDynamic) value;
Object[] bootstrapMethodArguments = constantDynamic.getBootstrapMethodArguments();
Object[] remappedBootstrapMethodArguments = new Object[bootstrapMethodArguments.length];
for (int i = 0; i < bootstrapMethodArguments.length; ++i) {
remappedBootstrapMethodArguments[i] = mapValue(bootstrapMethodArguments[i]);
int bootstrapMethodArgumentCount = constantDynamic.getBootstrapMethodArgumentCount();
Object[] remappedBootstrapMethodArguments = new Object[bootstrapMethodArgumentCount];
for (int i = 0; i < bootstrapMethodArgumentCount; ++i) {
remappedBootstrapMethodArguments[i] =
mapValue(constantDynamic.getBootstrapMethodArgument(i));
}
String descriptor = constantDynamic.getDescriptor();
return new ConstantDynamic(
......
......@@ -1395,10 +1395,10 @@ public class ASMifier extends Printer {
stringBuilder.append(constantDynamic.getDescriptor()).append("\", ");
appendConstant(constantDynamic.getBootstrapMethod());
stringBuilder.append(", new Object[] {");
Object[] bootstrapMethodArguments = constantDynamic.getBootstrapMethodArguments();
for (int i = 0; i < bootstrapMethodArguments.length; ++i) {
appendConstant(bootstrapMethodArguments[i]);
if (i != bootstrapMethodArguments.length - 1) {
int bootstrapMethodArgumentCount = constantDynamic.getBootstrapMethodArgumentCount();
for (int i = 0; i < bootstrapMethodArgumentCount; ++i) {
appendConstant(constantDynamic.getBootstrapMethodArgument(i));
if (i != bootstrapMethodArgumentCount - 1) {
stringBuilder.append(", ");
}
}
......
......@@ -1215,8 +1215,9 @@ public class CheckMethodAdapter extends MethodVisitor {
checkMethodIdentifier(this.version, constantDynamic.getName(), "constant dynamic name");
checkDescriptor(this.version, constantDynamic.getDescriptor(), false);
checkLdcConstant(constantDynamic.getBootstrapMethod());
for (Object bootstrapArgument : constantDynamic.getBootstrapMethodArguments()) {
checkLdcConstant(bootstrapArgument);
int bootstrapMethodArgumentCount = constantDynamic.getBootstrapMethodArgumentCount();
for (int i = 0; i < bootstrapMethodArgumentCount; ++i) {
checkLdcConstant(constantDynamic.getBootstrapMethodArgument(i));
}
} else {
checkConstant(value);
......
......@@ -104,16 +104,16 @@ public class Attribute {
* ClassReader.
*
* @param classReader the class that contains the attribute to be read.
* @param offset index of the first byte of the attribute's content in {@link ClassReader#b}. The
* 6 attribute header bytes (attribute_name_index and attribute_length) are not taken into
* account here.
* @param offset index of the first byte of the attribute's content in {@link
* ClassReader#getBytes()}. The 6 attribute header bytes (attribute_name_index and
* attribute_length) are not taken into account here.
* @param length the length of the attribute's content (excluding the 6 attribute header bytes).
* @param charBuffer the buffer to be used to call the ClassReader methods requiring a
* 'charBuffer' parameter.
* @param codeAttributeOffset index of the first byte of content of the enclosing Code attribute
* in {@link ClassReader#b}, or -1 if the attribute to be read is not a code attribute. The 6
* attribute header bytes (attribute_name_index and attribute_length) are not taken into
* account here.
* in {@link ClassReader#getBytes()}, or -1 if the attribute to be read is not a code
* attribute. The 6 attribute header bytes (attribute_name_index and attribute_length) are not
* taken into account here.
* @param labels the labels of the method's code, or {@literal null} if the attribute to be read
* is not a code attribute.
* @return a <i>new</i> {@link Attribute} object corresponding to the specified bytes.
......@@ -127,7 +127,7 @@ public class Attribute {
final Label[] labels) {
Attribute attribute = new Attribute(type);
attribute.content = new byte[length];
System.arraycopy(classReader.b, offset, attribute.content, 0, length);
System.arraycopy(classReader.getBytesUnsafe(), offset, attribute.content, 0, length);
return attribute;
}
......@@ -156,7 +156,7 @@ public class Attribute {
final int codeLength,
final int maxStack,
final int maxLocals) {
return new ByteVector(content);
return ByteVector.createUnsafe(content); // Safe because 'content' is not user provided.
}
/**
......
......@@ -56,15 +56,27 @@ public class ByteVector {
}
/**
* Constructs a new {@link ByteVector} from the given initial data.
* Constructs a new {@link ByteVector} from the given initial data. WARNING: the provided array is
* not defensively copied, make sure to never pass a user provided array to this constructor!
*
* @param data the initial data of the new byte vector.
*/
ByteVector(final byte[] data) {
private ByteVector(final byte[] data) { // NOPMD(ArrayIsStoredDirectly): private API.
this.data = data;
this.length = data.length;
}
/**
* Creates a new ByteVector from the given initial data. WARNING: the provided array is not
* defensively copied, make sure to never pass a user provided array to this method!
*
* @param data the initial data of the new byte vector.
* @return a new ByteVector containing the given data.
*/
static ByteVector createUnsafe(final byte[] data) {
return new ByteVector(data);
}
/**
* Puts a byte into this byte vector. The byte vector is automatically enlarged if necessary.
*
......
......@@ -689,7 +689,7 @@ public class ClassWriter extends ClassVisitor {
nestMemberClasses = null;
firstAttribute = null;
compute = hasFrames ? MethodWriter.COMPUTE_INSERTED_FRAMES : MethodWriter.COMPUTE_NOTHING;
new ClassReader(classFile, 0, /* checkClassVersion = */ false)
ClassReader.createUnsafe(classFile) // Safe because classFile is not user provided.
.accept(
this,
attributes,
......
......@@ -68,7 +68,7 @@ public final class ConstantDynamic {
this.name = name;
this.descriptor = descriptor;
this.bootstrapMethod = bootstrapMethod;
this.bootstrapMethodArguments = bootstrapMethodArguments;
this.bootstrapMethodArguments = bootstrapMethodArguments.clone();
}
/**
......@@ -99,13 +99,36 @@ public final class ConstantDynamic {
}
/**
* Returns the arguments to pass to the bootstrap method, in order to compute the value of this
* Returns the number of arguments passed to the bootstrap method, in order to compute the value
* of this constant.
*
* @return the number of arguments passed to the bootstrap method, in order to compute the value
* of this constant.
*/
public int getBootstrapMethodArgumentCount() {
return bootstrapMethodArguments.length;
}
/**
* Returns an argument passed to the bootstrap method, in order to compute the value of this
* constant.
*
* @param index an argument index, between 0 and {@link #getBootstrapMethodArgumentCount()}
* (exclusive).
* @return the argument passed to the bootstrap method, with the given index.
*/
public Object getBootstrapMethodArgument(final int index) {
return bootstrapMethodArguments[index];
}
/**
* Returns the arguments to pass to the bootstrap method, in order to compute the value of this
* constant. WARNING: this array must not be modified, and must not be returned to the user.
*
* @return the arguments to pass to the bootstrap method, in order to compute the value of this
* constant.
*/
public Object[] getBootstrapMethodArguments() {
Object[] getBootstrapMethodArgumentsUnsafe() {
return bootstrapMethodArguments;
}
......
......@@ -2211,7 +2211,7 @@ final class MethodWriter extends MethodVisitor {
output.putShort(accessFlags & ~mask).putShort(nameIndex).putShort(descriptorIndex);
// If this method_info must be copied from an existing one, copy it now and return early.
if (sourceOffset != 0) {
output.putByteArray(symbolTable.getSource().b, sourceOffset, sourceLength);
output.putByteArray(symbolTable.getSource().getBytesUnsafe(), sourceOffset, sourceLength);
return;
}
// For ease of reference, we use here the same attribute order as in Section 4.7 of the JVMS.
......
......@@ -139,7 +139,7 @@ final class SymbolTable {
this.sourceClassReader = classReader;
// Copy the constant pool binary content.
byte[] inputBytes = classReader.b;
byte[] inputBytes = classReader.getBytesUnsafe();
int constantPoolOffset = classReader.getItem(1) - 1;
int constantPoolLength = classReader.header - constantPoolOffset;
constantPoolCount = classReader.getItemCount();
......@@ -242,7 +242,6 @@ final class SymbolTable {
*/
private void copyBootstrapMethods(final ClassReader classReader, final char[] charBuffer) {
// Find attributOffset of the 'bootstrap_methods' array.
byte[] inputBytes = classReader.b;
int currentAttributeOffset = classReader.getFirstAttributeOffset();
for (int i = classReader.readUnsignedShort(currentAttributeOffset - 2); i > 0; --i) {
String attributeName = classReader.readUTF8(currentAttributeOffset, charBuffer);
......@@ -257,7 +256,8 @@ final class SymbolTable {
int bootstrapMethodsOffset = currentAttributeOffset + 8;
int bootstrapMethodsLength = classReader.readInt(currentAttributeOffset + 2) - 2;
bootstrapMethods = new ByteVector(bootstrapMethodsLength);
bootstrapMethods.putByteArray(inputBytes, bootstrapMethodsOffset, bootstrapMethodsLength);
bootstrapMethods.putByteArray(
classReader.getBytesUnsafe(), bootstrapMethodsOffset, bootstrapMethodsLength);
// Add each bootstrap method in the symbol table entries.
int currentOffset = bootstrapMethodsOffset;
......@@ -496,7 +496,7 @@ final class SymbolTable {
constantDynamic.getName(),
constantDynamic.getDescriptor(),
constantDynamic.getBootstrapMethod(),
constantDynamic.getBootstrapMethodArguments());
constantDynamic.getBootstrapMethodArgumentsUnsafe());
} else {
throw new IllegalArgumentException("value " + value);
}
......
......@@ -153,7 +153,7 @@ public final class Type {
* @param valueBuffer a buffer containing the value of this field or method type.
* @param valueBegin the beginning index, inclusive, of the value of this field or method type in
* valueBuffer.
* @param valueEnd tne end index, exclusive, of the value of this field or method type in
* @param valueEnd the end index, exclusive, of the value of this field or method type in
* valueBuffer.
*/
private Type(final int sort, final String valueBuffer, final int valueBegin, final int valueEnd) {
......
......@@ -34,7 +34,7 @@ package org.objectweb.asm;
*
* @author Eric Bruneton
*/
public class TypePath {
public final class TypePath {
/** A type path step that steps into the element type of an array type. See {@link #getStep}. */
public static final int ARRAY_ELEMENT = 0;
......@@ -63,17 +63,33 @@ public class TypePath {
private final int typePathOffset;
/**
* Constructs a new TypePath.
* Constructs a new TypePath. WARNING: the provided array is not defensively copied, make sure to
* never pass a user provided array to this constructor!
*
* @param typePathContainer a byte array containing a type_path JVMS structure.
* @param typePathOffset the offset of the first byte of the type_path structure in
* typePathContainer.
*/
TypePath(final byte[] typePathContainer, final int typePathOffset) {
private TypePath(
final byte[] typePathContainer, // NOPMD(ArrayIsStoredDirectly): private API.
final int typePathOffset) {
this.typePathContainer = typePathContainer;
this.typePathOffset = typePathOffset;
}
/**
* Creates a new TypePath. WARNING: the provided array is not defensively copied, make sure to
* never pass a user provided array to this method!
*
* @param typePathContainer a byte array containing a type_path JVMS structure.
* @param typePathOffset the offset of the first byte of the type_path structure in
* typePathContainer.
* @return the created TypePath.
*/
static TypePath createUnsafe(final byte[] typePathContainer, final int typePathOffset) {
return new TypePath(typePathContainer, typePathOffset);
}
/**
* Returns the length of this path, i.e. its number of steps.
*
......@@ -150,7 +166,7 @@ public class TypePath {
}
}
output.data[0] = (byte) (output.length / 2);
return new TypePath(output.data, 0);
return createUnsafe(output.data, 0); // Safe because output.data is not user provided.
}
/**
......
......@@ -68,7 +68,7 @@ public class ClassReaderTest extends AsmTest implements Opcodes {
@Test
public void testReadByte() throws IOException {
ClassReader classReader = new ClassReader(getClass().getName());
assertEquals(classReader.b[0] & 0xFF, classReader.readByte(0));
assertEquals(classReader.getBytesUnsafe()[0] & 0xFF, classReader.readByte(0));
}
@Test
......
......@@ -55,7 +55,6 @@ cons public init(byte[])
cons public init(byte[],int,int)
cons public init(java.io.InputStream) throws java.io.IOException
cons public init(java.lang.String) throws java.io.IOException
fld public final byte[] b
fld public final int header
fld public final static int EXPAND_FRAMES = 8
fld public final static int SKIP_CODE = 1
......
......@@ -55,7 +55,6 @@ cons public init(byte[])
cons public init(byte[],int,int)
cons public init(java.io.InputStream) throws java.io.IOException
cons public init(java.lang.String) throws java.io.IOException
fld public final byte[] b
fld public final int header
fld public final static int EXPAND_FRAMES = 8
fld public final static int SKIP_CODE = 1
......@@ -472,7 +471,7 @@ meth public static org.objectweb.asm.Type[] getArgumentTypes(java.lang.reflect.M
supr java.lang.Object
hfds a,b,c,d
CLSS public org.objectweb.asm.TypePath
CLSS public final org.objectweb.asm.TypePath
fld public final static int ARRAY_ELEMENT = 0
fld public final static int INNER_TYPE = 1
fld public final static int TYPE_ARGUMENT = 3
......
......@@ -55,7 +55,6 @@ cons public init(byte[])
cons public init(byte[],int,int)
cons public init(java.io.InputStream) throws java.io.IOException
cons public init(java.lang.String) throws java.io.IOException
fld public final byte[] b
fld public final int header
fld public final static int EXPAND_FRAMES = 8
fld public final static int SKIP_CODE = 1
......@@ -475,7 +474,7 @@ meth public static org.objectweb.asm.Type[] getArgumentTypes(java.lang.reflect.M
supr java.lang.Object
hfds a,b,c,d
CLSS public org.objectweb.asm.TypePath
CLSS public final org.objectweb.asm.TypePath
fld public final static int ARRAY_ELEMENT = 0
fld public final static int INNER_TYPE = 1
fld public final static int TYPE_ARGUMENT = 3
......
......@@ -55,7 +55,6 @@ cons public init(byte[])
cons public init(byte[],int,int)
cons public init(java.io.InputStream) throws java.io.IOException
cons public init(java.lang.String) throws java.io.IOException
fld public final byte[] b
fld public final int header
fld public final static int EXPAND_FRAMES = 8
fld public final static int SKIP_CODE = 1
......@@ -505,7 +504,7 @@ meth public static org.objectweb.asm.Type[] getArgumentTypes(java.lang.reflect.M
supr java.lang.Object
hfds buf,len,off,sort
CLSS public org.objectweb.asm.TypePath
CLSS public final org.objectweb.asm.TypePath
fld public final static int ARRAY_ELEMENT = 0
fld public final static int INNER_TYPE = 1
fld public final static int TYPE_ARGUMENT = 3
......
......@@ -56,7 +56,6 @@ cons public init(byte[])
cons public init(byte[],int,int)
cons public init(java.io.InputStream) throws java.io.IOException
cons public init(java.lang.String) throws java.io.IOException
fld public final byte[] b
fld public final int header
fld public final static int EXPAND_FRAMES = 8
fld public final static int SKIP_CODE = 1
......@@ -506,7 +505,7 @@ meth public static org.objectweb.asm.Type[] getArgumentTypes(java.lang.reflect.M
supr java.lang.Object
hfds INTERNAL,PRIMITIVE_DESCRIPTORS,sort,valueBuffer,valueLength,valueOffset
CLSS public org.objectweb.asm.TypePath
CLSS public final org.objectweb.asm.TypePath
fld public final static int ARRAY_ELEMENT = 0
fld public final static int INNER_TYPE = 1
fld public final static int TYPE_ARGUMENT = 3
......
......@@ -65,7 +65,6 @@ cons public init(byte[])
cons public init(byte[],int,int)
cons public init(java.io.InputStream) throws java.io.IOException
cons public init(java.lang.String) throws java.io.IOException
fld public final byte[] b
fld public final int header
fld public final static int EXPAND_FRAMES = 8
fld public final static int SKIP_CODE = 1
......@@ -146,18 +145,6 @@ meth public int newUTF8(java.lang.String)
supr org.objectweb.asm.ClassVisitor
hfds accessFlags,compute,debugExtension,enclosingClassIndex,enclosingMethodIndex,firstAttribute,firstField,firstMethod,innerClasses,interfaceCount,interfaces,lastField,lastMethod,lastRuntimeInvisibleAnnotation,lastRuntimeInvisibleTypeAnnotation,lastRuntimeVisibleAnnotation,lastRuntimeVisibleTypeAnnotation,moduleWriter,nestHostClassIndex,nestMemberClasses,numberOfInnerClasses,numberOfNestMemberClasses,signatureIndex,sourceFileIndex,superClass,symbolTable,thisClass,version
CLSS public final org.objectweb.asm.ConstantDynamic
cons public !varargs init(java.lang.String,java.lang.String,org.objectweb.asm.Handle,java.lang.Object[])
meth public boolean equals(java.lang.Object)
meth public int hashCode()
meth public java.lang.Object[] getBootstrapMethodArguments()
meth public java.lang.String getDescriptor()
meth public java.lang.String getName()
meth public java.lang.String toString()
meth public org.objectweb.asm.Handle getBootstrapMethod()
supr java.lang.Object
hfds bootstrapMethod,bootstrapMethodArguments,descriptor,name
CLSS public abstract org.objectweb.asm.FieldVisitor
cons public init(int)
cons public init(int,org.objectweb.asm.FieldVisitor)
......@@ -529,7 +516,7 @@ meth public static org.objectweb.asm.Type[] getArgumentTypes(java.lang.reflect.M
supr java.lang.Object
hfds INTERNAL,PRIMITIVE_DESCRIPTORS,sort,valueBegin,valueBuffer,valueEnd
CLSS public org.objectweb.asm.TypePath
CLSS public final org.objectweb.asm.TypePath
fld public final static int ARRAY_ELEMENT = 0
fld public final static int INNER_TYPE = 1
fld public final static int TYPE_ARGUMENT = 3
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment