Skip to main content
Bounty Ended with 50 reputation awarded by MD XF
added 1942 characters in body
Source Link
Poke
  • 3.2k
  • 14
  • 30

This should now work even if the method is called multiple times in the program. Unfortunately it does not yet work if you have multiple invocations on a single line. This may require more than just an idempotent function.(For one that does, see below)

 

For fun, here's one which handles multiple function calls on the same line. This causes the function to not be idempotent but that's kind of the point. Try it online!

This should now work even if the method is called multiple times in the program. Unfortunately it does not yet work if you have multiple invocations on a single line. This may require more than just an idempotent function.

This should now work even if the method is called multiple times in the program. Unfortunately it does not yet work if you have multiple invocations on a single line. (For one that does, see below)

 

For fun, here's one which handles multiple function calls on the same line. This causes the function to not be idempotent but that's kind of the point. Try it online!

deleted 403 characters in body
Source Link
Poke
  • 3.2k
  • 14
  • 30
List<String> javapLines = Arrays.asList(javapOut.toString().split("\\r?\\n"));
int byteCodeStart = -1;
Map<Integer, Integer> byteCodePointerToJavaPLine = new HashMap<Integer, Integer>();
Pattern byteCodeIndexPattern = Pattern.compile("^\\s*(\\d+): ");
for (int n = 0;n < javapLines.size();n++) {
    String javapLine = javapLines.get(n);
    if (byteCodeStart > -1 && (javapLine == null || "".equals(javapLine))) {
        break;
    }
    Matcher byteCodeIndexMatcher = byteCodeIndexPattern.matcher(javapLine);
    if (byteCodeIndexMatcher.find()) {
        byteCodePointerToJavaPLine.put(Integer.parseInt(byteCodeIndexMatcher.group(1)), n);
    } else if (javapLine.contains("line " + lineNum + ":")) {
        byteCodeStart = Integer.parseInt(javapLine.substring(javapLine.indexOf(": ") + 2));
    }
}
 
int varLoadIndex = -1;
int varTableIndex = -1;
for (int i = byteCodePointerToJavaPLine.get(byteCodeStart) + 1;i < javapLines.size();i++) {
    if (varLoadIndex < 0 && javapLines.get(i).contains("Method " + methodName + ":")) {
        varLoadIndex = i;
        continue;
    }
    
    if (varLoadIndex > -1 && javapLines.get(i).contains("LocalVariableTable:")) {
        varTableIndex = i;
        break;
    }
}
List<String> javapLines = Arrays.asList(javapOut.toString().split("\\r?\\n"));
int byteCodeStart = -1;
Map<Integer, Integer> byteCodePointerToJavaPLine = new HashMap<Integer, Integer>();
Pattern byteCodeIndexPattern = Pattern.compile("^\\s*(\\d+): ");
for (int n = 0;n < javapLines.size();n++) {
    String javapLine = javapLines.get(n);
    if (byteCodeStart > -1 && (javapLine == null || "".equals(javapLine))) {
        break;
    }
    Matcher byteCodeIndexMatcher = byteCodeIndexPattern.matcher(javapLine);
    if (byteCodeIndexMatcher.find()) {
        byteCodePointerToJavaPLine.put(Integer.parseInt(byteCodeIndexMatcher.group(1)), n);
    } else if (javapLine.contains("line " + lineNum + ":")) {
        byteCodeStart = Integer.parseInt(javapLine.substring(javapLine.indexOf(": ") + 2));
    }
}
 
int varLoadIndex = -1;
int varTableIndex = -1;
for (int i = byteCodePointerToJavaPLine.get(byteCodeStart) + 1;i < javapLines.size();i++) {
    if (varLoadIndex < 0 && javapLines.get(i).contains("Method " + methodName + ":")) {
        varLoadIndex = i;
        continue;
    }
    
    if (varLoadIndex > -1 && javapLines.get(i).contains("LocalVariableTable:")) {
        varTableIndex = i;
        break;
    }
}
List<String> javapLines = Arrays.asList(javapOut.toString().split("\\r?\\n"));
int byteCodeStart = -1;
Map<Integer, Integer> byteCodePointerToJavaPLine = new HashMap<Integer, Integer>();
Pattern byteCodeIndexPattern = Pattern.compile("^\\s*(\\d+): ");
for (int n = 0;n < javapLines.size();n++) {
    String javapLine = javapLines.get(n);
    if (byteCodeStart > -1 && (javapLine == null || "".equals(javapLine))) {
        break;
    }
    Matcher byteCodeIndexMatcher = byteCodeIndexPattern.matcher(javapLine);
    if (byteCodeIndexMatcher.find()) {
        byteCodePointerToJavaPLine.put(Integer.parseInt(byteCodeIndexMatcher.group(1)), n);
    } else if (javapLine.contains("line " + lineNum + ":")) {
        byteCodeStart = Integer.parseInt(javapLine.substring(javapLine.indexOf(": ") + 2));
    }
}
added 186 characters in body
Source Link
Poke
  • 3.2k
  • 14
  • 30

