/*
  * Returns the identifier for the specified symbol defined in the specified
  * scope or in any scope above it. Returns null if this symbol does not have
  * a corresponding identifier.
  */
 private JavaScriptIdentifier getIdentifier(String symbol, ScriptOrFnScope scope) {
   JavaScriptIdentifier identifier;
   while (scope != null) {
     identifier = scope.getIdentifier(symbol);
     if (identifier != null) {
       return identifier;
     }
     scope = scope.getParentScope(); // ??此代码不知何意
   }
   return null;
 }
 private void manMung() { // 手动混淆,即由用户输入混淆变量
   JTable table = AnalyseWarnPanel.table;
   Set<ScriptOrFnScope> scopes = scopeSymbolMaping.keySet();
   Iterator<ScriptOrFnScope> scopesSetIt = scopes.iterator();
   int i = 0;
   while (scopesSetIt.hasNext()) {
     ScriptOrFnScope tmpScope = scopesSetIt.next();
     List<String> symbolList = scopeSymbolMaping.get(tmpScope);
     Iterator<String> symbolListIt = symbolList.iterator();
     while (symbolListIt.hasNext()) {
       String symbol = symbolListIt.next();
       JavaScriptIdentifier identifier = tmpScope.getIdentifier(symbol);
       String mungedValue = table.getValueAt(i, 1).toString().trim();
       if (identifier.isMarkedForMunging() && !mungedValue.equals("null")) {
         identifier.setMungedValue(mungedValue);
       }
       i++;
     }
   }
 }
  private void parseScope(ScriptOrFnScope scope) {

    String symbol;
    JavaScriptToken token;
    JavaScriptIdentifier identifier;

    int length = tokens.size();

    enterScope(scope); // 进入范围(将scope压入栈scopes中)

    while (offset < length) { // 在此函数调用前已设置为0

      token = consumeToken(); // 消费tokens,offset自增

      switch (token.getType()) {
        case Token.VAR: // 注意后面没有break;
          if (mode == BUILDING_SYMBOL_TREE) {
            scope.incrementVarCount(); // 当前范围内的var数量加1
          }

          /* FALLSTHROUGH(falls through) 失败 */

        case Token.CONST:

          // The var keyword is followed by at least one symbol name.
          // If several symbols follow, they are comma(逗号) separated.
          for (; ; ) {
            token = consumeToken();

            assert token.getType() == Token.NAME;
            // 判断关键字var、const后的变量是否已经定义,未定义则定义,已定义则输出警告
            if (mode == BUILDING_SYMBOL_TREE) {
              symbol = token.getValue();
              if (scope.getIdentifier(symbol) == null) {
                scope.declareIdentifier(symbol, true); // 声明标识符
              } else {
                warn("变量" + symbol + "已经在相同的作用域中声明", true);
              }
            }

            token = getToken(0); // offset不自增,token.getValue()的值一般为'='或';'

            assert token.getType() == Token.SEMI
                || token.getType() == Token.ASSIGN
                || token.getType() == Token.COMMA
                || token.getType() == Token.IN;

            if (token.getType() == Token.IN) {
              break;
            } else {
              parseExpression(); // 解释表达式(例如解释var a = 'u';或var a;)
              token = getToken(-1); // 得到的是';'
              if (token.getType() == Token.SEMI) { // 变量声明完成
                break;
              }
            }
          }
          break;

        case Token.FUNCTION:
          parseFunctionDeclaration();
          break;

        case Token.LC:
          braceNesting++;
          break;

        case Token.RC:
          braceNesting--;
          assert braceNesting >= scope.getBraceNesting();
          if (braceNesting == scope.getBraceNesting()) { // 说明要离开当前作用域了
            leaveCurrentScope(); // 离开当前作用域
            return;
          }
          break;

        case Token.WITH:
          if (mode == BUILDING_SYMBOL_TREE) {
            // Inside a 'with' block, it is impossible to figure out
            // statically whether a symbol is a local variable or an
            // object member. As a consequence, the only thing we can
            // do is turn the obfuscation off for the highest scope
            // containing the 'with' block.
            protectScopeFromObfuscation(scope);
            warn("不推荐使用with关键字。" + (munge ? " 使用with会降低压缩水平" : ""), true);
          }
          break;

        case Token.CATCH:
          parseCatch();
          break;

        case Token.CONDCOMMENT:
          if (mode == BUILDING_SYMBOL_TREE) {
            protectScopeFromObfuscation(scope);
            warn("不推荐使用JScript条件注释。" + (munge ? "使用JScript条件解释会降低压缩水平。" : ""), true);
          }
          break;

        case Token.NAME:
          symbol = token.getValue();

          if (mode == BUILDING_SYMBOL_TREE) {

            if (symbol.equals("eval")) {

              protectScopeFromObfuscation(scope);
              warn("不推荐使用'eval'关键字。" + (munge ? "使用'eval'会降低压缩水平!" : ""), true);
            }

          } else if (mode == CHECKING_SYMBOL_TREE) {

            if ((offset < 2 || getToken(-2).getType() != Token.DOT)
                && getToken(0).getType() != Token.OBJECTLIT) {

              identifier = getIdentifier(symbol, scope);

              if (identifier == null) {

                if (symbol.length() <= 3 && !builtin.contains(symbol)) {
                  // Here, we found an undeclared and
                  // un-namespaced symbol that is
                  // 3 characters or less in length. Declare it in
                  // the global scope.
                  // We don't need to declare longer symbols since
                  // they won't cause
                  // any conflict with other munged symbols.
                  globalScope.declareIdentifier(symbol, false);
                  // warn("Found an undeclared symbol: " + symbol,
                  // true);
                }

              } else {

                identifier.incrementRefcount();
              }
            }
          }
          break;
      }
    }
  }
  private void parseFunctionDeclaration() {

    String symbol;
    JavaScriptToken token;
    ScriptOrFnScope currentScope, fnScope;
    JavaScriptIdentifier identifier;

    currentScope = getCurrentScope(); // 得到此函数声明所在的域

    token = consumeToken();

    // 如果是函数名(说明这种函数的声明方式是:function funName(){})
    // 否则是:var varName = function(){}
    if (token.getType() == Token.NAME) {
      if (mode == BUILDING_SYMBOL_TREE) {
        // Get the name of the function and declare it in the current
        // scope.
        symbol = token.getValue(); // 得到的是函数名
        if (currentScope.getIdentifier(symbol) != null) {
          warn("函数" + symbol + "已经在相同的作用域中声明。", true);
        }
        currentScope.declareIdentifier(symbol, false); // 在本作用域中声明标识符
      }
      token = consumeToken(); // 得到左圆括号
    }

    assert token.getType() == Token.LP;
    if (mode == BUILDING_SYMBOL_TREE) {
      fnScope = new ScriptOrFnScope(braceNesting, currentScope); // 新建一个作用域,并把当前域传递过去最为父作用域
      indexedScopes.put(new Integer(offset), fnScope); // 圆括号的下一个索引映射到函数作用域
    } else {
      fnScope = indexedScopes.get(new Integer(offset));
    }

    // Parse function arguments.解释函数参数
    int argpos = 0;
    while ((token = consumeToken()).getType() != Token.RP) {
      assert token.getType() == Token.NAME || token.getType() == Token.COMMA;
      if (token.getType() == Token.NAME && mode == BUILDING_SYMBOL_TREE) {
        symbol = token.getValue();
        identifier = fnScope.declareIdentifier(symbol, false);
        if (symbol.equals("$super") && argpos == 0) {
          // Exception for Prototype 1.6...
          identifier.preventMunging();
        }
        argpos++;
      }
    }

    token = consumeToken(); // 得到函数的左花括号
    assert token.getType() == Token.LC;
    braceNesting++;

    token = getToken(0);
    if (token.getType() == Token.STRING && getToken(1).getType() == Token.SEMI) {
      // This is a hint. Hints are empty statements that look like
      // "localvar1:nomunge, localvar2:nomunge"; They allow developers
      // to prevent specific symbols from getting obfuscated (some heretic
      // implementations, such as Prototype 1.6, require specific variable
      // names, such as $super for example, in order to work
      // appropriately.
      // Note: right now, only "nomunge" is supported in the right hand
      // side
      // of a hint. However, in the future, the right hand side(右边) may
      // contain
      // other values.
      consumeToken();
      String hints = token.getValue();
      // Remove the leading and trailing quotes...
      hints = hints.substring(1, hints.length() - 1).trim();
      StringTokenizer st1 = new StringTokenizer(hints, ","); // Tokenizer分词器,以','为分词界限
      while (st1.hasMoreTokens()) {
        String hint = st1.nextToken();
        int idx = hint.indexOf(':');
        if (idx <= 0 || idx >= hint.length() - 1) {
          if (mode == BUILDING_SYMBOL_TREE) {
            // No need to report the error twice, hence the test...
            warn("提示语句有语法错误:" + hint, true);
          }
          break;
        }
        String variableName = hint.substring(0, idx).trim();
        String variableType = hint.substring(idx + 1).trim();
        if (mode == BUILDING_SYMBOL_TREE) {
          fnScope.addHint(variableName, variableType);
        } else if (mode == CHECKING_SYMBOL_TREE) {
          identifier = fnScope.getIdentifier(variableName);
          if (identifier != null) {
            if (variableType.equals("nomunge")) {
              identifier.preventMunging();
            } else {
              warn("不支持的提示指令: " + hint, true);
            }
          } else {
            warn("提示指令指向未知的标识符: " + hint, true);
          }
        }
      }
    }

    parseScope(fnScope); // 解释函数域
  }