Why does the call is faster than the callvirt in IL?
I am exploring the C# via CLR book and I come across the following excerpt:
The call IL instruction can be used to call static, instance, and virtual methods. When the call
instruction is used to call a static method, you must specify the type that defines the method
that the CLR should call. When the call instruction is used to call an instance or virtual
method, you must specify a variable that refers to an object. The call instruction assumes that
this variable is not null. In other words, the type of the variable itself indicates which type
defines the method that the CLR should call. If the variable’s type doesn’t define the method,
base types are checked for a matching method. The call instruction is frequently used to call a
virtual method nonvirtually.
The callvirt IL instruction can be used to call instance and virtual methods, not static
methods. When the callvirt instruction is used to call an instance or virtual method, you
must specify a variable that refers to an object. When the callvirt IL instruction is used to call
a nonvirtual instance method, the type of the variable indicates which type defines the method
that the CLR should call. When the callvirt IL instruction is used to call a virtual instance
method, the CLR discovers the actual type of the object being used to make the call and then
calls the method polymorphically. In order to determine the type, the variable being used to
make the call must not be null. In other words, when compiling this call, the JIT compiler
generates code that verifies that the variable’s value is not null. If it is null, the callvirt
instruction causes the CLR to throw a NullReferenceException. This additional check means
that the callvirt IL instruction executes slightly more slowly than the call instruction.
Note that this null check is performed even when the callvirt instruction is used to call a
nonvirtual instance method.
And I can not wrap my head around that part: This additional check means that the callvirt IL instruction executes slightly more slowly than the call instruction.. Both the call and the callvirt assume that the object is not null. Hence, both of them should check that the object is not null. And as a result the speed should be the same.
Could someone explain it in a foolproof manner?
#HansPassant answer to you already about the time difference between direct and indirect calls.
I want to add something that related directly to your question:
Both the call and the callvirt assume that the object is not null. Hence, both of them should check that the object is not null. And as a result the speed should be the same.
Pay close attention to the text.
For call instruction:
The call instruction assumes that this variable is not null
And for callvirt instruction:
the JIT compiler generates code that verifies that the variable’s value is not null
The thing is that call instruction not generating the null check. Yes as #Hans points out, the null check is only one assembly instruction, and it's almost for free, but it's important to know about the missing null check.
It's legal to use call or callvirt for both calls - virtual and non-virtual.
What's happening is that for all calls except those the compiler can be sure that the this type isn't null or there is no this, callvirt will be used, in rest of the cases, call will be used.
All of this is just from the Compiler\IL point of view. What happening later, is as #Hans wrote, direct call when it's possible or indirect call when it's required. But this will happen anyway whether there was a call or a callvirt.
For more info see IL Call Vs. Callvirt Instruction - You will find there more relevant links.
Related
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.
For CALL METHOD - Static Method Call (Obsolete), the ABAP keyword documentation says: "If CALL METHOD is used for the standalone method call, no chained method calls are possible ..."
Nevertheless, the following happily executes on an 7.40 system. Isn't that an example of a standalone method call? Or else, what am I getting wrong?
REPORT ZUTEST3.
CLASS class_parent Definition.
PUBLIC Section.
METHODS m1 returning value(r) type ref to class_parent.
ENDCLASS.
CLASS class_parent Implementation.
Method m1.
create object r.
write / 'm1'.
EndMethod.
ENDCLASS.
start-of-selection.
data cl type ref to class_parent.
CREATE OBJECT cl.
CALL METHOD cl->m1( )->m1( ).
Edit: Disclaimer
We are writing a tool in Java that parses and transforms ABAP code. In particular, we have no intention to write new ABAP code. But instead, our tool has to handle all of ABAP, even obsolete statements and obscure syntax variants. Furthermore, I'd like to mention that I'm not an ABAP expert.
Addendum February 23rd, the right answer is given by Florian in the comments : "I reported the bug to the docu team and they answered that it has already been reported and they corrected it in the latest version. The new statement is: "With the second variant without round brackets, chained method calls are not possible and the operators NEW and CAST cannot be used.""
I let my original answer below (by the way, I think now that in CALL METHOD static_meth..., the term "standalone method call" refers to the part "static_meth", so it refers to the two groups of constructs, hence my answer is inexact and the one by SAP is 100% correct)
As I can see, the documentation says that the term "standalone method call" refers to these constructs (observe the use of parentheses), which are declared obsolete :
CALL METHOD method( ).
CALL METHOD method( 25 ).
CALL METHOD method( a = 1 ).
CALL METHOD method( EXPORTING a = 1 ).
CALL METHOD instance->method( ).
CALL METHOD class=>method( ).
etc.
The term ""standalone method call" doesn't refer to these constructs :
CALL METHOD method.
CALL METHOD method EXPORTING a = 1.
CALL METHOD instance->method.
CALL METHOD class=>method.
etc.
I guess that CALL METHOD cl->m1( ) belongs to the first group of constructs so there's an error in the documentation.
Probably a not is missing because it should apply to the second group of constructs (for instance, CALL METHOD method->method( ) is invalid).
Conclusion by me: you should read "If CALL METHOD is not used for the standalone method call, no chained method calls are possible ..."
Conclusion by Florian & SAP: In the comments below, Florian has asked the SAP support and indicates which exact sentence SAP should use in the next official release of the documentation
ADDENDUM (read it if you think wrongly that the documentation page is about "static methods", I hope I will make it clear that it's not).
The answers in this question prove that the documentation "CALL METHOD - Static Method Call (Obsolete)" is quite confusing.
The documentation title: here "static method call" means "static call of methods", not "call of static methods" (while at other places it could possibly have this meaning). If we could add parentheses in the written language, that would give these two possibilities, respectively :
static (method call) : static call of a method (whatever this method is of type "static" or "instance"; we could have a static call of an instance method)
(static method) call : call of a static method
Definitions :
static call : the class, interface or method names are "hardcoded" as symbols in the source code, not text literals, so that they are known by the compiler (for instance, CALL METHOD class=>method.). The opposite, a dynamic call, means that the names are passed through variables, which are only known at runtime (for instance, DATA classvar TYPE seoclsname VALUE 'CL_ABAP_TYPEDESCR'. CALL METHOD (classvar)=>(methodvar).) This other documentation page shows well that "static method call" is opposed to "dynamic method call", it never talks about "static and instance methods", only about "static method call" and "dynamic method call".
static method : a method being declared with CLASS-METHODS. For instance, a static call could be cl_ixml=>create( ), and a dynamic call could be DATA classvar TYPE seoclsname VALUE 'CL_IXML'. CALL METHOD (classvar)=>create.
Something in the documentation which confused me too, is the use of the term "static method" and examples based on static method only, because in fact the documentation page is about "static call", not static methods (instance methods could have been used) :
Syntax : CALL METHOD { static_meth( ) | static_meth( a ) | ... : what does mean "static_meth" here? In fact "static_meth" doesn't mean that it's a static method but it's any method in the context of a static method call. If you look at the documentation pages about "static calls" and "dynamic calls", you will see that the syntax is respectively static_meth( ) ... and CALL METHOD dynamic_meth ...
Example : a static method is again used in the three calls, all three of them having the same exact meaning but written with different syntaxes, to demonstrate that the first two calls are obsolete, and only the third one is recommended. In fact all three examples should have better used an instance method to avoid the confusion!
First of all, the method m1 is not static in your example and the quotation from the documentation has it that it is about a static method (CLASS-METHOD).
The only possible thing might be like in this example.
REPORT zutest3.
CLASS class_parent DEFINITION.
PUBLIC SECTION.
METHODS m1 RETURNING VALUE(r) TYPE REF TO class_parent.
CLASS-METHODS m1_static RETURNING VALUE(r) TYPE REF TO class_parent.
ENDCLASS.
CLASS class_parent IMPLEMENTATION.
METHOD m1.
CREATE OBJECT r.
WRITE / 'm1'.
ENDMETHOD.
METHOD m1_static.
CREATE OBJECT r.
WRITE / 'm2'.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
* this seems to be possible but no one sane calls a static method on an object reference
CALL METHOD class_parent=>m1_static( )->m1_static( ).
* the following two are not possible and will not compile either
* CALL METHOD class_parent=>m1_static( )=>m1_static( ).
* class_parent=>m1_static( )=>m1_static( ).
Second of all the CALL METHOD statement in this case is just a redundancy and its role is only informative.
Those two are equivalents
CALL METHOD cl->m1( ).
cl->m1( ).
analogically to for example this
DATA i TYPE i.
COMPUTE i = i + 1.
i = i + 1.
A bug in the docu. I reported it to the docu team and they answered that it has already been reported and they corrected it in the latest version.
The new statement is:
With the second variant without round brackets, chained method calls
are not possible and the operators NEW and CAST cannot be used.
My COM object has a method, in IDL defined as:
HRESULT _stdcall my_method( [in] long value, [in, out] IAnotherObject **result );
Is the caller allowed to call this method like so:
ptr->my_method(1234, NULL);
or would the caller be violating the COM specification in doing so?
In other words, should my code which implements this function check result != NULL before proceeding; and if so, does the COM spec require that I return E_INVALIDARG or E_POINTER or something; or would it be acceptable for my function to continue on and return 0 without allocating an AnotherObject ?
My object is intended to be Automation-compatible; and it uses standard marshaling.
Note: Question edited since my original text. After posting this question I discovered that optional should only be used for VARIANT, and an [in, out] parameter where result != NULL but *result == NULL should be treated like an out parameter, and I must allocate an object.
The Rules of the Component Object Model say:
The in-out parameters are initially allocated by the caller, then freed and re-allocated by the callee if necessary. As with out parameters, the caller is responsible for freeing the final returned value. The standard COM memory allocator must be used.
So, passing NULL is a violation. You can see several violations of COM rules even in Microsoft's own interfaces, such as IDispatch, where a few [out] parameters accept NULL, but that's because they have remote interface methods (see [local] and [call_as]) that most probably allocate the needed memory when crossing apartments, or otherwise perform custom marshaling.
EDIT: To further answer your questions.
I recommend you check for NULL [out] (or [in, out]) arguments and return E_POINTER when you find one. This will allow you to catch/detect most common errors early instead of raising an access violation.
Yes, you should check for argument validity.
If the client is in-process (and same apartment, etc.) with the server, there's nothing (no proxy, no stub) to protect your code from being called with a NULL.
So you're the only one left there to enforce any COM rule, whether that's considered to be a "violation" or not.
PS: defining in+out (w/o using VARIANTs) for Automation clients seems a bit unusual IMHO. I'm not sure all Automation clients can use this (VBScript?)
Example method in IDL:
HRESULT _stdcall a_method( [in] long value, [out] BSTR *comment );
My function logic is that for some values, no comment is necessary. Should I throw an exception if this function is called with comment == NULL by a client? Or is it OK to be permissive and allow this case?
(I'm developing my object in C++).
My rationale for trying to be strict with parameter checking is that I'm concerned about memory leaks, and about having the client make calls that are correct according to the COM spec but my object not accepting the call.
The semantics of [out] parameters are very explicit about this.
A method that gets an [out] parameter should never - ever - look at the parameter's value until it puts something on it. It is uninitialized memory. Garbage. In fact, if your method is called via a marshalled call (inter-apartment or inter-process), garbage is exactly what you get: whatever your caller might have put there when it called your method, was discarded and ignored by the proxy/stub; you never get it.
If the client/caller puts something on the parameter before making a call to your method, it is definitely a memory leak (given that it's an allocated object like a BSTR, of course), but it's the caller's fault. It is never the responsibility of a called method to deal with it. The called method can't handle the leak even if it wanted to.
If you want to handle whatever values might be passed in by the caller, you need to use an [in, out] parameter instead of [out].
One last warning: Automation clients (VBA, VBScript, etc.) don't support [out] parameters. Automation will silently handle any [out] parameter as if it was [in, out], which puts you in an awkward position: any value placed in the parameter by the client application will be leaked, and your method can't do anything about it.
If you plan on your object being used by an automation client, don't use [out] parameters. Use [in, out] instead, and make sure to check if the caller put a value on the parameter before the call. The proxy/stub will always marshal values both ways for an [in, out] parameter. If the caller placed a value on the parameter before the call, your method is responsible for releasing that value before writing to the parameter.
Edit: Expanding on the pointer itself being NULL:
You could think about checking for NULL and return E_INVALIDARG if it's NULL, but I wouldn't recommend it.
It is illegal to pass NULL as the pointer value for an [out] parameter. Even if your code handles a NULL value, if the call is marshalled, the marshaller will hit an Access Violation. The marshaller has to access the pointed value on the way back (to store the marshalled output on it) and it will do so without checking for null.
In your specific scenario (the call semantic being that there is nothing to return in a given case), the proper process is for the caller to always provide a pointer to storage, and for the called method to set the value to NULL. Something like this:
// Caller
BSTR comment;
hr = obj->a_method( 42, &comment);
// Callee
HRESULT a_method( value, BSTR *comment )
{
if (...)
{
//... I've decided we don't need to return a comment
*comment = NULL;
}
...
}
If you really want to have the pure null pointer semantic you mentioned, you can; but you have to mark the parameter with the [ptr] attribute. As far as I know, that doesn't work very well with Automation clients, and you have to use a custom marshaller. If you don't anticipate ever using an Automation client, this is clearly an option.
Can anyone tell me what the difference is between these 2 lines of code, which one is better to use?
System::String ^MyStr = gcnew System::String(MyStr);
System::String ^MyStr;
Those lines are not equivalent. In the first one, you will get an exception beacuse you're trying to create a String from an uninitialized tracking handle (MyStr). In the second one, MyStr is declared, not defined, it points to garbage and will throw an exception if you attempt to use it. Which one you should use depends on the rest of the code
The second one creates a new handle variable. If it's a local variable, then as #dario_ramos says, it's uninitialized, and your program will likely crash if you try to use the handle before assigning it. If it's a member variable or global, then it will be nullptr.
The first one is similar, although it can only be used for locals or globals (member variables use the ctor-initializer syntax in C++/CLI just like plain C++), and does exactly what you're not permitted to do. It reads the brand new uninitialized handle and passes it to the System::String constructor. If by chance the constructor finishes, a handle to the newly constructed String will be placed into the variable as part of initialization. But because the constructor is trying to make a copy of random garbage (if it's a local) or nullptr (if a global), most likely it will simply crash.
It's a bad idea to use the value of any variable in its own initializer (sometimes you need to use the address, never the value).