Re: Behaviour of Analyzer and SimpleVerifier

Kasra Faghihi <[email protected]> Sun, 26 Aug 2018 06:12:08 -0700
Newsgroups gmane.comp.java.objectweb.asm
Message-ID <CAERR1vmH+qz6fiG74Fr+QmgjfrRwNWp69_n4exSR9WRi-X0NdA@mail.gmail.com>
I think the issue only happens when the objects being merged are both
interfaces. The exact steps to reproduce are...


1. Get the exact class to reproduce the issue by going to the link below.
Both the source and the compiled class are in this zip.

https://github.com/offbynull/coroutines/blob/1cf7a1ceddfa91241854f17af745c8b302035a36/instrumenter/src/test/resources/Issue84Test.zip


2. Read in the compiled class from step 1, analyze it, and write out the
data needed to debug.

         // read
        ClassReader cr = new ClassReader(classContent);
        ClassNode classNode = new SimpleClassNode();
        cr.accept(classNode, SKIP_FRAMES);

        MethodNode methodNode = classNode.methods.stream().filter(m ->
m.name.equals("run")).findFirst().get();

         // analyze
        Analyzer<BasicValue> analyzer = new Analyzer<>(new
SimpleVerifier());
        Frame<BasicValue>[] frames = analyzer.analyze(ISSUE_84_TEST,
methodNode);

        // output debugging info
        StringWriter writer = new StringWriter();
        Printer printer = new Textifier();
        PrintWriter printWriter = new PrintWriter(writer);
        TraceMethodVisitor traceMethodVisitor = new
TraceMethodVisitor(printer);

        AbstractInsnNode insn = methodNode.instructions.getFirst();
        int idx = 0;
        while (insn != null) {
            insn.accept(traceMethodVisitor);
            insn = insn.getNext();

            if (idx == 39) { // INSTRUCTIONS 39 = LAST LINE OF SOURCE
                printer.getText().add("      [[[START LVT]]]\n");
                for (int i = 0; i < frames[idx].getLocals(); i++) {
                    Type type = frames[idx].getLocal(i).getType();
                    printer.getText().add("         " + type + "\n");
                }
                printer.getText().add("      [[[FINISH LVT]]]\n");
            }

            idx++;
        }
        printer.print(printWriter);
        printWriter.flush();

        System.out.println(writer.toString());


3. STDOUT should contain the method's instruction listing along with the
LVT produced by the analyzer for the last line of the method (bolded).
Notice how LVT index=3 is a List. My understanding is that analyzer should
identify this as Object, because at the last line of the method LVT index=3
could be either a Long or a List (it should merge to a Object).
   ...
   L6
    LINENUMBER 19 L6
*      [[[START LVT]]]*
*         LIssue84Test;*
*         Lcom/offbynull/coroutines/user/Continuation;*
*         Ljava/lang/String;*
*         Ljava/util/List;* <-- at this insn, this LVT slot could be either
Long or List, so it should merge to Object?
*      [[[FINISH LVT]]]*
    ALOAD 0
    ALOAD 1
    ICONST_2
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
    INVOKESPECIAL Issue84Test.echo
(Lcom/offbynull/coroutines/user/Continuation;Ljava/lang/Object;)Ljava/util/List;
    POP
   L8
    LINENUMBER 20 L8
    RETURN
   L9



Am I explaining this clearly enough? Please let me know if anything is
vague or doesn't make sense.




On Sun, 26 Aug 2018 at 02:40, <[email protected]> wrote:

