On Dec 1, 2011, at 5:04 PM, Attila Szegedi wrote: > Hey folks, > > lately I've started extending Dynalink with support for honoring the Lookup > object passed to the bootstrap methods.
Nice! > However, actually making the changes in BeanLinker for POJO linking prove to > be quite daunting… You would think I should just rely on JLS to resolve the > issues, but unfortunately there's one really big distinction between access > as described in JLS, and access as it happens with dynamic linking: with JLS, > your callee is a compile-type declared type; with dynamic linking, it's > always a run-time concrete type. We ran into this in JSR 292. See the weasel words "must have a supertype" in the doc for Lookup::bind. > > How's that problematic? > > Consider these examples: > > Example 1: > > class C { > private f() { } > } > > class D extends C { > } > > Let's assume class C has an invokedynamic call site for "obj.f()", and ends > up getting an instance of D. Should it be able to invoke C.f()? I think yes. I agree, but it is a corner case. > Okay, but how about this: > > class C { > private f() { } > } > > class D extends C { > private f() { } > } > > If C should dynamically link its call site for method "f()" and got an > instance of D, what should it invoke? Should it invoke C.f(), or should it > throw an error because D.f() exists but is not visible to it? I think either would be acceptable. C.f is more correct. D.f would be a disaster, which is why the Lookup parameter matters. > Okay, how about this: > > class C { > private f() { } > } > > class D extends C { > public f() { } > } > > Intuitively, I'd want C to invoke D.f() in this situation if it'd have a call > site for f() and got an instance of D as a callee. Think in terms of the equivalent statically written code: class C { private f() { } ... void test() { C x1 = ...; x1.f(); // private C::f D x2 = ...; x2.f(); // public D::f } } It doesn't matter if x1 == x2 or not. If x1 == x2, x1 and x2 are still different views of the same object. If this level of accuracy is desired, the compiler of the indy call site should specify which view is intended. In fact, the signature of the call site (type of leading receiver arg x1/x2) provides this information. > You might ask, wouldn't it break encapsulation somewhere? I think not, > because if the code of C is invoking something dynamically, it will not have > an expectation of that something being an instance of C, so the code better > not rely on it always being C.f() that is invoked. Fair enough? Yes, but it would be best to use the static call site information, if available. And not suppress the information. I guess I'm saying that the leading receiver argument type is almost as important as the Lookup parameter. (They are, of course, logically independent.) > Oh wait, but what if the dynamic call site actually specifies C.class as the > type of the 0th argument? Then you could argue the intent of the program is > to invoke C.f()? But why would a class invoke its own private member using > invokedynamic? It probably wouldn't, so I most likely shouldn't care about > the declared type of the receiver at the call site. Granted; that's why the less accurate design would be acceptable. One "acid test" of this API would be what I call "Ninja" (Ninja Is Not Java Anymore), in which every single imperative construct in a Java program is compiled to an indy instruction, including self-invocations of private methods. Can it be made to work behaviorally the same as the normal Java program? It's a challenge. (And a fun one, when you start injecting non-Java semantics, such as int-overflow-to-BigInteger.) > I'm putting these up for debate, as I'm not sure myself either what'd be the > correct approach here. > > Here's another example: > > Example 2: > (I'm using p1 and p2 as package prefixes) > > public class p1.Base { > f() { } > } > > public class p2.Derived { (You mean "Derived extends p1.Base".) > } > > class p1.Caller { > ... > } > > Imagine p1.Caller has a dynamic call site for f(), and it gets an instance of > p2.Derived as the callee. Should it be able to link to p1.Base.f() as an > invocation of f() on p2.Derived? Intuition tells me that, yes, it should -- > after all, the method is declared in the p1 package even if the p2.Derived > isn't. Again, we don't rely on declared types; the call site in p1.Caller > probably says java.lang.Object as the 0th argument. (Package puzzles make my brain hurt. I generally write test programs to figure out what's going to happen, since the JVMs have fixed most scoping bugs by now. It's not immediately clear to me whether the view from p1.Caller to p2.Derived is specified to include Base.f or not. Sorry.) If p1.Caller has erased the receiver type to Object, then you need to pick a choice, either the most widely applicable interpretation (which is the weakest) or the most powerful (in which you assume the caller chose a static type that was most advantageous). If p1.Caller provides the locally intended receiver type (either Base or Derived) then it would be best to use that information. > How about if p2.Derived also declares a package-private method f()? I don't think that matters, since Caller can't ever see Derived.f, no matter what the static receiver declaration might have been. > public class p1.Base { > f() { } > } > > public class p2.Derived { > f() { } > } > > class p1.Caller { > ... > } > > Should p1.Caller's f()-invoking dynamic call site now invoke p1.Base.f() when > we supply it a p2.Derived instance as the callee, or should it throw an error > claiming p2.Derived.f() is not visible to it? I don't know the correct > answer. I lean towards invoking p1.Base.f(), but this is not nearly clear cut. Is there a way to code this so that you get a static error because of Derived.f? I don't think so, and that would imply that a dynamic exception would not help model any static behavior. So I also lean towards ignoring the foreign Derived.f and invoking Base.f. > Would it make a difference if the call site in p1.Caller declared its 0th > argument to be p1.Base? (Again, why would any call site with such a specific > knowledge of the target be emitted using invokedynamic?) A Ninja-type Java emulator would emit the types. In general, the signature types are there to be used! > Similar issues apply to protected access. Yes. -- John -- You received this message because you are subscribed to the Google Groups "JVM Languages" group. To post to this group, send email to jvm-languages@googlegroups.com. To unsubscribe from this group, send email to jvm-languages+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/jvm-languages?hl=en.