Skip to content

Synthesizing names

icefapper edited this page Feb 19, 2017 · 10 revisions
  • Synthesizing begins when the whole source is parsed, to avoid things like below:
var l; // emitName=self:l
{
  let l; // emitName=synth:l->l1
  (function() {
    var l1; // emitName=self:l1
    l; // emitName=synth:l->l1 // conflict
  
  })();
}
  • if a catch parameter is an identifier, it will be treated as a real, non-synthesized name.

  • a var name is treated as a real, non-synthesized name.

  • a complex catch parameter is replace by a synthesized name; that synthesized name will not be added to the surrounding concrete scope's list of synthesized names, but the names that are contained in the original param are synthesized and added to the surrounding concrete scope's list of synthesized names.

  • name synthesis begins from the topmost scope; name synthesis is outlined in the pseudo-code below:

  function synthName(n: name): name {
    var mustNotBe = [ownerScope.varNames, ownerScope.createMapForTheEmitNamesOfReferencedNames()];
    for each (var scope that has referenced n) {
      mustNotBe.push(scope.varNames);
    }
    var nameMap = createMapContainingExistingVarNames(mustNotBe);
    var nonce = 0, candidate = n;
    while (true) {
      if (n not in.own nameMap) { break }
      nonce++;
      candidate = n + "" + nonce;
    }
    
    return candidate;
  }

#Some terminology ##Handover When a scope ends, all the references that have not been resolved in it are handed over to the parent scope, where they are referenced again; for example:

{ // scope A:
  // A.unresolved: {}
  { // scope B:
    // B.unresolved: {}
    a; // B.ref('a')-> B.unresolved: {'a'}
    // B.unresolved: {'a'}
  } // handover B -> A = A.ref('a') -> A.unresolved: {'a'}
  // A.unresolved: {'a'}
  // etc.
}

or

{ // scope A
  // A.unresolved: {}
  var a = 12;
  { // scope B
    // B.unresolved: {}
    a; // B.ref('a')-> B.unresolved: {'a'}
    // B.unresolved: {'a'}
  } // handover B -> A = A.ref('a') -> <resolved>
  // A.unresolved: {}
  // etc.
}

##Real Scope A scope whose own names need not be synthesized; function/modules/script scopes are the only such scopes.

##List of referencing real scopes: It is the list of all "Real scope" (cf. above) a binding has directly or indirectly been referenced in; please note that the elements of this list are necessarily the descendants of the scope the binding has been defined in; for example:

// scope A
var a;
{ // scope B; not real
  a; // ref.a.lorrs = []
  (function() { // scope E; it's real
    var a1;
    { // scope L
    a; // ref.a.lorrs = []
    } // handover will cause 'a' get referenced in E: E.ref('a') = E.ref.a.lorrs += B.ref.a.lorrs
  }) // E -> B: B.ref('a') = B.ref.a.lorrs += [E, E.ref.a.lorrs]
} // B -> A: A.ref('a') = A.ref.a.lorrs += B.ref.a.lorrs
// A.ref.a.lorrs ==> [E]

#Further comments on how the name synthesizer works the synthName thing, listed above, gives all the details on how name synthesis works; but in case it does not look intuitive to you, then this is how it actually works -- given a name <n>, it keeps renaming it to <n>1, <n>2, etc., until the following criteria are all met:

  • the synth name does not exist in the surrounding real scope's varNames
    • because it is where is is going to be added
  • the synth name does not exist in the list of the emit-names of the surrounding real scope's referenced names
    • because when it is added to the varNames, it must not cause the surrounding real scope's unresolved references to resolve
  • the synth name does not exist in the list of the emit-names of the current scope's referenced names
    • this is not necessary -- the referenced names in the current scope are either local non-synthesized names, in which case they are in the surrounding concerete scope's varNames, or they are local synthesized names, in which case they are also in the list of the surrounding concrete scope's varNames, or they are non-local names (i.e., they are not in the surrounding concrete scope's varNames), in which case they are in list of emit-names of the surrounding concrete scope's referenced names
  • the synth name does not exist in the varNames of any of the current binding's lorrs list
    • so that the synth name will not collide with the varNames in those scopes; please note that name synthesis has not yet been run on any of the scopes in that lorrs (because they all descend from the current scope, on which name synthesis is still running); consequently, in any of those scopes, the varNames still only contains the scope's VarDeclaredNames, which are all real non-synthesized names.

#Synthesizing complex catch params a complex catch param is a bit tricky to handle -- "plain" catch clauses may only have a single identifier param; the solution is to create one such synthetic id param, set it as the new param, and add an assignment to the catch clause like so:

// before
try {} catch ([a,b]) {}

// after
try {} catch (synthID) { [a, b] = synthID; }

The problem in this case is how to synthesize one such id; it is somewhat different from synthesizing a name that is not a catch-param, mostly because catch params do not belong to the surrounding concrete scope's list of variable names.

However, they follow more or less the same lines; a catch clause with a complex parameter is transformed this way:

  • first, all the bindings contained in the parameter are synthesized as if they are plain lexical variables belonging to the catch body; example:
// before
try {} catch ([a, b]) {}

// after
try {} catch (/* later */) {
let a, b;
[a, b] = /* later */;
}
  • then, name synthesis is run on the catch-scope (please note that the catch parameter is not synthesized.) after that, the parameter is synthesized like an ordinary name, except that the criteria that must be met are these:
    • the synth name must not be in the catch-scope's varNames
    • the synth name must not be in the list of emit names of the catch-scope's referenced names please note that the synthesized catch parameter is not added to the surrounding concrete scope's varNames list.

NOTE: if the catch is part of a try/catch/finally whose yield-count is not 0, or else, the catch param is not Identifier, it has to get synthesized to be usable.