How does V8 compiles and executes JavaScript events?

220 views
Skip to first unread message

Hanyun Tao

unread,
Aug 27, 2017, 4:06:45 PM8/27/17
to v8-users
Hi all,

I'm trying to understand how V8 engine compiles and execute JavaScript events. I used GDB to track the function call path when handling an event and it look like this.



 
#0  v8::internal::Logger::TimerEvent (this=<optimized out>, se=<optimized out>, name=<optimized out>) at ../../v8/src/log.cc:866


 
#1  0x00007f0cfcb23b39 in TimerEventScope (this=<optimized out>, isolate=<optimized out>) at ../../v8/src/log.h:354


 
#2  v8::Function::Call (this=<optimized out>, context=..., recv=..., argc=<optimized out>, argv=<optimized out>) at ../../v8/src/api.cc:5094


 
#3  0x00007f0cf18daa0b in blink::V8ScriptRunner::callFunction (function=..., context=0x201b099829d0, receiver=..., argc=1, args=0x7fffe9e1cea0,
    isolate
=0x2def318c6020) at ../../third_party/WebKit/Source/bindings/core/v8/V8ScriptRunner.cpp:658


 
#4  0x00007f0cf18a1c26 in blink::V8EventListener::callListenerFunction (this=0xe9381bfbea0, scriptState=0x37fa7244c710, jsEvent=..., event=
   
0x36f97e2ba60) at ../../third_party/WebKit/Source/bindings/core/v8/V8EventListener.cpp:112


 
 
#5  0x00007f0cf1887b16 in blink::V8AbstractEventListener::invokeEventHandler (this=0xe9381bfbea0, scriptState=0x37fa7244c710,
   
event=0x36f97e2ba60, jsEvent=...) at ../../third_party/WebKit/Source/bindings/core/v8/V8AbstractEventListener.cpp:142


 
#6  0x00007f0cf188787f in blink::V8AbstractEventListener::handleEvent (this=0xe9381bfbea0, scriptState=0x37fa7244c710, event=0x36f97e2ba60)
    at
../../third_party/WebKit/Source/bindings/core/v8/V8AbstractEventListener.cpp:101


 
#7  0x00007f0cf1887689 in blink::V8AbstractEventListener::handleEvent (this=0xe9381bfbea0, executionContext=0x201b099829d0, event=0x36f97e2ba60)
    at
../../third_party/WebKit/Source/bindings/core/v8/V8AbstractEventListener.cpp:89


 
#8  0x00007f0cf211c596 in blink::EventTarget::fireEventListeners (this=0x201b09982858, event=0x36f97e2ba60, d=0xe9381be9500, entry=...)
    at
../../third_party/WebKit/Source/core/events/EventTarget.cpp:700


 
#9  0x00007f0cf211b7cb in blink::EventTarget::fireEventListeners (this=0x201b09982858, event=0x36f97e2ba60)
    at
../../third_party/WebKit/Source/core/events/EventTarget.cpp:56

I tried to read the source code to find out where does V8 compiles the JavaScript, but unfortunately, I could not find it.

It would be really helpful if someone can explain the process to me, or let me know which files(functions) I should read.

Best regards,

Jakob Kummerow

unread,
Aug 27, 2017, 7:58:37 PM8/27/17
to v8-users
Hi Hanyun,

V8 is fairly complicated, and pretty much all it does is to compile and execute JavaScript. Do you have a more specific question? 

If you just want a starting point for reading code: maybe "CompileTopLevel" in src/compiler.cc would be a reasonable choice.

--
--
v8-users mailing list
v8-u...@googlegroups.com
http://groups.google.com/group/v8-users
---
You received this message because you are subscribed to the Google Groups "v8-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to v8-users+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Hanyun Tao

unread,
Aug 28, 2017, 12:06:13 PM8/28/17
to v8-users
Hi Jakob,

Thank you for replying!

To be more specific, I'm looking for the point (function) that initiate the compilation process.

In my understanding, when handling an "event", the renderer process in the browser will figure out the JavaScript related to the event, and ask the V8 engine to execute it by calling some api function.

Inside those api function, there should be a point where V8 initiate the compilation process, and that is what I'm looking for.

Best regards,


To unsubscribe from this group and stop receiving emails from it, send an email to v8-users+u...@googlegroups.com.

Jakob Kummerow

unread,
Aug 28, 2017, 1:29:29 PM8/28/17
to v8-users
The main API entry point for compilation is v8::ScriptCompiler::Compile().

I don't think event handling itself triggers compilation; but I'm not an expert on that part of the system. AFAIK event handlers are installed during page load (or more precisely: DOM element creation); they may still be compiled on-demand on first use but that's not controlled via the V8 API.

To unsubscribe from this group and stop receiving emails from it, send an email to v8-users+unsubscribe@googlegroups.com.

Hanyun Tao

unread,
Aug 29, 2017, 12:03:53 AM8/29/17
to v8-users
Hi Jakob,

Thanks again!

According to what you have said, v8 compiles the event handlers are installed(compiled?) before it is executed. If it is true, then I believe v8 will store the compiled code somewhere in the system.
Would it be possible for the user to get access to the compiled code? 

Best regards,

Jakob Kummerow

unread,
Aug 29, 2017, 12:29:33 PM8/29/17
to v8-users
No, compiled code is an internal implementation detail and as such is hidden from JavaScript and other external access. If there ever is a way for users to get to compiled code, then it's a (probably severe security) bug and we would like to hear about it! :-)

