Why is this loop changed?
I just encountered this decompiled class file of my class:
MyClass
while ((line = reader.readLine()) != null) {
System.out.println("line: " + line);
if (i == 0) {
colArr = line.split(Pattern.quote("|"));
} else {
i++;
}
}
The while
loop has been changed to a for
loop in the class file:
Decompiled MyClass
for (String colArr = null; (line = reader.readLine()) != null; ++i) {
System.out.println("line: " + line);
if (i == 0) {
colArr = line.split(Pattern.quote("|"));
} else {
}
}
Why has this loop been changed to a for
?
I think it might be another way of code optimization by the compiler, I could be wrong.
I just wanted to know if it is, what advantages does a for
loop provide over a while
loop or other loop?
What is the category of such code optimizations?
java loops optimization
|
show 10 more comments
I just encountered this decompiled class file of my class:
MyClass
while ((line = reader.readLine()) != null) {
System.out.println("line: " + line);
if (i == 0) {
colArr = line.split(Pattern.quote("|"));
} else {
i++;
}
}
The while
loop has been changed to a for
loop in the class file:
Decompiled MyClass
for (String colArr = null; (line = reader.readLine()) != null; ++i) {
System.out.println("line: " + line);
if (i == 0) {
colArr = line.split(Pattern.quote("|"));
} else {
}
}
Why has this loop been changed to a for
?
I think it might be another way of code optimization by the compiler, I could be wrong.
I just wanted to know if it is, what advantages does a for
loop provide over a while
loop or other loop?
What is the category of such code optimizations?
java loops optimization
23
Hard to tell from source code along, but it might just be a choice of the decompiler to show you this version. Note that a decompiler has to "guess" a bit - it chooses one possible source code that might lead to the given bytecode.
– Hulk
Dec 14 '18 at 11:02
6
@KumarAnkit - No necessarily optimization, no. There isn't a one-to-one relationship between source constructs and bytecode. That's what Hulk means when he/she says "a decompiler has to 'guess' a bit."
– T.J. Crowder
Dec 14 '18 at 11:04
8
@KumarAnkit what people are trying to explain is that maybe there's no optimization at all. Try translating a hindi (or whatever is your regional dialect) sentence in english using Google translate, and then back to hindi. You'll be lucky if it's the same sentence than at the beginning. Here it's the same thing, got it?
– Dici
Dec 14 '18 at 11:07
13
@KumarAnkit -for
(andwhile
, etc.) don't exist at the bytecode level. It's jump instructions, assignments, etc. One decompiler might look at some bytecode and say "that looks like afor
" while another might look at it and say "that looks like awhile
". "So, does it mean these loops are identical at the byte-code level?" Not necessarily. If you compile the code output by a decompiler, you don't necessarily end up with identical bytecode. In fact, I suspect you rarely would.
– T.J. Crowder
Dec 14 '18 at 11:12
8
OP must be hiding something from us. The two snippets are not equivalent. The first one will never updatei
once it's equal to zero, so it may set the value ofcolArr
multiple times in a reasonable scenario. The second one will updatecolArr
only once.
– ach
Dec 14 '18 at 13:42
|
show 10 more comments
I just encountered this decompiled class file of my class:
MyClass
while ((line = reader.readLine()) != null) {
System.out.println("line: " + line);
if (i == 0) {
colArr = line.split(Pattern.quote("|"));
} else {
i++;
}
}
The while
loop has been changed to a for
loop in the class file:
Decompiled MyClass
for (String colArr = null; (line = reader.readLine()) != null; ++i) {
System.out.println("line: " + line);
if (i == 0) {
colArr = line.split(Pattern.quote("|"));
} else {
}
}
Why has this loop been changed to a for
?
I think it might be another way of code optimization by the compiler, I could be wrong.
I just wanted to know if it is, what advantages does a for
loop provide over a while
loop or other loop?
What is the category of such code optimizations?
java loops optimization
I just encountered this decompiled class file of my class:
MyClass
while ((line = reader.readLine()) != null) {
System.out.println("line: " + line);
if (i == 0) {
colArr = line.split(Pattern.quote("|"));
} else {
i++;
}
}
The while
loop has been changed to a for
loop in the class file:
Decompiled MyClass
for (String colArr = null; (line = reader.readLine()) != null; ++i) {
System.out.println("line: " + line);
if (i == 0) {
colArr = line.split(Pattern.quote("|"));
} else {
}
}
Why has this loop been changed to a for
?
I think it might be another way of code optimization by the compiler, I could be wrong.
I just wanted to know if it is, what advantages does a for
loop provide over a while
loop or other loop?
What is the category of such code optimizations?
java loops optimization
java loops optimization
edited Dec 14 '18 at 15:06
Boann
36.7k1288121
36.7k1288121
asked Dec 14 '18 at 11:00
KumarAnkitKumarAnkit
332413
332413
23
Hard to tell from source code along, but it might just be a choice of the decompiler to show you this version. Note that a decompiler has to "guess" a bit - it chooses one possible source code that might lead to the given bytecode.
– Hulk
Dec 14 '18 at 11:02
6
@KumarAnkit - No necessarily optimization, no. There isn't a one-to-one relationship between source constructs and bytecode. That's what Hulk means when he/she says "a decompiler has to 'guess' a bit."
– T.J. Crowder
Dec 14 '18 at 11:04
8
@KumarAnkit what people are trying to explain is that maybe there's no optimization at all. Try translating a hindi (or whatever is your regional dialect) sentence in english using Google translate, and then back to hindi. You'll be lucky if it's the same sentence than at the beginning. Here it's the same thing, got it?
– Dici
Dec 14 '18 at 11:07
13
@KumarAnkit -for
(andwhile
, etc.) don't exist at the bytecode level. It's jump instructions, assignments, etc. One decompiler might look at some bytecode and say "that looks like afor
" while another might look at it and say "that looks like awhile
". "So, does it mean these loops are identical at the byte-code level?" Not necessarily. If you compile the code output by a decompiler, you don't necessarily end up with identical bytecode. In fact, I suspect you rarely would.
– T.J. Crowder
Dec 14 '18 at 11:12
8
OP must be hiding something from us. The two snippets are not equivalent. The first one will never updatei
once it's equal to zero, so it may set the value ofcolArr
multiple times in a reasonable scenario. The second one will updatecolArr
only once.
– ach
Dec 14 '18 at 13:42
|
show 10 more comments
23
Hard to tell from source code along, but it might just be a choice of the decompiler to show you this version. Note that a decompiler has to "guess" a bit - it chooses one possible source code that might lead to the given bytecode.
– Hulk
Dec 14 '18 at 11:02
6
@KumarAnkit - No necessarily optimization, no. There isn't a one-to-one relationship between source constructs and bytecode. That's what Hulk means when he/she says "a decompiler has to 'guess' a bit."
– T.J. Crowder
Dec 14 '18 at 11:04
8
@KumarAnkit what people are trying to explain is that maybe there's no optimization at all. Try translating a hindi (or whatever is your regional dialect) sentence in english using Google translate, and then back to hindi. You'll be lucky if it's the same sentence than at the beginning. Here it's the same thing, got it?
– Dici
Dec 14 '18 at 11:07
13
@KumarAnkit -for
(andwhile
, etc.) don't exist at the bytecode level. It's jump instructions, assignments, etc. One decompiler might look at some bytecode and say "that looks like afor
" while another might look at it and say "that looks like awhile
". "So, does it mean these loops are identical at the byte-code level?" Not necessarily. If you compile the code output by a decompiler, you don't necessarily end up with identical bytecode. In fact, I suspect you rarely would.
– T.J. Crowder
Dec 14 '18 at 11:12
8
OP must be hiding something from us. The two snippets are not equivalent. The first one will never updatei
once it's equal to zero, so it may set the value ofcolArr
multiple times in a reasonable scenario. The second one will updatecolArr
only once.
– ach
Dec 14 '18 at 13:42
23
23
Hard to tell from source code along, but it might just be a choice of the decompiler to show you this version. Note that a decompiler has to "guess" a bit - it chooses one possible source code that might lead to the given bytecode.
– Hulk
Dec 14 '18 at 11:02
Hard to tell from source code along, but it might just be a choice of the decompiler to show you this version. Note that a decompiler has to "guess" a bit - it chooses one possible source code that might lead to the given bytecode.
– Hulk
Dec 14 '18 at 11:02
6
6
@KumarAnkit - No necessarily optimization, no. There isn't a one-to-one relationship between source constructs and bytecode. That's what Hulk means when he/she says "a decompiler has to 'guess' a bit."
– T.J. Crowder
Dec 14 '18 at 11:04
@KumarAnkit - No necessarily optimization, no. There isn't a one-to-one relationship between source constructs and bytecode. That's what Hulk means when he/she says "a decompiler has to 'guess' a bit."
– T.J. Crowder
Dec 14 '18 at 11:04
8
8
@KumarAnkit what people are trying to explain is that maybe there's no optimization at all. Try translating a hindi (or whatever is your regional dialect) sentence in english using Google translate, and then back to hindi. You'll be lucky if it's the same sentence than at the beginning. Here it's the same thing, got it?
– Dici
Dec 14 '18 at 11:07
@KumarAnkit what people are trying to explain is that maybe there's no optimization at all. Try translating a hindi (or whatever is your regional dialect) sentence in english using Google translate, and then back to hindi. You'll be lucky if it's the same sentence than at the beginning. Here it's the same thing, got it?
– Dici
Dec 14 '18 at 11:07
13
13
@KumarAnkit -
for
(and while
, etc.) don't exist at the bytecode level. It's jump instructions, assignments, etc. One decompiler might look at some bytecode and say "that looks like a for
" while another might look at it and say "that looks like a while
". "So, does it mean these loops are identical at the byte-code level?" Not necessarily. If you compile the code output by a decompiler, you don't necessarily end up with identical bytecode. In fact, I suspect you rarely would.– T.J. Crowder
Dec 14 '18 at 11:12
@KumarAnkit -
for
(and while
, etc.) don't exist at the bytecode level. It's jump instructions, assignments, etc. One decompiler might look at some bytecode and say "that looks like a for
" while another might look at it and say "that looks like a while
". "So, does it mean these loops are identical at the byte-code level?" Not necessarily. If you compile the code output by a decompiler, you don't necessarily end up with identical bytecode. In fact, I suspect you rarely would.– T.J. Crowder
Dec 14 '18 at 11:12
8
8
OP must be hiding something from us. The two snippets are not equivalent. The first one will never update
i
once it's equal to zero, so it may set the value of colArr
multiple times in a reasonable scenario. The second one will update colArr
only once.– ach
Dec 14 '18 at 13:42
OP must be hiding something from us. The two snippets are not equivalent. The first one will never update
i
once it's equal to zero, so it may set the value of colArr
multiple times in a reasonable scenario. The second one will update colArr
only once.– ach
Dec 14 '18 at 13:42
|
show 10 more comments
4 Answers
4
active
oldest
votes
In this situation changing while()
to for()
is not an optimization. There is simply no way to know from bytecode which one was used in a source code.
There are many situations when:
while(x)
is the same as:
for(;x;)
Suppose we have a three similar java applications - one with while()
statement, and two with corresponting for()
. First for()
with stopping criterion only like in the standard while()
, and second for()
also with iterator declaration and incrementation.
APPLICATION #1 - SOURCE
public class While{
public static void main(String args) {
int i = 0;
while(i<5){
System.out.println(i);
i++;
}
}
}
APPLICATION #2 - SOURCE
public class For{
public static void main(String args) {
int i = 0;
for(; i<5 ;){
System.out.println(i);
i++;
}
}
}
APPLICATION #3 - SOURCE
public class For2{
public static void main(String args) {
for(int i=0;i<5;i++){
System.out.println(i);
}
}
}
If we compile all of them we have got:
APPLICATION #1 - BYTECODE
public class While {
public While();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iconst_5
4: if_icmpge 20
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
14: iinc 1, 1
17: goto 2
20: return
}
APPLICATION #2 - BYTECODE
public class For {
public For();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iconst_5
4: if_icmpge 20
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
14: iinc 1, 1
17: goto 2
20: return
}
APPLICATION #3 - BYTECODE
public class For2 extends java.lang.Object{
public For2();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iconst_5
4: if_icmpge 20
7: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #3; //Method java/io/PrintStream.println:(I)V
14: iinc 1, 1
17: goto 2
20: return
}
So you can see, there is no difference associated with for
and while
usage.
3
Maybe you could put the actual bytecode of both snippets in your answer to prove your point :p it's the intuitive answer, but it doesn't mean it's right
– Dici
Dec 14 '18 at 11:12
The bytecode will simply have aGOTO
.
– Jörg W Mittag
Dec 14 '18 at 13:44
1
@Dici I've put a sample for better explanation.
– dgebert
Dec 14 '18 at 15:10
@dgebert +1. :p
– Dici
Dec 14 '18 at 19:31
4
I like this answer: I think the demonstration can be improved by moving thei++
into thefor
-loop to make:for(; i<5 ; i++){
. That way you can show that either way, the bytecode puts theiinc
at the end of the loop body at the bytecode level, and so the decompiler can't always tell if thei++
goes into the last field of the for loop or as the last statement in the loop body.
– mtraceur
Dec 14 '18 at 21:06
|
show 2 more comments
As others have already pointed out: The decompiler (usually) cannot distinguish between different source codes that result in the same byte code.
Unfortunately, you did not provide the full code of the method. So the following contains some guesses about where and how this loop appears inside a method (and these guesses might, to some extent, distort the result).
But let's have a look at some roundtrips here. Consider the following class, containing methods with both versions of the code that you posted:
import java.io.BufferedReader;
import java.io.IOException;
import java.util.regex.Pattern;
public class DecompileExample {
public static void methodA(BufferedReader reader) throws IOException {
String line = null;
int i = 0;
while ((line = reader.readLine()) != null) {
System.out.println("line: " + line);
if (i == 0) {
String colArr = line.split(Pattern.quote("|"));
} else {
i++;
}
}
}
public static void methodB(BufferedReader reader) throws IOException {
String line = null;
int i = 0;
for (String colArr = null; (line = reader.readLine()) != null; ++i) {
System.out.println("line: " + line);
if (i == 0) {
colArr = line.split(Pattern.quote("|"));
} else {
}
}
}
}
Compiling it with
javac DecompileExample.java -g:none
will create the corresponding class file. (Note: The -g:none
parameter will cause the compiler to omit all debug information. The debug information might otherwise be used by the decompiler to reconstruct a more verbatim version of the original code, particularly, including the original variable names)
Now looking at the byte code of both methods, with
javap -c DecompileExample.class
will yield the following:
public static void methodA(java.io.BufferedReader) throws java.io.IOException;
Code:
0: aconst_null
1: astore_1
2: iconst_0
3: istore_2
4: aload_0
5: invokevirtual #2 // Method java/io/BufferedReader.readLine:()Ljava/lang/String;
8: dup
9: astore_1
10: ifnull 61
13: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
16: new #4 // class java/lang/StringBuilder
19: dup
20: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
23: ldc #6 // String line:
25: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: aload_1
29: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
32: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
35: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
38: iload_2
39: ifne 55
42: aload_1
43: ldc #10 // String |
45: invokestatic #11 // Method java/util/regex/Pattern.quote:(Ljava/lang/String;)Ljava/lang/String;
48: invokevirtual #12 // Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String;
51: astore_3
52: goto 4
55: iinc 2, 1
58: goto 4
61: return
and
public static void methodB(java.io.BufferedReader) throws java.io.IOException;
Code:
0: aconst_null
1: astore_1
2: iconst_0
3: istore_2
4: aconst_null
5: astore_3
6: aload_0
7: invokevirtual #2 // Method java/io/BufferedReader.readLine:()Ljava/lang/String;
10: dup
11: astore_1
12: ifnull 60
15: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
18: new #4 // class java/lang/StringBuilder
21: dup
22: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
25: ldc #6 // String line:
27: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
30: aload_1
31: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
34: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
37: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
40: iload_2
41: ifne 54
44: aload_1
45: ldc #10 // String |
47: invokestatic #11 // Method java/util/regex/Pattern.quote:(Ljava/lang/String;)Ljava/lang/String;
50: invokevirtual #12 // Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String;
53: astore_3
54: iinc 2, 1
57: goto 6
60: return
}
(There is a small difference: The String colArr = null
is translated into an
aconst null
astore_3
at the beginning of the second version. But this is one of the aspects that is related to parts of the code that you have omitted in the question).
You did not mention which one you are using, but the JD-GUI decompiler from http://jd.benow.ca/ decompiles this into the following:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintStream;
import java.util.regex.Pattern;
public class DecompileExample
{
public static void methodA(BufferedReader paramBufferedReader)
throws IOException
{
String str = null;
int i = 0;
while ((str = paramBufferedReader.readLine()) != null)
{
System.out.println("line: " + str);
if (i == 0) {
String arrayOfString = str.split(Pattern.quote("|"));
} else {
i++;
}
}
}
public static void methodB(BufferedReader paramBufferedReader)
throws IOException
{
String str = null;
int i = 0;
String arrayOfString = null;
while ((str = paramBufferedReader.readLine()) != null)
{
System.out.println("line: " + str);
if (i == 0) {
arrayOfString = str.split(Pattern.quote("|"));
}
i++;
}
}
}
You can see that the code is the same for both cases (at least regarding the loop - there one more is a difference regarding the "dummy variables" that I had to introduce in order to compile it, but this is unrelated to the question, so to speak).
The tl;dr message is clear:
Different source codes can be compiled into the same byte code. Consequently, the same byte code can be decompiled into different source codes. But every decompiler has to settle for one version of the source code.
(A side note: I was a bit surprised to see that when compiling without -g:none
(that is, when the debug information is retained), JD-GUI even somehow manages to reconstruct that the first one used a while
-loop and the second one used a for
-loop. But in general, and when the debug information is omitted, this is simply no longer possible).
I am using the IntelliJ IDEA, which uses the fernflower decompiler. Thanks for the elaborated answer.
– KumarAnkit
Dec 14 '18 at 12:45
@KumarAnkit As indicated by the comments about the-g:none
flag, the result does not only depend on the decompiler, but also on how the.class
files are generated in the first place. (But I assume that IDEs will usually not omit the debug information - in the end, they are basically intended for retaining it in order to use it in some nice debugger UI)
– Marco13
Dec 14 '18 at 12:53
+1 I think you can make it more clear by more explicitly emphasizing how thei++
at the end of the source code's loop body and the++i
at the end of the for-loop declaration map to the same instructions at the end of the bytecode's loop body.
– mtraceur
Dec 14 '18 at 21:08
@mtraceur Yes, more generally: I considered to make the initial code more "sensible", that is, actually do something sensible withi
and thecolArr
. But at some point, this involved so many changes that I was afraid to bury the code that was posted originally under these changes. Not sure about the best solution for this. But if the question was updated with compileable code, I'd update the answer accordingly.
– Marco13
Dec 14 '18 at 22:05
add a comment |
That's basically because of the nature of bytecode. Java bytecode is something like assembly language, so there are no such things as for
and while
loop, there is simply jump instruction: goto
. So there may be no difference between while
and for
loop, Both can be compiled to similar code and decompiler is just making guess.
3
While true, I think a good answer should make an analysis of the actual byte code. I don't have the motivation to do it now so I'll stick to comments, but just sayin'
– Dici
Dec 14 '18 at 11:11
add a comment |
Both the for
loop and the while
loop code segments can be translated into similar machine code. After that when de-compiling the de-compiler has to pick one of the two possible
scenarios.
I guess that is what's happening here.
simply:
compile(A) -> C
compile(B) -> C
So when you are given C
, then there should be a guess to pick A
or B
4
Maybe you could put the actual bytecode of both snippets in your answer to prove your point :p it's the intuitive answer, but it doesn't mean it's right
– Dici
Dec 14 '18 at 11:12
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53778436%2fwhy-is-this-loop-changed%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
In this situation changing while()
to for()
is not an optimization. There is simply no way to know from bytecode which one was used in a source code.
There are many situations when:
while(x)
is the same as:
for(;x;)
Suppose we have a three similar java applications - one with while()
statement, and two with corresponting for()
. First for()
with stopping criterion only like in the standard while()
, and second for()
also with iterator declaration and incrementation.
APPLICATION #1 - SOURCE
public class While{
public static void main(String args) {
int i = 0;
while(i<5){
System.out.println(i);
i++;
}
}
}
APPLICATION #2 - SOURCE
public class For{
public static void main(String args) {
int i = 0;
for(; i<5 ;){
System.out.println(i);
i++;
}
}
}
APPLICATION #3 - SOURCE
public class For2{
public static void main(String args) {
for(int i=0;i<5;i++){
System.out.println(i);
}
}
}
If we compile all of them we have got:
APPLICATION #1 - BYTECODE
public class While {
public While();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iconst_5
4: if_icmpge 20
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
14: iinc 1, 1
17: goto 2
20: return
}
APPLICATION #2 - BYTECODE
public class For {
public For();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iconst_5
4: if_icmpge 20
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
14: iinc 1, 1
17: goto 2
20: return
}
APPLICATION #3 - BYTECODE
public class For2 extends java.lang.Object{
public For2();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iconst_5
4: if_icmpge 20
7: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #3; //Method java/io/PrintStream.println:(I)V
14: iinc 1, 1
17: goto 2
20: return
}
So you can see, there is no difference associated with for
and while
usage.
3
Maybe you could put the actual bytecode of both snippets in your answer to prove your point :p it's the intuitive answer, but it doesn't mean it's right
– Dici
Dec 14 '18 at 11:12
The bytecode will simply have aGOTO
.
– Jörg W Mittag
Dec 14 '18 at 13:44
1
@Dici I've put a sample for better explanation.
– dgebert
Dec 14 '18 at 15:10
@dgebert +1. :p
– Dici
Dec 14 '18 at 19:31
4
I like this answer: I think the demonstration can be improved by moving thei++
into thefor
-loop to make:for(; i<5 ; i++){
. That way you can show that either way, the bytecode puts theiinc
at the end of the loop body at the bytecode level, and so the decompiler can't always tell if thei++
goes into the last field of the for loop or as the last statement in the loop body.
– mtraceur
Dec 14 '18 at 21:06
|
show 2 more comments
In this situation changing while()
to for()
is not an optimization. There is simply no way to know from bytecode which one was used in a source code.
There are many situations when:
while(x)
is the same as:
for(;x;)
Suppose we have a three similar java applications - one with while()
statement, and two with corresponting for()
. First for()
with stopping criterion only like in the standard while()
, and second for()
also with iterator declaration and incrementation.
APPLICATION #1 - SOURCE
public class While{
public static void main(String args) {
int i = 0;
while(i<5){
System.out.println(i);
i++;
}
}
}
APPLICATION #2 - SOURCE
public class For{
public static void main(String args) {
int i = 0;
for(; i<5 ;){
System.out.println(i);
i++;
}
}
}
APPLICATION #3 - SOURCE
public class For2{
public static void main(String args) {
for(int i=0;i<5;i++){
System.out.println(i);
}
}
}
If we compile all of them we have got:
APPLICATION #1 - BYTECODE
public class While {
public While();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iconst_5
4: if_icmpge 20
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
14: iinc 1, 1
17: goto 2
20: return
}
APPLICATION #2 - BYTECODE
public class For {
public For();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iconst_5
4: if_icmpge 20
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
14: iinc 1, 1
17: goto 2
20: return
}
APPLICATION #3 - BYTECODE
public class For2 extends java.lang.Object{
public For2();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iconst_5
4: if_icmpge 20
7: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #3; //Method java/io/PrintStream.println:(I)V
14: iinc 1, 1
17: goto 2
20: return
}
So you can see, there is no difference associated with for
and while
usage.
3
Maybe you could put the actual bytecode of both snippets in your answer to prove your point :p it's the intuitive answer, but it doesn't mean it's right
– Dici
Dec 14 '18 at 11:12
The bytecode will simply have aGOTO
.
– Jörg W Mittag
Dec 14 '18 at 13:44
1
@Dici I've put a sample for better explanation.
– dgebert
Dec 14 '18 at 15:10
@dgebert +1. :p
– Dici
Dec 14 '18 at 19:31
4
I like this answer: I think the demonstration can be improved by moving thei++
into thefor
-loop to make:for(; i<5 ; i++){
. That way you can show that either way, the bytecode puts theiinc
at the end of the loop body at the bytecode level, and so the decompiler can't always tell if thei++
goes into the last field of the for loop or as the last statement in the loop body.
– mtraceur
Dec 14 '18 at 21:06
|
show 2 more comments
In this situation changing while()
to for()
is not an optimization. There is simply no way to know from bytecode which one was used in a source code.
There are many situations when:
while(x)
is the same as:
for(;x;)
Suppose we have a three similar java applications - one with while()
statement, and two with corresponting for()
. First for()
with stopping criterion only like in the standard while()
, and second for()
also with iterator declaration and incrementation.
APPLICATION #1 - SOURCE
public class While{
public static void main(String args) {
int i = 0;
while(i<5){
System.out.println(i);
i++;
}
}
}
APPLICATION #2 - SOURCE
public class For{
public static void main(String args) {
int i = 0;
for(; i<5 ;){
System.out.println(i);
i++;
}
}
}
APPLICATION #3 - SOURCE
public class For2{
public static void main(String args) {
for(int i=0;i<5;i++){
System.out.println(i);
}
}
}
If we compile all of them we have got:
APPLICATION #1 - BYTECODE
public class While {
public While();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iconst_5
4: if_icmpge 20
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
14: iinc 1, 1
17: goto 2
20: return
}
APPLICATION #2 - BYTECODE
public class For {
public For();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iconst_5
4: if_icmpge 20
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
14: iinc 1, 1
17: goto 2
20: return
}
APPLICATION #3 - BYTECODE
public class For2 extends java.lang.Object{
public For2();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iconst_5
4: if_icmpge 20
7: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #3; //Method java/io/PrintStream.println:(I)V
14: iinc 1, 1
17: goto 2
20: return
}
So you can see, there is no difference associated with for
and while
usage.
In this situation changing while()
to for()
is not an optimization. There is simply no way to know from bytecode which one was used in a source code.
There are many situations when:
while(x)
is the same as:
for(;x;)
Suppose we have a three similar java applications - one with while()
statement, and two with corresponting for()
. First for()
with stopping criterion only like in the standard while()
, and second for()
also with iterator declaration and incrementation.
APPLICATION #1 - SOURCE
public class While{
public static void main(String args) {
int i = 0;
while(i<5){
System.out.println(i);
i++;
}
}
}
APPLICATION #2 - SOURCE
public class For{
public static void main(String args) {
int i = 0;
for(; i<5 ;){
System.out.println(i);
i++;
}
}
}
APPLICATION #3 - SOURCE
public class For2{
public static void main(String args) {
for(int i=0;i<5;i++){
System.out.println(i);
}
}
}
If we compile all of them we have got:
APPLICATION #1 - BYTECODE
public class While {
public While();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iconst_5
4: if_icmpge 20
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
14: iinc 1, 1
17: goto 2
20: return
}
APPLICATION #2 - BYTECODE
public class For {
public For();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iconst_5
4: if_icmpge 20
7: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
14: iinc 1, 1
17: goto 2
20: return
}
APPLICATION #3 - BYTECODE
public class For2 extends java.lang.Object{
public For2();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String);
Code:
0: iconst_0
1: istore_1
2: iload_1
3: iconst_5
4: if_icmpge 20
7: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
10: iload_1
11: invokevirtual #3; //Method java/io/PrintStream.println:(I)V
14: iinc 1, 1
17: goto 2
20: return
}
So you can see, there is no difference associated with for
and while
usage.
edited Dec 23 '18 at 21:04
answered Dec 14 '18 at 11:08
dgebertdgebert
732513
732513
3
Maybe you could put the actual bytecode of both snippets in your answer to prove your point :p it's the intuitive answer, but it doesn't mean it's right
– Dici
Dec 14 '18 at 11:12
The bytecode will simply have aGOTO
.
– Jörg W Mittag
Dec 14 '18 at 13:44
1
@Dici I've put a sample for better explanation.
– dgebert
Dec 14 '18 at 15:10
@dgebert +1. :p
– Dici
Dec 14 '18 at 19:31
4
I like this answer: I think the demonstration can be improved by moving thei++
into thefor
-loop to make:for(; i<5 ; i++){
. That way you can show that either way, the bytecode puts theiinc
at the end of the loop body at the bytecode level, and so the decompiler can't always tell if thei++
goes into the last field of the for loop or as the last statement in the loop body.
– mtraceur
Dec 14 '18 at 21:06
|
show 2 more comments
3
Maybe you could put the actual bytecode of both snippets in your answer to prove your point :p it's the intuitive answer, but it doesn't mean it's right
– Dici
Dec 14 '18 at 11:12
The bytecode will simply have aGOTO
.
– Jörg W Mittag
Dec 14 '18 at 13:44
1
@Dici I've put a sample for better explanation.
– dgebert
Dec 14 '18 at 15:10
@dgebert +1. :p
– Dici
Dec 14 '18 at 19:31
4
I like this answer: I think the demonstration can be improved by moving thei++
into thefor
-loop to make:for(; i<5 ; i++){
. That way you can show that either way, the bytecode puts theiinc
at the end of the loop body at the bytecode level, and so the decompiler can't always tell if thei++
goes into the last field of the for loop or as the last statement in the loop body.
– mtraceur
Dec 14 '18 at 21:06
3
3
Maybe you could put the actual bytecode of both snippets in your answer to prove your point :p it's the intuitive answer, but it doesn't mean it's right
– Dici
Dec 14 '18 at 11:12
Maybe you could put the actual bytecode of both snippets in your answer to prove your point :p it's the intuitive answer, but it doesn't mean it's right
– Dici
Dec 14 '18 at 11:12
The bytecode will simply have a
GOTO
.– Jörg W Mittag
Dec 14 '18 at 13:44
The bytecode will simply have a
GOTO
.– Jörg W Mittag
Dec 14 '18 at 13:44
1
1
@Dici I've put a sample for better explanation.
– dgebert
Dec 14 '18 at 15:10
@Dici I've put a sample for better explanation.
– dgebert
Dec 14 '18 at 15:10
@dgebert +1. :p
– Dici
Dec 14 '18 at 19:31
@dgebert +1. :p
– Dici
Dec 14 '18 at 19:31
4
4
I like this answer: I think the demonstration can be improved by moving the
i++
into the for
-loop to make: for(; i<5 ; i++){
. That way you can show that either way, the bytecode puts the iinc
at the end of the loop body at the bytecode level, and so the decompiler can't always tell if the i++
goes into the last field of the for loop or as the last statement in the loop body.– mtraceur
Dec 14 '18 at 21:06
I like this answer: I think the demonstration can be improved by moving the
i++
into the for
-loop to make: for(; i<5 ; i++){
. That way you can show that either way, the bytecode puts the iinc
at the end of the loop body at the bytecode level, and so the decompiler can't always tell if the i++
goes into the last field of the for loop or as the last statement in the loop body.– mtraceur
Dec 14 '18 at 21:06
|
show 2 more comments
As others have already pointed out: The decompiler (usually) cannot distinguish between different source codes that result in the same byte code.
Unfortunately, you did not provide the full code of the method. So the following contains some guesses about where and how this loop appears inside a method (and these guesses might, to some extent, distort the result).
But let's have a look at some roundtrips here. Consider the following class, containing methods with both versions of the code that you posted:
import java.io.BufferedReader;
import java.io.IOException;
import java.util.regex.Pattern;
public class DecompileExample {
public static void methodA(BufferedReader reader) throws IOException {
String line = null;
int i = 0;
while ((line = reader.readLine()) != null) {
System.out.println("line: " + line);
if (i == 0) {
String colArr = line.split(Pattern.quote("|"));
} else {
i++;
}
}
}
public static void methodB(BufferedReader reader) throws IOException {
String line = null;
int i = 0;
for (String colArr = null; (line = reader.readLine()) != null; ++i) {
System.out.println("line: " + line);
if (i == 0) {
colArr = line.split(Pattern.quote("|"));
} else {
}
}
}
}
Compiling it with
javac DecompileExample.java -g:none
will create the corresponding class file. (Note: The -g:none
parameter will cause the compiler to omit all debug information. The debug information might otherwise be used by the decompiler to reconstruct a more verbatim version of the original code, particularly, including the original variable names)
Now looking at the byte code of both methods, with
javap -c DecompileExample.class
will yield the following:
public static void methodA(java.io.BufferedReader) throws java.io.IOException;
Code:
0: aconst_null
1: astore_1
2: iconst_0
3: istore_2
4: aload_0
5: invokevirtual #2 // Method java/io/BufferedReader.readLine:()Ljava/lang/String;
8: dup
9: astore_1
10: ifnull 61
13: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
16: new #4 // class java/lang/StringBuilder
19: dup
20: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
23: ldc #6 // String line:
25: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: aload_1
29: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
32: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
35: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
38: iload_2
39: ifne 55
42: aload_1
43: ldc #10 // String |
45: invokestatic #11 // Method java/util/regex/Pattern.quote:(Ljava/lang/String;)Ljava/lang/String;
48: invokevirtual #12 // Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String;
51: astore_3
52: goto 4
55: iinc 2, 1
58: goto 4
61: return
and
public static void methodB(java.io.BufferedReader) throws java.io.IOException;
Code:
0: aconst_null
1: astore_1
2: iconst_0
3: istore_2
4: aconst_null
5: astore_3
6: aload_0
7: invokevirtual #2 // Method java/io/BufferedReader.readLine:()Ljava/lang/String;
10: dup
11: astore_1
12: ifnull 60
15: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
18: new #4 // class java/lang/StringBuilder
21: dup
22: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
25: ldc #6 // String line:
27: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
30: aload_1
31: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
34: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
37: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
40: iload_2
41: ifne 54
44: aload_1
45: ldc #10 // String |
47: invokestatic #11 // Method java/util/regex/Pattern.quote:(Ljava/lang/String;)Ljava/lang/String;
50: invokevirtual #12 // Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String;
53: astore_3
54: iinc 2, 1
57: goto 6
60: return
}
(There is a small difference: The String colArr = null
is translated into an
aconst null
astore_3
at the beginning of the second version. But this is one of the aspects that is related to parts of the code that you have omitted in the question).
You did not mention which one you are using, but the JD-GUI decompiler from http://jd.benow.ca/ decompiles this into the following:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintStream;
import java.util.regex.Pattern;
public class DecompileExample
{
public static void methodA(BufferedReader paramBufferedReader)
throws IOException
{
String str = null;
int i = 0;
while ((str = paramBufferedReader.readLine()) != null)
{
System.out.println("line: " + str);
if (i == 0) {
String arrayOfString = str.split(Pattern.quote("|"));
} else {
i++;
}
}
}
public static void methodB(BufferedReader paramBufferedReader)
throws IOException
{
String str = null;
int i = 0;
String arrayOfString = null;
while ((str = paramBufferedReader.readLine()) != null)
{
System.out.println("line: " + str);
if (i == 0) {
arrayOfString = str.split(Pattern.quote("|"));
}
i++;
}
}
}
You can see that the code is the same for both cases (at least regarding the loop - there one more is a difference regarding the "dummy variables" that I had to introduce in order to compile it, but this is unrelated to the question, so to speak).
The tl;dr message is clear:
Different source codes can be compiled into the same byte code. Consequently, the same byte code can be decompiled into different source codes. But every decompiler has to settle for one version of the source code.
(A side note: I was a bit surprised to see that when compiling without -g:none
(that is, when the debug information is retained), JD-GUI even somehow manages to reconstruct that the first one used a while
-loop and the second one used a for
-loop. But in general, and when the debug information is omitted, this is simply no longer possible).
I am using the IntelliJ IDEA, which uses the fernflower decompiler. Thanks for the elaborated answer.
– KumarAnkit
Dec 14 '18 at 12:45
@KumarAnkit As indicated by the comments about the-g:none
flag, the result does not only depend on the decompiler, but also on how the.class
files are generated in the first place. (But I assume that IDEs will usually not omit the debug information - in the end, they are basically intended for retaining it in order to use it in some nice debugger UI)
– Marco13
Dec 14 '18 at 12:53
+1 I think you can make it more clear by more explicitly emphasizing how thei++
at the end of the source code's loop body and the++i
at the end of the for-loop declaration map to the same instructions at the end of the bytecode's loop body.
– mtraceur
Dec 14 '18 at 21:08
@mtraceur Yes, more generally: I considered to make the initial code more "sensible", that is, actually do something sensible withi
and thecolArr
. But at some point, this involved so many changes that I was afraid to bury the code that was posted originally under these changes. Not sure about the best solution for this. But if the question was updated with compileable code, I'd update the answer accordingly.
– Marco13
Dec 14 '18 at 22:05
add a comment |
As others have already pointed out: The decompiler (usually) cannot distinguish between different source codes that result in the same byte code.
Unfortunately, you did not provide the full code of the method. So the following contains some guesses about where and how this loop appears inside a method (and these guesses might, to some extent, distort the result).
But let's have a look at some roundtrips here. Consider the following class, containing methods with both versions of the code that you posted:
import java.io.BufferedReader;
import java.io.IOException;
import java.util.regex.Pattern;
public class DecompileExample {
public static void methodA(BufferedReader reader) throws IOException {
String line = null;
int i = 0;
while ((line = reader.readLine()) != null) {
System.out.println("line: " + line);
if (i == 0) {
String colArr = line.split(Pattern.quote("|"));
} else {
i++;
}
}
}
public static void methodB(BufferedReader reader) throws IOException {
String line = null;
int i = 0;
for (String colArr = null; (line = reader.readLine()) != null; ++i) {
System.out.println("line: " + line);
if (i == 0) {
colArr = line.split(Pattern.quote("|"));
} else {
}
}
}
}
Compiling it with
javac DecompileExample.java -g:none
will create the corresponding class file. (Note: The -g:none
parameter will cause the compiler to omit all debug information. The debug information might otherwise be used by the decompiler to reconstruct a more verbatim version of the original code, particularly, including the original variable names)
Now looking at the byte code of both methods, with
javap -c DecompileExample.class
will yield the following:
public static void methodA(java.io.BufferedReader) throws java.io.IOException;
Code:
0: aconst_null
1: astore_1
2: iconst_0
3: istore_2
4: aload_0
5: invokevirtual #2 // Method java/io/BufferedReader.readLine:()Ljava/lang/String;
8: dup
9: astore_1
10: ifnull 61
13: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
16: new #4 // class java/lang/StringBuilder
19: dup
20: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
23: ldc #6 // String line:
25: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: aload_1
29: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
32: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
35: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
38: iload_2
39: ifne 55
42: aload_1
43: ldc #10 // String |
45: invokestatic #11 // Method java/util/regex/Pattern.quote:(Ljava/lang/String;)Ljava/lang/String;
48: invokevirtual #12 // Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String;
51: astore_3
52: goto 4
55: iinc 2, 1
58: goto 4
61: return
and
public static void methodB(java.io.BufferedReader) throws java.io.IOException;
Code:
0: aconst_null
1: astore_1
2: iconst_0
3: istore_2
4: aconst_null
5: astore_3
6: aload_0
7: invokevirtual #2 // Method java/io/BufferedReader.readLine:()Ljava/lang/String;
10: dup
11: astore_1
12: ifnull 60
15: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
18: new #4 // class java/lang/StringBuilder
21: dup
22: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
25: ldc #6 // String line:
27: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
30: aload_1
31: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
34: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
37: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
40: iload_2
41: ifne 54
44: aload_1
45: ldc #10 // String |
47: invokestatic #11 // Method java/util/regex/Pattern.quote:(Ljava/lang/String;)Ljava/lang/String;
50: invokevirtual #12 // Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String;
53: astore_3
54: iinc 2, 1
57: goto 6
60: return
}
(There is a small difference: The String colArr = null
is translated into an
aconst null
astore_3
at the beginning of the second version. But this is one of the aspects that is related to parts of the code that you have omitted in the question).
You did not mention which one you are using, but the JD-GUI decompiler from http://jd.benow.ca/ decompiles this into the following:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintStream;
import java.util.regex.Pattern;
public class DecompileExample
{
public static void methodA(BufferedReader paramBufferedReader)
throws IOException
{
String str = null;
int i = 0;
while ((str = paramBufferedReader.readLine()) != null)
{
System.out.println("line: " + str);
if (i == 0) {
String arrayOfString = str.split(Pattern.quote("|"));
} else {
i++;
}
}
}
public static void methodB(BufferedReader paramBufferedReader)
throws IOException
{
String str = null;
int i = 0;
String arrayOfString = null;
while ((str = paramBufferedReader.readLine()) != null)
{
System.out.println("line: " + str);
if (i == 0) {
arrayOfString = str.split(Pattern.quote("|"));
}
i++;
}
}
}
You can see that the code is the same for both cases (at least regarding the loop - there one more is a difference regarding the "dummy variables" that I had to introduce in order to compile it, but this is unrelated to the question, so to speak).
The tl;dr message is clear:
Different source codes can be compiled into the same byte code. Consequently, the same byte code can be decompiled into different source codes. But every decompiler has to settle for one version of the source code.
(A side note: I was a bit surprised to see that when compiling without -g:none
(that is, when the debug information is retained), JD-GUI even somehow manages to reconstruct that the first one used a while
-loop and the second one used a for
-loop. But in general, and when the debug information is omitted, this is simply no longer possible).
I am using the IntelliJ IDEA, which uses the fernflower decompiler. Thanks for the elaborated answer.
– KumarAnkit
Dec 14 '18 at 12:45
@KumarAnkit As indicated by the comments about the-g:none
flag, the result does not only depend on the decompiler, but also on how the.class
files are generated in the first place. (But I assume that IDEs will usually not omit the debug information - in the end, they are basically intended for retaining it in order to use it in some nice debugger UI)
– Marco13
Dec 14 '18 at 12:53
+1 I think you can make it more clear by more explicitly emphasizing how thei++
at the end of the source code's loop body and the++i
at the end of the for-loop declaration map to the same instructions at the end of the bytecode's loop body.
– mtraceur
Dec 14 '18 at 21:08
@mtraceur Yes, more generally: I considered to make the initial code more "sensible", that is, actually do something sensible withi
and thecolArr
. But at some point, this involved so many changes that I was afraid to bury the code that was posted originally under these changes. Not sure about the best solution for this. But if the question was updated with compileable code, I'd update the answer accordingly.
– Marco13
Dec 14 '18 at 22:05
add a comment |
As others have already pointed out: The decompiler (usually) cannot distinguish between different source codes that result in the same byte code.
Unfortunately, you did not provide the full code of the method. So the following contains some guesses about where and how this loop appears inside a method (and these guesses might, to some extent, distort the result).
But let's have a look at some roundtrips here. Consider the following class, containing methods with both versions of the code that you posted:
import java.io.BufferedReader;
import java.io.IOException;
import java.util.regex.Pattern;
public class DecompileExample {
public static void methodA(BufferedReader reader) throws IOException {
String line = null;
int i = 0;
while ((line = reader.readLine()) != null) {
System.out.println("line: " + line);
if (i == 0) {
String colArr = line.split(Pattern.quote("|"));
} else {
i++;
}
}
}
public static void methodB(BufferedReader reader) throws IOException {
String line = null;
int i = 0;
for (String colArr = null; (line = reader.readLine()) != null; ++i) {
System.out.println("line: " + line);
if (i == 0) {
colArr = line.split(Pattern.quote("|"));
} else {
}
}
}
}
Compiling it with
javac DecompileExample.java -g:none
will create the corresponding class file. (Note: The -g:none
parameter will cause the compiler to omit all debug information. The debug information might otherwise be used by the decompiler to reconstruct a more verbatim version of the original code, particularly, including the original variable names)
Now looking at the byte code of both methods, with
javap -c DecompileExample.class
will yield the following:
public static void methodA(java.io.BufferedReader) throws java.io.IOException;
Code:
0: aconst_null
1: astore_1
2: iconst_0
3: istore_2
4: aload_0
5: invokevirtual #2 // Method java/io/BufferedReader.readLine:()Ljava/lang/String;
8: dup
9: astore_1
10: ifnull 61
13: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
16: new #4 // class java/lang/StringBuilder
19: dup
20: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
23: ldc #6 // String line:
25: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: aload_1
29: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
32: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
35: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
38: iload_2
39: ifne 55
42: aload_1
43: ldc #10 // String |
45: invokestatic #11 // Method java/util/regex/Pattern.quote:(Ljava/lang/String;)Ljava/lang/String;
48: invokevirtual #12 // Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String;
51: astore_3
52: goto 4
55: iinc 2, 1
58: goto 4
61: return
and
public static void methodB(java.io.BufferedReader) throws java.io.IOException;
Code:
0: aconst_null
1: astore_1
2: iconst_0
3: istore_2
4: aconst_null
5: astore_3
6: aload_0
7: invokevirtual #2 // Method java/io/BufferedReader.readLine:()Ljava/lang/String;
10: dup
11: astore_1
12: ifnull 60
15: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
18: new #4 // class java/lang/StringBuilder
21: dup
22: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
25: ldc #6 // String line:
27: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
30: aload_1
31: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
34: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
37: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
40: iload_2
41: ifne 54
44: aload_1
45: ldc #10 // String |
47: invokestatic #11 // Method java/util/regex/Pattern.quote:(Ljava/lang/String;)Ljava/lang/String;
50: invokevirtual #12 // Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String;
53: astore_3
54: iinc 2, 1
57: goto 6
60: return
}
(There is a small difference: The String colArr = null
is translated into an
aconst null
astore_3
at the beginning of the second version. But this is one of the aspects that is related to parts of the code that you have omitted in the question).
You did not mention which one you are using, but the JD-GUI decompiler from http://jd.benow.ca/ decompiles this into the following:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintStream;
import java.util.regex.Pattern;
public class DecompileExample
{
public static void methodA(BufferedReader paramBufferedReader)
throws IOException
{
String str = null;
int i = 0;
while ((str = paramBufferedReader.readLine()) != null)
{
System.out.println("line: " + str);
if (i == 0) {
String arrayOfString = str.split(Pattern.quote("|"));
} else {
i++;
}
}
}
public static void methodB(BufferedReader paramBufferedReader)
throws IOException
{
String str = null;
int i = 0;
String arrayOfString = null;
while ((str = paramBufferedReader.readLine()) != null)
{
System.out.println("line: " + str);
if (i == 0) {
arrayOfString = str.split(Pattern.quote("|"));
}
i++;
}
}
}
You can see that the code is the same for both cases (at least regarding the loop - there one more is a difference regarding the "dummy variables" that I had to introduce in order to compile it, but this is unrelated to the question, so to speak).
The tl;dr message is clear:
Different source codes can be compiled into the same byte code. Consequently, the same byte code can be decompiled into different source codes. But every decompiler has to settle for one version of the source code.
(A side note: I was a bit surprised to see that when compiling without -g:none
(that is, when the debug information is retained), JD-GUI even somehow manages to reconstruct that the first one used a while
-loop and the second one used a for
-loop. But in general, and when the debug information is omitted, this is simply no longer possible).
As others have already pointed out: The decompiler (usually) cannot distinguish between different source codes that result in the same byte code.
Unfortunately, you did not provide the full code of the method. So the following contains some guesses about where and how this loop appears inside a method (and these guesses might, to some extent, distort the result).
But let's have a look at some roundtrips here. Consider the following class, containing methods with both versions of the code that you posted:
import java.io.BufferedReader;
import java.io.IOException;
import java.util.regex.Pattern;
public class DecompileExample {
public static void methodA(BufferedReader reader) throws IOException {
String line = null;
int i = 0;
while ((line = reader.readLine()) != null) {
System.out.println("line: " + line);
if (i == 0) {
String colArr = line.split(Pattern.quote("|"));
} else {
i++;
}
}
}
public static void methodB(BufferedReader reader) throws IOException {
String line = null;
int i = 0;
for (String colArr = null; (line = reader.readLine()) != null; ++i) {
System.out.println("line: " + line);
if (i == 0) {
colArr = line.split(Pattern.quote("|"));
} else {
}
}
}
}
Compiling it with
javac DecompileExample.java -g:none
will create the corresponding class file. (Note: The -g:none
parameter will cause the compiler to omit all debug information. The debug information might otherwise be used by the decompiler to reconstruct a more verbatim version of the original code, particularly, including the original variable names)
Now looking at the byte code of both methods, with
javap -c DecompileExample.class
will yield the following:
public static void methodA(java.io.BufferedReader) throws java.io.IOException;
Code:
0: aconst_null
1: astore_1
2: iconst_0
3: istore_2
4: aload_0
5: invokevirtual #2 // Method java/io/BufferedReader.readLine:()Ljava/lang/String;
8: dup
9: astore_1
10: ifnull 61
13: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
16: new #4 // class java/lang/StringBuilder
19: dup
20: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
23: ldc #6 // String line:
25: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: aload_1
29: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
32: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
35: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
38: iload_2
39: ifne 55
42: aload_1
43: ldc #10 // String |
45: invokestatic #11 // Method java/util/regex/Pattern.quote:(Ljava/lang/String;)Ljava/lang/String;
48: invokevirtual #12 // Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String;
51: astore_3
52: goto 4
55: iinc 2, 1
58: goto 4
61: return
and
public static void methodB(java.io.BufferedReader) throws java.io.IOException;
Code:
0: aconst_null
1: astore_1
2: iconst_0
3: istore_2
4: aconst_null
5: astore_3
6: aload_0
7: invokevirtual #2 // Method java/io/BufferedReader.readLine:()Ljava/lang/String;
10: dup
11: astore_1
12: ifnull 60
15: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
18: new #4 // class java/lang/StringBuilder
21: dup
22: invokespecial #5 // Method java/lang/StringBuilder."<init>":()V
25: ldc #6 // String line:
27: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
30: aload_1
31: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
34: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
37: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
40: iload_2
41: ifne 54
44: aload_1
45: ldc #10 // String |
47: invokestatic #11 // Method java/util/regex/Pattern.quote:(Ljava/lang/String;)Ljava/lang/String;
50: invokevirtual #12 // Method java/lang/String.split:(Ljava/lang/String;)[Ljava/lang/String;
53: astore_3
54: iinc 2, 1
57: goto 6
60: return
}
(There is a small difference: The String colArr = null
is translated into an
aconst null
astore_3
at the beginning of the second version. But this is one of the aspects that is related to parts of the code that you have omitted in the question).
You did not mention which one you are using, but the JD-GUI decompiler from http://jd.benow.ca/ decompiles this into the following:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintStream;
import java.util.regex.Pattern;
public class DecompileExample
{
public static void methodA(BufferedReader paramBufferedReader)
throws IOException
{
String str = null;
int i = 0;
while ((str = paramBufferedReader.readLine()) != null)
{
System.out.println("line: " + str);
if (i == 0) {
String arrayOfString = str.split(Pattern.quote("|"));
} else {
i++;
}
}
}
public static void methodB(BufferedReader paramBufferedReader)
throws IOException
{
String str = null;
int i = 0;
String arrayOfString = null;
while ((str = paramBufferedReader.readLine()) != null)
{
System.out.println("line: " + str);
if (i == 0) {
arrayOfString = str.split(Pattern.quote("|"));
}
i++;
}
}
}
You can see that the code is the same for both cases (at least regarding the loop - there one more is a difference regarding the "dummy variables" that I had to introduce in order to compile it, but this is unrelated to the question, so to speak).
The tl;dr message is clear:
Different source codes can be compiled into the same byte code. Consequently, the same byte code can be decompiled into different source codes. But every decompiler has to settle for one version of the source code.
(A side note: I was a bit surprised to see that when compiling without -g:none
(that is, when the debug information is retained), JD-GUI even somehow manages to reconstruct that the first one used a while
-loop and the second one used a for
-loop. But in general, and when the debug information is omitted, this is simply no longer possible).
edited Dec 14 '18 at 14:50
answered Dec 14 '18 at 12:41
Marco13Marco13
42k855108
42k855108
I am using the IntelliJ IDEA, which uses the fernflower decompiler. Thanks for the elaborated answer.
– KumarAnkit
Dec 14 '18 at 12:45
@KumarAnkit As indicated by the comments about the-g:none
flag, the result does not only depend on the decompiler, but also on how the.class
files are generated in the first place. (But I assume that IDEs will usually not omit the debug information - in the end, they are basically intended for retaining it in order to use it in some nice debugger UI)
– Marco13
Dec 14 '18 at 12:53
+1 I think you can make it more clear by more explicitly emphasizing how thei++
at the end of the source code's loop body and the++i
at the end of the for-loop declaration map to the same instructions at the end of the bytecode's loop body.
– mtraceur
Dec 14 '18 at 21:08
@mtraceur Yes, more generally: I considered to make the initial code more "sensible", that is, actually do something sensible withi
and thecolArr
. But at some point, this involved so many changes that I was afraid to bury the code that was posted originally under these changes. Not sure about the best solution for this. But if the question was updated with compileable code, I'd update the answer accordingly.
– Marco13
Dec 14 '18 at 22:05
add a comment |
I am using the IntelliJ IDEA, which uses the fernflower decompiler. Thanks for the elaborated answer.
– KumarAnkit
Dec 14 '18 at 12:45
@KumarAnkit As indicated by the comments about the-g:none
flag, the result does not only depend on the decompiler, but also on how the.class
files are generated in the first place. (But I assume that IDEs will usually not omit the debug information - in the end, they are basically intended for retaining it in order to use it in some nice debugger UI)
– Marco13
Dec 14 '18 at 12:53
+1 I think you can make it more clear by more explicitly emphasizing how thei++
at the end of the source code's loop body and the++i
at the end of the for-loop declaration map to the same instructions at the end of the bytecode's loop body.
– mtraceur
Dec 14 '18 at 21:08
@mtraceur Yes, more generally: I considered to make the initial code more "sensible", that is, actually do something sensible withi
and thecolArr
. But at some point, this involved so many changes that I was afraid to bury the code that was posted originally under these changes. Not sure about the best solution for this. But if the question was updated with compileable code, I'd update the answer accordingly.
– Marco13
Dec 14 '18 at 22:05
I am using the IntelliJ IDEA, which uses the fernflower decompiler. Thanks for the elaborated answer.
– KumarAnkit
Dec 14 '18 at 12:45
I am using the IntelliJ IDEA, which uses the fernflower decompiler. Thanks for the elaborated answer.
– KumarAnkit
Dec 14 '18 at 12:45
@KumarAnkit As indicated by the comments about the
-g:none
flag, the result does not only depend on the decompiler, but also on how the .class
files are generated in the first place. (But I assume that IDEs will usually not omit the debug information - in the end, they are basically intended for retaining it in order to use it in some nice debugger UI)– Marco13
Dec 14 '18 at 12:53
@KumarAnkit As indicated by the comments about the
-g:none
flag, the result does not only depend on the decompiler, but also on how the .class
files are generated in the first place. (But I assume that IDEs will usually not omit the debug information - in the end, they are basically intended for retaining it in order to use it in some nice debugger UI)– Marco13
Dec 14 '18 at 12:53
+1 I think you can make it more clear by more explicitly emphasizing how the
i++
at the end of the source code's loop body and the ++i
at the end of the for-loop declaration map to the same instructions at the end of the bytecode's loop body.– mtraceur
Dec 14 '18 at 21:08
+1 I think you can make it more clear by more explicitly emphasizing how the
i++
at the end of the source code's loop body and the ++i
at the end of the for-loop declaration map to the same instructions at the end of the bytecode's loop body.– mtraceur
Dec 14 '18 at 21:08
@mtraceur Yes, more generally: I considered to make the initial code more "sensible", that is, actually do something sensible with
i
and the colArr
. But at some point, this involved so many changes that I was afraid to bury the code that was posted originally under these changes. Not sure about the best solution for this. But if the question was updated with compileable code, I'd update the answer accordingly.– Marco13
Dec 14 '18 at 22:05
@mtraceur Yes, more generally: I considered to make the initial code more "sensible", that is, actually do something sensible with
i
and the colArr
. But at some point, this involved so many changes that I was afraid to bury the code that was posted originally under these changes. Not sure about the best solution for this. But if the question was updated with compileable code, I'd update the answer accordingly.– Marco13
Dec 14 '18 at 22:05
add a comment |
That's basically because of the nature of bytecode. Java bytecode is something like assembly language, so there are no such things as for
and while
loop, there is simply jump instruction: goto
. So there may be no difference between while
and for
loop, Both can be compiled to similar code and decompiler is just making guess.
3
While true, I think a good answer should make an analysis of the actual byte code. I don't have the motivation to do it now so I'll stick to comments, but just sayin'
– Dici
Dec 14 '18 at 11:11
add a comment |
That's basically because of the nature of bytecode. Java bytecode is something like assembly language, so there are no such things as for
and while
loop, there is simply jump instruction: goto
. So there may be no difference between while
and for
loop, Both can be compiled to similar code and decompiler is just making guess.
3
While true, I think a good answer should make an analysis of the actual byte code. I don't have the motivation to do it now so I'll stick to comments, but just sayin'
– Dici
Dec 14 '18 at 11:11
add a comment |
That's basically because of the nature of bytecode. Java bytecode is something like assembly language, so there are no such things as for
and while
loop, there is simply jump instruction: goto
. So there may be no difference between while
and for
loop, Both can be compiled to similar code and decompiler is just making guess.
That's basically because of the nature of bytecode. Java bytecode is something like assembly language, so there are no such things as for
and while
loop, there is simply jump instruction: goto
. So there may be no difference between while
and for
loop, Both can be compiled to similar code and decompiler is just making guess.
edited Dec 14 '18 at 13:46
Andrei Suvorkov
4,1574929
4,1574929
answered Dec 14 '18 at 11:09
Wojtek MlodzianowskiWojtek Mlodzianowski
308113
308113
3
While true, I think a good answer should make an analysis of the actual byte code. I don't have the motivation to do it now so I'll stick to comments, but just sayin'
– Dici
Dec 14 '18 at 11:11
add a comment |
3
While true, I think a good answer should make an analysis of the actual byte code. I don't have the motivation to do it now so I'll stick to comments, but just sayin'
– Dici
Dec 14 '18 at 11:11
3
3
While true, I think a good answer should make an analysis of the actual byte code. I don't have the motivation to do it now so I'll stick to comments, but just sayin'
– Dici
Dec 14 '18 at 11:11
While true, I think a good answer should make an analysis of the actual byte code. I don't have the motivation to do it now so I'll stick to comments, but just sayin'
– Dici
Dec 14 '18 at 11:11
add a comment |
Both the for
loop and the while
loop code segments can be translated into similar machine code. After that when de-compiling the de-compiler has to pick one of the two possible
scenarios.
I guess that is what's happening here.
simply:
compile(A) -> C
compile(B) -> C
So when you are given C
, then there should be a guess to pick A
or B
4
Maybe you could put the actual bytecode of both snippets in your answer to prove your point :p it's the intuitive answer, but it doesn't mean it's right
– Dici
Dec 14 '18 at 11:12
add a comment |
Both the for
loop and the while
loop code segments can be translated into similar machine code. After that when de-compiling the de-compiler has to pick one of the two possible
scenarios.
I guess that is what's happening here.
simply:
compile(A) -> C
compile(B) -> C
So when you are given C
, then there should be a guess to pick A
or B
4
Maybe you could put the actual bytecode of both snippets in your answer to prove your point :p it's the intuitive answer, but it doesn't mean it's right
– Dici
Dec 14 '18 at 11:12
add a comment |
Both the for
loop and the while
loop code segments can be translated into similar machine code. After that when de-compiling the de-compiler has to pick one of the two possible
scenarios.
I guess that is what's happening here.
simply:
compile(A) -> C
compile(B) -> C
So when you are given C
, then there should be a guess to pick A
or B
Both the for
loop and the while
loop code segments can be translated into similar machine code. After that when de-compiling the de-compiler has to pick one of the two possible
scenarios.
I guess that is what's happening here.
simply:
compile(A) -> C
compile(B) -> C
So when you are given C
, then there should be a guess to pick A
or B
edited Dec 14 '18 at 11:39
meowgoesthedog
9,20131226
9,20131226
answered Dec 14 '18 at 11:11
primeprime
4,75964275
4,75964275
4
Maybe you could put the actual bytecode of both snippets in your answer to prove your point :p it's the intuitive answer, but it doesn't mean it's right
– Dici
Dec 14 '18 at 11:12
add a comment |
4
Maybe you could put the actual bytecode of both snippets in your answer to prove your point :p it's the intuitive answer, but it doesn't mean it's right
– Dici
Dec 14 '18 at 11:12
4
4
Maybe you could put the actual bytecode of both snippets in your answer to prove your point :p it's the intuitive answer, but it doesn't mean it's right
– Dici
Dec 14 '18 at 11:12
Maybe you could put the actual bytecode of both snippets in your answer to prove your point :p it's the intuitive answer, but it doesn't mean it's right
– Dici
Dec 14 '18 at 11:12
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53778436%2fwhy-is-this-loop-changed%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
23
Hard to tell from source code along, but it might just be a choice of the decompiler to show you this version. Note that a decompiler has to "guess" a bit - it chooses one possible source code that might lead to the given bytecode.
– Hulk
Dec 14 '18 at 11:02
6
@KumarAnkit - No necessarily optimization, no. There isn't a one-to-one relationship between source constructs and bytecode. That's what Hulk means when he/she says "a decompiler has to 'guess' a bit."
– T.J. Crowder
Dec 14 '18 at 11:04
8
@KumarAnkit what people are trying to explain is that maybe there's no optimization at all. Try translating a hindi (or whatever is your regional dialect) sentence in english using Google translate, and then back to hindi. You'll be lucky if it's the same sentence than at the beginning. Here it's the same thing, got it?
– Dici
Dec 14 '18 at 11:07
13
@KumarAnkit -
for
(andwhile
, etc.) don't exist at the bytecode level. It's jump instructions, assignments, etc. One decompiler might look at some bytecode and say "that looks like afor
" while another might look at it and say "that looks like awhile
". "So, does it mean these loops are identical at the byte-code level?" Not necessarily. If you compile the code output by a decompiler, you don't necessarily end up with identical bytecode. In fact, I suspect you rarely would.– T.J. Crowder
Dec 14 '18 at 11:12
8
OP must be hiding something from us. The two snippets are not equivalent. The first one will never update
i
once it's equal to zero, so it may set the value ofcolArr
multiple times in a reasonable scenario. The second one will updatecolArr
only once.– ach
Dec 14 '18 at 13:42