private void ParseAst(JS.AST ast, string sp) { if (ast == null) { return; } if (CheckWorker()) { return; } //_logView.LogStr("JSM->" + sp + ast.ToString() + "\t\t" + ast.GetType().Name); if (ast is JS.FunctionDeclaration) { JS.Function func = ast as JS.Function; ParseAstList(func.func_obj.body.elems, sp + " "); } else if (ast is JS.Assign) { JS.Assign ass = ast as JS.Assign; ParseAst(ass.left, sp + "l "); ParseAst(ass.right, sp + "r "); } else if (ast is JS.Binary) { JS.Binary bin = ast as JS.Binary; string[] parts = bin.ToString().Split('.'); if (parts.Length > 1) { if (parts[parts.Length - 2] == "This") { string calledId = parts[parts.Length - 1] + "()"; //dup // If we have a method of this name in this file/class if (_addedNodes.ContainsKey(calledId)) { // Create an edge. // A definite functional assignment link. AddEdge(_methodNodeId, calledId, EdgeStyle.NormalArrow, Color.Aqua); } else { // It's a call to a method outside this class/file. // //_logView.LogStr("skipped assign ref -->" + _methodNodeId + " --------> " + calledId); // } } else if (parts[parts.Length - 2] == "self") { string calledId = parts[parts.Length - 1] + "()"; //dup // If we have a method of this name in this file/class if (_addedNodes.ContainsKey(calledId)) { // Get the graph node that we're linking to. //Node calledNode = _addedNodes[calledId]; // Create an edge. // A definite functional assignment link. AddEdge(_methodNodeId, calledId, EdgeStyle.NormalArrow, Color.Aqua); } else { // It's a call to a method outside this class/file. _logView.LogStr("skipped assign ref -->" + _methodNodeId + " --------> " + calledId); } } } } else if (ast is JS.Expression) { JS.Expression expr = ast as JS.Expression; ParseAstList(expr.exprs, sp + " "); } else if (ast is JS.FunctionExpression) { JS.FunctionExpression expr = ast as JS.FunctionExpression; ParseAstList(expr.func_obj.body.elems, sp + " "); } else if (ast is JS.For) { JS.For fr = ast as JS.For; ParseAst(fr.stms, sp + " "); } else if (ast is JS.If) { JS.If iff = ast as JS.If; ParseAst(iff.false_stm, sp + "f "); ParseAst(iff.true_stm, sp + "t "); } else if (ast is JS.Block) { JS.Block block = ast as JS.Block; ParseAstList(block.elems, sp + " "); } else if (ast is JS.VariableStatement) { JS.VariableStatement var = ast as JS.VariableStatement; //var. ParseAstList(var.var_decls, sp + " "); } else if (ast is JS.Return) { JS.Return ret = ast as JS.Return; ParseAst(ret.expression, sp + " "); } else if (ast is JS.VariableDeclaration) { JS.VariableDeclaration var = ast as JS.VariableDeclaration; Microsoft.JScript.New newval = var.val as Microsoft.JScript.New; if (newval != null && newval.exp != null) { //_logView.LogStr("new:" + newval.exp.ToString()); string[] parts = newval.exp.ToString().Split('.'); if (parts.Length > 0) { string calledId = parts[parts.Length - 1] + "()"; // If we have a method of this name in this file/class, then // we have a possible constructor link. if (_addedNodes.ContainsKey(calledId)) { // Create an edge. AddEdge(_methodNodeId, calledId, EdgeStyle.NormalArrow, Color.Green); } } } else { if (var.val != null) { string valStr = var.val.ToString(); string[] parts = valStr.Split('.'); if (parts.Length > 1 && parts[0] == "self") { // dup.. string calledId = parts[parts.Length - 1] + "()"; // If we have a method of this name in this file/class, then // we have a possible constructor link. if (_addedNodes.ContainsKey(calledId)) { // Create an edge. AddEdge(_methodNodeId, calledId, EdgeStyle.NormalArrow, Color.Green); } } } } //ParseAstList(var.var_decls, sp + " "); } else if (ast is JS.Call) { JS.Call call = ast as JS.Call; string[] parts = call.ToString().Split(' '); string[] bits = parts[0].Split('.'); string calledId = bits[bits.Length - 1] + "()"; bool methodInThisClass = true; if (bits.Length > 1) { if ((bits[bits.Length - 2] != "This") && (bits[bits.Length - 2] != "self")) { methodInThisClass = false; } } // If we have a method of this name in this file/class if (_addedNodes.ContainsKey(calledId)) { // Create an edge. if (methodInThisClass) { // A definite link. AddEdge(_methodNodeId, calledId, EdgeStyle.NormalArrow, Color.Black); } else { // A tentative link. AddEdge(_methodNodeId, calledId, EdgeStyle.NormalArrow, Color.Gray); } } else { // It's a call to a method outside this class/file. // //_logView.LogStr("skipped -------->" + _methodNodeId + " --------> " + parts[0]); // } } }
/** * Whether the "catch (e: e instanceof Exception) { ... }" syntax * is implemented. */ AST StatementHelper(AST parent) { AST pn = null; // If skipsemi == true, don't add SEMI + EOL to source at the // end of this statment. For compound statements, IF/FOR etc. bool skip_semi = false; int tt; tt = ts.GetToken (); if (tt == Token.IF) { skip_semi = true; decompiler.AddToken (Token.IF); AST cond = Condition (parent); decompiler.AddEOL (Token.LC); AST if_true = Statement (parent); AST if_false = null; if (ts.MatchToken (Token.ELSE)) { decompiler.AddToken (Token.RC); decompiler.AddToken (Token.ELSE); decompiler.AddEOL (Token.LC); if_false = Statement (parent); } decompiler.AddEOL (Token.RC); pn = new If (parent, cond, if_true, if_false, new Location (ts.SourceName, ts.LineNumber)); } else if (tt == Token.SWITCH) { skip_semi = true; decompiler.AddToken (Token.SWITCH); pn = new Switch (parent, new Location (ts.SourceName, ts.LineNumber)); Clause cur_case; MustMatchToken (Token.LP, "msg.no.paren.switch"); decompiler.AddToken (Token.LP); ((Switch) pn).exp = Expr (parent, false); MustMatchToken (Token.RP, "msg.no.paren.after.switch"); decompiler.AddToken (Token.RP); MustMatchToken (Token.LC, "msg.no.brace.switch"); decompiler.AddEOL (Token.LC); ClauseType clause_type = ClauseType.Case; while ((tt = ts.GetToken ()) != Token.RC && tt != Token.EOF) { if (tt == Token.CASE) { decompiler.AddToken (Token.CASE); cur_case = new Clause (pn, new Location (ts.SourceName, ts.LineNumber)); cur_case.exp = Expr (pn, false); decompiler.AddEOL (Token.COLON); if (clause_type == ClauseType.Default) clause_type = ClauseType.CaseAfterDefault; } else if (tt == Token.DEFAULT) { cur_case = null; clause_type = ClauseType.Default; decompiler.AddToken (Token.DEFAULT); decompiler.AddEOL (Token.COLON); } else { cur_case = null; ReportError ("msg.bad.switch"); } MustMatchToken (Token.COLON, "msg.no.colon.case"); while ((tt = ts.PeekToken ()) != Token.RC && tt != Token.CASE && tt != Token.DEFAULT && tt != Token.EOF) { if (clause_type == ClauseType.Case || clause_type == ClauseType.CaseAfterDefault) cur_case.AddStm (Statement (pn)); else if (clause_type == ClauseType.Default) ((Switch) pn).default_clauses.Add (Statement (pn)); } ((Switch) pn).AddClause (cur_case, clause_type); } decompiler.AddEOL (Token.RC); } else if (tt == Token.WHILE) { skip_semi = true; decompiler.AddToken (Token.WHILE); While w = new While (new Location (ts.SourceName, ts.LineNumber)); AST cond = Condition (w); decompiler.AddEOL (Token.LC); AST body = Statement (w); decompiler.AddEOL (Token.RC); w.Init (parent, cond, body); pn = w; } else if (tt == Token.DO) { decompiler.AddToken (Token.DO); decompiler.AddEOL (Token.LC); int line_number = ts.LineNumber; DoWhile do_while = new DoWhile (new Location (ts.SourceName, line_number)); AST body = Statement (do_while); decompiler.AddToken (Token.RC); MustMatchToken (Token.WHILE, "msg.no.while.do"); decompiler.AddToken (Token.WHILE); AST cond = Condition (do_while); do_while.Init (parent, body, cond); pn = do_while; } else if (tt == Token.FOR) { skip_semi = true; decompiler.AddToken (Token.FOR); AST init, cond, incr = null, body; MustMatchToken (Token.LP, "msg.no.paren.for"); decompiler.AddToken (Token.LP); tt = ts.PeekToken (); if (tt == Token.SEMI) init = new EmptyAST (); else { if (tt == Token.VAR) { // set init to a var list or initial ts.GetToken (); // throw away the 'var' token init = Variables (parent, true); } else init = Expr (parent, true); } if (ts.MatchToken (Token.IN)) { decompiler.AddToken (Token.IN); cond = Expr (parent, false); // 'cond' is the object over which we're iterating } else { // ordinary for loop MustMatchToken (Token.SEMI, "msg.no.semi.for"); decompiler.AddToken (Token.SEMI); if (ts.PeekToken () == Token.SEMI) cond = new EmptyAST (); // no loop condition else cond = Expr (parent, false); MustMatchToken (Token.SEMI, "msg.no.semi.for.cond"); decompiler.AddToken (Token.SEMI); if (ts.PeekToken () == Token.RP) incr = new EmptyAST (); else incr = Expr (parent, false); } MustMatchToken (Token.RP, "msg.no.paren.for.ctrl"); decompiler.AddToken (Token.RP); decompiler.AddEOL (Token.LC); body = Statement (pn); decompiler.AddEOL (Token.RC); if (incr == null) // cond could be null if 'in obj' got eaten by the init node. pn = new ForIn (parent, init, cond, body, new Location (ts.SourceName, ts.LineNumber)); else pn = new For (parent, init, cond, incr, body, new Location (ts.SourceName, ts.LineNumber)); body.PropagateParent (pn); } else if (tt == Token.TRY) { int line_number = ts.LineNumber; AST try_block; ArrayList catch_blocks = null; AST finally_block = null; skip_semi = true; decompiler.AddToken (Token.TRY); decompiler.AddEOL (Token.LC); try_block = Statement (parent); decompiler.AddEOL (Token.RC); catch_blocks = new ArrayList (); bool saw_default_catch = false; int peek = ts.PeekToken (); if (peek == Token.CATCH) { while (ts.MatchToken (Token.CATCH)) { if (saw_default_catch) ReportError ("msg.catch.unreachable"); decompiler.AddToken (Token.CATCH); MustMatchToken (Token.LP, "msg.no.paren.catch"); decompiler.AddToken (Token.LP); MustMatchToken (Token.NAME, "msg.bad.catchcond"); string var_name = ts.GetString; decompiler.AddName (var_name); AST catch_cond = null; if (ts.MatchToken (Token.IF)) { decompiler.AddToken (Token.IF); catch_cond = Expr (parent, false); } else saw_default_catch = true; MustMatchToken (Token.RP, "msg.bad.catchcond"); decompiler.AddToken (Token.RP); MustMatchToken (Token.LC, "msg.no.brace.catchblock"); decompiler.AddEOL (Token.LC); catch_blocks.Add (new Catch (var_name, catch_cond, Statements (null), parent, new Location (ts.SourceName, line_number))); MustMatchToken (Token.RC, "msg.no.brace.after.body"); decompiler.AddEOL (Token.RC); } } else if (peek != Token.FINALLY) MustMatchToken (Token.FINALLY, "msg.try.no.catchfinally"); if (ts.MatchToken (Token.FINALLY)) { decompiler.AddToken (Token.FINALLY); decompiler.AddEOL (Token.LC); finally_block = Statement (parent); decompiler.AddEOL (Token.RC); } pn = new Try (try_block, catch_blocks, finally_block, parent, new Location (ts.SourceName, ts.LineNumber)); } else if (tt == Token.THROW) { int line_number = ts.LineNumber; decompiler.AddToken (Token.THROW); pn = new Throw (Expr (parent, false), new Location (ts.SourceName, ts.LineNumber)); if (line_number == ts.LineNumber) CheckWellTerminated (); } else if (tt == Token.BREAK) { decompiler.AddToken (Token.BREAK); // MatchLabel only matches if there is one string label = MatchLabel (); if (label != null) decompiler.AddName (label); pn = new Break (parent, label, new Location (ts.SourceName, ts.LineNumber)); } else if (tt == Token.CONTINUE) { decompiler.AddToken (Token.CONTINUE); // MatchLabel only matches if there is one string label = MatchLabel (); if (label != null) decompiler.AddName (label); pn = new Continue (parent, label, new Location (ts.SourceName, ts.LineNumber)); } else if (tt == Token.WITH) { skip_semi = true; decompiler.AddToken (Token.WITH); MustMatchToken (Token.LP, "msg.no.paren.with"); decompiler.AddToken (Token.LP); AST obj = Expr (parent, false); MustMatchToken (Token.RP, "msg.no.paren.after.with"); decompiler.AddToken (Token.RP); decompiler.AddToken (Token.LC); ++nesting_of_with; AST body; try { body = Statement (parent); } finally { --nesting_of_with; } decompiler.AddEOL (Token.RC); pn = new With (parent, obj, body, new Location (ts.SourceName, ts.LineNumber)); } else if (tt == Token.VAR) { int line_number = ts.LineNumber; pn = Variables (parent, false); if (ts.LineNumber == line_number) CheckWellTerminated (); } else if (tt == Token.RETURN) { AST ret_expr = null; decompiler.AddToken (Token.RETURN); pn = new Return (new Location (ts.SourceName, ts.LineNumber)); if (!InsideFunction) ReportError ("msg.bad.return"); /* This is ugly, but we don't want to require a semicolon. */ ts.allow_reg_exp = true; tt = ts.PeekTokenSameLine (); ts.allow_reg_exp = false; int line_number = ts.LineNumber; if (tt != Token.EOF && tt != Token.EOL && tt != Token.SEMI && tt != Token.RC) { ret_expr = Expr (pn, false); if (ts.LineNumber == line_number) CheckWellTerminated (); } ((Return) pn).Init (parent, ret_expr); } else if (tt == Token.LC) { skip_semi = true; pn = Statements (parent); MustMatchToken (Token.RC, "msg.no.brace.block"); } else if (tt == Token.ERROR || tt == Token.EOL || tt == Token.SEMI) { pn = new EmptyAST (); skip_semi = true; } else if (tt == Token.FUNCTION) { pn = Function (parent, FunctionType.ExpressionStatement); } else if (tt == Token.IMPORT) { decompiler.AddToken (Token.IMPORT); pn = Import (parent); } else { int last_expr_type = tt; int token_number = ts.TokenNumber; ts.UnGetToken (tt); int line_number = ts.LineNumber; pn = Expr (parent, false); if (ts.PeekToken () == Token.COLON) { /* check that the last thing the tokenizer returned was a * NAME and that only one token was consumed. */ if (last_expr_type != Token.NAME || (ts.TokenNumber != token_number)) ReportError ("msg.bad.label"); ts.GetToken (); // eat the colon string name = ts.GetString; // bind 'Statement (pn)' to the label Labelled labelled = new Labelled (parent, new Location (ts.SourceName, ts.LineNumber)); labelled.Init (parent, name, Statement (labelled), new Location (ts.SourceName, ts.LineNumber)); pn = labelled; // depend on decompiling lookahead to guess that that // last name was a label. decompiler.AddEOL (Token.COLON); return pn; } // FIXME: // pn = nf.createExprStatement(pn, lineno); if (ts.LineNumber == line_number) CheckWellTerminated (); } ts.MatchToken (Token.SEMI); if (!skip_semi) decompiler.AddEOL (Token.SEMI); return pn; }