GET Compiling and decompiling try-with-resources / Sudo Null IT News FREE

Compiling and decompiling try-with-resources, Beaver State a tarradiddle about how I firm a bug and what came of it.

Introduction

PitestSome time ago, the backlog of a working draft copy was almost empty, and different research tasks surfaced. One of them sounded very intriguing: to fasten modification testing to the fancy victimisation PITest . Connected Habré there is already a rattling detailed review of this depository library (with examples and pictures). I will not recite this article in my own words, but still I urge that you familiarize yourself with information technology front.

I hold that the idea of ​​mutational testing, I fired up. With almost no extra drive, acquiring a puppet for determination potentially dangerous places in your computer code is worth it! I at once got low to byplay. At that time, the library was relatively young, and as a result, very crude: here you need to play a little with the maven configuration, and there - patch the hack-in for Sonar. Nevertheless, after a while I was still healthy to verify the entire project. Result: hundreds of surviving mutations! Scale evolution on our form server.

Rolling rising my sleeves, I plunged into work. In some tests, there is non enough verification of stubs, in others, instead of logic, it is generally not clear what is being tested. Edit out, improve, rewrite. In general, the march began, but the number of surviving mutations did not decrease as rapidly as we craved. The reason was simple: PIT gave a Brobdingnagian amount of false positives on the essay-with-resources hinder . A short search showed that the bug is known , but ease not determinate. Well, the library code is open. Why not persuade him and see what is the matter?

We infer the reasons

TryExample

I threw a swordlike instance , a building block try out for it and ran PITest. The result is before you: instead of matchless, there are 11 mutations that survived, ten of which argue a line with the "}" symbol. Calls to the tightlipped and addSupressed methods suggest that this line includes the code generated for the try out-with-resources block. To confirm this conjecture, I decided to decompile the class single file. To DO this, I used the JD-Graphical user interface , although I would now commend the built-in IntelliJ Estimation 14 decompiler .

          unexclusive static void primary(String[] args) throws IOException {     ByteArrayOutputStream baos = new ByteArrayOutputStream();     Throwable var2 = null;     try {         baos.flush();     } catch (Throwable var11) {         var2 = var11;         throw var11;     } finally {         if (baos != null) {             if (var2 != null) {                 try {                     baos.close();                 } catch (Throwable var10) {                     var2.addSuppressed(var10);                 }             } else {                 baos.conclusion();             }         }     } }        

The guess was official, merely the interrogative sentence remained: how did two try-with-resources lines turn into a cardinal try-catch-finally lines? gvsmirnov bequeathed to us in any incomprehensible state of affairs to download OpenJDK sources . This is what I did.

All encode related to the try-with-resources compilation tax is located between lines 1428 and 1580 of the Lower form . Javadoc tells us that this class is designed to translate syntactic sugar: no magic, only the simplest modifications to the syntax tree. Everything is in accordance with JLS 14.20.3 .

We figured come out of the closet the compiler behavior. It corpse to understand wherefore the library is trying to mutate the inscribe generated past the compiling program and how it full treatmen. Rummaging done the source, I found KO'd the pursuing. PITest only manipulates bytecode loaded into RAM. It replaces the instructions according to certain rules, and then runs unit tests. For work with bytecode ASM is used .

The number 1 idea was to intercept the line act of the class method visitGeneratedTryCatchBlock MethodVisitor , and then fair tell the library what line should constitute ignored. Similar functionality has already been enforced.for finally block. However, I was surprised to learn that the visitGeneratedTryCatchBlock method does not exist. ASM does not distinguish between the code generated by the encyclopedist and the encode generated by the programmer. Ambush. I had to look into the bytecode, the output and formatting of which was kindly provided by Textifier .