To unsubscribe from this group and stop receiving emails from it, send an email to v8-users+unsubscribe@googlegroups.com.

Hanyun Tao

unread,
Aug 29, 2017, 3:32:55 PM8/29/17
to v8-users
Thank you Jakob!

I can understand why it is hidden from external access. But would it be possible for the user to get access to the instruction addresses of the compiled code? 

We are studying the cache performance of JavaScript execution and we want to know if it is possible to prefetch the instructions in the next event handlers into the cache before it is executed based on the information collected in the v8 engine.

Best regards,

Jakob Kummerow

unread,
Aug 30, 2017, 8:39:00 PM8/30/17
to v8-users
Object addresses are not exposed either. You would have to build such instrumentation into V8's internals.

To unsubscribe from this group and stop receiving emails from it, send an email to v8-users+unsubscribe@googlegroups.com.

Hanyun Tao

unread,
Aug 31, 2017, 2:56:28 PM8/31/17
to v8-users
Hi Jakob,

Thank you for answering my questions! 

I would like to modify V8's internals to support such functionality. But before I start I would like to learn a little more about how chromium interact with V8 to process JavaScript event.

By reading the source code, I believe that the WebKit rendering engine will call v8::Function::Call in api.cc, and after that the V8 engine will execute the Javascript code. Am I correct?

If this is how things going to work, could you please point out where in this process, V8 compiles the code, or "read" the compiled code correspond to the JavaScript?

Thank you!

Jakob Kummerow

unread,
Aug 31, 2017, 3:36:53 PM8/31/17
to v8-users
A v8::Function maps to a v8::internal::JSFunction, which has a code() property. That's either the existing compiled code, or a stub that will trigger (re-)compilation based on the script() in the JSFunction's shared_function_info(). It will be retrieved and called by the JSEntryStub.

You might want to put your instrumentation into Invoke(...) in execution.cc.

To unsubscribe from this group and stop receiving emails from it, send an email to v8-users+unsubscribe@googlegroups.com.

Hanyun Tao

unread,
Aug 31, 2017, 4:06:03 PM8/31/17
to v8-users
Thank you! That helps a lot!

Hanyun Tao

unread,
Sep 4, 2017, 7:07:45 PM9/4/17
to v8-users
Hi Jakob!

I'm reading the Invoke(..) function you mentioned, and I believe the following part of the code plays an important role in executing the JavaScript but I cannot fully understand it.

  typedef Object* (*JSEntryFunction)(Object* new_target, Object* target,
                                     
Object* receiver, int argc,
                                     
Object*** args);


 
Handle<Code> code = is_construct
     
? isolate->factory()->js_construct_entry_code()
     
: isolate->factory()->js_entry_code();


 
{
   
// Save and restore context around invocation and block the
   
// allocation of handles without explicit handle scopes.
   
SaveContext save(isolate);
   
SealHandleScope shs(isolate);
   
JSEntryFunction stub_entry = FUNCTION_CAST<JSEntryFunction>(code->entry());


   
if (FLAG_clear_exceptions_on_js_entry) isolate->clear_pending_exception();


   
// Call the function through the right JS entry stub.
   
Object* orig_func = *new_target;
   
Object* func = *target;
   
Object* recv = *receiver;
   
Object*** argv = reinterpret_cast<Object***>(args);
   
if (FLAG_profile_deserialization && target->IsJSFunction()) {
     
PrintDeserializedCodeInfo(Handle<JSFunction>::cast(target));
   
}
   
RuntimeCallTimerScope timer(isolate, &RuntimeCallStats::JS_Execution);
    value
= CALL_GENERATED_CODE(isolate, stub_entry, orig_func, func, recv,
                                argc
, argv);
 
}


I could not find some good document to help me understand those data structures especially JSEntryFunction ,and Handle<Code>.  It would be really helpful if you can give me some document that talks about the definition of V8's basic data structures like Code.

For the functionality of the above code, here is my understanding (guess).

The first part of the code defines a special type of object named JSEntryFunction.

The next part of the code tries to obtain the executable code by calling isolate->factory()->js_entry_code();  , which might be the part that triggers the (re-)compilation or finds the existing compiled code.

The reset of the code executes the generated code.

Could you please verify if my understanding is correct?

Thanks a lot!

Jakob Kummerow

unread,
Sep 5, 2017, 3:10:03 PM9/5/17
to v8-users
Code: https://cs.chromium.org/chromium/src/v8/src/objects.h?type=cs&q=class+code&sq=package:chromium&l=3646 (as the name suggests, it's the kind of object V8 uses to store generated code)

Look around the code for more! :-)

For the functionality of the above code, here is my understanding (guess).

The first part of the code defines a special type of object named JSEntryFunction.

That's a C++ typedef defining a particular (C++) function signature. 

The next part of the code tries to obtain the executable code by calling isolate->factory()->js_entry_code();  , which might be the part that triggers the (re-)compilation or finds the existing compiled code.

No, it's the "stub" that handles calling into generated code. The function being called is target, and as you can see, it is passed as an argument to the call.
"Invoke" does not deal with compilation. As I wrote before: 
v8::internal::JSFunction has a code() property. That's either the existing compiled code, or a stub that will trigger (re-)compilation based on the script() in the JSFunction's shared_function_info(). It will be retrieved and called by the JSEntryStub.
You've identified the place where the JSEntryStub is called. That stub will look up the function's stored code and call it. This "code" could be another stub that triggers compilation if needed.

To unsubscribe from this group and stop receiving emails from it, send an email to v8-users+unsubscribe@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages