示例#1
0
 public JavaScriptIdentifier(string value,
                             ScriptOrFunctionScope declaredScope) : base(Token.NAME,
                                                                         value)
 {
     MarkedForMunging = true;
     DeclaredScope    = declaredScope;
 }
 public JavaScriptIdentifier(string value, 
     ScriptOrFunctionScope declaredScope) : base(Token.NAME, 
         value)
 {
     MarkedForMunging = true;
     DeclaredScope = declaredScope;
 }
 public ScriptOrFunctionScope(int braceNesting,
                              ScriptOrFunctionScope parentScope)
 {
     BraceNesting = braceNesting;
     ParentScope  = parentScope;
     SubScopes    = new ArrayList();
     if (parentScope != null)
     {
         parentScope.SubScopes.Add(this);
     }
 }
 public ScriptOrFunctionScope(int braceNesting, 
     ScriptOrFunctionScope parentScope)
 {
     this.BraceNesting = braceNesting;
     this.ParentScope = parentScope;
     this.SubScopes = new ArrayList();
     if (parentScope != null)
     {
         parentScope.SubScopes.Add(this);
     }
 }
        private ArrayList GetAllUsedSymbols()
        {
            var result = new ArrayList();
            ScriptOrFunctionScope scope = this;

            while (scope != null)
            {
                result.AddRange(scope.GetUsedSymbols());
                scope = scope.ParentScope;
            }

            return(result);
        }
        private void ParseFunctionDeclaration()
        {
            string symbol;
            ScriptOrFunctionScope functionScope;
            JavaScriptIdentifier identifier;


            ScriptOrFunctionScope currentScope = GetCurrentScope();

            JavaScriptToken token = ConsumeToken();

            if (token.TokenType == Token.NAME)
            {
                if (_mode == BUILDING_SYMBOL_TREE)
                {
                    // Get the name of the function and declare it in the current scope.
                    symbol = token.Value;
                    if (currentScope.GetIdentifier(symbol) != null)
                    {
                        Warn("The function " + symbol + " has already been declared in the same scope...", true);
                    }

                    currentScope.DeclareIdentifier(symbol);
                }

                token = ConsumeToken();
            }

            if (token.TokenType != Token.LP)
            {
                throw new InvalidOperationException();
            }

            if (_mode == BUILDING_SYMBOL_TREE)
            {
                functionScope = new ScriptOrFunctionScope(_braceNesting, currentScope);
                _indexedScopes.Add(_offset, functionScope);
            }
            else
            {
                functionScope = (ScriptOrFunctionScope) _indexedScopes[_offset];
            }

            // Parse function arguments.
            int argpos = 0;
            while ((token = ConsumeToken()).TokenType != Token.RP)
            {
                if (token.TokenType != Token.NAME &&
                    token.TokenType != Token.COMMA)
                {
                    throw new InvalidOperationException();
                }

                if (token.TokenType == Token.NAME &&
                    _mode == BUILDING_SYMBOL_TREE)
                {
                    symbol = token.Value;
                    identifier = functionScope.DeclareIdentifier(symbol);

                    if (symbol.Equals("$super", StringComparison.OrdinalIgnoreCase) && argpos == 0)
                    {
                        // Exception for Prototype 1.6...
                        identifier.MarkedForMunging = false;
                    }

                    argpos++;
                }
            }

            token = ConsumeToken();

            if (token.TokenType != Token.LC)
            {
                throw new InvalidOperationException();
            }

            _braceNesting++;

            token = GetToken(0);
            if (token.TokenType == Token.STRING &&
                GetToken(1).TokenType == 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.Value;

                // Remove the leading and trailing quotes...
                hints = hints.Substring(1, hints.Length - 1).Trim();

                foreach (string hint in hints.Split(','))
                {
                    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("Invalid hint syntax: " + hint, true);
                        }

                        break;
                    }

                    string variableName = hint.Substring(0, idx).Trim();
                    string variableType = hint.Substring(idx + 1).Trim();
                    if (_mode == BUILDING_SYMBOL_TREE)
                    {
                        functionScope.AddHint(variableName, variableType);
                    }
                    else if (_mode == CHECKING_SYMBOL_TREE)
                    {
                        identifier = functionScope.GetIdentifier(variableName);
                        if (identifier != null)
                        {
                            if (variableType.Equals("nomunge", StringComparison.OrdinalIgnoreCase))
                            {
                                identifier.MarkedForMunging = false;
                            }
                            else
                            {
                                Warn("Unsupported hint value: " + hint, true);
                            }
                        }
                        else
                        {
                            Warn("Hint refers to an unknown identifier: " + hint, true);
                        }
                    }
                }
            }

            ParseScope(functionScope);
        }
        /*
         * If either 'eval' or 'with' is used in a local scope, we must make
         * sure that all containing local scopes don't get munged. Otherwise,
         * the obfuscation would potentially introduce bugs.
         */

        private void ProtectScopeFromObfuscation(ScriptOrFunctionScope scope)
        {
            if (scope == null)
            {
                throw new ArgumentNullException("scope");
            }

            if (scope == _globalScope)
            {
                // The global scope does not get obfuscated,
                // so we don't need to worry about it...
                return;
            }

            // Find the highest local scope containing the specified scope.
            while (scope.ParentScope != _globalScope)
            {
                scope = scope.ParentScope;
            }

            if (scope.ParentScope != _globalScope)
            {
                throw new InvalidOperationException();
            }

            scope.PreventMunging();
        }
        /*
         * 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 static JavaScriptIdentifier GetIdentifier(string symbol,
                                                          ScriptOrFunctionScope scope)
        {
            while (scope != null)
            {
                JavaScriptIdentifier identifier = scope.GetIdentifier(symbol);
                if (identifier != null)
                {
                    return identifier;
                }

                scope = scope.ParentScope;
            }

            return null;
        }
 private void EnterScope(ScriptOrFunctionScope scope)
 {
     _scopes.Push(scope);
 }
        private void ParseScope(ScriptOrFunctionScope scope)
        {
            string symbol;
            JavaScriptToken token;
            JavaScriptIdentifier identifier;


            int length = _tokens.Count;

            EnterScope(scope);

            while (_offset < length)
            {
                token = ConsumeToken();

                switch (token.TokenType)
                {
                    case Token.VAR:
                    case Token.CONST:
                        if (token.TokenType == Token.VAR)
                        {
                            if (_mode == BUILDING_SYMBOL_TREE &&
                                scope.VarCount++ > 1)
                            {
                                Warn("Try to use a single 'var' statement per scope.", true);
                            }
                        }

                        // The var keyword is followed by at least one symbol name.
                        // If several symbols follow, they are comma separated.
                        //for (; ;)
                        while (true)
                        {
                            token = ConsumeToken();
                            if (token.TokenType != Token.NAME)
                            {
                                throw new InvalidOperationException();
                            }

                            if (_mode == BUILDING_SYMBOL_TREE)
                            {
                                symbol = token.Value;
                                if (scope.GetIdentifier(symbol) == null)
                                {
                                    scope.DeclareIdentifier(symbol);
                                }
                                else
                                {
                                    Warn(
                                        "The variable " + symbol + " has already been declared in the same scope...",
                                        true);
                                }
                            }

                            token = GetToken(0);
                            if (token.TokenType != Token.SEMI &&
                                token.TokenType != Token.ASSIGN &&
                                token.TokenType != Token.COMMA &&
                                token.TokenType != Token.IN)
                            {
                                throw new InvalidOperationException();
                            }

                            if (token.TokenType == Token.IN)
                            {
                                break;
                            }

                            ParseExpression();
                            token = GetToken(-1);
                            if (token.TokenType == Token.SEMI)
                            {
                                break;
                            }
                        }

                        break;

                    case Token.FUNCTION:
                        ParseFunctionDeclaration();
                        break;

                    case Token.LC:
                        _braceNesting++;
                        break;

                    case Token.RC:
                        _braceNesting--;
                        if (_braceNesting < scope.BraceNesting)
                        {
                            throw new InvalidOperationException();
                        }

                        if (_braceNesting == scope.BraceNesting)
                        {
                            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(
                                "Using 'with' is not recommended." +
                                (_munge ? " Moreover, using 'with' reduces the level of compression!" : ""), true);
                        }
                        break;

                    case Token.CATCH:
                        ParseCatch();
                        break;

                    case Token.CONDCOMMENT:
                        if (_mode == BUILDING_SYMBOL_TREE)
                        {
                            ProtectScopeFromObfuscation(scope);
                            Warn(
                                "Using JScript conditional comments is not recommended." +
                                (_munge
                                     ? " Moreover, using JScript conditional comments reduces the level of compression."
                                     : ""), true);
                        }
                        break;

                    case Token.NAME:
                        symbol = token.Value;

                        if (_mode == BUILDING_SYMBOL_TREE)
                        {
                            if (!_isEvalIgnored &&
                                symbol.Equals("eval", StringComparison.OrdinalIgnoreCase))
                            {
                                ProtectScopeFromObfuscation(scope);
                                Warn(
                                    "Using 'eval' is not recommended." +
                                    (_munge ? " Moreover, using 'eval' reduces the level of compression!" : ""),
                                    true);
                            }
                        }
                        else if (_mode == CHECKING_SYMBOL_TREE)
                        {
                            if ((_offset < 2 ||
                                 GetToken(-2).TokenType != Token.DOT) &&
                                GetToken(0).TokenType != 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);
                                        Warn("Found an undeclared symbol: " + symbol, true);
                                    }
                                }
                                else
                                {
                                    identifier.RefCount++;
                                }
                            }
                        }

                        break;
                }
            }
        }
示例#11
0
        protected override string DoCompress(string source)
        {
            if (ErrorReporter == null)
            {
                ErrorReporter = new CustomErrorReporter(LoggingType);
            }

            var originalCultureInfo = Thread.CurrentThread.CurrentCulture;
            var originalUiCulture = Thread.CurrentThread.CurrentUICulture;

            try
            {
                // Change the current Thread Culture if the user has asked for something specific.
                // Reference: http://www.codeplex.com/YUICompressor/WorkItem/View.aspx?WorkItemId=3219
                Thread.CurrentThread.CurrentCulture = ThreadCulture;
                Thread.CurrentThread.CurrentUICulture = ThreadCulture;

                var memoryStream = new MemoryStream(Encoding.GetBytes(source));
                _tokens = Parse(new StreamReader(memoryStream, Encoding), ErrorReporter);

                ProcessStringLiterals(_tokens, !DisableOptimizations);

                if (!DisableOptimizations)
                {
                    OptimizeObjectMemberAccess(_tokens);
                    OptimizeObjLitMemberDecl(_tokens);
                }
                _globalScope = new ScriptOrFunctionScope(-1, null);
                BuildSymbolTree();
                MungeSymboltree();
                var result = PrintSymbolTree(LineBreakPosition, PreserveAllSemicolons);
                return result.ToString();
            }
            finally
            {
                Thread.CurrentThread.CurrentCulture = originalCultureInfo;
                Thread.CurrentThread.CurrentUICulture = originalUiCulture;
            }
        }
示例#12
0
        public void Munge()
        {
            if (!this._markedForMunging)
            {
                // Stop right here if this scope was flagged as unsafe for munging.
                return;
            }

            int pickFromSet = 1;

            // Do not munge symbols in the global scope!
            if (this.ParentScope != null)
            {
                ArrayList freeSymbols = new ArrayList();

                freeSymbols.AddRange(JavaScriptCompressor.Ones);
                foreach (string symbol in this.GetAllUsedSymbols())
                {
                    freeSymbols.Remove(symbol);
                }

                if (freeSymbols.Count == 0)
                {
                    pickFromSet = 2;
                    freeSymbols.AddRange(JavaScriptCompressor.Twos);
                    foreach (string symbol in this.GetAllUsedSymbols())
                    {
                        freeSymbols.Remove(symbol);
                    }
                }

                if (freeSymbols.Count == 0)
                {
                    pickFromSet = 3;
                    freeSymbols.AddRange(JavaScriptCompressor.Threes);
                    foreach (string symbol in this.GetAllUsedSymbols())
                    {
                        freeSymbols.Remove(symbol);
                    }
                }

                if (freeSymbols.Count == 0)
                {
                    throw new InvalidOperationException("The YUI Compressor ran out of symbols. Aborting...");
                }

                foreach (JavaScriptIdentifier identifier in this._identifiers.Values)
                {
                    if (freeSymbols.Count == 0)
                    {
                        pickFromSet++;
                        if (pickFromSet == 2)
                        {
                            freeSymbols.AddRange(JavaScriptCompressor.Twos);
                        }
                        else if (pickFromSet == 3)
                        {
                            freeSymbols.AddRange(JavaScriptCompressor.Threes);
                        }
                        else
                        {
                            throw new InvalidOperationException("The YUI Compressor ran out of symbols. Aborting...");
                        }
                        // It is essential to remove the symbols already used in
                        // the containing scopes, or some of the variables declared
                        // in the containing scopes will be redeclared, which can
                        // lead to errors.
                        foreach (string symbol in this.GetAllUsedSymbols())
                        {
                            freeSymbols.Remove(symbol);
                        }
                    }

                    string mungedValue;
                    if (identifier.MarkedForMunging)
                    {
                        mungedValue = (string)freeSymbols[0];
                        freeSymbols.RemoveAt(0);
                    }
                    else
                    {
                        mungedValue = identifier.Value;
                    }

                    identifier.MungedValue = mungedValue;
                }
            }

            for (int i = 0; i < this.SubScopes.Count; i++)
            {
                ScriptOrFunctionScope scope = (ScriptOrFunctionScope)this.SubScopes[i];
                scope.Munge();
            }
        }