On 10/21/11 3:01 PM, Luc Maisonobe wrote: > Le 21/10/2011 20:33, Phil Steitz a écrit : >> should get picked up. I will look at this some more. >>> You are right, the data flow analysis is not sufficient here. In >>> fact, we should not only track what comes from the method >>> parameters (i.e. the local variables), but also make sure that a >>> GETFIELD that refers to the original instance should be either >>> split in two GETFIELD if the variable is extended to include a >>> deriviative (when there is another PUTFIELD elsewhere), or that >>> event if the field is only read, that we try to recover it from >>> the primitive and not from the instance itself. >> Thanks again, Luc, for your patience explaining things that I should >> have been able to see in the code. Things are getting much clearer >> now. I have been able to get field access to work as follows: >> >> The data flow analysis is never going to flag the GETFIELD >> instruction as needing to be transformed. It works beautifully for >> what it does, but instructions that are not producers or consumers >> of values originating from method parameters are not going to be >> picked up. To just handle GETFIELD, I added the following at the >> end of MethodDifferentiator#changeCode: >> >> // transform GETFIELD >> Iterator<AbstractInsnNode> iter = instructions.iterator(); >> while (iter.hasNext()) { >> AbstractInsnNode insn = iter.next(); >> if (insn.getOpcode() == Opcodes.GETFIELD) { >> instructions.insert(insn, getReplacement(insn)); >> instructions.remove(insn); >> } >> } >> >> and then inside getReplacement replace the RTE on GETFIELD with >> case Opcodes.GETFIELD : >> return >> GetFieldTransformer.getInstance().getReplacement(insn, this); >> >> where GetFieldTransformer does >> FieldInsnNode fieldIns = (FieldInsnNode) insn.clone(null); >> String owner = fieldIns.owner; >> final InsnList list = new InsnList(); >> list.add(new FieldInsnNode(Opcodes.GETFIELD, owner + >> "$NablaForwardModeUnivariateDerivative", >> "primitive", "L" + owner + ";")); // primitive >> list.add(fieldIns); >> return list; >> >> For a primitive class named "PartialFunction" this generates >> ALOAD 0 >> GETFIELD >> PartialFunction$NablaForwardModeUnivariateDerivative.primitive : >> LPartialFunction; >> GETFIELD PartialFunction.x : D >> >> This works great except for one problem: >> >> If the field in the primitive, "x" in the example above, is not >> public, you get >> java.lang.IllegalAccessError: tried to access field >> PartialFunction.x from class >> PartialFunction$NablaForwardModeUnivariateDerivative >> at >> PartialFunction$NablaForwardModeUnivariateDerivative.f(Unknown Source) >> >> (one case where I am not cursing the "Unknown Source" he he) >> >> I am not sure exactly what to do about that. Is >> PartialFunction$NablaForwardModeUnivariateDerivative a static inner >> class? > It's an inner class, but not static. This was done precisely to be able > to access private fields :-( > >> Shouldn't it in theory be able to access the field from an >> instance reference? I think the compiler generates a hidden field >> accessor when static inner classes are defined. It looks like it >> does that when I manually add a static inner class, in any case. >> Could be we need to manufacture that and attach it to the >> primitive. Or could be I am misunderstanding things. > Good catch. I guess the accessor is in the enclosing class then ? > Unortunately, we cannot change this class.
Yes, I am not sure exactly how it works, but here is what I see in the bytecode when I try to simulate what I am trying to make happen. Using either a static or non-static inner class, it is possible to access private fields of an instance of the parent class (not necessarily the parent instance in the non-static case) - e.g. this kind of thing works inside a PartialFunction: class InnerClass { public InnerClass() {} public double display(PartialFunction f) { return f.x; } } where x is private in PartialFunction. This works with static in front as well. In either case, what happens here is that the bytecode for PartialFunction ends up including: static access$0(LPartialFunction;)D that gets the field and PartialFunction$InnerClass#display loads the actual parameter and does INVOKESTATIC on this method. Unfortunately, the same thing happens if you just access the field directly - i.e., if display has no parameter and just returns "x". In that case, a reference to the parent is manufactured first: aload_0 getfield #10 <PartialFunction$InnerClass.this$0> invokestatic #21 <PartialFunction.access$0> dreturn > >> Alternatively, we could document and require that for a method to be >> "differentiable" it must use only fields that have public getters >> with bean naming and we could generate code to invoke the getter on >> the primitive. > This would be a big drawback. If we can find some way to do it, we > should really try hard. Perhaps using the reflection API ? It's ugly, > but it allows to temporarily lift access restrictions. I used it > sometimes in unit tests to reset some singletons to null. Yes, that seems like it may be the best way to go, unless we want to 0) copy the parent's bytecode and play games like above to create a subclass exposing fields that are used in differentiated methods 1) instantiate the subclass using the parent and 2) use the subclass instance as the primitive. That amounts to transforming the primitive, which is probably more complicated than it is worth. Phil > > Luc > >> Phil >> >> >> >> --------------------------------------------------------------------- >> To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org >> For additional commands, e-mail: dev-h...@commons.apache.org > > --------------------------------------------------------------------- > To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org > For additional commands, e-mail: dev-h...@commons.apache.org > > --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@commons.apache.org For additional commands, e-mail: dev-h...@commons.apache.org