Zhongxiaoli (Liigo)
Date: March 3, 2015 night
Original link: http://blog.csdn.net/liigo/article/details/44045177
Copyright, reproduced Please specify the source: Http://blog.csdn.net/liigo
Two days ago I helped to solve a technical problem, which is briefly recorded and summarized here.
Specifically, in the process of using the WebKit engine-based encapsulation component Wke, a simple language function needs to be registered with the JavaScript engine so that it can be called in a Web page (just like a normal JavaScript function in a Web page). If we can do this, it basically realizes the two-way communication mechanism from the JavaScript to the easy language, the easy language return value to the JavaScript, later has the extensive application space.
In the overall thinking, it is quite simple. Because Wke has provided quite intuitive interface functions (though severely lacking documentation):
#define Js_call __fastcalltypedef Jsvalue (Js_call *jsnativefunction) (jsexecstate es); WKE_API void jsbindfunction (const char* name, jsnativefunction FN, unsigned int argcount); WKE_API void Jsbindgetter (const char* name, jsnativefunction fn); /*get PROPERTY*/WKE_API void Jsbindsetter (const char* name, jsnativefunction fn); /*set Property*/wke_api int Jsargcount (jsexecstate es); Wke_api jstype jsargtype (jsexecstate es, int argidx); Wke_api jsvalue jsarg (jsexecstate es, int argidx);
The core function here is jsbindfunction (), which is called to register a new JavaScript function, providing only the function name, implementing the callback function, and the number of arguments. Inside the callback function, through the Jsargcount/jsargtype/jsarg read JS passed in parameters, through some other interface functions to create the JS value object, are at a glance, this is not a thing.
callback function (Fastcall)
The first card is stuck on the calling convention of the callback function : The second parameter of Jsbindfunction is called the callback function of fastcall calling convention! However, the easy language compiler simply does not support compiling functions that generate fastcall calling conventions (only support stdcall). The Fastcall convention passes the first two parameters through the register ECX and edx, and the remaining parameters are stacked in the order of right-to-left (from backward forward), and the callee is responsible for cleaning up and balancing the stack. This is somewhat similar but distinctly different from the stdcall. If 3,721 blindly passes the callback function of the stdcall calling convention, the program should run without crashing.
What about that? Easy language compiler does not support fastcall, we have to self-reliance, pure hand-generated binary X86 machine instructions, human compilation generates a callback function that conforms to the fastcall calling convention. The prototype of the function declaration is: jsvalue (__fastcall *jsnativefunction) (jsexecstate es), the only parameter can be read from the ECX register, there is no stack parameters, and therefore do not have to balance the stack, the direct RET is finished. For the sake of convenience, we introduced two easy-to-write functions: Proxy functions and user functions, in which the proxy function is responsible for JS and the type conversion of the language, the user function is responsible for the specific execution logic, these two functions can no doubt only be the stdcall calling convention (Easy language compiler also does not support what other conventions). Here we design our callback function structure, which is represented by pseudo-assembly code:
Push user function Address push ecxmov EAX, proxy function address call Eaxret
These pseudo-assembly code, if written in easy language, is actually a sentence: return (agent function (es, user function)). (Note: The parameter ES is the transparent data that the JavaScript engine passes in through the ECX register.) )
Easy language code is simple, but because of compiler limitations, we can't write that. The assembler code is a little bit more complicated, but we still can't embed the assembly directly (not supported by the easy language compiler). Only Handwriting machine code! Take out the Intel instruction set manual, check the table and start working. Since it is dynamically generated code, it is necessary to request a piece of memory, then fill in the machine code, and then return the first address of this memory-the first address of this memory is the first address of the callback function that our human flesh compiles to conform to the fastcall calling convention. The specific code is as follows:
Agent functions (stdcall) and user functions (stdcall)
The previously mentioned proxy function, is a very common easy-to-use language function (stdcall), it is responsible for interpreting JavaScript passed in parameters, converted into easy language data type, transpose easy language version of the user function (also stdcall), Finally, the return value of the easy language user function is converted to the JavaScript type and returned to the JavaScript engine. It receives two parameters, all of which are passed in by a manually generated callback function in front of us. The code is as follows:
The return value of the proxy function is a long integer, which is a 64-bit integer. According to the definition of jsvalue, it is a 64-bit pointer that can be represented by a long integer of easy language.
JavaScript text is determined to be UTF-8 encoded, before the conversion to easy language text, it is best to perform the encoding conversion (UTF-8 = GB18030), otherwise Chinese garbled. This is a very simple step, just as a post-school assignment.
We can completely improve this proxy function, or write another proxy function that supports different types of user functions (such as different parameter types and number of parameters and return value types).
The rest of the user function is simpler, and here's just a general example (this function is used in the following test code):
Registering the easy language function as a JavaScript function dynamically generates a callback function that is passed as a parameter to the jsbindfunction:
Summary of Function call order
It's time to summarize: We use dynamic code generation technology to generate a callback function (Jsnativefunction) that conforms to the fastcall calling convention at run time, registering it with the JavaScript engine by Jsbindfunction. Give it a JavaScript function name at the same time. When the Web page script calls this JS function, the callback function is called, and the callback function calls the proxy function, and the proxy function calls the user function, and the return value is returned to the JS engine by layer after the user function returns.
The test code first registers a test with the JS function, easy language code: Register the JS function ("Plus1", the user function example). First, a piece of HTML, loaded into the browser:
<a href= ' # ' onclick= ' document.getElementById (' result '). Value=plus1 (' Liigo '); " >link</a><p><textarea rows= ' 6 ' cols= ' id= ' result ' >hello</textarea>
When you click on a link in the webpage, the previously registered JS function Plus1 will be executed, and the user function example of the easy language function is called. The text returned by the easy function, which is the return value of the PLUS1, is finally exported to the edit box in the Web page. If the text in the edit box is displayed as "Liigo Hohoho", the test is successful.
Write in the final thought can you replace the previous implementation with the easy language built-in function "Place code"? I (Liigo) think that there are at least two resistance to prevent us from applying the place code: 1, the placed code can only be used for existing functions, but not the dynamic generation of new functions, 2, the place code is the compile-time behavior, the placed code can not contain a variable amount (such as the above call EAX will not work). Reluctantly apply the place code, also not no, but will make each user function is very complex, contains both the place code, but also contains the JS and language type conversion, including the business logic, there is no encapsulation, ease of use is approximately equal to zero.
Complete the full text. Thanks for watching! Do you know who I am?
The famous four-item nurse with knife!
Using dynamic code generation technology to invoke the easy language function in the HTML5 Web page JS based on the WebKit engine