How can I invoke a virtual method handle using ByteBuddy's InvokeDynamic class? - byte-buddy

I've found the InvokeDynamic class and have made it work with a static method handle acquired via MethodHandles.Lookup.findStatic().
Now I am trying to do the same thing, but with a virtual method handle acquired via MethodHandles.Lookup.findVirtual().
I can cause my bootstrap method to run, and I make sure in my bootstrap method that I'm returning a ConstantCallSite(mh), where mh is the result of calling MethodHandles.Lookup.findVirtual(). (This part all works fine, i.e. I understand how "indy" works.)
However, when I use the resulting Implementation as the argument to an intercept() call, I cannot pass the actual object on which the method represented by the method handle is to be invoked. This is due to the withArgument() method being used for two contradictory purposes.
Here is my recipe:
Implementation impl =
InvokeDynamic.bootstrap(myBootstrapDescription, someOtherConstantArgumentsHere)
.invoke(theMethodName, theMethodReturnType)
// 0 is the object on which I want to invoke my virtual-method-represented-by-a-method-handle;
// 1 is the sole argument that the method actually takes.
.withArgument(0, 1);
There are some problems here.
Specifically, it seems that withArgument() is used by ByteBuddy for two things, not just one:
Specifying the parameter types that will be used to build a MethodType that will be supplied to the bootstrap method. Let's say my virtual method takes one argument.
Specifying how the instrumented method's arguments are passed to the actual method handle execution.
If I have supplied only one argument, the receiver type is left unbound and execution of the resulting MethodHandle cannot happen, because I haven't passed an argument that will be used for the receiver type "slot". If I accordingly supply two arguments to (1) above (as I do in my recipe), then the method handle is not found by my bootstrap method, because the supplied MethodType indicates that the method I am searching for requires two arguments, and my actual method that I'm finding only takes one.
Finally, I can work around this (and validate my hypothesis) by doing some fairly ugly stuff in my bootstrap method:
First, I deliberately continue to pass two arguments, not one, even though my method only takes two arguments: withArgument(0, 1)
In my bootstrap method, I now know that the MethodType it will receive will be "incorrect" (it will have two parameter types, not one, where the first parameter type will represent the receiver type). I drop the first parameter using MethodType#dropParameterTypes(int, int).
I call findVirtual() with the new MethodType. It returns a MethodType with two parameter types: the receiver type that it adds automatically, and the existing non-dropped parameter type.
(More simply I can just pass a MethodType as a constant to my bootstrap method via, for example, JavaConstant.MethodType.of(myMethodDescription) or built however I like, and ignore the one that ByteBuddy synthesizes. It would still be nice if there were instead a way to control the MethodType that ByteBuddy supplies (is obligated to supply) to the bootstrap method.)
When I do things like this in my bootstrap method, my recipe works. I'd prefer not to tailor my bootstrap method to ByteBudddy, but will here if I have to.
Is it a bug that ByteBuddy does not seem to allow InvokeDynamic to specify the ingredients for a MethodType directly, without also specifying the receiver?

What you described, is entirely independent of Byte-Buddy. It’s just the way how invokedynamic works.
JVMS, §5.4.3.6
5.4.3.6. Dynamically-Computed Constant and Call Site Resolution
To resolve an unresolved symbolic reference R to a dynamically-computed constant or call site, there are three tasks. First, R is examined to determine which code will serve as its bootstrap method, and which arguments will be passed to that code. Second, the arguments are packaged into an array and the bootstrap method is invoked. Third, the result of the bootstrap method is validated, and used as the result of resolution.
…
The second task, to invoke the bootstrap method handle, involves the following steps:
An array is allocated with component type Object and length n+3, where n is the number of static arguments given by R (n ≥ 0).
The zeroth component of the array is set to a reference to an instance of java.lang.invoke.MethodHandles.Lookup for the class in which R occurs, produced as if by invocation of the lookup method of java.lang.invoke.MethodHandles.
The first component of the array is set to a reference to an instance of String that denotes N, the unqualified name given by R.
The second component of the array is set to the reference to an instance of Class or java.lang.invoke.MethodType that was obtained earlier for the field descriptor or method descriptor given by R.
Subsequent components of the array are set to the references that were obtained earlier from resolving R's static arguments, if any. The references appear in the array in the same order as the corresponding static arguments are given by R.
A Java Virtual Machine implementation may be able to skip allocation of the array and, without any change in observable behavior, pass the arguments directly to the bootstrap method.
So the first three arguments to the bootstrap method are provided by the JVM according to the rules cited above. Only the other arguments are under the full control of the programmer.
The method type provided as 3rd argument always matches the type of the invokedynamic instruction describing the element types to pop from the stack and the type to push afterwards, if not void. Since this happens automatically, there’s not even a possibility to create contradicting, invalid bytecode in that regard; there is just a single method type stored in the class file.
If you want to bind the invokedynamic instruction to an invokevirtual operation using a receiver from the operand stack, you have exactly the choices already mentioned in your question. You may derive the method from other bootstrap arguments or drop the first parameter type of the instruction’s type. You can also use that first parameter type to determine the target of the method lookup. There’s nothing ugly in this approach; it’s the purpose of bootstrap methods to perform adaptations.

