Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix decoding UTF-8 constant pool entries #150

Merged
merged 1 commit into from
Oct 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions core/src/main/java/org/jboss/jandex/Indexer.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@
import static org.jboss.jandex.ClassInfo.EnclosingMethodInfo;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Modifier;
import java.nio.charset.Charset;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -864,7 +864,7 @@ private void resolveTypeAnnotations() {
}
}

private void resolveUsers() {
private void resolveUsers() throws IOException {
byte[] pool = constantPool;
int[] offsets = constantPoolOffsets;

Expand Down Expand Up @@ -1665,19 +1665,19 @@ private void verifyMagic(DataInputStream stream) throws IOException {

}

private DotName decodeClassEntry(int index) {
private DotName decodeClassEntry(int index) throws IOException {
return index == 0 ? null : decodeDotNameEntry(index, CONSTANT_CLASS, "Class_info", '/');
}

private DotName decodeModuleEntry(int index) {
private DotName decodeModuleEntry(int index) throws IOException {
return index == 0 ? null : decodeDotNameEntry(index, CONSTANT_MODULE, "Module_info", '.');
}

private DotName decodePackageEntry(int index) {
private DotName decodePackageEntry(int index) throws IOException {
return index == 0 ? null : decodeDotNameEntry(index, CONSTANT_PACKAGE, "Package_info", '/');
}

private DotName decodeDotNameEntry(int index, int constantType, String typeName, char delim) {
private DotName decodeDotNameEntry(int index, int constantType, String typeName, char delim) throws IOException {
byte[] pool = constantPool;
int[] offsets = constantPoolOffsets;

Expand All @@ -1691,20 +1691,23 @@ private DotName decodeDotNameEntry(int index, int constantType, String typeName,
return names.convertToName(decodeUtf8Entry(nameIndex), delim);
}

private String decodeOptionalUtf8Entry(int index) {
private String decodeOptionalUtf8Entry(int index) throws IOException {
return index == 0 ? null : decodeUtf8Entry(index);
}

private String decodeUtf8Entry(int index) {
private String decodeUtf8Entry(int index) throws IOException {
byte[] pool = constantPool;
int[] offsets = constantPoolOffsets;

int pos = offsets[index - 1];
if (pool[pos] != CONSTANT_UTF8)
throw new IllegalStateException("Constant pool entry is not a utf8 info type: " + index + ":" + pos);

int len = (pool[++pos] & 0xFF) << 8 | (pool[++pos] & 0xFF);
return new String(pool, ++pos, len, Charset.forName("UTF-8"));
pos++;

// DataInputStream needs to read the length again
int len = (pool[pos] & 0xFF) << 8 | (pool[pos + 1] & 0xFF);
return new DataInputStream(new ByteArrayInputStream(pool, pos, len + 2)).readUTF();
}

private byte[] decodeUtf8EntryAsBytes(int index) {
Expand All @@ -1730,7 +1733,7 @@ private NameAndType(String name, String descriptor) {
}
}

private NameAndType decodeNameAndTypeEntry(int index) {
private NameAndType decodeNameAndTypeEntry(int index) throws IOException {
byte[] pool = constantPool;
int[] offsets = constantPoolOffsets;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package org.jboss.jandex.test;

import static org.junit.Assert.assertEquals;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.Index;
import org.jboss.jandex.IndexReader;
import org.jboss.jandex.IndexWriter;
import org.jboss.jandex.Indexer;
import org.junit.Test;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.ClassFileVersion;
import net.bytebuddy.NamingStrategy;
import net.bytebuddy.description.annotation.AnnotationDescription;
import net.bytebuddy.description.type.TypeDescription;

public class Utf8ConstantEncodingTest {
@interface MyAnnotation {
String value();
}

private static final String CLASS_NAME = "org.jboss.jandex.test.MyTestClass";

private static final String LONG_STRING;

static {
// in UTF-8, the null character is encoded as 0x00, while in "modified UTF-8" per `DataInput`,
// it is encoded as 0xC0 0x80 (this is not the only difference between the two encodings,
// but is the easiest to test with)
StringBuilder longString = new StringBuilder();
for (int i = 0; i < 25_000; i++) {
longString.append('\0');
}
LONG_STRING = longString.toString();
}

@Test
public void test() throws IOException {
byte[] clazz = new ByteBuddy()
.with(ClassFileVersion.JAVA_V8)
.with(new NamingStrategy.AbstractBase() {
@Override
protected String name(TypeDescription superClass) {
return CLASS_NAME;
}
})
.subclass(Object.class)
.annotateType(AnnotationDescription.Builder.ofType(MyAnnotation.class)
.define("value", LONG_STRING)
.build())
.make()
.getBytes();

Indexer indexer = new Indexer();
indexer.indexClass(MyAnnotation.class);
indexer.index(new ByteArrayInputStream(clazz));
Index index = indexer.complete();

verifyAnnotationValue(index);

Index index2 = roundtrip(index);

verifyAnnotationValue(index2);
}

private void verifyAnnotationValue(Index index) {
ClassInfo clazz = index.getClassByName(DotName.createSimple(CLASS_NAME));
String annotationValue = clazz.classAnnotation(DotName.createSimple(MyAnnotation.class.getName())).value().asString();
assertEquals(LONG_STRING, annotationValue);
}

private Index roundtrip(Index index) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
new IndexWriter(baos).write(index);
return new IndexReader(new ByteArrayInputStream(baos.toByteArray())).read();
}
}