> Hi,
>
> I can't reproduce your issue (or maybe I don't understand it). I tried with
>
> public class Test {
>
>     public static void run(Object o) {
>         if (o == null) {
>             Long i = 0L;
>             run(i);
>         } else {
>             String i = "0";
>             run(i);
>         }
>         run(o);
>     }
> }
>
> and I get with CheckClassAdapter (modified to always print the analysis
> result - replace false with true at l172):
>
> run(Ljava/lang/Object;)V
> 00000 Object .  :  :     ALOAD 0
> 00001 Object .  : Object  :     IFNONNULL L0
> 00002 Object .  :  :     LCONST_0
> 00003 Object .  : J  :     INVOKESTATIC java/lang/Long.valueOf
> (J)Ljava/lang/Long;
> 00004 Object .  : Long  :     ASTORE 1
> 00005 Object Long  :  :     ALOAD 1
> 00006 Object Long  : Long  :     INVOKESTATIC Test.run
> (Ljava/lang/Object;)V
> 00007 Object Long  :  :     GOTO L1
> 00008 Object .  :  :    L0
> 00009 Object .  :  :    FRAME SAME
> 00010 Object .  :  :     LDC "0"
> 00011 Object .  : String  :     ASTORE 1
> 00012 Object String  :  :     ALOAD 1
> 00013 Object String  : String  :     INVOKESTATIC Test.run
> (Ljava/lang/Object;)V
> 00014 Object Object  :  :    L1
> 00015 Object Object  :  :    FRAME SAME
> 00016 Object Object  :  :     ALOAD 0
> 00017 Object Object  : Object  :     INVOKESTATIC Test.run
> (Ljava/lang/Object;)V
> 00018 Object Object  :  :     RETURN
>
> so Long and String are correctly merged into Object at L1.
>
> ----- Mail original -----
> > De: "Kasra Faghihi" <[email protected]>
> > À: [email protected]
> > Envoyé: Mardi 21 Août 2018 20:19:26
> > Objet: [asm] Behaviour of Analyzer and SimpleVerifier
> >
> >
> >
> >
> > Can someone please help me understand Analyzer/SimpleVerifier. It
> > isn't working the way I expect it to. Either this is a bug or I've
> > misunderstood how things should work.
> >
> >
> >
> >
> >
> >
> >
> > Take the following method...
> >
> >
> >
> > public class Issue84Test implements Coroutine {
> >
> >
> > public boolean allInstrum = false; // MUST BE PUBLIC
> >
> >
> > public void run(Continuation c) {
> > final String tag = new String("run()");
> > if (!allInstrum) {
> > final Long i = echo();
> > } else {
> > final List<Integer> i = echo(c, 1);
> > }
> > echo(c, 2);
> > }
> >
> >
> > ...
> > }
> >
> >
> >
> >
> >
> >
> >
> >
> > Once you compile it using Oracle JDK and dump it through ASM, this is
> > the output...
> >
> >
> >
> > L0
> > LINENUMBER 11 L0
> > NEW String
> > DUP
> > LDC "run()"
> > INVOKESPECIAL String.<init>(String) : void
> > ASTORE 2
> > L1
> > LINENUMBER 12 L1
> > ALOAD 0: this
> > GETFIELD Issue84Test.allInstrum : boolean
> > IFNE L2
> > L3
> > LINENUMBER 13 L3
> > ALOAD 0: this
> > INVOKESPECIAL Issue84Test.echo() : Long
> > ASTORE 3
> > L4
> > LINENUMBER 14 L4
> > GOTO L5
> > L2
> > LINENUMBER 15 L2
> > FRAME APPEND [String]
> > ALOAD 0: this
> > ALOAD 1: c
> > ICONST_1
> > INVOKESTATIC Integer.valueOf(int) : Integer
> > INVOKESPECIAL Issue84Test.echo(Continuation, Object) : List
> > ASTORE 3
> > L5
> > LINENUMBER 17 L5
> > FRAME SAME
> > ALOAD 0: this
> > ALOAD 1: c
> > ICONST_2
> > INVOKESTATIC Integer.valueOf(int) : Integer
> > INVOKESPECIAL Issue84Test.echo(Continuation, Object) : List
> > POP
> > L6
> > LINENUMBER 18 L6
> > RETURN
> > L7
> > LOCALVARIABLE this Issue84Test L0 L7 0
> > LOCALVARIABLE c Continuation L0 L7 1
> > LOCALVARIABLE tag String L1 L7 2
> > MAXSTACK = 3
> > MAXLOCALS = 4
> >
> >
> >
> >
> >
> >
> >
> >
> >
> > The problem here is that both "Long i" in the if block and "List i"
> > in the else block share the same LVT index (index=3).
> >
> > Analyzer/SimpleVerifier seems to be identifying the final line of the
> > method as having a "List" at LVT index=3, but technically it could
> > be either "Long" or "List" (depending on whether the if block was
> > executed or the else block was executed). For the last line,
> > shouldn't Analyzer/SimpleVerifier give back a type of Object for LVT
> > index=3? Object is the parent of both Long and List.
> >
> > This relates to coroutines issue#84. Please let me know if I'm not
> > explaining things clearly.
> >
> > --
> > You receive this message as a subscriber of the [email protected] mailing
> > list.
> > To unsubscribe: mailto:[email protected]
> > For general help: mailto:[email protected]?subject=help
> > OW2 mailing lists service home page: http://www.ow2.org/wws
> >
>


-- 
You receive this message as a subscriber of the [email protected] mailing list.
To unsubscribe: mailto:[email protected]
For general help: mailto:[email protected]?subject=help
OW2 mailing lists service home page: http://www.ow2.org/wws