Interface Methods
Method invocation requires a bit more kruft than we would like, it that kruft does have its
benefits. (For an example of method definition, see Emitter.)
Consider the example where we'd like to invoke Integer.compare(int, int):
var mdescIntegerCompare = MthDesc.derive(Integer::compare)
.check(MthDesc::returns, Types.T_INT)
.check(MthDesc::param, Types.T_INT)
.check(MthDesc::param, Types.T_INT)
.check(MthDesc::build);
em
.emit(Op::iload, params.a)
.emit(Op::ldc__i, 20)
.emit(Op::invokestatic, Types.refOf(Integer.class), "compare", mdescIntegerCompare,
false)
.step(Inv::takeArg)
.step(Inv::takeArg)
.step(Inv::ret)
.emit(Op::ireturn, retReq);
The first requirement (as in the case of defining a method) is to obtain the descriptor of the
target method. There are a few ways to generate method descriptors, but the safest when calling a
compiled or library method is to derive it from a reference to that method. There is no such
thing as a "method literal" in Java, but there are method references, and we can match the types
of those references. One wrinkle to this, however, is that we cannot distinguish auto-boxed
parameters from those that genuinely accept the boxed type, i.e., both void f(int a) and
void g(Integer b) can be made into references of type Consumer<Integer>,
because <int> is not a legal type signature. Thus, we require the user to call
Methods.MthDescCheckedBuilderP.check(BiFunction, Object) with, e.g.,
Methods.MthDesc.param(MthDescCheckedBuilderP, TInt) to specify that an Integer is
actually an int. However, we cannot check that the user did this correctly until runtime.
Still, we are able to prevent a Hippo parameter from receiving an int, and that
is much better than nothing.
Once we have the method descriptor, we can use it in invocation operators, e.g.,
Op.invokestatic(Emitter, TRef, String, MthDesc, boolean). In an of itself, this operator
does not consume nor push anything to the stack. Instead, it returns an Methods.Inv
object, which facilitates the popping and checking of each parameter, followed by the pushing of
the returned value, if non-void. It is not obvious to us (if such a technique even exists) to pop
an arbitrary number of entries from <N> in a single method. Instead, we have to treat
Java's type checker as a sort of automaton that we can step, one pop at a time, by invoking a
method. This method is Methods.Inv.takeArg(Inv), which for chaining purposes, is most easily
invoked using the aptly-named Methods.Inv.step(Function) instance method. We would rather not
have to do it this way, as it is unnecessary kruft that may also have a run-time cost. One
benefit, however, is that if the arguments on the stack do not match the parameters required by
the descriptor, the first mismatched takeArg line (corresponding to the right-most
mismatched parameter) will fail to compile, and so we know which argument is incorrect.
Finally, we must do one last step to to push the return value, e.g., Methods.Inv.ret(Inv). This
will check that all parameters have been popped, and then push a value of the descriptor's return
type. It returns the resulting emitter. For a void method, use Methods.Inv.retVoid(Inv) to avert
the push. Unfortunately, ret is still permitted, but at least downstream operators are
likely to fail, since nothing should consume Types.TVoid.
-
Nested Class Summary
Nested ClassesModifier and TypeInterfaceDescriptionstatic interfaceThe analog ofEmitter.Bot, but forMethods.CkNextstatic interfaceMethods.CkEnt<N extends Methods.CkNext,T> The analog ofEmitter.Ent, but forMethods.CkNextstatic interfaceThe analog ofEmitter.Next, but for unspecified parameter types to be checkedstatic final recordA static method definition (builder)static final recordMethods.Inv<MR extends Types.BType,SN extends Emitter.Next, MN extends Emitter.Next> An invocation object to facilitate the checked popping of arguments for a static method invocation and the final push of its returned value.static final recordMethods.MthDesc<MR extends Types.BType,N extends Emitter.Next> A method descriptorstatic classMethods.MthDescBuilder<MR extends Types.BType,N extends Emitter.Next> An unchecked builder of a method descriptorstatic classMethods.MthDescCheckedBuilderP<MR extends Types.BType,N extends Emitter.Next, CN extends Methods.CkNext> A checked builder (stage 2) of a method descriptorstatic classMethods.MthDescCheckedBuilderR<CR,CN extends Methods.CkNext> A checked builder (stage 1) of a method descriptorstatic final recordMethods.MthParam<T extends Types.BNonVoid>A defined parameter, which was checked against a method descriptorstatic final recordAn instance method definition (builder)static final recordMethods.ObjInv<MR extends Types.BType,OT, SN extends Emitter.Next, MN extends Emitter.Next> An invocation object to facilitate the checked popping of arguments for an instance method invocation and the final push of its returned value.static final recordMethods.RetReq<T extends Types.BType>A return requeststatic final recordMethods.RetReqEm<T extends Types.BType>A tuple of return request and emitter with empty stack