The bytecode of the main method of the TryExample separate

              // access flags 0x9 public static main([Ljava/lang/String;)V throws java/io/IOException    TRYCATCHBLOCK L0 L1 L2 java/lang/Throwable   TRYCATCHBLOCK L3 L4 L5 java/lang/Throwable   TRYCATCHBLOCK L3 L4 L6 null   TRYCATCHBLOCK L7 L8 L9 java/lang/Throwable   TRYCATCHBLOCK L5 L10 L6 nix  L11   LINENUMBER 12 L11   NEW java/Io/ByteArrayOutputStream   DUP   INVOKESPECIAL coffee/io/ByteArrayOutputStream.                  ()V   ASTORE 1  L12   ACONST_NULL   ASTORE 2  L3   LINENUMBER 13 L3   ALOAD 1   INVOKEVIRTUAL java/io/ByteArrayOutputStream.flush ()V  L4   LINENUMBER 14 L4   ALOAD 1   IFNULL L13   ALOAD 2   IFNULL L14  L0   ALOAD 1   INVOKEVIRTUAL Java/io/ByteArrayOutputStream.boon ()V  L1   GOTO L13  L2  FRAME FULL [[Ljava/lang/String; java/io/ByteArrayOutputStream java/lang/Throwable] [Java/lang/Throwable]   ASTORE 3  L15   ALOAD 2   ALOAD 3   INVOKEVIRTUAL java/lang/Throwable.addSuppressed (Ljava/lang/Throwable;)V  L16   GOTO L13  L14  FRAME SAME   ALOAD 1   INVOKEVIRTUAL java/Io/ByteArrayOutputStream.close ()V   GOTO L13  L5   LINENUMBER 12 L5  FRAME SAME1 java/lang/Throwable   ASTORE 3   ALOAD 3   ASTORE 2   ALOAD 3   ATHROW  L6   LINENUMBER 14 L6  FRAME SAME1 java/lang/Throwable   ASTORE 4  L10   ALOAD 1   IFNULL L17   ALOAD 2   IFNULL L18  L7   ALOAD 1   INVOKEVIRTUAL coffee/io/ByteArrayOutputStream.close ()V  L8   GOTO L17  L9  FRAME FULL [[Ljava/lang/String; coffee/io/ByteArrayOutputStream java/lang/Throwable T java/lang/Throwable] [Java/lang/Throwable]   ASTORE 5  L19   ALOAD 2   ALOAD 5   INVOKEVIRTUAL java/lang/Throwable.addSuppressed (Ljava/lang/Throwable;)V  L20   GOTO L17  L18  FRAME One   ALOAD 1   INVOKEVIRTUAL java/Io/ByteArrayOutputStream.close ()V  L17  FRAME Unvaried   ALOAD 4   ATHROW  L13   LINENUMBER 15 L13  FRAME FULL [[Ljava/lang/Chain;] []   Generate  L21   LOCALVARIABLE x2 Ljava/lang/Throwable; L15 L16 3   LOCALVARIABLE x2 Ljava/lang/Throwable; L19 L20 5   LOCALVARIABLE baos Ljava/io/ByteArrayOutputStream; L12 L13 1   LOCALVARIABLE args [Ljava/lang/String; L11 L21 0   MAXSTACK = 2   MAXLOCALS = 6                          

The naive supposal that the try-bewitch-finally draw a blank is enforced at the JVM level has not been confirmed . There is nobelium specialized instruction for it, only an exception postpone and goto between labels. It turns out that past modular means information technology will not sour to recognize the generated obturate. Need to search another solution.

What if…

