Menu

Problem with static methods

2004-08-26
2004-09-19
  • Mattias Jiderhamn

    I'm completely new to EMMA and just made my first stumbling tries on getting it running. It failed.

    We have a J2EE environment running under Resin and there is something wierd already at application initialization.

    There is a servlet loaded at application startup that calls a couple of static method. Method call one works fine. That static method makes another static method call, pointing at a subclass of the class declaring the method (that is A declares static m(), B is subclass of A and we call B.m()). When using the instrumented code it seems to just skip that method call.

    I changed this code to point to the class declaring the method (i.e. A.m()), but inside that method there is a call to another static method in a super class (suppose A is subclass of Z, Z declaring static n(), then A.m() tries to call n()). This also seems to be "skipped". Even if i "qualify" the method name (Z.n()), the method is never executed.

    I have no ideas on how to get past this.
    Any suggestions?

     
    • Vlad Roubtsov

      Vlad Roubtsov - 2004-08-26

      Hmm, this kind of problem has never been reported.

      You seem to think that calling a method defined in a superclass via a reference to its subclass matters here. I personally don't think it can. Just in case, I have tried to reproduce your scenario in a standalone program and of course all methods that I expected to get called were called, whether m() was referenced via A or via B.

      When you say "seems to just skip" -- what do you mean? Is there an exception thrown? It is exceedingly unlikely that EMMA instrumentation just removes a method invocation from a class: it is purely additive.

       
      • Mattias Jiderhamn

        I have now had time to look at the problem more thorougly, and it seems it was all because of a missunderstanding or two.

        I have now learnt that emma not only does not intrument interfaces, but does not even copy the interface .class file to the output dir in "copy" output mode. This causes a java.lang.NoClassDefFoundError which, when the method call is within the init() of a Servlet was printed to the error log but not the "standard error" stream (where exceptions are explicitly printed by my code)...

        Though I did expect the .class file of the interfaces to be copied along with the instrumented .class files in "copy" output mode, I will have to use "fullcopy" (or possibly "overwrite") instead.

         
    • Dean Hiller

      Dean Hiller - 2004-08-30

      I had some ideas here.  First, it is good to really really understand statics(and even I don't claim to), but from what I remember, it can be quite tricky as statics don't override methods.

      I guess I am trying to say, if you do something like

      instanceofSubclass.callStatic() and there are two callStatic methods, one in the subclass and one in the superclass, if I remember correctly, the one in the subclass gets called.

      Try to do this, add print statements and make sure that code is covered without emma in the picture.  This should solve your problem.  I think you will see that emma is probably correct!!! :) 

       
    • Dean Hiller

      Dean Hiller - 2004-08-30

      ps.  It is good practice never to call object.staticMethod()!!!

      ClassName.staticMethod should always be used, otherwise you can really confuse yourself.

      overrides and all static methods work differently than non-static methods.

       
    • Vlad Roubtsov

      Vlad Roubtsov - 2004-09-01

      Yes, static methods are not virtual. Hence they can be "shadowed", but not overridden. What this means is that when B extends A and both classes define a static method foo() with the same signature the call to

      Exp.foo()

      will be resolved statically, at compilation time. The compiler will analyze Exp to emit either A::foo() or B::foo() depending on the *static* type of Exp.

      The language also requires Exp to be evaluated at  runtime but for the result of such evaluation to be discarded.

      This can make for an interesting discussion (see a little puzzle at the end of my post), but it has little to do with EMMA. EMMA does not work with your source syntax. It work with your compiled bytecode instead and whether the call is to A or B will be quite unambiguous in the bytecode. Furthermore, EMMA does not intervene with method call opcodes in any way so I am really puzzled why it could make a method call just not happen after instrumentation.

      If the original poster indeed experienced a bug, I don't have any good guesses as to what it might be. I am waiting for him to provide more data.

      **********************************
      Here is a neat little puzzle. Try to answer the following quickly and without compiling it first:

      int i = 0;
      Thread [] t = new Thread [0];
      t [-- i].sleep (1);
      System.out.println (i);

      (A) this will never compile. My faith in Java is strong.
      (B) this will compile and later throw a runtime exception
      (C) this will compile, run without any problems, and print "-1"
      (D) this will compile, run without any problems, and print "0"
      (E) it is all relative
      (F) my universe has just collapsed. My faith in Java is gone. I am a pale shadow of the human being I once was.

       
    • Dean Hiller

      Dean Hiller - 2004-09-01

      B, but I haven't compiled it yet.  maybe I am too confident sometimes.

      ps. My bet is the original developer missed the shadowing.  Many developers on my team have done this and ended up in confusion until we walked through everything.  won't know though until we see a response.

       
    • Dean Hiller

      Dean Hiller - 2004-09-01

      I should have posted a counter-puzzle with that previous post.  I think this one will be worthy of your talent Vlad.

      If you have one thread waiting on "hello".wait(),

      which of these notifyAll methods will release that wait().

      1. ("hel"+"lo").notifyAll();
      2. String s = "hel"; s+"lo".notifyAll();
      3. none of the above

      The answer is no where on the net to my knowledge, but there is info in the JLS that leads you to the answer.(that may be too big of a hint).  Last hint so don't scroll down.

      I ran into this problem when a developer of mine came to me with a bug he couldn't solve and at first it made me think, huh, that's impossible and then I remembered the special feature that the JLS describes in Java. ps. The answer is not none of the above, and is definetely 1 or 2, so now you have a 50/50 chance.

       
    • Vlad Roubtsov

      Vlad Roubtsov - 2004-09-01

      The answer to my puzzle is E, "it's all relative". The reason for that is even though based on our previous discussion the correct answer should be B, it took Sun several Java versions to get it right. If you compile my code snippet with javac v1.1, v1.2, v1,3 you will see the behavior change at every version bump.

      Regarding your puzzle I was going to answer "it is normally #1 but could also be 'none of the above'".

      The traditional thinking here is that string literals (including the ones statically compiled into a single string literal, like the "hel" +"lo" one) are automatically interned at runtime and thus equal literals represent the same global instances. I am guessing this is the answer you were looking for (#1).

      There is, however, another layer of depth here. The interned string literals are not always truly global to the JVM. Any two classes are guaranteed to see the same string literal as the same instance only as long as these classes use that string literal at the same time. Otherwise, the literal can be "reincarnated" as different instances at different times. This is pretty obscure and to the best of my knowledge I might have written the only article demonstrating this behavior: http://www.javaworld.com/javaworld/javaqa/2003-12/01-qa-1212-intern.html

      And finally, just to add another little gem, how your case #2 works will depend on whether var s is declared final or not... (with recent versions of javac).

       
      • Dean Hiller

        Dean Hiller - 2004-09-02

        very impressed.  nice article, and great answer.

         
    • Nobody/Anonymous

      The answer to the puzzle is "indeterminate".
      Two String literals of the same content are not guaranteed to be the same object in memory.
      Even though "hel" + "lo" == "hello", it has not been made clear if either of the two String literals ever become eligible for garbage collection. If either of them do, then the results are not known.
      This can be proven if there is disbelief.

       
    • Dean Hiller

      Dean Hiller - 2004-09-19

      interesting.  indeterminate???  If you read the JLS, "hel"+"lo" == "hello" unless as vlad said, it has been garbage collected.  Therefore, if you have "hello".wait() already waiting, it can't quite be garbage collected.  "hel"+"lo".notifyAll() will wake up the "hello".wait() method.  Similarly
      String s = "hel"; s+"lo".notifyAll();
      will not wake up the "hello".wait() method.  You can always go try it.  I discovered this when a developer ran into a bug surrounding this...luckily I remember what it said about all this in the JLS.

       

Log in to post a comment.