public void EnterPseudoScope(ast.Node n) { SymbolScope currentScope = m_PseudoScopes.Peek(); n.PseudoScope = new SymbolScope(currentScope.Compiler, n, Accessibility.Private); currentScope.InnerScopes.Add(n.PseudoScope); n.PseudoScope.OuterScope = currentScope; m_PseudoScopes.Push(n.PseudoScope); }
public void EnterScope(SymbolScope Scope) { System.Diagnostics.Debug.Assert(Scope.OuterScope == m_CurrentScope); m_CurrentScope = Scope; m_SymbolAllocator.EnterScope(); m_MemberAllocator.EnterScope(); // Obfuscate symbols? if (!Compiler.NoObfuscate) { Scope.ObfuscateSymbols(this); } }
public bool OnEnterNode(MiniME.ast.Node n) { // Descending into an inner scope if (n.Scope != null) { System.Diagnostics.Debug.Assert(n.Scope.OuterScope == currentScope); currentScope = n.Scope; } // Is is a "var <name>=<literal_int_or_double>", inside a function if (currentScope.OuterScope!=null && n.GetType() == typeof(ast.StatementVariableDeclaration)) { var vardecl = (ast.StatementVariableDeclaration)n; foreach (var v in vardecl.Variables) { // Must have initial value if (v.InitialValue == null) continue; // Must evaluate to a constant object val = v.InitialValue.RootNode.EvalConstLiteral(); if (val==null) continue; // Must be a number if (val.GetType() != typeof(long) && val.GetType() != typeof(DoubleLiteral)) continue; // Find the symbol in the current scope Symbol s = currentScope.Symbols.FindLocalSymbol(v.Name); System.Diagnostics.Debug.Assert(s != null); // Store the constant value for this symbol if (s.ConstValue == null && s.ConstAllowed) { s.ConstValue = val; } else { s.ConstAllowed = false; s.ConstValue = null; } } } return true; }
public bool OnEnterNode(MiniME.ast.Node n) { if (n.Scope != null) { currentScope = n.Scope; } if (n.PseudoScope!=null) { currentPseudoScope=n.PseudoScope; DetectMultipleDeclarations(); } // Check 'if' statement var ifStatement = n as ast.StatementIfElse; if (ifStatement != null) { CheckControlCondition(ifStatement, ifStatement.Condition); } // Check 'while' statement var whileStatement = n as ast.StatementWhile; if (whileStatement != null) { CheckControlCondition(whileStatement, whileStatement.Condition); } // Check 'do' statement var doStatement = n as ast.StatementDoWhile; if (doStatement != null) { CheckControlCondition(doStatement, doStatement.Condition); } // Check 'for' statement var forStatement = n as ast.StatementFor; if (forStatement != null) { CheckControlCondition(forStatement, forStatement.Condition); } // Check for variable used outself declaring pseudo scope var ident = n as ast.ExprNodeIdentifier; if (ident!=null && ident.Lhs == null) { var symbol=currentScope.FindLocalSymbol(ident.Name); if (symbol!=null) { // Now walk the pseudo scopes and make sure that it's defined in the current scope too // (and not in a child scope) var scope = currentPseudoScope; bool bFound=false; while (scope != null && !bFound) { // Check scope if (scope.FindLocalSymbol(ident.Name) != null) { bFound = true; break; } // Are we finished on the actual local scope if (scope.Node!=null && scope.Node.Scope != null) break; // Get next outer scope scope = scope.OuterScope; } if (!bFound) { currentScope.Compiler.RecordWarning(n.Bookmark, "symbol `{0}` used outside declaring pseudo scope", ident.Name); foreach (var decl in symbol.Declarations) { currentScope.Compiler.RecordWarning(decl, n.Bookmark, "see also declaration of `{0}`", ident.Name); } } } } return true; }
// Constructor public VisitorLint(SymbolScope rootScope, SymbolScope rootPseudoScope) { currentScope = rootScope; currentPseudoScope = rootPseudoScope; DetectMultipleDeclarations(); }
// Constructor public VisitorScopeBuilder(SymbolScope rootScope, SymbolScope rootPseudoScope) { m_Scopes.Push(rootScope); m_PseudoScopes.Push(rootPseudoScope); }
public void OnLeaveNode(MiniME.ast.Node n) { if (n.Scope != null) { currentScope = n.Scope.OuterScope; } }
public VisitorConstDetectorPass1(SymbolScope rootScope) { currentScope = rootScope; }
public bool OnEnterNode(MiniME.ast.Node n) { // Descending into an inner scope if (n.Scope != null) { System.Diagnostics.Debug.Assert(n.Scope.OuterScope == currentScope); currentScope = n.Scope; } // Look for assignments to property if (n.GetType() == typeof(ast.ExprNodeAssignment)) { // Is it an assignment var assignOp = (ast.ExprNodeAssignment)n; RejectConstVariable(assignOp.Lhs); } // Look for increment/decrement operators if (n.GetType() == typeof(ast.ExprNodeUnary)) { var oneOp = (ast.ExprNodeUnary)n; if (oneOp.Op == Token.increment || oneOp.Op == Token.decrement) { RejectConstVariable(oneOp.Rhs); } } // Postfix too if (n.GetType() == typeof(ast.ExprNodePostfix)) { var oneOp = (ast.ExprNodePostfix)n; if (oneOp.Op == Token.increment || oneOp.Op == Token.decrement) { RejectConstVariable(oneOp.Lhs); } } return true; }
// Constructor public VisitorSymbolDeclaration(SymbolScope rootScope, SymbolScope rootPseudoScope) { currentScope = rootScope; currentPseudoScope = rootPseudoScope; }
public void EnterScope(SymbolScope Scope) { System.Diagnostics.Debug.Assert(Scope.OuterScope == m_CurrentScope); m_CurrentScope = Scope; m_SymbolAllocator.EnterScope(); m_MemberAllocator.EnterScope(); // Obfuscate symbols? if (!Compiler.NoObfuscate) Scope.ObfuscateSymbols(this); }
public VisitorCombineVarDecl(SymbolScope rootScope) { }
public bool OnEnterNode(ast.Node n) { // New actual scope (function body or catch clause) if (n.GetType() == typeof(ast.ExprNodeFunction) || n.GetType() == typeof(ast.CatchClause)) { SymbolScope currentScope = m_Scopes.Peek(); n.Scope = new SymbolScope(currentScope.Compiler, n, Accessibility.Private); // Add this function to the parent function's list of nested functions currentScope.InnerScopes.Add(n.Scope); n.Scope.OuterScope = currentScope; // Enter scope m_Scopes.Push(n.Scope); // Also create a pseudo scope EnterPseudoScope(n); return(true); } // New pseudo scope (statement body or braced code block) if (n.GetType() == typeof(ast.CodeBlock) || n.GetType() == typeof(ast.StatementBlock) || n.GetType() == typeof(ast.StatementFor) || n.GetType() == typeof(ast.StatementForIn)) { // Create pseudo scope EnterPseudoScope(n); return(true); } // Is it an evil? if (n.GetType() == typeof(ast.StatementWith)) { m_Scopes.Peek().Compiler.RecordWarning(n.Bookmark, "use of `with` statement prevents local symbol obfuscation of all containing scopes"); m_Scopes.Peek().DefaultAccessibility = Accessibility.Public; return(true); } // More evil if (n.GetType() == typeof(ast.ExprNodeIdentifier)) { var m = (ast.ExprNodeIdentifier)n; if (m.Lhs == null && m.Name == "eval") { m_Scopes.Peek().Compiler.RecordWarning(n.Bookmark, "use of `eval` prevents local symbol obfuscation of all containing scopes"); m_Scopes.Peek().DefaultAccessibility = Accessibility.Public; } return(true); } // Private member declaration? if (n.GetType() == typeof(ast.StatementAccessibility)) { var p = (ast.StatementAccessibility)n; foreach (var s in p.Specs) { m_Scopes.Peek().AddAccessibilitySpec(p.Bookmark, s); } } // Try to guess name of function by assignment in variable declaration var decl = n as ast.StatementVariableDeclaration; if (decl != null) { foreach (var i in decl.Variables) { if (i.InitialValue != null) { var fn = i.InitialValue.RootNode as ast.ExprNodeFunction; if (fn != null) { fn.AssignedToName = i.Name; } } } } // Try to guess name of function by assignment var assignment = n as ast.ExprNodeAssignment; if (assignment != null) { var fn = assignment.Rhs as ast.ExprNodeFunction; if (fn != null) { var id = assignment.Lhs as ast.ExprNodeIdentifier; if (id != null) { fn.AssignedToName = id.Name; } } } // Try to guess name of function in object literal var objLiteral = n as ast.ExprNodeObjectLiteral; if (objLiteral != null) { foreach (var i in objLiteral.Values) { var fn = i.Value as ast.ExprNodeFunction; if (fn != null) { var id = i.Key as ast.ExprNodeIdentifier; if (id != null) { fn.AssignedToName = id.Name; } } } } return(true); }
public bool OnEnterNode(MiniME.ast.Node n) { // Define name of function in outer scope, before descending if (n.GetType() == typeof(ast.ExprNodeFunction)) { var fn = (ast.ExprNodeFunction)n; // Define a symbol for the new function if (!String.IsNullOrEmpty(fn.Name)) { DefineLocalSymbol(fn.Name, fn.Bookmark); currentScope.ProcessAccessibilitySpecs(fn.Name, fn.Bookmark); } } // Descending into an inner scope if (n.Scope != null) { System.Diagnostics.Debug.Assert(n.Scope.OuterScope == currentScope); currentScope = n.Scope; } // Descending into an inner pseudo scope if (n.PseudoScope != null) { System.Diagnostics.Debug.Assert(n.PseudoScope.OuterScope == currentPseudoScope); currentPseudoScope = n.PseudoScope; } // Define catch clause exception variables in the inner scope if (n.GetType() == typeof(ast.CatchClause)) { var cc = (ast.CatchClause)n; DefineLocalSymbol(cc.ExceptionVariable, n.Bookmark); return true; } // Define variables in the current scope if (n.GetType() == typeof(ast.StatementVariableDeclaration)) { var vardecl = (ast.StatementVariableDeclaration)n; foreach (var v in vardecl.Variables) { DefineLocalSymbol(v.Name, v.Bookmark); currentScope.ProcessAccessibilitySpecs(v.Name, v.Bookmark); if (v.InitialValue!=null && v.InitialValue.RootNode.GetType()==typeof(ast.ExprNodeObjectLiteral)) { // Get the object literal var literal=(ast.ExprNodeObjectLiteral)v.InitialValue.RootNode; // Create a fake/temp identifier node while we process accessibility specs var target = new ast.ExprNodeIdentifier(null, v.Name); // Process all keys that are identifiers foreach (var x in literal.Values) { var identifierKey=x.Key as ast.ExprNodeIdentifier; if (identifierKey!=null && identifierKey.Lhs==null) { currentScope.ProcessAccessibilitySpecs(target, identifierKey.Name, identifierKey.Bookmark); } } } } return true; } // Define parameters in the current scope if (n.GetType() == typeof(ast.Parameter)) { var p = (ast.Parameter)n; DefineLocalSymbol(p.Name, p.Bookmark); currentScope.ProcessAccessibilitySpecs(p.Name, p.Bookmark); return true; } // Automatic declaration of private member? // We're looking for an assignment to a matching private spec if (n.GetType() == typeof(ast.StatementExpression)) { var exprstmt = (ast.StatementExpression)n; if (exprstmt.Expression.RootNode.GetType()==typeof(ast.ExprNodeAssignment)) { var assignOp = (ast.ExprNodeAssignment)exprstmt.Expression.RootNode; if (assignOp.Op == Token.assign) { // Lhs must be an identifier member // eg: target.member=<expr> if (assignOp.Lhs.GetType()==typeof(ast.ExprNodeIdentifier)) { var identifier=(ast.ExprNodeIdentifier)assignOp.Lhs; if (identifier.Lhs!=null) { // For member specs, the identifier must have a lhs if (identifier.Lhs.GetType() != typeof(ast.ExprNodeIdentifier)) return false; currentScope.ProcessAccessibilitySpecs((ast.ExprNodeIdentifier)identifier.Lhs, identifier.Name, identifier.Bookmark); } } // Assignment of an object literal // eg: target={member:value,member:value}; if (assignOp.Lhs.GetType() == typeof(ast.ExprNodeIdentifier) && assignOp.Rhs.GetType() == typeof(ast.ExprNodeObjectLiteral)) { var target = (ast.ExprNodeIdentifier)assignOp.Lhs; var literal=(ast.ExprNodeObjectLiteral)assignOp.Rhs; if (target.Lhs == null) { foreach (var x in literal.Values) { var identifierKey=x.Key as ast.ExprNodeIdentifier; if (identifierKey!=null && identifierKey.Lhs==null) { currentScope.ProcessAccessibilitySpecs(target, identifierKey.Name, identifierKey.Bookmark); } } } } /* if (assignOp.Rhs.GetType() == typeof(ast.ExprNodeObjectLiteral)) { var literal=ast.ExprNode } */ } } } // Normally accessibility specs are already processed in the scope builder, but this // is a special case for declaring external symbols. We need to pick up the explicit // symbol declaration and add it to the pseudo scope to avoid incorrect warnings. // Use case is declaring `x` as an external global var by: // // // public:x // if (n.GetType() == typeof(ast.StatementAccessibility)) { var p = (ast.StatementAccessibility)n; foreach (var s in p.Specs) { if (!s.IsWildcard() && !s.IsMemberSpec()) currentPseudoScope.Symbols.DefineSymbol(s.GetExplicitName(), n.Bookmark); } } return true; }
public void LeaveScope() { m_SymbolAllocator.LeaveScope(); m_MemberAllocator.LeaveScope(); m_CurrentScope = m_CurrentScope.OuterScope; }
// Constructor public VisitorSymbolUsage(SymbolScope rootScope) { currentScope = rootScope; }
/// <summary> /// Parses all loaded scripts as javascript and compiles them to a string. /// </summary> /// <returns>A string containing the minified javascript.</returns> public string CompileJavascriptToString() { // Create a symbol allocator SymbolAllocator SymbolAllocator = new SymbolAllocator(this); // Don't let the symbol allocator use any reserved words or common Javascript bits // We only go up to three letters - symbol allocation of more than 3 letters is // highly unlikely. // (based on list here: http://www.quackit.com/javascript/javascript_reserved_words.cfm) string[] words = new string[] { "if", "in", "do", "for", "new", "var", "int", "try", "NaN", "ref", "sun", "top" }; foreach (var s in words) { SymbolAllocator.ClaimSymbol(s); } // Create a member allocator SymbolAllocator MemberAllocator = new SymbolAllocator(this); // Render RenderContext r = new RenderContext(this, SymbolAllocator, MemberAllocator); // Process all files bool bNeedSemicolon = false; foreach (var file in m_files) { Console.WriteLine("Processing {0}...", System.IO.Path.GetFileName(file.filename)); // Create a tokenizer and parser Warnings = new List<Warning>(); WarningsEnabledStack = new Stack<bool>(); Tokenizer t = new Tokenizer(this, file.content, file.filename, file.warnings); Parser p = new Parser(t); // Create the global statement block var code = new ast.CodeBlock(null, TriState.No); // Parse the file into a namespace p.ParseStatements(code); // Ensure everything processed if (t.more) { throw new CompileError("Unexpected end of file", t); } // Dump the abstract syntax tree if (DumpAST) code.Dump(0); // Create the root symbol scope and build scopes for all // constained function scopes SymbolScope rootScope = new SymbolScope(this, null, Accessibility.Public); SymbolScope rootPseudoScope = new SymbolScope(this, null, Accessibility.Public); code.Visit(new VisitorScopeBuilder(rootScope, rootPseudoScope)); // Combine consecutive var declarations into a single one code.Visit(new VisitorCombineVarDecl(rootScope)); // Find all variable declarations code.Visit(new VisitorSymbolDeclaration(rootScope, rootPseudoScope)); // Do lint stuff code.Visit(new VisitorLint(rootScope, rootPseudoScope)); // Try to eliminate const declarations if (DetectConsts && !NoObfuscate) { code.Visit(new VisitorConstDetectorPass1(rootScope)); code.Visit(new VisitorConstDetectorPass2(rootScope)); code.Visit(new VisitorConstDetectorPass3(rootScope)); } // Simplify expressions code.Visit(new VisitorSimplifyExpressions()); // If obfuscation is allowed, find all in-scope symbols and then // count the frequency of their use. if (!NoObfuscate) { code.Visit(new VisitorSymbolUsage(rootScope)); } // Process all symbol scopes, applying default accessibility levels // and determining the "rank" of each symbol rootScope.Prepare(); // Dump scopes to stdout if (DumpScopes) rootScope.Dump(0); // Tell the global scope to claim all locally defined symbols // so they're not re-used (and therefore hidden) by the // symbol allocation rootScope.ClaimSymbols(SymbolAllocator); // Create a credit comment on the first file if (!NoCredit && file==m_files[0]) { int iInsertPos = 0; while (iInsertPos < code.Content.Count && code.Content[iInsertPos].GetType() == typeof(ast.StatementComment)) iInsertPos++; code.Content.Insert(iInsertPos, new ast.StatementComment(null, "// Minified by MiniME from toptensoftware.com")); } if (bNeedSemicolon) { r.Append(";"); } // Render it r.EnterScope(rootScope); bNeedSemicolon=code.Render(r); r.LeaveScope(); // Display warnings Warnings.Sort(delegate(Warning w1, Warning w2) { int Compare = w1.Order.file.FileName.CompareTo(w2.Order.file.FileName); if (Compare == 0) Compare = w1.Order.position - w2.Order.position; if (Compare == 0) Compare = w1.OriginalOrder - w2.OriginalOrder; return Compare; }); foreach (var w in Warnings) { Console.WriteLine("{0}: {1}", w.Bookmark, w.Message); } } // return the final script string strResult = r.GetGeneratedOutput(); return strResult; }
public bool OnEnterNode(MiniME.ast.Node n) { // Descending into an inner scope if (n.Scope != null) { System.Diagnostics.Debug.Assert(n.Scope.OuterScope == currentScope); currentScope = n.Scope; } // Is it a variable declaration if (n.GetType() == typeof(ast.StatementVariableDeclaration)) { var vardecl = (ast.StatementVariableDeclaration)n; for (int i = vardecl.Variables.Count - 1; i >= 0; i-- ) { var v = vardecl.Variables[i]; // Find the symbol (must exist in current scope) var s = currentScope.Symbols.FindLocalSymbol(v.Name); System.Diagnostics.Debug.Assert(s != null); // Is it a const? if (s.ConstValue != null) { // Yes! Remove it vardecl.Variables.RemoveAt(i); } } } return true; }
public bool OnEnterNode(MiniME.ast.Node n) { // Define name of function in outer scope, before descending if (n.GetType() == typeof(ast.ExprNodeFunction)) { var fn = (ast.ExprNodeFunction)n; // Define a symbol for the new function if (!String.IsNullOrEmpty(fn.Name)) { currentScope.Symbols.UseSymbol(fn.Name); } } // Descending into an inner scope if (n.Scope != null) { System.Diagnostics.Debug.Assert(n.Scope.OuterScope == currentScope); currentScope = n.Scope; } // Identifier? if (n.GetType() == typeof(ast.ExprNodeIdentifier)) { var m = (ast.ExprNodeIdentifier)n; if (m.Lhs == null) { currentScope.Symbols.UseSymbol(m.Name); } else { currentScope.Members.UseSymbol(m.Name); } } // Use catch clause exception variables in the inner scope if (n.GetType() == typeof(ast.CatchClause)) { var cc = (ast.CatchClause)n; currentScope.Symbols.UseSymbol(cc.ExceptionVariable); return true; } // Use variables in the current scope if (n.GetType() == typeof(ast.StatementVariableDeclaration)) { var vardecl = (ast.StatementVariableDeclaration)n; foreach (var v in vardecl.Variables) { currentScope.Symbols.UseSymbol(v.Name); } return true; } // Use parameters in the current scope if (n.GetType() == typeof(ast.Parameter)) { var p = (ast.Parameter)n; currentScope.Symbols.UseSymbol(p.Name); return true; } // Look for assignment to undefined variable if (n.GetType() == typeof(ast.ExprNodeAssignment)) { var rtlOp = (ast.ExprNodeAssignment)n; if (rtlOp.Op == Token.assign) { if (rtlOp.Lhs.GetType() == typeof(ast.ExprNodeIdentifier)) { var identifier = (ast.ExprNodeIdentifier)rtlOp.Lhs; if (identifier.Lhs == null) { // Assignment to an identifier if (currentScope.FindSymbol(identifier.Name) == null) { currentScope.Compiler.RecordWarning(identifier.Bookmark, "assignment to undeclared variable `{0}` introduces new global variable", identifier.Name); } } } } } return true; }
// Compile all loaded script to a string public string CompileJavascriptToString() { // Create a symbol allocator SymbolAllocator SymbolAllocator = new SymbolAllocator(this); // Don't let the symbol allocator use any reserved words or common Javascript bits // We only go up to three letters - symbol allocation of more than 3 letters is // highly unlikely. // (based on list here: http://www.quackit.com/javascript/javascript_reserved_words.cfm) string[] words = new string[] { "if", "in", "do", "for", "new", "var", "int", "try", "NaN", "ref", "sun", "top" }; foreach (var s in words) { SymbolAllocator.ClaimSymbol(s); } // Create a member allocator SymbolAllocator MemberAllocator = new SymbolAllocator(this); // Render RenderContext r = new RenderContext(this, SymbolAllocator, MemberAllocator); // Process all files bool bNeedSemicolon = false; foreach (var file in m_files) { Console.WriteLine("Processing {0}...", System.IO.Path.GetFileName(file.filename)); // Create a tokenizer and parser Warnings = new List <Warning>(); WarningsEnabledStack = new Stack <bool>(); Tokenizer t = new Tokenizer(this, file.content, file.filename, file.warnings); Parser p = new Parser(t); // Create the global statement block var code = new ast.CodeBlock(null, TriState.No); // Parse the file into a namespace p.ParseStatements(code); // Ensure everything processed if (t.more) { throw new CompileError("Unexpected end of file", t); } // Dump the abstract syntax tree if (DumpAST) { code.Dump(0); } // Create the root symbol scope and build scopes for all // constained function scopes SymbolScope rootScope = new SymbolScope(this, null, Accessibility.Public); SymbolScope rootPseudoScope = new SymbolScope(this, null, Accessibility.Public); code.Visit(new VisitorScopeBuilder(rootScope, rootPseudoScope)); // Combine consecutive var declarations into a single one code.Visit(new VisitorCombineVarDecl(rootScope)); // Find all variable declarations code.Visit(new VisitorSymbolDeclaration(rootScope, rootPseudoScope)); // Do lint stuff code.Visit(new VisitorLint(rootScope, rootPseudoScope)); // Try to eliminate const declarations if (DetectConsts && !NoObfuscate) { code.Visit(new VisitorConstDetectorPass1(rootScope)); code.Visit(new VisitorConstDetectorPass2(rootScope)); code.Visit(new VisitorConstDetectorPass3(rootScope)); } // Simplify expressions code.Visit(new VisitorSimplifyExpressions()); // If obfuscation is allowed, find all in-scope symbols and then // count the frequency of their use. if (!NoObfuscate) { code.Visit(new VisitorSymbolUsage(rootScope)); } // Process all symbol scopes, applying default accessibility levels // and determining the "rank" of each symbol rootScope.Prepare(); // Dump scopes to stdout if (DumpScopes) { rootScope.Dump(0); } // Tell the global scope to claim all locally defined symbols // so they're not re-used (and therefore hidden) by the // symbol allocation rootScope.ClaimSymbols(SymbolAllocator); // Create a credit comment on the first file if (!NoCredit && file == m_files[0]) { int iInsertPos = 0; while (iInsertPos < code.Content.Count && code.Content[iInsertPos].GetType() == typeof(ast.StatementComment)) { iInsertPos++; } code.Content.Insert(iInsertPos, new ast.StatementComment(null, "// Minified by MiniME from toptensoftware.com")); } if (bNeedSemicolon) { r.Append(";"); } // Render it r.EnterScope(rootScope); bNeedSemicolon = code.Render(r); r.LeaveScope(); // Display warnings Warnings.Sort(delegate(Warning w1, Warning w2) { int Compare = w1.Order.file.FileName.CompareTo(w2.Order.file.FileName); if (Compare == 0) { Compare = w1.Order.position - w2.Order.position; } if (Compare == 0) { Compare = w1.OriginalOrder - w2.OriginalOrder; } return(Compare); }); foreach (var w in Warnings) { Console.WriteLine("{0}: {1}", w.Bookmark, w.Message); } } // return the final script string strResult = r.GetGeneratedOutput(); return(strResult); }