arai-a / smoosh-sync

Automation to make jsparagus and SpiderMonkey bytecode in sync
2 stars 0 forks source link

/js/src/frontend/BytecodeEmitter.cpp and 5 more files have been updated (443b10f2) #123

Closed github-actions[bot] closed 3 years ago

github-actions[bot] commented 3 years ago

Files

Changesets

Diffs

/js/src/frontend/BytecodeEmitter.cpp

--- 136f05c1bbc46466c3334240f0a2f6ac2c657c25/js/src/frontend/BytecodeEmitter.cpp
+++ 443b10f2927593c3c882e0c4c5ab44e13fbb7eb8/js/src/frontend/BytecodeEmitter.cpp
@@ -2379,16 +2379,86 @@ bool BytecodeEmitter::defineHoistedTopLe

   if (!body->as<ListNode>().hasTopLevelFunctionDeclarations()) {
     return true;
   }

   return emitHoistedFunctionsInList(&body->as<ListNode>());
 }

+// For Global and sloppy-Eval scripts, this performs most of the steps of the
+// spec's [GlobalDeclarationInstantiation] and [EvalDeclarationInstantiation]
+// operations.
+//
+// Note that while strict-Eval is handled in the same part of the spec, it never
+// fails for global-redeclaration checks so those scripts initialize directly in
+// their bytecode.
+bool BytecodeEmitter::emitDeclarationInstantiation(ParseNode* body) {
+  if (sc->isModuleContext()) {
+    // ES Modules have dedicated variable and lexial environments and therefore
+    // do not have to perform redeclaration checks. We initialize their bindings
+    // elsewhere in bytecode.
+    return true;
+  }
+
+  if (sc->isEvalContext() && sc->strict()) {
+    // Strict Eval has a dedicated variables (and lexical) environment and
+    // therefore does not have to perform redeclaration checks. We initialize
+    // their bindings elsewhere in the bytecode.
+    return true;
+  }
+
+  // If we have no variables bindings, then we are done!
+  if (sc->isGlobalContext()) {
+    if (!sc->asGlobalContext()->bindings) {
+      return true;
+    }
+  } else {
+    MOZ_ASSERT(sc->isEvalContext());
+
+    if (!sc->asEvalContext()->bindings) {
+      return true;
+    }
+  }
+
+#if DEBUG
+  // There should be no emitted functions yet.
+  for (const auto& thing : perScriptData().gcThingList().objects()) {
+    MOZ_ASSERT(thing.isEmptyGlobalScope() || thing.isScope());
+  }
+#endif
+
+  // Emit the hoisted functions to gc-things list. There is no bytecode
+  // generated yet to bind them.
+  if (!defineHoistedTopLevelFunctions(body)) {
+    return false;
+  }
+
+  // Save the last GCThingIndex emitted. The hoisted functions are contained in
+  // the gc-things list up until this point. This set of gc-things also contain
+  // initial scopes (of which there must be at least one).
+  MOZ_ASSERT(perScriptData().gcThingList().length() > 0);
+  GCThingIndex lastFun =
+      GCThingIndex(perScriptData().gcThingList().length() - 1);
+
+#if DEBUG
+  for (const auto& thing : perScriptData().gcThingList().objects()) {
+    MOZ_ASSERT(thing.isEmptyGlobalScope() || thing.isScope() ||
+               thing.isFunction());
+  }
+#endif
+
+  // Check for declaration conflicts and initialize the bindings.
+  if (!emitGCIndexOp(JSOp::GlobalOrEvalDeclInstantiation, lastFun)) {
+    return false;
+  }
+
+  return true;
+}
+
 bool BytecodeEmitter::emitScript(ParseNode* body) {
   AutoFrontendTraceLog traceLog(cx, TraceLogger_BytecodeEmission,
                                 parser->errorReporter(), body);

   setScriptStartOffsetIfUnset(body->pn_pos.begin);

   MOZ_ASSERT(inPrologue());

@@ -2409,29 +2479,33 @@ bool BytecodeEmitter::emitScript(ParseNo
     }
   }

   setFunctionBodyEndPos(body->pn_pos.end);

   bool isSloppyEval = sc->isEvalContext() && !sc->strict();
   if (isSloppyEval && body->is<LexicalScopeNode>() &&
       !body->as<LexicalScopeNode>().isEmptyScope()) {
-    // Sloppy eval scripts may need to emit DEFFUNs in the prologue. If there is
-    // an immediately enclosed lexical scope, we need to enter the lexical
-    // scope in the prologue for the DEFFUNs to pick up the right
-    // environment chain.
+    // Sloppy eval scripts may emit hoisted functions bindings with a
+    // `JSOp::GlobalOrEvalDeclInstantiation` opcode below. If this eval needs a
+    // top-level lexical environment, we must ensure that environment is created
+    // before those functions are created and bound.
+    //
+    // This differs from the global-script case below because the global-lexical
+    // environment exists outside the script itself. In the case of strict eval
+    // scripts, the `emitterScope` above is already sufficient.
     EmitterScope lexicalEmitterScope(this);
     LexicalScopeNode* scope = &body->as<LexicalScopeNode>();

     if (!lexicalEmitterScope.enterLexical(this, ScopeKind::Lexical,
                                           scope->scopeBindings())) {
       return false;
     }

-    if (!defineHoistedTopLevelFunctions(scope->scopeBody())) {
+    if (!emitDeclarationInstantiation(scope->scopeBody())) {
       return false;
     }

     if (!switchToMain()) {
       return false;
     }

     ParseNode* scopeBody = scope->scopeBody();
@@ -2442,20 +2516,18 @@ bool BytecodeEmitter::emitScript(ParseNo
     if (!updateSourceCoordNotes(scopeBody->pn_pos.end)) {
       return false;
     }

     if (!lexicalEmitterScope.leave(this)) {
       return false;
     }
   } else {
-    if (sc->isGlobalContext() || isSloppyEval) {
-      if (!defineHoistedTopLevelFunctions(body)) {
-        return false;
-      }
+    if (!emitDeclarationInstantiation(body)) {
+      return false;
     }

     if (!switchToMain()) {
       return false;
     }

     if (!emitTree(body)) {
       return false;

/js/src/frontend/BytecodeEmitter.h

--- 5f378a0b0be6b0e904ba5790377358fcfd758207/js/src/frontend/BytecodeEmitter.h
+++ a25a8624af260e4e193be8f67206cd132cb2b1dd/js/src/frontend/BytecodeEmitter.h
@@ -350,16 +350,18 @@ struct MOZ_STACK_CLASS BytecodeEmitter {
                              ValueUsage valueUsage = ValueUsage::WantValue,
                              EmitLineNumberNote emitLineNote = EMIT_LINENOTE,
                              bool isInner = false);

   MOZ_MUST_USE bool emitOptionalTree(
       ParseNode* pn, OptionalEmitter& oe,
       ValueUsage valueUsage = ValueUsage::WantValue);

+  MOZ_MUST_USE bool emitDeclarationInstantiation(ParseNode* body);
+
   // Emit global, eval, or module code for tree rooted at body. Always
   // encompasses the entire source.
   MOZ_MUST_USE bool emitScript(ParseNode* body);

   // Calculate the `nslots` value for BCEScriptStencil constructor parameter.
   // Fails if it overflows.
   MOZ_MUST_USE bool getNslots(uint32_t* nslots);

/js/src/frontend/EmitterScope.cpp

--- 394718eef5059ae3cffdb30a8b91b1c2032cd188/js/src/frontend/EmitterScope.cpp
+++ a25a8624af260e4e193be8f67206cd132cb2b1dd/js/src/frontend/EmitterScope.cpp
@@ -770,40 +770,16 @@ bool EmitterScope::enterFunctionExtraBod
   // The extra var scope needs a note to be mapped from a pc.
   if (!appendScopeNote(bce)) {
     return false;
   }

   return checkEnvironmentChainLength(bce);
 }

-class DynamicBindingIter : public ParserBindingIter {
- public:
-  explicit DynamicBindingIter(GlobalSharedContext* sc)
-      : ParserBindingIter(*sc->bindings) {}
-
-  explicit DynamicBindingIter(EvalSharedContext* sc)
-      : ParserBindingIter(*sc->bindings, /* strict = */ false) {
-    MOZ_ASSERT(!sc->strict());
-  }
-
-  JSOp bindingOp() const {
-    switch (kind()) {
-      case BindingKind::Var:
-        return JSOp::DefVar;
-      case BindingKind::Let:
-        return JSOp::DefLet;
-      case BindingKind::Const:
-        return JSOp::DefConst;
-      default:
-        MOZ_CRASH("Bad BindingKind");
-    }
-  }
-};
-
 bool EmitterScope::enterGlobal(BytecodeEmitter* bce,
                                GlobalSharedContext* globalsc) {
   MOZ_ASSERT(this == bce->innermostEmitterScopeNoCheck());

   // TODO-Stencil
   //   This is another snapshot-sensitive location.
   //   The incoming atoms from the global scope object should be snapshotted.
   //   For now, converting them to ParserAtoms here individually.
@@ -838,39 +814,27 @@ bool EmitterScope::enterGlobal(BytecodeE
   if (!internBodyScopeCreationData(bce, createScope)) {
     return false;
   }

   // See: JSScript::outermostScope.
   MOZ_ASSERT(bce->bodyScopeIndex == GCThingIndex::outermostScopeIndex(),
              "Global scope must be index 0");

-  // Resolve binding names and emit Def{Var,Let,Const} prologue ops.
+  // Resolve binding names.
+  //
+  // NOTE: BytecodeEmitter::emitDeclarationInstantiation will emit the
+  //       redeclaration check and initialize these bindings.
   if (globalsc->bindings) {
-    // Check for declaration conflicts before the Def* ops.
-    if (!bce->emit1(JSOp::CheckGlobalOrEvalDecl)) {
-      return false;
-    }
-
-    for (DynamicBindingIter bi(globalsc); bi; bi++) {
+    for (ParserBindingIter bi(*globalsc->bindings); bi; bi++) {
       NameLocation loc = NameLocation::fromBinding(bi.kind(), bi.location());
       const ParserAtom* name = bi.name();
       if (!putNameInCache(bce, name, loc)) {
         return false;
       }
-
-      // Define the name in the prologue. Do not emit DefVar for
-      // functions that we'll emit DefFun for.
-      if (bi.isTopLevelFunction()) {
-        continue;
-      }
-
-      if (!bce->emitAtomOp(bi.bindingOp(), name)) {
-        return false;
-      }
     }
   }

   // Note that to save space, we don't add free names to the cache for
   // global scopes. They are assumed to be global vars in the syntactic
   // global scope, dynamic accesses under non-syntactic global scope.
   if (globalsc->scopeKind() == ScopeKind::Global) {
     fallbackFreeNameLocation_ = Some(NameLocation::Global(BindingKind::Var));
@@ -908,41 +872,19 @@ bool EmitterScope::enterEval(BytecodeEmi
     return false;
   }

   if (hasEnvironment()) {
     if (!bce->emitInternedScopeOp(index(), JSOp::PushVarEnv)) {
       return false;
     }
   } else {
-    // Resolve binding names and emit DefVar prologue ops if we don't have
-    // an environment (i.e., a sloppy eval).
-    // Eval scripts always have their own lexical scope, but non-strict
-    // scopes may introduce 'var' bindings to the nearest var scope.
-    //
-    // TODO: We may optimize strict eval bindings in the future to be on
-    // the frame. For now, handle everything dynamically.
-    if (!hasEnvironment() && evalsc->bindings) {
-      // Check for declaration conflicts before the DefVar ops.
-      if (!bce->emit1(JSOp::CheckGlobalOrEvalDecl)) {
-        return false;
-      }
-
-      for (DynamicBindingIter bi(evalsc); bi; bi++) {
-        MOZ_ASSERT(bi.bindingOp() == JSOp::DefVar);
-
-        if (bi.isTopLevelFunction()) {
-          continue;
-        }
-
-        if (!bce->emitAtomOp(JSOp::DefVar, bi.name())) {
-          return false;
-        }
-      }
-    }
+    // NOTE: BytecodeEmitter::emitDeclarationInstantiation will emit the
+    //       redeclaration check and initialize these bindings for sloppy
+    //       eval.

     // As an optimization, if the eval does not have its own var
     // environment and is directly enclosed in a global scope, then all
     // free name lookups are global.
     if (scope(bce).enclosing().is<GlobalScope>()) {
       fallbackFreeNameLocation_ = Some(NameLocation::Global(BindingKind::Var));
     }
   }

/js/src/frontend/FunctionEmitter.cpp

--- f15e01260d1c0c06467bc2a81195e538d371890f/js/src/frontend/FunctionEmitter.cpp
+++ 443b10f2927593c3c882e0c4c5ab44e13fbb7eb8/js/src/frontend/FunctionEmitter.cpp
@@ -2,16 +2,17 @@
  * vim: set ts=8 sts=2 et sw=2 tw=80:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

 #include "frontend/FunctionEmitter.h"

 #include "mozilla/Assertions.h"  // MOZ_ASSERT
+#include "mozilla/Unused.h"

 #include "builtin/ModuleObject.h"          // ModuleObject
 #include "frontend/BytecodeEmitter.h"      // BytecodeEmitter
 #include "frontend/FunctionSyntaxKind.h"   // FunctionSyntaxKind
 #include "frontend/ModuleSharedContext.h"  // ModuleSharedContext
 #include "frontend/NameAnalysisTypes.h"    // NameLocation
 #include "frontend/NameOpEmitter.h"        // NameOpEmitter
 #include "frontend/ParseContext.h"         // BindingIter
@@ -302,24 +303,21 @@ bool FunctionEmitter::emitTopLevelFuncti
     return bce_->sc->asModuleContext()->builder.noteFunctionDeclaration(
         bce_->cx, index);
   }

   MOZ_ASSERT(bce_->sc->isGlobalContext() || bce_->sc->isEvalContext());
   MOZ_ASSERT(syntaxKind_ == FunctionSyntaxKind::Statement);
   MOZ_ASSERT(bce_->inPrologue());

-  if (!bce_->emitGCIndexOp(JSOp::Lambda, index)) {
-    //              [stack] FUN
-    return false;
-  }
-  if (!bce_->emit1(JSOp::DefFun)) {
-    //              [stack]
-    return false;
-  }
+  // NOTE: The `index` is not directly stored as an opcode, but we collect the
+  // range of indices in `BytecodeEmitter::emitDeclarationInstantiation` instead
+  // of discrete indices.
+  mozilla::Unused << index;
+
   return true;
 }

 bool FunctionEmitter::emitNewTargetForArrow() {
   //                [stack]

   if (bce_->sc->allowNewTarget()) {
     if (!bce_->emit1(JSOp::NewTarget)) {

/js/src/vm/BytecodeUtil.cpp

--- 394718eef5059ae3cffdb30a8b91b1c2032cd188/js/src/vm/BytecodeUtil.cpp
+++ fd55f1950eb4736852cc0ae20f8cdb23a28e2ede/js/src/vm/BytecodeUtil.cpp
@@ -1559,16 +1559,22 @@ static unsigned Disassemble1(JSContext*
       break;

     case JOF_LOCAL:
       if (!sp->jsprintf(" %u", GET_LOCALNO(pc))) {
         return 0;
       }
       break;

+    case JOF_GCTHING:
+      if (!sp->jsprintf(" %u", unsigned(GET_GCTHING_INDEX(pc)))) {
+        return 0;
+      }
+      break;
+
     case JOF_UINT32:
       if (!sp->jsprintf(" %u", GET_UINT32(pc))) {
         return 0;
       }
       break;

     case JOF_ICINDEX:
       if (!sp->jsprintf(" (ic: %u)", GET_ICINDEX(pc))) {

/js/src/vm/Opcodes.h

--- 3a3723ac01526070c0a5f4bfac33e9d7329b3e18/js/src/vm/Opcodes.h
+++ 3fc26b5eba8401b97142b679b2ea1d7480f76c3e/js/src/vm/Opcodes.h
@@ -2743,18 +2743,18 @@
      *   Type: Initialization
      *   Operands: uint24_t localno
      *   Stack: v => v
      */ \
     MACRO(InitLexical, init_lexical, NULL, 4, 1, 1, JOF_LOCAL|JOF_NAME) \
     /*
      * Initialize a global lexical binding.
      *
-     * The binding must already have been created by `DefLet` or `DefConst` and
-     * must be uninitialized.
+     * The binding must already have been created by
+     * `GlobalOrEvalDeclInstantiation` and must be uninitialized.
      *
      * Like `JSOp::InitLexical` but for global lexicals. Unlike `InitLexical`
      * this can't be used to mark a binding as uninitialized.
      *
      *   Category: Variables and scopes
      *   Type: Initialization
      *   Operands: uint32_t nameIndex
      *   Stack: val => val
@@ -3322,97 +3322,37 @@
      *
      *   Category: Variables and scopes
      *   Type: Creating and deleting bindings
      *   Operands:
      *   Stack: => env
      */ \
     MACRO(BindVar, bind_var, NULL, 1, 0, 1, JOF_BYTE) \
     /*
-     * Create a new binding on the current VariableEnvironment (the environment
-     * on the environment chain designated to receive new variables).
-     *
-     * `JSOp::Def{Var,Let,Const,Fun}` instructions must appear in the script
-     * before anything else that might add bindings to the environment, and
-     * only once per binding. There must be a correct entry for the new binding
-     * in `script->bodyScope()`. (All this ensures that at run time, there is
-     * no existing conflicting binding. This is checked by the
-     * `JSOp::CheckGlobalOrEvalDecl` bytecode instruction that must appear
-     * before `JSOp::Def{Var,Let,Const,Fun}`.)
-     *
-     * Throw a SyntaxError if the current VariableEnvironment is the global
-     * environment and a binding with the same name exists on the global
-     * lexical environment.
-     *
-     * This is used for global scripts and also in some cases for function
-     * scripts where use of dynamic scoping inhibits optimization.
-     *
-     *   Category: Variables and scopes
-     *   Type: Creating and deleting bindings
-     *   Operands: uint32_t nameIndex
-     *   Stack: =>
-     */ \
-    MACRO(DefVar, def_var, NULL, 5, 0, 0, JOF_ATOM) \
-    /*
-     * Create a new binding for the given function on the current scope.
-     *
-     * `fun` must be a function object with an explicit name. The new
-     * variable's name is `fun->explicitName()`, and its value is `fun`. In
-     * global scope, this creates a new property on the global object.
-     *
-     * Implements: The body of the loop in [GlobalDeclarationInstantiation][1]
-     * step 17 ("For each Parse Node *f* in *functionsToInitialize*...") and
-     * the corresponding loop in [EvalDeclarationInstantiation][2].
-     *
-     * [1]: https://tc39.es/ecma262/#sec-globaldeclarationinstantiation
-     * [2]: https://tc39.es/ecma262/#sec-evaldeclarationinstantiation
-     *
-     *   Category: Variables and scopes
-     *   Type: Creating and deleting bindings
-     *   Operands:
-     *   Stack: fun =>
-     */ \
-    MACRO(DefFun, def_fun, NULL, 1, 1, 0, JOF_BYTE) \
-    /*
-     * Create a new uninitialized mutable binding in the global lexical
-     * environment. Throw a SyntaxError if a binding with the same name already
-     * exists on that environment, or if a var binding with the same name
-     * exists on the global.
-     *
-     *   Category: Variables and scopes
-     *   Type: Creating and deleting bindings
-     *   Operands: uint32_t nameIndex
-     *   Stack: =>
-     */ \
-    MACRO(DefLet, def_let, NULL, 5, 0, 0, JOF_ATOM) \
-    /*
-     * Like `DefLet`, but create an uninitialized constant binding.
-     *
-     *   Category: Variables and scopes
-     *   Type: Creating and deleting bindings
-     *   Operands: uint32_t nameIndex
-     *   Stack: =>
-     */ \
-    MACRO(DefConst, def_const, NULL, 5, 0, 0, JOF_ATOM) \
-    /*
-     * Check for conflicting bindings before `JSOp::Def{Var,Let,Const,Fun}` in
-     * global or sloppy eval scripts.
-     *
-     * Implements: [GlobalDeclarationInstantiation][1] steps 5, 6, 10 and 12,
-     * and [EvalDeclarationInstantiation][2] steps 5 and 8.
+     * Check for conflicting bindings and then initialize them in global or
+     * sloppy eval scripts. This is required for global scripts with any
+     * top-level bindings, or any sloppy-eval scripts with any non-lexical
+     * top-level bindings.
+     *
+     * Implements: [GlobalDeclarationInstantiation][1] and
+     *             [EvalDeclarationInstantiation][2] (except step 12).
+     *
+     * The `lastFun` argument is a GCThingIndex of the last hoisted top-level
+     * function that is part of top-level script initialization. The gcthings
+     * from index `0` thru `lastFun` contain only scopes and hoisted functions.
      *
      * [1]: https://tc39.es/ecma262/#sec-globaldeclarationinstantiation
      * [2]: https://tc39.es/ecma262/#sec-evaldeclarationinstantiation
      *
      *   Category: Variables and scopes
      *   Type: Creating and deleting bindings
-     *   Operands:
+     *   Operands: uint32_t lastFun
      *   Stack: =>
      */ \
-    MACRO(CheckGlobalOrEvalDecl, check_global_or_eval_decl, NULL, 1, 0, 0, JOF_BYTE) \
+    MACRO(GlobalOrEvalDeclInstantiation, global_or_eval_decl_instantiation, NULL, 5, 0, 0, JOF_GCTHING) \
     /*
      * Look up a variable on the environment chain and delete it. Push `true`
      * on success (if a binding was deleted, or if no such binding existed in
      * the first place), `false` otherwise (most kinds of bindings can't be
      * deleted).
      *
      * Implements: [`delete` *Identifier*][1], which [is a SyntaxError][2] in
      * strict mode code.
@@ -3674,16 +3614,20 @@

 // clang-format on

 /*
  * In certain circumstances it may be useful to "pad out" the opcode space to
  * a power of two.  Use this macro to do so.
  */
 #define FOR_EACH_TRAILING_UNUSED_OPCODE(MACRO) \
+  MACRO(235)                                   \
+  MACRO(236)                                   \
+  MACRO(237)                                   \
+  MACRO(238)                                   \
   MACRO(239)                                   \
   MACRO(240)                                   \
   MACRO(241)                                   \
   MACRO(242)                                   \
   MACRO(243)                                   \
   MACRO(244)                                   \
   MACRO(245)                                   \
   MACRO(246)                                   \
arai-a commented 3 years ago

handled by mozilla-spidermonkey/jsparagus#636 and https://bugzilla.mozilla.org/show_bug.cgi?id=1678170