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

Upgrade ASM to 7.1 #851

Merged
merged 13 commits into from
Mar 8, 2019
Merged

Upgrade ASM to 7.1 #851

merged 13 commits into from
Mar 8, 2019

Conversation

Godin
Copy link
Member

@Godin Godin commented Mar 4, 2019

Fixes #839

@Godin Godin added this to the 0.8.4 milestone Mar 4, 2019
@Godin Godin self-assigned this Mar 4, 2019
@Godin Godin added this to Implementation in Current work items via automation Mar 4, 2019
Copy link
Member

@marchof marchof left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normally we have an change log entry for this. Should we also have one for a minor version upgrade?

@Godin
Copy link
Member Author

Godin commented Mar 5, 2019

@marchof this is because you picked PR which is still in "implementation" 😆 btw hard to predict url for changelog before creation of PR 😉

@Godin Godin requested a review from marchof March 5, 2019 22:42
@Godin Godin moved this from Implementation to Review in Current work items Mar 5, 2019
@@ -238,7 +236,7 @@ public static void push(final MethodVisitor mv, final int value) {
*/
public static ClassReader classReaderFor(final byte[] b) {
final byte[] originalVersion = new byte[] { b[4], b[5], b[6], b[7] };
if (getVersionMajor(b) == Opcodes.V12 + 1) {
if (b[6] == 0x00 && b[7] == 0x39) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Magic number? The intend was more clear with Opcodes.V12 + 1.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marchof inlining of former implementation of getVersionMajor into this method results in

final int majorVersion = (short) (((b[MAJOR_VERSION_INDEX] & 0xFF) << 8)
		| (b[MAJOR_VERSION_INDEX + 1] & 0xFF));

if (majorVersion == Opcodes.V12 + 1)

which IMO contains far more magic and far less readable than

if (b[6] == 0x00 && b[7] == 0x39)

Can also add comment.

Readable and non-magical alternative

final int majorVersion = java.nio.ByteBuffer.wrap(b)
		.order(java.nio.ByteOrder.BIG_ENDIAN).getShort(6);

looks like a bit of overkill.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW numbers 6, 7 and 0x39 are even shown in https://en.wikipedia.org/wiki/Java_class_file 😉

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So why inlining? If we keep the previous method (in addition to the new Reader version) we have testable and readable code ;)

@marchof
Copy link
Member

marchof commented Mar 6, 2019

@Godin If you like we can merge as is and check in a separate PR how we can streamline version reading/writing. Ok?

@marchof
Copy link
Member

marchof commented Mar 6, 2019

@Godin What about requesting a new public API ClassReader.getVersion() from the ASM project?

@marchof
Copy link
Member

marchof commented Mar 6, 2019

@Godin With explicit methods to read versions the code becomes way more understandable:

public static ClassReader classReaderFor(final byte[] b) {
	final int originalVersion = getVersion(b);
	if (getVersionMajor(b) == Opcodes.V12 + 1) {
		// temporary downgrade version for ASM
		setVersion(Opcodes.V12, b);
	}
	final ClassReader classReader = new ClassReader(b);
	setVersion(originalVersion, b);
	return classReader;
}

@Godin
Copy link
Member Author

Godin commented Mar 7, 2019

@marchof

What about requesting a new public API ClassReader.getVersion() from the ASM project?

Feel free to ask them 😉 😇


Speaking about understandability, testability and magic 🔮 - actually implementation of getVersionMajor(byte[]) in 0.8.3

public static int getVersionMajor(final byte[] b) {
return (short) (((b[MAJOR_VERSION_INDEX] & 0xFF) << 8)
| (b[MAJOR_VERSION_INDEX + 1] & 0xFF));
}

is incorrect, as well as

final int majorVersion = java.nio.ByteBuffer.wrap(b)
		.order(java.nio.ByteOrder.BIG_ENDIAN).getShort(6);

because according to JVMS major_version is unsigned 😉

For example let's consider major_version that with current temps (2 per year) will be approximately in 16393 years from now 😈 - 32786 (0x8012) and thus following test

final byte[] bytes = new byte[] { //
		(byte) 0xCA, (byte) 0xFE, (byte) 0xBA, (byte) 0xBE, // magic
		0x00, 0x00, // minor_version
		(byte) 0x80, (byte) 0x12 // major_version
};
assertEquals(0x8000, InstrSupport.getVersionMajor(bytes));

which fails

Expected :32786
Actual   :-32750

IMO such mistake is impossible in case of proposed exact comparison of bytes

if (b[6] == 0x80 && b[7] == 0x12)

😉

However proposed implementation of getVersionMajor(ClassReader)

public static int getVersionMajor(final ClassReader reader) {
final int firstConstantPoolEntryOffset = reader.getItem(1) - 1;
return reader.readShort(firstConstantPoolEntryOffset - 4);
}

has the same mistake because of readShort which reads signed value.

Both come from the fact that ASM itself uses same signed readShort to read major_version 😆 🤣 This also means that instead of "downgrade" trick, we can actually do "upgrade" trick 🤣 🤣


Moreover major_version is 2 bytes and so implementation of needsFrames(int)

public static boolean needsFrames(final int version) {
// consider major version only (due to 1.1 anomaly)
return (version & 0xff) >= Opcodes.V1_6;
}

is also incorrect and will fail / require change earlier - in about 122 years 😈

assertEquals(true, InstrSupport.needsFrames(0x0100));

Morever similarly to #851 (comment) about getVersionMajor(ClassReader), needsFrames currently always receives major_version and so doesn't need to "consider 1.1 anomaly" ? 😉


I added correct implementations of getMajorVersion(byte[]) and setMajorVersion(int, byte[]),
fixed, renamed and commented getMajorVersion(ClassReader). And let's fix needsFrames separately.

@marchof
Copy link
Member

marchof commented Mar 8, 2019

@Godin Thanks for fixing this right away!

will fail / require change earlier - in about 122 years

Ok, we document this as know limitation -- along with > 32 dimensional arrays 🤣

@marchof marchof merged commit 5128d1b into master Mar 8, 2019
Current work items automation moved this from Review to Done Mar 8, 2019
@marchof marchof deleted the asm_7_1 branch March 8, 2019 10:02
@jacoco jacoco locked as resolved and limited conversation to collaborators May 8, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
Development

Successfully merging this pull request may close these issues.

Failure after instrumentation of code which has stackmap frames and array of more than 7 dimensions
2 participants