how does Javascript do this??? |
The steps are pretty much the same as for Java, only instead of running in separate processes, the bytecode compiler and the JIT run as separate functions in the same process[1].
It makes things confusing because the browser sits in the way, does the browser interact with the CPU(because you will be changing the look of what the browser contains/the web page) or does the Javascript interpreter send the translated machine code to the CPU? |
I think you're confused about how execution works. You never "send" stuff to the CPU. The CPU is not a device like a disk is and has no capacity to "receive" things. The CPU controls what the machine (including itself) does. As such, it can do things like
* Read numbers from memory into its registers.
* Write numberes from its registers into memory.
* Change the contents of its registers.
* Execute an instruction in memory, based on the value of its registers.
* Request a device to write data to memory from its internal memory, or vice versa.
So what the JS engine does is "simply":
1. Compile the JS to bytecode.
2. Translate the bytecode into native machine instructions. When I say "native machine instructions" I mean that the CPU can directly execute them.
3. Place these instructions into executable memory. What I mean by "executable memory" is that most OSs organize memory into pages (typically of around 4 KiB) and give each a number of toggable flags. Flags such as writable, executable, etc. Pages that don't have the executable flag set cannot be executed (obviously).
4. When the browser is ready to execute JS code, it jumps into it. From the point of view of the code, what this would look like is that you'd have a bunch of points in some data structure like
|
std::map<std::string, void *> js_functions;
|
The pointers point to raw byte arrays. When you want to execute function foo you do
1 2 3 4 5 6
|
typename void (*js_function)(js_param *, size_t);
auto f = js_functions["foo"];
auto foo = (js_function)f;
js_param p[] = { 42 };
foo(p, 1);
|
The final pointer dereference tells the CPU "pull the value of pointer 'foo' from memory and load it into the instruction pointer, saving its previous value by pushing it onto the stack". The instruction pointer contains the next instruction that should be executed, so this causes the CPU to jump to the address pointed to by foo, and when the function finally returns the previous value of the instruction pointer will be popped off the stack, which will cause the caller to resume executing.
[1] Probably. Modern browsers makes things more complicated because the same logical "instance" runs as multiple processes that cooperate via IPC.