This first part gets some general information about what class we're in and what the name of the function is. This is accomplished by creating an exception and parsing the first 2 entries of the stack trace. The first entry is the line that the exception is thrown on which we can grab the methodName from and the second entry is where the function was called from.

java.lang.Exception
    at E.getParamName(E.java:28)
    at E.main(E.java:17)

The first entry is the line that the exception is thrown on which we can grab the methodName from and the second entry is where the function was called from.

Putting it all together

 public static void main(java.lang.String[]);
    Code:
...
      18: getstatic     #19                 // Field java/lang/System.out:Ljava/io/PrintStream;
      21: aload_1
      22: aload_2
      23: invokevirtual #25                 // Method getParamName:(Ljava/lang/String;)Ljava/lang/String;
...
    LineNumberTable:
...
      line 17: 18
      line 18: 29
      line 19: 40
...
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0      83     0  args   [Ljava/lang/String;
          8      75     1     e   LE;
         11      72     2   str   Ljava/lang/String;
         14      69     3  str2   Ljava/lang/String;
         18      65     4  str4   Ljava/lang/String;
         77       5     5    e1   Ljava/lang/Exception;

This is basically what we're looking at. In the example code the first invocation is line 17. line 17 in the LineNumberTable shows that the beginning of that line is bytecode line index 18. That is the System.out load. Then we have aload_2 right before the method call so we look for the variable in slot 2 of the LocalVariableTable which is str in this case.

This first part gets some general information about what class we're in and what the name of the function is. This is accomplished by creating an exception and parsing the first 2 entries of the stack trace. The first entry is the line that the exception is thrown on which we can grab the methodName from and the second entry is where the function was called from.

This first part gets some general information about what class we're in and what the name of the function is. This is accomplished by creating an exception and parsing the first 2 entries of the stack trace.

java.lang.Exception
    at E.getParamName(E.java:28)
    at E.main(E.java:17)

The first entry is the line that the exception is thrown on which we can grab the methodName from and the second entry is where the function was called from.

Putting it all together

 public static void main(java.lang.String[]);
    Code:
...
      18: getstatic     #19                 // Field java/lang/System.out:Ljava/io/PrintStream;
      21: aload_1
      22: aload_2
      23: invokevirtual #25                 // Method getParamName:(Ljava/lang/String;)Ljava/lang/String;
...
    LineNumberTable:
...
      line 17: 18
      line 18: 29
      line 19: 40
...
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0      83     0  args   [Ljava/lang/String;
          8      75     1     e   LE;
         11      72     2   str   Ljava/lang/String;
         14      69     3  str2   Ljava/lang/String;
         18      65     4  str4   Ljava/lang/String;
         77       5     5    e1   Ljava/lang/Exception;

This is basically what we're looking at. In the example code the first invocation is line 17. line 17 in the LineNumberTable shows that the beginning of that line is bytecode line index 18. That is the System.out load. Then we have aload_2 right before the method call so we look for the variable in slot 2 of the LocalVariableTable which is str in this case.

added 186 characters in body
Source Link
Poke
  • 3.2k
  • 14
  • 30
Loading
added 345 characters in body
Source Link
Poke
  • 3.2k
  • 14
  • 30
Loading
added 4269 characters in body
Source Link
Poke
  • 3.2k
  • 14
  • 30
Loading
added 84 characters in body
Source Link
Poke
  • 3.2k
  • 14
  • 30
Loading
added 33 characters in body
Source Link
Poke
  • 3.2k
  • 14
  • 30
Loading
added 886 characters in body
Source Link
Poke
  • 3.2k
  • 14
  • 30
Loading
Source Link
Poke
  • 3.2k
  • 14
  • 30
Loading