Ejemplo n.º 1
0
        /// <summary>
        /// Set the collection of defined names for the preprocessor
        /// </summary>
        /// <param name="definedNames">array of defined name strings</param>
        /// <returns>number of names successfully added to the collection</returns>
        public int SetPreprocessorDefines(params string[] definedNames)
        {
            PreprocessorValues.Clear();
            if (definedNames != null && definedNames.Length > 0)
            {
                // validate that each name in the array is a valid JS identifier
                foreach (var define in definedNames)
                {
                    string trimmedName;
                    var    ndxEquals = define.IndexOf('=');
                    if (ndxEquals < 0)
                    {
                        trimmedName = define.Trim();
                    }
                    else
                    {
                        trimmedName = define.Substring(0, ndxEquals).Trim();
                    }

                    // must be a valid JS identifier
                    if (JsScanner.IsValidIdentifier(trimmedName))
                    {
                        PreprocessorValues.Add(trimmedName, ndxEquals < 0 ? string.Empty : define.Substring(ndxEquals + 1));
                    }
                }
            }

            return(PreprocessorValues.Count);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Adds a debug lookup namespace to this settings object.
        /// </summary>
        /// <param name="debugNamespace">The debug namespace to add.</param>
        /// <returns>Whether it was added or not.</returns>
        public bool AddDebugLookup(string debugNamespace)
        {
            // a blank identifier is okay -- we just ignore it
            if (!string.IsNullOrEmpty(debugNamespace))
            {
                // but if it's not blank, it better be a valid JavaScript identifier or member chain
                if (debugNamespace.IndexOf('.') > 0)
                {
                    // it's a member chain -- check that each part is a valid JS identifier
                    var names = debugNamespace.Split('.');
                    foreach (var name in names)
                    {
                        if (!JsScanner.IsValidIdentifier(name))
                        {
                            return(false);
                        }
                    }
                }
                else
                {
                    // no dot -- just an identifier
                    if (!JsScanner.IsValidIdentifier(debugNamespace))
                    {
                        return(false);
                    }
                }

                m_debugLookups.Add(debugNamespace);
                return(true);
            }

            return(false);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Creates an instance of the JSParser class that can be used to parse the given source code.
        /// </summary>
        /// <param name="source">Source code to parse.</param>
        public JsParser(string source)
        {
            m_severity = 5;
            m_blockType = new List<BlockType>(16);
            m_labelTable = new Dictionary<string, LabelInfo>();
            m_noSkipTokenSet = new NoSkipTokenSet();
            m_importantComments = new List<JsContext>();

            m_document = new JsDocumentContext(this, source);
            m_scanner = new JsScanner(new JsContext(m_document));
            m_currentToken = new JsContext(m_document);

            // if the scanner encounters a special "globals" comment, it'll fire this event
            // at which point we will define a field with that name in the global scope.
            m_scanner.GlobalDefine += (sender, ea) =>
                {
                    var globalScope = GlobalScope;
                    if (globalScope[ea.Name] == null)
                    {
                        var field = globalScope.CreateField(ea.Name, null, FieldAttributes.SpecialName);
                        globalScope.AddField(field);
                    }
                };

            // this event is fired whenever a ///#SOURCE comment is encountered
            m_scanner.NewModule += (sender, ea) =>
                {
                    m_newModule = true;

                    // we also want to assume that we found a newline character after
                    // the comment
                    m_foundEndOfLine = true;
                };
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Add a known global identifier to the list
        /// </summary>
        /// <param name="identifier">global identifier</param>
        /// <returns>true if valid identifier; false if invalid identifier</returns>
        public bool AddKnownGlobal(string identifier)
        {
            if (JsScanner.IsValidIdentifier(identifier))
            {
                m_knownGlobals.Add(identifier);
                return(true);
            }

            return(false);
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Adds an identifier to the auto-rename exclusion list.
        /// </summary>
        /// <param name="noRename">The identifier to avoid renaming.</param>
        /// <returns>Whether it was added or not.</returns>
        public bool AddNoAutoRename(string noRename)
        {
            if (!JsScanner.IsValidIdentifier(noRename))
            {
                return(false);
            }

            m_noRenameSet.Add(noRename);
            return(true);
        }
Ejemplo n.º 6
0
        internal string NextName()
        {
            string name;

            do
            {
                // advance to the next name
                ++m_currentName;
                name = CurrentName;
                // keep advancing until we find one that isn't in the skip list or a keyword
                // (use strict mode to be safe)
            }while (m_skipNames.Contains(name) || JsScanner.IsKeyword(name, true));
            return(name);
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Set the dictionary of preprocessor defines and values
        /// </summary>
        /// <param name="defines">dictionary to set</param>
        public int SetPreprocessorValues(IDictionary <string, string> defines)
        {
            PreprocessorValues.Clear();
            if (defines != null && defines.Count > 0)
            {
                foreach (var define in defines)
                {
                    if (JsScanner.IsValidIdentifier(define.Key))
                    {
                        PreprocessorValues.Add(define.Key, define.Value);
                    }
                }
            }

            return(PreprocessorValues.Count);
        }
        private void UnreferencedFunction(JsVariableField variableField, JsFunctionObject functionObject)
        {
            // if there is no name, then ignore this declaration because it's malformed.
            // (won't be a function expression because those are automatically referenced).
            // also ignore ghosted function fields.
            if (functionObject.Name != null && variableField.FieldType != JsFieldType.GhostFunction)
            {
                // if the function name isn't a simple identifier, then leave it there and mark it as
                // not renamable because it's probably one of those darn IE-extension event handlers or something.
                if (JsScanner.IsValidIdentifier(functionObject.Name))
                {
                    // unreferenced function declaration. fire a warning.
                    var ctx = functionObject.IdContext ?? variableField.OriginalContext;
                    ctx.HandleError(JsError.FunctionNotReferenced, false);

                    // hide it from the output if our settings say we can.
                    // we don't want to delete it, per se, because we still want it to
                    // show up in the scope report so the user can see that it was unreachable
                    // in case they are wondering where it went.
                    // ES6 has the notion of block-scoped function declarations. ES5 says functions can't
                    // be defined inside blocks -- only at the root level of the global scope or function scopes.
                    // so if this is a block scope, don't hide the function, even if it is unreferenced because
                    // of the cross-browser difference.
                    if (this.IsKnownAtCompileTime &&
                        m_settings.MinifyCode &&
                        m_settings.RemoveUnneededCode &&
                        !(this is JsBlockScope))
                    {
                        functionObject.HideFromOutput = true;
                    }
                }
                else
                {
                    // not a valid identifier name for this function. Don't rename it because it's
                    // malformed and we don't want to mess up the developer's intent.
                    variableField.CanCrunch = false;
                }
            }
        }
Ejemplo n.º 9
0
        public bool Match(JsAstNode node, string identifiers)
        {
            // set the match to false
            m_isMatch = false;

            // identifiers cannot be null or blank and must match: IDENT(.IDENT)*
            // since for JS there has to be at least a global object, the dot must be AFTER the first character.
            if (node != null && !string.IsNullOrEmpty(identifiers))
            {
                // get all the parts
                var parts = identifiers.Split('.');

                // each part must be a valid JavaScript identifier. Assume everything is valid
                // unless at least one is invalid -- then forget it
                var isValid = true;
                foreach (var part in parts)
                {
                    if (!JsScanner.IsValidIdentifier(part))
                    {
                        isValid = false;
                        break;
                    }
                }

                // must be valid to continue
                if (isValid)
                {
                    // save the parts and start the index on the last one, since we'll be walking backwards
                    m_parts = parts;
                    m_index = parts.Length - 1;

                    node.Accept(this);
                }
            }

            return(m_isMatch);
        }
Ejemplo n.º 10
0
        /// <summary>
        /// Add a rename pair to the identifier rename map
        /// </summary>
        /// <param name="sourceName">name of the identifier in the source code</param>
        /// <param name="newName">new name with which to replace the source name</param>
        /// <returns>true if added; false if either name is not a valid JavaScript identifier</returns>
        public bool AddRenamePair(string sourceName, string newName)
        {
            bool successfullyAdded = false;

            // both names MUST be valid JavaScript identifiers
            if (JsScanner.IsValidIdentifier(sourceName) && JsScanner.IsValidIdentifier(newName))
            {
                if (m_identifierReplacementMap.ContainsKey(sourceName))
                {
                    // just replace the value
                    m_identifierReplacementMap[sourceName] = newName;
                }
                else
                {
                    // add the new pair
                    m_identifierReplacementMap.Add(sourceName, newName);
                }

                // if we get here, we added it (or updated it if it's a dupe)
                successfullyAdded = true;
            }

            return(successfullyAdded);
        }
        private static void ResolveLookup(JsActivationObject scope, JsLookup lookup, JsSettings settings)
        {
            // resolve lookup via the lexical scope
            lookup.VariableField = scope.FindReference(lookup.Name);
            if (lookup.VariableField.FieldType == JsFieldType.UndefinedGlobal)
            {
                // couldn't find it.
                // if the lookup isn't generated and isn't the object of a typeof operator,
                // then we want to throw an error.
                if (!lookup.IsGenerated)
                {
                    var parentUnaryOp = lookup.Parent as JsUnaryOperator;
                    if (parentUnaryOp != null && parentUnaryOp.OperatorToken == JsToken.TypeOf)
                    {
                        // this undefined lookup is the target of a typeof operator.
                        // I think it's safe to assume we're going to use it. Don't throw an error
                        // and instead add it to the "known" expected globals of the global scope
                        MakeExpectedGlobal(lookup.VariableField);
                    }
                    else
                    {
                        // report this undefined reference
                        lookup.Context.ReportUndefined(lookup);

                        // possibly undefined global (but definitely not local).
                        // see if this is a function or a variable.
                        var callNode   = lookup.Parent as JsCallNode;
                        var isFunction = callNode != null && callNode.Function == lookup;
                        lookup.Context.HandleError((isFunction ? JsError.UndeclaredFunction : JsError.UndeclaredVariable), false);
                    }
                }
            }
            else if (lookup.VariableField.FieldType == JsFieldType.Predefined)
            {
                if (string.CompareOrdinal(lookup.Name, "window") == 0)
                {
                    // it's the global window object
                    // see if it's the child of a member or call-brackets node
                    var member = lookup.Parent as JsMember;
                    if (member != null)
                    {
                        // we have window.XXXX. Add XXXX to the known globals if it
                        // isn't already a known item.
                        scope.AddGlobal(member.Name);
                    }
                    else
                    {
                        var callNode = lookup.Parent as JsCallNode;
                        if (callNode != null && callNode.InBrackets &&
                            callNode.Arguments.Count == 1 &&
                            callNode.Arguments[0] is JsConstantWrapper &&
                            callNode.Arguments[0].FindPrimitiveType() == JsPrimitiveType.String)
                        {
                            // we have window["XXXX"]. See if XXXX is a valid identifier.
                            // TODO: we could get rid of the ConstantWrapper restriction and use an Evaluate visitor
                            // to evaluate the argument, since we know for sure that it's a string.
                            var identifier = callNode.Arguments[0].ToString();
                            if (JsScanner.IsValidIdentifier(identifier))
                            {
                                // Add XXXX to the known globals if it isn't already a known item.
                                scope.AddGlobal(identifier);
                            }
                        }
                    }
                }
                else if (settings.EvalTreatment != JsEvalTreatment.Ignore &&
                         string.CompareOrdinal(lookup.Name, "eval") == 0)
                {
                    // it's an eval -- but are we calling it?
                    // TODO: what if we are assigning it to a variable? Should we track that variable and see if we call it?
                    // What about passing it as a parameter to a function? track that as well in case the function calls it?
                    var parentCall = lookup.Parent as JsCallNode;
                    if (parentCall != null && parentCall.Function == lookup)
                    {
                        scope.IsKnownAtCompileTime = false;
                    }
                }
            }

            // add the reference
            lookup.VariableField.AddReference(lookup);

            // we are actually referencing this field, so it's no longer a placeholder field if it
            // happens to have been one.
            lookup.VariableField.IsPlaceholder = false;
        }