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 parseExpression() { // Parse the expression until we encounter a comma or a semi-colon // in the same brace nesting, bracket nesting(方括号嵌套) and paren nesting. // Parse functions if any... String symbol; JavaScriptToken token; ScriptOrFnScope currentScope; JavaScriptIdentifier identifier; int expressionBraceNesting = braceNesting; // 花括号嵌套 int bracketNesting = 0; // 方括号嵌套 int parensNesting = 0; // int length = tokens.size(); while (offset < length) { token = consumeToken(); currentScope = getCurrentScope(); // return scopes.peek(); switch (token.getType()) { case Token.SEMI: case Token.COMMA: if (braceNesting == expressionBraceNesting && bracketNesting == 0 && parensNesting == 0) { return; } break; case Token.FUNCTION: parseFunctionDeclaration(); break; case Token.LC: braceNesting++; break; case Token.RC: braceNesting--; assert braceNesting >= expressionBraceNesting; break; case Token.LB: bracketNesting++; break; case Token.RB: bracketNesting--; break; case Token.LP: parensNesting++; break; case Token.RP: parensNesting--; break; case Token.CONDCOMMENT: if (mode == BUILDING_SYMBOL_TREE) { protectScopeFromObfuscation(currentScope); warn("不推荐使用JScript条件注释。" + (munge ? "使用JScript条件注释会降低压缩水平!" : ""), true); } break; case Token.NAME: symbol = token.getValue(); if (mode == BUILDING_SYMBOL_TREE) { if (symbol.equals("eval")) { protectScopeFromObfuscation(currentScope); warn("不推荐使用'eval'关键字。" + (munge ? "使用'eval'会降低压缩水平!" : ""), true); } } else if (mode == CHECKING_SYMBOL_TREE) { if ((offset < 2 || (getToken(-2).getType() != Token.DOT && getToken(-2).getType() != Token.GET && getToken(-2).getType() != Token.SET)) && getToken(0).getType() != Token.OBJECTLIT) { identifier = getIdentifier(symbol, currentScope); 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); // I removed the warning since was only being // done when // for identifiers 3 chars or less, and was just // causing // noise for people who happen to rely on an // externally // declared variable that happen to be that // short. We either // should always warn or never warn -- the fact // that we // declare the short symbols in the global space // doesn't // change anything. // warn("Found an undeclared symbol: " + symbol, // true); } } else { identifier.incrementRefcount(); } } } break; } } }
private static boolean isValidIdentifier(String s) { Matcher m = SIMPLE_IDENTIFIER_NAME_PATTERN.matcher(s); return (m.matches() && !reserved.contains(s)); }
static { // This list contains all the 3 characters or less built-in global // symbols available in a browser. Please add to this list if you // see anything missing. builtin.add("NaN"); builtin.add("top"); ones = new ArrayList<String>(); for (char c = 'a'; c <= 'z'; c++) ones.add(Character.toString(c)); for (char c = 'A'; c <= 'Z'; c++) ones.add(Character.toString(c)); twos = new ArrayList<String>(); for (int i = 0; i < ones.size(); i++) { String one = ones.get(i); for (char c = 'a'; c <= 'z'; c++) twos.add(one + Character.toString(c)); for (char c = 'A'; c <= 'Z'; c++) twos.add(one + Character.toString(c)); for (char c = '0'; c <= '9'; c++) twos.add(one + Character.toString(c)); } // Remove two-letter JavaScript reserved words and built-in globals... twos.remove("as"); twos.remove("is"); twos.remove("do"); twos.remove("if"); twos.remove("in"); twos.removeAll(builtin); threes = new ArrayList<String>(); for (int i = 0; i < twos.size(); i++) { String two = twos.get(i); for (char c = 'a'; c <= 'z'; c++) threes.add(two + Character.toString(c)); for (char c = 'A'; c <= 'Z'; c++) threes.add(two + Character.toString(c)); for (char c = '0'; c <= '9'; c++) threes.add(two + Character.toString(c)); } // Remove three-letter JavaScript reserved words and built-in globals... threes.remove("for"); threes.remove("int"); threes.remove("new"); threes.remove("try"); threes.remove("use"); threes.remove("var"); threes.removeAll(builtin); // That's up to ((26+26)*(1+(26+26+10)))*(1+(26+26+10))-8 // (206,380 symbols per scope) // The following list comes from // org/mozilla/javascript/Decompiler.java... // 注意:此处并为认为地设定键与值的acsii码相等,比如Token.ASSIGN是89,而'='为61 literals.put(new Integer(Token.GET), "get "); literals.put(new Integer(Token.SET), "set "); literals.put(new Integer(Token.TRUE), "true"); literals.put(new Integer(Token.FALSE), "false"); literals.put(new Integer(Token.NULL), "null"); literals.put(new Integer(Token.THIS), "this"); literals.put(new Integer(Token.FUNCTION), "function"); literals.put(new Integer(Token.COMMA), ","); literals.put(new Integer(Token.LC), "{"); literals.put(new Integer(Token.RC), "}"); literals.put(new Integer(Token.LP), "("); literals.put(new Integer(Token.RP), ")"); literals.put(new Integer(Token.LB), "["); literals.put(new Integer(Token.RB), "]"); literals.put(new Integer(Token.DOT), "."); literals.put(new Integer(Token.NEW), "new "); literals.put(new Integer(Token.DELPROP), "delete "); literals.put(new Integer(Token.IF), "if"); literals.put(new Integer(Token.ELSE), "else"); literals.put(new Integer(Token.FOR), "for"); literals.put(new Integer(Token.IN), " in "); literals.put(new Integer(Token.WITH), "with"); literals.put(new Integer(Token.WHILE), "while"); literals.put(new Integer(Token.DO), "do"); literals.put(new Integer(Token.TRY), "try"); literals.put(new Integer(Token.CATCH), "catch"); literals.put(new Integer(Token.FINALLY), "finally"); literals.put(new Integer(Token.THROW), "throw"); literals.put(new Integer(Token.SWITCH), "switch"); literals.put(new Integer(Token.BREAK), "break"); literals.put(new Integer(Token.CONTINUE), "continue"); literals.put(new Integer(Token.CASE), "case"); literals.put(new Integer(Token.DEFAULT), "default"); literals.put(new Integer(Token.RETURN), "return"); literals.put(new Integer(Token.VAR), "var "); literals.put(new Integer(Token.SEMI), ";"); literals.put(new Integer(Token.ASSIGN), "="); literals.put(new Integer(Token.ASSIGN_ADD), "+="); literals.put(new Integer(Token.ASSIGN_SUB), "-="); literals.put(new Integer(Token.ASSIGN_MUL), "*="); literals.put(new Integer(Token.ASSIGN_DIV), "/="); literals.put(new Integer(Token.ASSIGN_MOD), "%="); literals.put(new Integer(Token.ASSIGN_BITOR), "|="); literals.put(new Integer(Token.ASSIGN_BITXOR), "^="); literals.put(new Integer(Token.ASSIGN_BITAND), "&="); literals.put(new Integer(Token.ASSIGN_LSH), "<<="); literals.put(new Integer(Token.ASSIGN_RSH), ">>="); literals.put(new Integer(Token.ASSIGN_URSH), ">>>="); literals.put(new Integer(Token.HOOK), "?"); literals.put(new Integer(Token.OBJECTLIT), ":"); literals.put(new Integer(Token.COLON), ":"); literals.put(new Integer(Token.OR), "||"); literals.put(new Integer(Token.AND), "&&"); literals.put(new Integer(Token.BITOR), "|"); literals.put(new Integer(Token.BITXOR), "^"); literals.put(new Integer(Token.BITAND), "&"); literals.put(new Integer(Token.SHEQ), "==="); literals.put(new Integer(Token.SHNE), "!=="); literals.put(new Integer(Token.EQ), "=="); literals.put(new Integer(Token.NE), "!="); literals.put(new Integer(Token.LE), "<="); literals.put(new Integer(Token.LT), "<"); literals.put(new Integer(Token.GE), ">="); literals.put(new Integer(Token.GT), ">"); literals.put(new Integer(Token.INSTANCEOF), " instanceof "); literals.put(new Integer(Token.LSH), "<<"); literals.put(new Integer(Token.RSH), ">>"); literals.put(new Integer(Token.URSH), ">>>"); literals.put(new Integer(Token.TYPEOF), "typeof"); literals.put(new Integer(Token.VOID), "void "); literals.put(new Integer(Token.CONST), "const "); literals.put(new Integer(Token.NOT), "!"); literals.put(new Integer(Token.BITNOT), "~"); literals.put(new Integer(Token.POS), "+"); literals.put(new Integer(Token.NEG), "-"); literals.put(new Integer(Token.INC), "++"); literals.put(new Integer(Token.DEC), "--"); literals.put(new Integer(Token.ADD), "+"); literals.put(new Integer(Token.SUB), "-"); literals.put(new Integer(Token.MUL), "*"); literals.put(new Integer(Token.DIV), "/"); literals.put(new Integer(Token.MOD), "%"); literals.put(new Integer(Token.COLONCOLON), "::"); literals.put(new Integer(Token.DOTDOT), ".."); literals.put(new Integer(Token.DOTQUERY), ".("); literals.put(new Integer(Token.XMLATTR), "@"); literals.put(new Integer(Token.LET), "let "); literals.put(new Integer(Token.YIELD), "yield "); // See // http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Reserved_Words // JavaScript 1.5 reserved words reserved.add("break"); reserved.add("case"); reserved.add("catch"); reserved.add("continue"); reserved.add("default"); reserved.add("delete"); reserved.add("do"); reserved.add("else"); reserved.add("finally"); reserved.add("for"); reserved.add("function"); reserved.add("if"); reserved.add("in"); reserved.add("instanceof"); reserved.add("new"); reserved.add("return"); reserved.add("switch"); reserved.add("this"); reserved.add("throw"); reserved.add("try"); reserved.add("typeof"); reserved.add("var"); reserved.add("void"); reserved.add("while"); reserved.add("with"); // Words reserved for future use reserved.add("abstract"); reserved.add("boolean"); reserved.add("byte"); reserved.add("char"); reserved.add("class"); reserved.add("const"); reserved.add("debugger"); reserved.add("double"); reserved.add("enum"); reserved.add("export"); reserved.add("extends"); reserved.add("final"); reserved.add("float"); reserved.add("goto"); reserved.add("implements"); reserved.add("import"); reserved.add("int"); reserved.add("interface"); reserved.add("long"); reserved.add("native"); reserved.add("package"); reserved.add("private"); reserved.add("protected"); reserved.add("public"); reserved.add("short"); reserved.add("static"); reserved.add("super"); reserved.add("synchronized"); reserved.add("throws"); reserved.add("transient"); reserved.add("volatile"); // These are not reserved, but should be taken into account // in isValidIdentifier (See jslint source code) reserved.add("arguments"); reserved.add("eval"); reserved.add("true"); reserved.add("false"); reserved.add("Infinity"); reserved.add("NaN"); reserved.add("null"); reserved.add("undefined"); }