6 ms·
Do modern JS engines still map an entire lexical scope to an environment at all times? It seems like it would be fairly simple to optimize it down and only box
by wwright 6y ago
Do modern JS engines still map an entire lexical scope to an environment at all times? It seems like it would be fairly simple to optimize it down and only box up and share the variables that the code actually closes over.
- hajile 6y agoModern JITs make a C++ class to represent objects. Closures are just objects. We'll ignore non-strict code which allows external functions to access variables. If you want to actually eliminate these variables, you'd have to make TWO classes per function. Next you'd have to COPY all the relevant stuff from one class to the other. Maybe you instead keep the same class and add some code to make the various pointers null. That saves all the copying at the expense of some literals taking up the same space, but doesn't solve this next issue. JS has weak reference maps/sets along with upcoming weak values. If I reference an object in a closure (one that is used in a weak map), but don't return that variable, what happens? If it's set to null, then something is going to potentially be GC'd when it shouldn't be. Perhaps this kind of lifetime issue could be detected with a sufficiently smart JIT, but in such a dynamic language without types, I doubt it's possible.
- ridiculous_fish 6y agoThere's a lot of misunderstanding here. A closure does not store the variables it creates. Instead, the closure's code block will allocate space for those variables when it is called. The GC must trace all reachable values. This includes the stack, and also variables captured by closures which are themselves reachable. The same principles apply whether or not a JIT is in use.
- hajile 6y agoI didn't misunderstand what I was writing (though I may not have succeeded in conveying what I meant). At compile time, a hidden class will be created to represent all the variables (both user-visible and user-inaccessible). Every function is actually called by an internal [[Call]]. This will create an instance of that class which is placed on the heap and may or may not copy some values into the stack (it's essentially a necessary performance optimization, but not actually hard requirement in all ISAs). A closure is an object that has only one public method or property (a function application sometimes called apply). In theory, if you could guarantee that the apply method and all nested lexical scopes never accessed a variable and the variable was not reference counted, you could safely null the entry in the class and allow whatever it pointed to to be GC'd. My only contention is that this is easier to theorize about than to actually do.
- ridiculous_fish 6y agoCaptured variables live in an environment, while non-captured variables live on the stack.