Related

How to make a class that inherits the same methods as IO::Path?

I want to build a class in Raku. Here's what I have so far:
unit class Vimwiki::File;
has Str:D $.path is required where *.IO.e;
method size {
return $.file.IO.s;
}
I'd like to get rid of the size method by simply making my class inherit the methods from IO::Path but I'm at a bit of a loss for how to accomplish this. Trying is IO::Path throws errors when I try to create a new object:
$vwf = Vimwiki::File.new(path => 't/test_file.md');
Must specify a non-empty string as a path
in block <unit> at t/01-basic.rakutest line 24
Must specify a non-empty string as a path
I always try a person's code when looking at someone's SO. Yours didn't work. (No declaration of $vwf.) That instantly alerts me that someone hasn't applied Minimal Reproducible Example principles.
So I did and less than 60 seconds later:
IO::Path.new
Yields the same error.
Why?
The doc for IO::Path.new shows its signature:
multi method new(Str:D $path, ...
So, IO::Path's new method expects a positional argument that's a Str. You (and my MRE) haven't passed a positional argument that's a Str. Thus the error message.
Of course, you've declared your own attribute $path, and have passed a named argument to set it, and that's unfortunately confused you because of the coincidence with the name path, but that's the fun of programming.
What next, take #1
Having a path attribute that duplicates IO::Path's strikes me as likely to lead to unnecessary complexity and/or bugs. So I think I'd nix that.
If all you're trying to do is wrap an additional check around the filename, then you could just write:
unit class Vimwiki::File is IO::Path;
method new ($path, |) { $path.IO.e ?? (callsame) !! die 'nope' }
callsame redispatches the ongoing routine call (the new method call), with the exact same arguments, to the next best fitting candidate(s) that would have been chosen if your new one containing the callsame hadn't been called. In this case, the next candidate(s) will be the existing new method(s) of IO::Path.
That seems fine to get started. Then you can add other attributes and methods as you see fit...
What next, take #2
...except for the IO::Path bug you filed, which means you can't initialize attributes in the normal way because IO::Path breaks the standard object construction protocol! :(
Liz shows one way to workaround this bug.
In an earlier version of this answer, I had not only showed but recommended another approach, namely delegation via handles instead of ordinary inheritance. I have since concluded that that was over-complicating things, and so removed it from this answer. And then I read your issue!
So I guess the delegation approach might still be appropriate as a workaround for a bug. So if later readers want to see it in action, follow #sdondley's link to their code. But I'm leaving it out of this (hopefully final! famous last words...) version of this answer in the hope that by the time you (later reader) read this, you just need to do something really simple like take #1.

How do I use MethodCall.invoke(someElementMatcher) to create a MethodCall representing a method I subsequently define in an instrumented type?

I am using ByteBuddy to generate a class.
Prior to working with DynamicType.Builder, I was going to store a MethodCall as an instance variable:
private final MethodCall frobCall =
MethodCall.invoke(ElementMatchers.named("frob")); // here I invoke a method I'm going to define as part of the instrumented type
Then later in my generation logic for the instrumented type I define the frob method to do something:
.defineMethod("frob")
.intercept(...etc....) // here I define frob to do something
…and I define the (let's say) baz method to invoke frob:
.defineMethod("baz")
.withParameter(...) // etc.
.intercept(frobCall); // invokes "frob", which I've just defined above
(I am trying to keep this simple and may have mistyped something but I hope you can see the gist of what I'm trying to do.)
When I make() my DynamicType, I receive an error that indicates that the dynamic type does not define frob. This is mystifying to me, because of course I have defined it, as you can see above.
Is there some restriction I am unaware of that prohibits ElementMatchers from identifying instrumented type methods that are defined later? Do I really have to use MethodDescription.Latent here?
It should match all methods of the instrumented type. If this is not happening as expected, please set a breakpoint in MethodCall.MethodLocator.ForElementMatcher to see why the method is not showing up. I assume it is filtered by your method matcher.
I noticed however that it did not include private methods which is now fixed and will be released within Byte Buddy 1.10.18.

Question mark in callback

static setItem(key: string, value: string, callback?: ?(error: ?Error) => void)
This is the declaration of setitem in AsyncStorage. the third parameter is a callback. Could some one explain the use of question marks here. I am familiar with how to use promise but couldn't get a handle of question mark.
AsyncStorage uses flow - Facebook's open-sourced static type checker. You will find #flow at the beginning of the file and it marks flow-enabled source. Flow does a lot of checking on the variable types (including automated type inference) but it also lets you specify the types for variables and parameters. In the example above 'key: string' for example indicates that key should be string type (it's not a valid javascript construct - you cannot specify type in javascript). React has built in transformers that transform it to pure javascript (so all the types are stripped) but before that flow checks if types are passed around properly and find things like passing null or undefined and using it later as object and many other checks. You can read the specs in http://flowtype.org/.
So answering your detailed questionmark question:
'?Error' indicates that error parameter is a "Maybe" type - i.e. it CAN be null and flow will not complain if null or undefined is passed here elsewhere in the code callback (http://flowtype.org/docs/nullable-types.html#type-annotating-null)
'callback?' indicates an optional parameter - so it might be skipped http://flowtype.org/docs/functions.html#function-based-type-annotations
'?(error...)' is another "Maybe" type - it simply indicates that the callback function might take one parameter ('error') or no parameters at all.

CLI/C++ function overload

I am currently writing a wrapper for a native C++ class in CLI/C++. I am on a little GamePacket class at the moment. Consider the following class:
public ref class GamePacket
{
public:
GamePacket();
~GamePacket();
generic<typename T>
where T : System::ValueType
void Write(T value)
{
this->bw->Write(value);
}
};
I want that I'm able to call the function as following in C#, using my Wrapper:
Packet.Write<Int32>(1234);
Packet.Write<byte>(1);
However, I can't compile my wrapper. Error:
Error 1 error C2664: 'void System::IO::BinaryWriter::Write(System::String ^)' : cannot convert argument 1 from 'T' to 'bool'
I don't understand this error, where does the System::String^ comes from. I'm seeing a lot of overloads of the Write() method, does CLI/C++ not call the correct one, and if so, how can I make it call the correct one?
Reference MSDN: http://msdn.microsoft.com/en-us/library/system.io.binarywriter.write(v=vs.110).aspx
Templates and generics don't work the same.
With templates, the code gets recompiled for each set of parameters, and the results can be pretty different (different local variable types, different function overloads selected). Specialization makes this really powerful.
With generics, the code only gets compiled once, and the overload resolution is done without actually knowing the final parameters. So when you call Write(value), the only things the compiler knows is that
value can be converted to Object^, because everything can
value derives from ValueType, because your constraint tells it
Unfortunately, using just that information, the compiler can't find an overload of Write that can be used.
It seems like you expected it to use Write(bool) when T is bool, Write(int) when T is int, and so on. Templates would work like that. Generics don't.
Your options are:
a dozen different copies of your method, each of which has a fixed argument type that can be used to select the right overload of BinaryWrite::Write
find the overload yourself using reflection, make a delegate matching the right overload, and call it
use expression trees or the dynamic language runtime to find and make a delegate matching the right overload, and then you call it

How to I pass a checkbox value by reference with CLI?

I have a GUI app written in C++/CLI which has a load of configurable options. I have some overloaded functions which grab values from my data source and I'd like to connect my options to those values.
So here's a couple of data retrievers:
bool GetConfigSingle(long paramToGet, String^% str, char* debug, long debugLength);
bool GetConfigSingle(long paramToGet, bool^% v_value, char* debug, long debugLength);
I was hoping to pass in the checkbox's Checked getter/setter as follows:
result = m_dataSource->GetConfigSingle(CONFIG_OPTION1, this->myOption->Checked, debug, debugLen);
...but for some reason I get an odd compiler error which suggests the Checked value isn't being passed as I'd expect:
1>.\DataInterface.cpp(825) : error C2664: 'bool DataInterface::GetConfigSingle(long,System::String ^%, char*, long)' : cannot convert parameter 2 from 'bool' to 'System::String ^%'
Previously this code passed the checkbox in and modified the values itself, but I'm keen to break the dependency our data collection currently has on windows forms.
So what am I missing here?
[Edit] I've filled out the function definitions as they originally were to avoid confusion - my attempt to reduce the irrelevent information failed.
I'm fairly certain that the CheckBox getter / setter returns a bool.
Figured I'd clarify my comments from above and make it a "real" answer...
When you call Checked, what you're getting back as a return value is a bool that represents the current state of the CheckBox. It is not, however, a reference to the actual data member that holds the CheckBox's state. In fact, a properly encapsulated class shouldn't give access to it. Furthermore, since Checked returns a bool by value, that bool is a temporary object that doesn't necessarily exist by the time GetCongigSingle is called.
This leaves you with several options. Either pass the bools by value, and later set the CheckBox's state, or pass the CheckBox itself by reference and "check" it wherever you want.
The two overload of the method GetConfigSingleFile that you have mentioned both take two arguments whereas you are passing 4 arguments to the method. Are there any default arguments? If yes, can you please reproduce the original method declarations?
Most probably, the 4 argument overload of this method is expecting a String^% as the 2nd argument. This is what the compiler is suggesting anyway. But if we can have a look at the method declarations that could help diagnosing the problem.
This isn't an answer to my question, but worth being aware of - apparently there's a quirk in passing properties by reference.