Before starting to guess on coffee grounds, I decided to put bytecode marks on the decompiled class. That's what came out of it.

          national static nullity independent(String[] args) throws IOException {     ByteArrayOutputStream baos = new ByteArrayOutputStream(); // L11     Throwable primaryExc = null; // L12     prove {         baos.purge(); // L3     } watch (Throwable t) { // L5         primaryExc = t;         throw t;     } finally { // L6         if (baos != null) { // L4 L10             if (primaryExc != void) {                 adjudicate {                     baos.close(); // L0 L7                 } catch (Throwable suppressedExc) { // L2 L9                     primaryExc.addSuppressed(suppressedExc); // L15 L19                 } // L1 L16 L8 L20             } else {                 baos.finale(); // L14 L18             }         } // L17     } // L13 }        

Two main ways of platform execution are clearly nascent:

L11 L12 L3 {L4 [L0 (L2 L15 L16) L1] L14} L13 L11 L12 L3 [L5 {L6] L10 [L7 (L9 L19 L20) L8] L18 L17}        

Underneath from each one other are labels whose code blocks match or about match. In parentheses is the code that volition live executed when the closemouthed method acting throws an exception. Likewise in guileless - when the even out method acting . Two shipway upset out because the last block was substituted by the compiler twice. Well, now, to completely break your visual parser: the labels in frizzly brackets refer to line 11. The false line PITest refers to the unvarying line.

Here is the root! A minimally repetition prepare of instructions should be highlighted. If much a jell is found in the bytecode being proved, and even on cardinal production line, the generated code for the try on-with-resources closure is available. It doesn't sound very hard, but I decided to give it a try. To a lower place is a list of operating instructions that I complete in the lead with.

          private static final Heel              JAVAC_CLASS_INS_SEQUENCE = Arrays.asList(   ASTORE, // store throwable   ALOAD, IFNULL, // closeable != zipp   ALOAD, IFNULL, // localThrowable2 != null   ALOAD, INVOKEVIRTUAL, GOTO, // closeable.fine()   ASTORE, // Throwable x2   ALOAD, ALOAD, INVOKEVIRTUAL, GOTO, // localThrowable2.addSuppressed(x2)   ALOAD, INVOKEVIRTUAL, // closeable.warm()   ALOAD, ATHROW); // have throwable                  

Something like this can be mapped to code in a finally block.

          } at length {     if (closeable != null) { // IFNULL         if (localThrowable2 != null) { // IFNULL             try {                 closeable.close(); // INVOKEVIRTUAL or INVOKEINTERFACE             } snap (Throwable x2) {                 localThrowable2.addSuppressed(x2); // INVOKEVIRTUAL             }         } else {             closeable.skinny(); // INVOKEVIRTUAL or INVOKEINTERFACE         }     } } // ATHROW        

"Non so difficult," I thought after several days of hard work. Threw few more examples ; wrote tests that use them. Everything is fine, everything works. I dependable to build PITest to run it connected animate encipher: the tests cruel. Not the ones I wrote; others.

Compilers are different

Indeed, the code has moved from the "does not compile" stage to the "does not work" represent. One of the tests that existed before this roughshod. Rolled back - it works. Inside the test, the Java7TryWithResources.class.binful single file that was already in the project is checked . Having printed the bytecode, I could not believe my eyes: a completely different order of instructions was used to compile effort-with-resources!

Difficult non to panic, I began to check completely the compilers at hand. I worked with javac from Oracle JDK, javac from OpenJDK expectedly gave a look-alike leave. I tried different versions: to nobelium service. Information technology was the turn of compilers that were not at hand. Eclipse Compiler for Coffee, ECJ. Compiled, written bytecode - at first glance it looks like-minded the unmatched I'm looking.

The bytecode of the main method of the TryExample by ECJ grade

                              // access flags 0x9  public static main([Ljava/lang/String;)V throws Java/io/IOException     TRYCATCHBLOCK L0 L1 L2 null    TRYCATCHBLOCK L3 L4 L4 null   L5    LINENUMBER 12 L5    ACONST_NULL    ASTORE 1    ACONST_NULL    ASTORE 2   L3    NEW coffee/io/ByteArrayOutputStream    DUP    INVOKESPECIAL java/io/ByteArrayOutputStream.                  ()V    ASTORE 3   L0    LINENUMBER 13 L0    ALOAD 3    INVOKEVIRTUAL java/Io/ByteArrayOutputStream.heyday ()V   L1    LINENUMBER 14 L1    ALOAD 3    IFNULL L6    ALOAD 3    INVOKEVIRTUAL Java/io/ByteArrayOutputStream.cozy ()V    GOTO L6   L2   Skeletal system FULL [[Ljava/lang/String; java/lang/Throwable java/lang/Throwable coffee/Io/ByteArrayOutputStream] [java/lang/Throwable]    ASTORE 1    ALOAD 3    IFNULL L7    ALOAD 3    INVOKEVIRTUAL java/io/ByteArrayOutputStream.end ()V   L7   FRAME CHOP 1    ALOAD 1    ATHROW   L4   FRAME SAME1 java/lang/Throwable    ASTORE 2    ALOAD 1    IFNONNULL L8    ALOAD 2    ASTORE 1    GOTO L9   L8   Build Indistinguishable    ALOAD 1    ALOAD 2    IF_ACMPEQ L9    ALOAD 1    ALOAD 2    INVOKEVIRTUAL java/lang/Throwable.addSuppressed (Ljava/lang/Throwable;)V   L9   FRAME SAME    ALOAD 1    ATHROW   L6    LINENUMBER 15 L6   FRAME Hack 2    RETURN    MAXSTACK = 2    MAXLOCALS = 4                          

After that, I distinct to decompile the resulting classify file. The solvent of the decompiler refused to compile hindmost. Recovered, nothing, you tail end already work with this. Hands bringing the program code in accordance of rights with the bytecode, I got the shadowing.

          unexclusive static void main(String[] paramArrayOfString) throws Throwable {     Throwable primaryExceptionVariable = null; // L5     Throwable caughtThrowableVariable = null;     try {         ByteArrayOutputStream baos = new ByteArrayOutputStream(); // L3         try on {             baos.flush(); // L0         } watch (Throwable t) {             primaryExceptionVariable = t; // L2             throw primaryExceptionVariable; // L7         } finally {             if (baos != null) { // L1                 baos.close();             }         }     } catch (Throwable t) {         caughtThrowableVariable = t; // L4         if (primaryExceptionVariable == null) {             primaryExceptionVariable = caughtThrowableVariable;         } other if (primaryExceptionVariable != caughtThrowableVariable) { // L8             primaryExceptionVariable.addSuppressed(caughtThrowableVariable);         }         throw primaryExceptionVariable; // L9     } // L6 }        

ECJ takes a completely different approach to compiling try-with-resources. Labels are observably small, blocks of code are observably larger. Instead of a bloated table, exceptions are simply thrown to the next layer. In the more complicated examples, you dismiss notice that it turns out a sort of nesting doll.

What is under the hood? I again went to download the informant, this time ECJ . Compilation of the try assertion is obscure in the TryStatement file . This time zero trees, only opcodes, only hardcore. The bytecode responsible for try-with-resources is generated between lines 500 and 604. From the history of commits, it is clearly seen that the body of the try block was simply framed by a range of mountains of calls to produce and close resources.

Because if there is no substitution of the finally embarras, then there is no duplication of encrypt. However, due to nesting, the same actions are perennial for assorted exceptions. I took advantage of this. The set of instructions for ECJ is as follows.

          private atmospherics final List              ECJ_INS_SEQUENCE = Arrays.asList(   ASTORE, // shop throwable2   ALOAD, IFNONNULL, // if (throwable1 == null)   ALOAD, ASTORE, GOTO, // throwable1 = throwable2;   ALOAD, ALOAD, IF_ACMPEQ, // if (throwable1 != throwable2) {   ALOAD, ALOAD, INVOKEVIRTUAL, // throwable1.addSuppressed(throwable2)   ALOAD, ATHROW); // throw away throwable1                  

And then the same java code looks like.

          if (throwable1 == null) { // IFNONNULL     throwable1 = throwable2; } else {     if (throwable1 != throwable2) { // IF_ACMPEQ         throwable1.addSuppressed(throwable2); // INVOKEVIRTUAL     } } // ATHROW        

What about the rest of the compilers? IT turned out that AspectJ generates almost the same bytecode equally ECJ. For him, there was no need to make up a separate sequence. I could not download the encyclopaedist from IBM (and I didn't actually want to). Other compilers were ignored due to contemptible preponderance.

results

An attentive reader has already noticed that the set of instructions for javac does non take into account one refinement. To call the methods of the class and the interface, different instructions are actually used: INVOKEVIRTUAL and INVOKEINTERFACE, respectively. The implementation described above takes into account single the first case and does not allow the second. Well, zero, it's not hard to fix.

So what is the result?

First, the main result of the work was a patch that fixes the tease mentioned at the outset of the clause. Almost completely of the code fits in 1 class (not counting the tests), which currently looks like this: TryWithResourcesMethodVisitor . I urge everyone to criticize and propose their unsurpassed options for solving this problem.

Secondly, I found out what are the ways to hoard a stress-with-resources block. As a result, I figured out what try-catch up-finally looks equal at the bytecode level. Good, a spin-off was the translation of an article that I mentioned above in the textbook.

Thirdly, I wrote this article where I told you everything. Perhaps now one of you will be able to increase the fundamental coefficient of squandering victimization the nonheritable knowledge.

And where is the use and morality, you ask? I leave their hunt to the reader. I only note that I enjoyed while piece of writing this article. Hope you got it from reading. See you soon!

PS Equally a bonus, I suggest look early proposals for the implementation of sample-with-resources from Joshua Ernest Bloch.

It looks funny.

          {     net LocalVariableDeclaration ;     mathematician #suppressSecondaryException = false;     try Block arrest (final Throwable #t) {         #suppressSecondaryException = rightful;         cam stroke #t;     } finally {         if (#suppressSecondaryException)             try { localVar.close(); } catch(Exception #ignore) { }         else             localVar.come together();     } }        

DOWNLOAD HERE

GET Compiling and decompiling try-with-resources / Sudo Null IT News FREE

Posted by: rollinsnowlielinuld81.blogspot.com

0 Response to "GET Compiling and decompiling try-with-resources / Sudo Null IT News FREE"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel