{"id":424,"date":"2012-10-02T09:23:55","date_gmt":"2012-10-02T16:23:55","guid":{"rendered":"http:\/\/blog.mozilla.org\/luke\/?p=424"},"modified":"2020-09-28T14:18:43","modified_gmt":"2020-09-28T21:18:43","slug":"optimizing-javascript-variable-access","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/luke\/2012\/10\/02\/optimizing-javascript-variable-access\/","title":{"rendered":"Optimizing JavaScript variable access"},"content":{"rendered":"<p>I recently finished a <a href=\"https:\/\/bugzilla.mozilla.org\/showdependencygraph.cgi?id=767013\">project<\/a> to improve how SpiderMonkey implements variable access so I thought this would be a good time to explain how it all works now.  Taking a note from mraleph&#8217;s <a href=\"http:\/\/blog.mrale.ph\/post\/24351748336\/explaining-js-vm-in-js\">post<\/a> (and <a href=\"http:\/\/mitpress.mit.edu\/sicp\/full-text\/book\/book-Z-H-25.html#%_chap_4\">SICP<\/a>), I&#8217;ll illustrate the implementation using JavaScript as the implementation language.  That is, I&#8217;ll translate JavaScript using full-featured variable access into JavaScript that doesn&#8217;t, rather like how the <a href=\"http:\/\/en.wikipedia.org\/wiki\/Cfront\">original C++ compiler<\/a> translated C++ into C.<\/p>\n<p>Before starting, let me set up the problem space.  By <em>variable<\/em> I&#8217;m referring not just to the variables introduced by <code>var<\/code>, but also those introduced by <a href=\"http:\/\/wiki.ecmascript.org\/doku.php?id=harmony:block_scoped_bindings\"><code>let<\/code><\/a>, <a href=\"http:\/\/wiki.ecmascript.org\/doku.php?id=harmony:const\"><code>const<\/code><\/a>, <code>catch<\/code>, <code>function<\/code> statements, and function argument lists.  By variable <em>access<\/em>, I mean a read or a write.  Variable access can take many forms:<\/p>\n<ul>\n<li><em>Local<\/em> access (i.e., access to a variable in the same function):\n<pre>function add(x,y) { return x+y }<\/pre>\n<\/li>\n<li><em>Non-local<\/em> access (i.e., access to a variable in an enclosing function):\n<pre>function add(x,y) { return (function() { return x+y })() }<\/pre>\n<\/li>\n<li>Access from dynamically-generated code:\n<pre>function add(x,y) { return eval(\"x+y\") }<\/pre>\n<\/li>\n<li>Access after dynamic scope modification via non-strict direct <code>eval<\/code>:\n<pre>function add(a,b) { eval(\"var x=\"+a+\", y=\"+b); return x+y }<\/pre>\n<\/li>\n<li>Dynamic function argument access via the <code>arguments<\/code> object:\n<pre>function add(x,y) { return arguments[0]+arguments[1] }<\/pre>\n<\/li>\n<li>Unexpected debugger snooping (via <a href=\"http:\/\/getfirebug.com\">Firebug<\/a>, the new <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Tools\/Debugger\">builtin Firefox debugger<\/a>, or directly from privileged JS using the new <a href=\"https:\/\/wiki.mozilla.org\/Debugger\"><code>Debugger<\/code><\/a> API):\n<pre>dbg.onDebugerStatement = function(f) { return f.eval(\"x+y\") }<\/pre>\n<\/li>\n<\/ul>\n<p>To keep the post small(-ish), I&#8217;ll pretend there is only (non-strict, direct) <code>eval<\/code> and ignore strict and indirect <code>eval<\/code> as well as <code>with<\/code> (which we generally deoptimize as if it was an <code>eval<\/code>).  I&#8217;ll also ignore <code>let<\/code>, global access optimizations, the <a href=\"http:\/\/statichtml.com\/2011\/spidermonkey-function-hoisting.html\">bizarre<\/a> things SpiderMonkey does for block-level function statements, and the debugger.<\/p>\n<h2 id=\"dynamic-lookup\">The worst case<\/h2>\n<p>To rise above, we must first see how low we need to go in the worst case.  Consider the following function:<\/p>\n<pre>\r\nfunction strange() {\r\n  eval(\"var x = 42\");\r\n  return function xPlus1() { var z = x + 1; return z }\r\n}\r\n<\/pre>\n<p>Here, <code>eval<\/code> is <em>dynamically<\/em> adding <code>x<\/code> to the scope of <code>strange<\/code> where it will be read by <code>xPlus1<\/code>.  Since <code>eval<\/code> can be called with a dynamically-constructed string we must, in general, treat function scopes as dynamic maps from names to values.  (Fun fact: names added by <code>eval<\/code> can be removed using the <code>delete<\/code> keyword, so the map can both grow and shrink at runtime!)<\/p>\n<p>To make this more concrete, we&#8217;ll implement scopes in JS using ES6 <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/JavaScript\/Reference\/Global_Objects\/Map\">Map<\/a> objects.  We&#8217;ll give every function its own <code>Map<\/code> that will be stored in a local variable named <code>scope<\/code> and hold all the function&#8217;s variables.  (Yes, we&#8217;re using a variable to implement variables; but since we&#8217;ll only use a small finite number of them, we can think of them as <a href=\"http:\/\/en.wikipedia.org\/wiki\/Processor_register\">registers<\/a>.)<\/p>\n<pre>\r\nfunction strange() {\r\n  \/\/ the scope of 'strange' is initially empty\r\n  var scope = new Map;\r\n\r\n  \/\/ eval(\"var x = 42\") effectively executes:\r\n  scope.set('x', 42);\r\n\r\n  return function xPlus1() {\r\n    \/\/ vars are hoisted so scope initially contains 'z'\r\n    var scope = new Map([['z', undefined]]);\r\n\r\n    \/\/ var z = x + 1\r\n    scope.set('z', scope.get('x') + 1);  \/\/ oops!\r\n\r\n    \/\/ return z\r\n    return scope.get('z');\r\n  }\r\n}\r\n<\/pre>\n<p>As the comment indicates, there is a bug in <code>xPlus1<\/code>: <code>x<\/code> isn&#8217;t in the <code>scope<\/code> of <code>xPlus1<\/code>, it&#8217;s in the <code>scope<\/code> of <code>strange<\/code>!  To fix this we need to do two things:<\/p>\n<ol>\n<li>Add an <code>enclosing<\/code> field to all scope objects indicating the enclosing function&#8217;s <code>scope<\/code> (or the global object if the function is top-level).<\/li>\n<li>Replace uses of <code>scope.get<\/code> with a <code>lookup<\/code> algorithm that walks the chain of scopes.<\/li>\n<\/ol>\n<pre>\r\nfunction strange() {\r\n  \/\/ the scope of 'strange' is initially empty\r\n  var scope = new Map;\r\n  <font color=\"red\">scope.enclosing = window;<\/font>\r\n\r\n  \/\/ eval(\"var x = 42\") effectively executes\r\n  scope.set('x', 42);\r\n\r\n  var tmp = function xPlus1() {\r\n    \/\/ vars are hoisted so scope initially contains 'z'\r\n    var scope = new Map([['z', undefined]]);\r\n    <font color=\"red\">scope.enclosing = xPlus1.enclosing;<\/font>\r\n\r\n    \/\/ var z = x + 1\r\n    scope.set('z', <font color=\"red\">lookup(scope, 'x')<\/font> + 1);\r\n\r\n    \/\/ return z\r\n    return lookup(scope, 'z');\r\n  }\r\n  <font color=\"red\">tmp.enclosing = scope;\r\n  return tmp;<\/font>\r\n}\r\n\r\n<div id=\"lookup-function\" \/><font color=\"red\">function lookup(scope, name) {\r\n  while (scope instanceof Map &amp;&amp; !scope.has(name))\r\n    scope = scope.enclosing;\r\n  return scope.get(name);\r\n}<\/font>\r\n<\/pre>\n<p>Note that, without being able to use non-local variable access (since that is what we are implementing), we must attach the <code>scope<\/code> of <code>strange<\/code> to the <code>xPlus1<\/code> function object.  This isn&#8217;t just some hack; it is a fundamental part of the implementation of languages with <a href=\"http:\/\/en.wikipedia.org\/wiki\/Scope_%28computer_science%29#Lexical_scoping_and_dynamic_scoping\">lexically-scoped first-class functions<\/a>.  More generally, we can establish the following relationship (pardon my ASCII-art):<\/p>\n<pre>\r\nFunction-scope\r\n  | *        ^ 0 or 1\r\n  |          |\r\n  | call of  | enclosing\r\n  |          |\r\n  V 1        | 1\r\nFunction-object\r\n  | *\r\n  |\r\n  | evaluation of\r\n  |\r\n  V 1\r\nFunction-literal\r\n<\/pre>\n<p>Each function literal can be evaluated any number of times, with each evaluation producing a function object that is associated with its enclosing scope.  Each of those function objects can be called any number of times, each of those calls producing a scope.  When using the language, it is easy to see just a single concept <em>function<\/em>, but hopefully this illustrates that there are really three &#8220;function&#8221; concepts at play here: <em>scope<\/em>, <em>object<\/em>, and <em>literal<\/em>.<\/p>\n<p>With these changes, we have successfully dealt with the ravages of <code>eval<\/code>, but at what cost?  Each variable access involves a call to an algorithm that iteratively performs hash-table lookups!  Fortunately, this problem isn&#8217;t that different from object-property lookup and the same type of optimizations apply: hidden classes and caches.  I won&#8217;t go into these techniques, as there are already <a href=\"http:\/\/blog.cdleary.com\/2010\/09\/picing-on-javascript-for-fun-and-profit\">two<\/a> <a href=\"http:\/\/blog.mrale.ph\/post\/24351748336\/explaining-js-vm-in-js\">great<\/a> explanations available.  (Caching has been used to speed up name access since Firefox 3.)  Even with these optimizations, however, name lookup isn&#8217;t as fast as we&#8217;d like it to be and we are still creating a <code>Map<\/code> object on every call.<\/p>\n<p>In summary, we&#8217;ve handled the worst case, but we&#8217;d like to do better in code that doesn&#8217;t exercise the worst case.<\/p>\n<h2 id='fast-local'>Fast local name access<\/h2>\n<p>Now let&#8217;s optimize local variable access when <em>all<\/em> accesses are local.  With this constraint, JavaScript starts to look like C and we can use some of the same techniques as a C compiler: store all variables in a stack and access variables by their offset in the stack.<\/p>\n<p>As a first (highly garbalicious) iteration, we create an array for each set of arguments and <code>var<\/code>s, thereby turning<\/p>\n<pre>\r\nfoo(13, 42);\r\n\r\nfunction foo(x,y) {\r\n  var a = x + y;\r\n  return bar(a);\r\n}\r\n<\/pre>\n<p>into:<\/p>\n<pre>\r\nfoo([13, 42]);\r\n\r\nfunction foo(args) {\r\n  var vars = [undefined];\r\n  vars[0] = args[0] + args[1];\r\n  return bar([vars[0]]);\r\n}\r\n<\/pre>\n<p>The second step is to avoid creating all those temporary arrays by using one big array, shared by all active functions.  There are many ways to do this (corresponding to different <a href=\"http:\/\/en.wikipedia.org\/wiki\/Calling_conventions\">calling conventions<\/a>); we&#8217;ll just do something simple here:<\/p>\n<pre>\r\n\/\/ executed some time before the first function call:\r\nvar stack = [];\r\n\r\nstack.push(13);\r\nstack.push(42);\r\nfoo(\/* number of arguments pushed = *\/ 2);\r\n\r\nfunction foo(numArgs) {\r\n  \/\/ push missing arguments, pop extra arguments\r\n  for (var i = numArgs; i &lt; 2; i--)\r\n    stack.pop();\r\n\r\n  \/\/ analogous to the <a href=\"http:\/\/en.wikipedia.org\/wiki\/Frame_pointer#Structure\">frame pointer<\/a> register\r\n  var firstLocal = stack.length;\r\n\r\n  \/\/ push local 'a'\r\n  stack.push(undefined);\r\n\r\n  \/\/ var a = x + y:\r\n  stack[firstLocal] = stack[firstLocal - 2] + stack[firstLocal - 1];\r\n\r\n  \/\/ prepare stack for call to 'bar(a)':\r\n  stack.push(stack[firstLocal]);\r\n  return bar(\/* number of arguments pushed = *\/ 1);\r\n\r\n  \/\/ in this calling convention, the callee pops the arguments\r\n  stack.pop(); \/\/ pop 'a'\r\n  stack.pop(); \/\/ pop 'y'\r\n  stack.pop(); \/\/ pop 'x'\r\n}\r\n<\/pre>\n<p>With this strategy, a JIT compiler can do some pretty great optimization.  To start with, each read from or write to <code>stack<\/code> in the above JS can be compiled down to a single CPU load or store.  This is achieved by caching the address of <code>stack[firstLocal]<\/code> in a register and rolling the remaining &#8220;<code>+&nbsp;INDEX<\/code>&#8221; into the load instruction as an <a href=\"http:\/\/en.wikipedia.org\/wiki\/Effective_address#Base_plus_offset.2C_and_variations\">offset<\/a>.  Even better, modern JavaScript JIT compilers do register allocation which can avoid the loads\/stores altogether.  (Register allocation has been in Firefox since version 3.5.)<\/p>\n<p>In summary, we can do pretty efficient things for local variable access, but only with some stringent restrictions.<\/p>\n<h2>Fast non-local access<\/h2>\n<p>While we shouldn&#8217;t expect great performance when functions call <code>eval<\/code> or <code>arguments<\/code>, the requirement made in the previous section that we only access local variables is pretty harsh and conflicts with both the <a href=\"http:\/\/www.joelonsoftware.com\/items\/2006\/08\/01.html\">functional<\/a> and <a href=\"http:\/\/javascriptplayground.com\/blog\/2012\/04\/javascript-module-pattern\">module<\/a> patterns of JavaScript programming.  In this section, we&#8217;ll optimize non-local access.<\/p>\n<p>We start with the observation that, in the absence of <code>eval<\/code> and other weirdos, there is no need for a fully dynamic scope lookup: we can know exactly where on the scope chain to find the variable being accessed.  The first step is to view each top-level function as a tree of nested functions, giving each node (function) in the tree an array of the variables defined in its scope.  For example, given this function:<\/p>\n<pre>\r\nfunction add3(arg1, arg2, arg3) {\r\n  function addInner(innerArg1) {\r\n    function innermost() { return innerArg1 + arg2 + getArg3() };\r\n    return innermost();\r\n  }\r\n  function getArg3() {\r\n    return arg3;\r\n  }\r\n  return addInner(arg1);\r\n}\r\n<\/pre>\n<p>we can distill the following tree:<\/p>\n<pre>\r\nfunction add3: [arg1, arg2, arg3, addInner, getArg3]\r\n |\\_ function addInner: [innerArg1, innermost]\r\n |    \\_ function innermost: []\r\n  \\_ function getArg3: []\r\n<\/pre>\n<p>The next step is to include uses as leaves of the tree that are linked to the innermost enclosing definition with the same name.  Rather than drawing terrible ASCII-art arrows, let&#8217;s represent a use-to-definition arrow with a two-number coordinate:<\/p>\n<ul>\n<li><em>hops<\/em> = the number of nodes in the tree to skip to get to the function node whose array contains the definition.<\/li>\n<li><em>index<\/em> = the index of the definition in the function node&#8217;s array.<\/li>\n<\/ul>\n<p>Linking uses to definitions in the above tree produces:<\/p>\n<pre>\r\nfunction add3: [arg1, arg2, arg3, addInner, getArg3]\r\n |\\_ function addInner: [innerArg1, innermost]\r\n |    |\\_ function innermost: []\r\n |    |    |\\_ \"innerArg1\"   {hops=1, index=0}\r\n |    |    |\\_ \"arg2\"        {hops=2, index=1}\r\n |    |     \\_ \"getArg3\"     {hops=2, index=4}\r\n |     \\_ \"innermost\":       {hops=0, index=1}\r\n |\\_ function getArg3: []\r\n |     \\_ \"arg3\"             {hops=1, index=2}\r\n |\\_ \"addInner\"              {hops=0, index=3}\r\n |\\_ \"getArg3\"               {hops=0, index=4}\r\n  \\_ \"arg1\"                  {hops=0, index=0}\r\n<\/pre>\n<p>As a last step, we&#8217;ll erase all variables that only have local uses.  We can also remove entire scopes if they are empty; we just need to be mindful not to include these removed scopes in any <code>hops<\/code> count.  Applying this last transformation produces the following, final tree:<\/p>\n<pre>\r\nfunction add3: [arg2, arg3, getArg3]\r\n |\\_ function addInner: [innerArg1]\r\n |     \\_ function innermost: \r\n |         |\\_ \"innerArg1\"   {hops=0, index=0}\r\n |         |\\_ \"arg2\"        {hops=1, index=0}\r\n |          \\_ \"getArg3\"     {hops=1, index=2}\r\n |\\_ function getArg3: \r\n |     \\_ \"arg3\"             {hops=0, index=1}\r\n  \\_ \"getArg3\"               {hops=0, index=2}\r\n<\/pre>\n<p>With this analysis, we have all the information we need to efficiently compile the program.  For the local-only variables that we removed in the last step, we can use the stack directly (as in the <a href=\"#fast-local\">second section<\/a>).  For variables with non-local access, we can represent the scope chain as a linked list of scopes (as in the <a href=\"#dynamic-lookup\">first section<\/a>), except this time we represent scopes as <em>arrays<\/em> instead of <em>maps<\/em>.  To compile an access, we use <code>{hops,index}<\/code> coordinate: <code>hops<\/code> tells us how many <code>.enclosing<\/code> links to follow, <code>index<\/code> tells us the index in the array.<\/p>\n<p>Applying this scheme to the original example (and eliding the missing\/extra arguments boilerplate) produces the following translated JS (with the scope access code highlighted in <font color=\"red\">red<\/font>):<\/p>\n<pre>\r\nfunction add3() {\r\n  var firstLocal = arguments.length;\r\n\r\n  \/\/ the optimized scope of add3 is: [arg2, arg3, getArg3]\r\n  var scope = [stack[firstLocal-2], stack[firstLocal-1], undefined];\r\n  scope.enclosing = window;\r\n\r\n  \/\/ initialize 'addInner':\r\n  stack.push(function addInner() {\r\n    var firstLocal = arguments.length;\r\n\r\n    \/\/ the optimized scope of addInner is: [innerArg1]\r\n    var scope = [stack[firstLocal - 1]];\r\n    scope.enclosing = addInner.enclosing;\r\n\r\n    \/\/ push local 'innermost'\r\n    stack.push(function innermost() {\r\n      \/\/ the scope of innermost is completely optimized away\r\n      var scope = innermost.enclosing;\r\n\r\n      \/\/ return innerArg1 {hops=0, index=0} +\r\n      \/\/        arg2      {hops=1, index=0} +\r\n      \/\/        getArg3() {hops=1, index=2}\r\n      return <font color=\"red\">scope[0]<\/font> +\r\n             <font color=\"red\">scope.enclosing[0]<\/font> +\r\n             (<font color=\"red\">scope.enclosing[2]<\/font>)();\r\n    });\r\n    stack[firstLocal].enclosing = scope;\r\n\r\n    \/\/ return innermost()\r\n    var returnValue = (stack[firstLocal])();\r\n    stack.pop();  \/\/ pop 'innermost'\r\n    stack.pop();  \/\/ pop 'innerArg1'\r\n    return returnValue;\r\n  });\r\n  stack[firstLocal].enclosing = scope;\r\n\r\n  \/\/ initialize 'getArg3' {hops=0, index=2}:\r\n  scope[2] = function getArg3() {\r\n    \/\/ the scope of getArg3 is completely optimized away\r\n    var scope = getArg3.enclosing;\r\n\r\n    \/\/ return arg3 {hops=0, index=1}\r\n    return <font color=\"red\">scope[1]<\/font>;\r\n  }\r\n  scope[2].enclosing = scope;\r\n\r\n  \/\/ return addInner(arg1)\r\n  stack.push(stack[firstLocal - 3]);\r\n  var returnValue = (stack[firstLocal])();\r\n  stack.pop();  \/\/ pop 'addInner'\r\n  stack.pop();  \/\/ pop 'arg3'\r\n  stack.pop();  \/\/ pop 'arg2'\r\n  stack.pop();  \/\/ pop 'arg1'\r\n  return returnValue;\r\n}\r\n<\/pre>\n<p>This strategy is good for JIT compilation in several ways:<\/p>\n<ul>\n<li>If a variable is only accessed locally, it can still live on the stack and receive full JIT optimization.<\/li>\n<li>Each <code>.enclosing<\/code> expression compiles to a single load instruction.  Furthermore, when there are multiple accesses to variables in the same scope, the compiler can factor out the common scope walking.<\/li>\n<li>Since a non-local name access in this scheme is much simpler than the name cache mentioned earlier, IonMonkey is more able to apply the optimizations it uses for local names such as <a href=\"http:\/\/en.wikipedia.org\/wiki\/Loop-invariant_code_motion\">LICM<\/a>, <a href=\"http:\/\/en.wikipedia.org\/wiki\/Global_value_numbering\">GVN<\/a>, and <a href=\"http:\/\/en.wikipedia.org\/wiki\/Dead_code_elimination\">DCE<\/a>.<\/li>\n<\/ul>\n<p>In summary, we&#8217;ve now optimized non-local access while keeping local access fast.  There are several other optimizations related to scopes that soften the blow when <code>eval<\/code> or <code>arguments<\/code> is used, but I think this is a good stopping point.<\/p>\n<h2>Next steps<\/h2>\n<p>The recent scope project basically catches us up to the level of other JS VMs.  I should also note that functional languages have been doing similar optimizations <a href=\"ftp:\/\/publications.ai.mit.edu\/ai-publications\/pdf\/AITR-474.pdf\">forever<\/a>.  Looking forward, there are some <a href=\"https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=784839\">straightforward<\/a> optimizations I think we could do to avoid creating scope objects as well as more <a href=\"http:\/\/flint.cs.yale.edu\/flint\/publications\/escc.html\">advanced<\/a> optimizations we can lift from the functional crowd.<\/p>\n<h2>In SpiderMonkey<\/h2>\n<p>If you are interested in seeing the code for all this in SpiderMonkey, you can use the following links to get started:<\/p>\n<ul>\n<li>The <code>{hops,index}<\/code> coordinate is called <a href=\"http:\/\/hg.mozilla.org\/mozilla-central\/file\/df69d95f636c\/js\/src\/vm\/ScopeObject.h#l79\">ScopeCoordinate<\/a>.<\/li>\n<li>The various scope objects are described in this <a href=\"http:\/\/hg.mozilla.org\/mozilla-central\/file\/2359243ee2b1\/js\/src\/vm\/ScopeObject.h#l111\">ASCII-art tree<\/a>.  (Note, for mostly historical reasons, we use the same underlying representation for objects and scopes.  Due to the <a href=\"http:\/\/hg.mozilla.org\/mozilla-central\/file\/df69d95f636c\/js\/src\/jsscope.h#l32\"><code>Shape<\/code><\/a> mechanism (which is pre-generated for scopes at compile-time), scopes are still, effectively, arrays.)<\/li>\n<li>Optimized non-local access is performed with <a href=\"http:\/\/hg.mozilla.org\/mozilla-central\/file\/df69d95f636c\/js\/src\/jsopcode.tbl#l331\">ALIASEDVAR<\/a> opcodes.  See the implementation of these ops in the <a href=\"http:\/\/hg.mozilla.org\/mozilla-central\/file\/df69d95f636c\/js\/src\/jsinterp.cpp#l2794\">interpreter<\/a> and <a href=\"http:\/\/hg.mozilla.org\/mozilla-central\/file\/df69d95f636c\/js\/src\/ion\/IonBuilder.cpp#l6272\">IonMonkey jit<\/a>.<\/li>\n<li>The frontend name analysis is a bit old and messy (and will hopefully be rewritten sometime in the near future).  However, the important part of the analysis is at the very end, when we emit the ALIASEDVAR ops in <a href=\"http:\/\/hg.mozilla.org\/mozilla-central\/file\/df69d95f636c\/js\/src\/frontend\/BytecodeEmitter.cpp#l908\"><code>EmitAliasedVarOp<\/code><\/a>.<\/li>\n<\/ul>\n<p><i>Nicolas Pierron has provided a <a href=\"http:\/\/tech.mozfr.org\/post\/2013\/02\/03\/Optimiser-les-acces-de-variables-JavaScript\">French translation<\/a> of this post.  Thanks!<\/i><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I recently finished a project to improve how SpiderMonkey implements variable access so I thought this would be a good time to explain how it all works now. Taking a note from mraleph&#8217;s post (and SICP), I&#8217;ll illustrate the implementation &hellip; <a href=\"https:\/\/blog.mozilla.org\/luke\/2012\/10\/02\/optimizing-javascript-variable-access\/\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":257,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[5],"tags":[],"_links":{"self":[{"href":"https:\/\/blog.mozilla.org\/luke\/wp-json\/wp\/v2\/posts\/424"}],"collection":[{"href":"https:\/\/blog.mozilla.org\/luke\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mozilla.org\/luke\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/luke\/wp-json\/wp\/v2\/users\/257"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/luke\/wp-json\/wp\/v2\/comments?post=424"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/luke\/wp-json\/wp\/v2\/posts\/424\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/luke\/wp-json\/wp\/v2\/media?parent=424"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/luke\/wp-json\/wp\/v2\/categories?post=424"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/luke\/wp-json\/wp\/v2\/tags?post=424"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}