[webkit-dev] PSA: JSC interface is changed from passing ExecState* to passing lexical JSGlobalObject*

Yusuke Suzuki ysuzuki at apple.com
Tue Oct 22 16:34:32 PDT 2019

Hello WebKittens,

We did long-wanted refactoring: JSC runtime functions start getting lexical realm JSGlobalObject* instead of ExecState*.
Since this slightly changes how we interact with JSC, we would like to show what is changed.

This refactoring involves several changes.

1. JSC interface starts getting JSGlobalObject* instead of ExecState*.
2. ExecState* is consistently renamed to CallFrame*. And it is used only when CallFrame* is actually required (accessing arguments etc.) And the name `ExecState*` is removed from the tree.

Previously we are using `ExecState*`. But this caused multiple problems so far. Here is the motivation behind this change.

1. Fix so many bugs in JSC, and makes JSC’s inlining finally sane

ExecState* is simply wrong in various cases. We are getting lexical JSGlobalObject* by calling `ExecState::lexicalGlobalObject()`. But it returns wrong value if the current function is inlined in DFG/FTL. ExecState is pointing the whole function’s CallFrame. And returned `JSGlobalObject*` is lexical JSGlobalObject* of top-most function. If inlined function has different lexical JSGlobalObject* from the top-most one, we are getting wrong JSGlobalObject*. Getting `CodeBlock*` from ExecState* is also wrong. We had multiple places that is getting CodeBlock to get strict-mode flag, and this code does not consider about inlining. Instead, we pass lexical JSGlobalObject* explicitly. This change makes fixing these issues very easy.

2. Clear separation between CallFrame* and JSGlobalObject*, making API clean

CallFrame* is only used when we actually want to access CallFrame* (to get arguments etc.). Previously, we are using CallFrame* as an useful abstraction to offer an access to various things including lexical JSGlobalObject*. But since this is CallFrame*, it exist only after executing some JS code. So if we want to call JSC C++ runtime functions without JS code (calling JS runtime things directly from C++ world), we do not have ExecState* at all at that time. To make it OK, we invented `globalExec()`, which is saying CallFrame*, but in fact, it was not a CallFrame*. It exists only for calling these C++ runtime functions when CallFrame* does not exist yet. This is ugly and we really wanted to remove this. It is now finally removed in 99% of code. Through this refactoring, we found multiple places that is accessing |thisValue| etc. while it should not have any CallFrame*. So they are accessing wrong thing because CallFrame* offers the way to access the wrong thing. We should not offer such a wrong abstraction.
And now, we can pass lexical JSGlobalObject* explicitly. Previously, we do not have a good way to create ExecState* conveniently. So we just randomly pass accessible ExecState* to C++ runtime functions. This looks working, but it is wrong when we consider about lexical JSGlobalObject* carefully. Now interface explicitly requires lexical JSGlobalObject*, so we can pass the correct JSGlobalObject* explicitly.

3. Steps towards putting all JSCells into IsoSubspace

JSC had very fancy hack: all callable JS cells must be small enough (it can be allocated in MarkedBlock). This was originally required to make `ExecState::vm()` function super fast. We are calling this so frequently and we needed to keep it fast. And we can make it fast if we have such a weird guarantee. But it turned out that this assumption is not good one for the world putting all JSCells into IsoSubspace. We would like to introduce some optimizations to IsoSubspace to make it scalable to various JSCell types. But this optimization requires that the above assumption is discarded. To make it work, we do this refactoring and now we get VM& from JSGlobalObject instead of ExecState.

I’m now doing some follow-up work which makes things more solid. And I’m planning to do some minor refactoring, like, naming things consistently etc.
I’m also planning to fix various bugs found while doing this refactoring.
And…, this change paves the way to (3), which I’m currently focusing on :)

Best regards,
Yusuke Suzuki

More information about the webkit-dev mailing list