docs: Scope enterScope() {
      final Map<VarKey, Variable> currentFrame = new LinkedHashMap<>();
      final Label scopeExit = new Label();
      frames.push(currentFrame);
      return new Scope() {
        boolean exited;
  
        @Override
        Variable createSynthetic(
            SyntheticVarName varName, Expression initExpr, SaveStrategy strategy) {
          checkState(!exited, "Scope already exited");
          VarKey key = VarKey.create(Kind.SYNTHETIC, varName.name());
          // synthetics are prefixed by $ by convention
          String name = fieldNames.generateName("$" + varName.name());
          return doCreate(name, new Label(), scopeExit, initExpr, key, strategy);
        }
  
        @Override
        Variable createTemporary(String name, Expression initExpr) {
          checkState(!exited, "Scope already exited");
          VarKey key = VarKey.create(Kind.TEMPORARY, name);
          name = fieldNames.generateName("$$" + name);
          return doCreate(name, new Label(), scopeExit, initExpr, key, SaveStrategy.NEVER);
        }
  
        @Override
        Variable create(String name, Expression initExpr, SaveStrategy strategy) {
          checkState(!exited, "Scope already exited");
          VarKey key = VarKey.create(Kind.USER_DEFINED, name);
          name = fieldNames.generateName(name);
          return doCreate(name, new Label(), scopeExit, initExpr, key, strategy);
        }
  
        @Override
        Statement exitScope() {
          checkState(!exited, "Scope already exited");
          exited = true;
          frames.pop();
          // Use identity semantics to make sure we visit each label at most once.  visiting a label
          // more than once tends to corrupt internal asm state.
          final Set<Label> endLabels = Sets.newIdentityHashSet();
          for (Variable var : currentFrame.values()) {
            endLabels.add(var.local.end());
            availableSlots.clear(
                var.local.index(), var.local.index() + var.local.resultType().getSize());
          }
          return new Statement() {
            // TODO(lukes): we could generate null writes for when object typed fields go out of
            // scope.  This would potentially allow intermediate results to be collected sooner.
            @Override
            protected void doGen(CodeBuilder adapter) {
              for (Label label : endLabels) {
                adapter.visitLabel(label);
              }
            }
          };
        }
  
        private Variable doCreate(
            String name,
            Label start,
            Label end,
            Expression initExpr,
            VarKey key,
            SaveStrategy strategy) {
          int index = reserveSlotFor(initExpr.resultType());
          LocalVariable local =
              LocalVariable.createLocal(name, index, initExpr.resultType(), start, end);
          Variable var;
          switch (strategy) {
            case DERIVED:
              var = new DerivedVariable(initExpr, local);
              break;
            case NEVER:
              var = new TemporaryVariable(initExpr, local);
              break;
            case STORE:
              var = new FieldSavedVariable(initExpr, local);
              break;
            default:
              throw new AssertionError();
          }
          currentFrame.put(key, var);
          allVariables.add(var);
          return var;
        }
      };
    }