Exemple #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);
        }
        private static void ReadRenameElement(XmlReader reader, IDictionary <string, string> renameIdentifiers)
        {
            string fromIdentifier = null;
            string toIdentifier   = null;

            reader.Read();
            while (reader.MoveToNextAttribute())
            {
                switch (reader.Name)
                {
                case FromAttributeName:
                    fromIdentifier = reader.Value;
                    break;

                case ToAttributeName:
                    toIdentifier = reader.Value;
                    break;
                }
            }

            // must exist and be unique (ignore duplicates)
            if (!fromIdentifier.IsNullOrWhiteSpace() && !toIdentifier.IsNullOrWhiteSpace() &&
                JSScanner.IsValidIdentifier(fromIdentifier) &&
                JSScanner.IsValidIdentifier(toIdentifier))
            {
                renameIdentifiers[fromIdentifier] = toIdentifier;
            }

            reader.Close();
        }
        private static void ReadNoRenameElement(XmlReader reader, ICollection <string> noRenameIdentifiers)
        {
            string identifier = null;

            reader.Read();
            while (reader.MoveToNextAttribute())
            {
                switch (reader.Name)
                {
                case IdentifierAttributeName:
                    identifier = reader.Value;
                    break;

                case NameAttributeName:
                    identifier = reader.Value;
                    break;
                }
            }

            // must exist and be unique (ignore duplicates)
            if (!identifier.IsNullOrWhiteSpace() &&
                JSScanner.IsValidIdentifier(identifier))
            {
                noRenameIdentifiers.Add(identifier);
            }

            reader.Close();
        }
Exemple #4
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 there isn't a rename map, create it now
                if (m_identifierReplacementMap == null)
                {
                    m_identifierReplacementMap = new Dictionary <string, string>();
                }

                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);
        }
Exemple #5
0
        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);
        }
Exemple #6
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)
        {
            int numAdded = 0;

            if (definedNames == null)
            {
                PreprocessorDefines = null;
            }
            else
            {
                // create a list with a capacity equal to the number of items in the array
                var checkedNames = new List <string>(definedNames.Length);

                // validate that each name in the array is a valid JS identifier
                foreach (var name in definedNames)
                {
                    // must be a valid JS identifier
                    string trimmedName = name.Trim();
                    if (JSScanner.IsValidIdentifier(trimmedName))
                    {
                        checkedNames.Add(trimmedName);
                    }
                }
                PreprocessorDefines = new ReadOnlyCollection <string>(checkedNames);
                numAdded            = checkedNames.Count;
            }

            return(numAdded);
        }
Exemple #7
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);
        }
Exemple #8
0
        public bool AddNoAutoRename(string noRename)
        {
            if (!JSScanner.IsValidIdentifier(noRename))
            {
                return(false);
            }

            m_noRenameSet.Add(noRename);
            return(true);
        }
Exemple #9
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, FunctionObject 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 != FieldType.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 BlockScope))
                    {
                        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;
                }
            }
        }
        public bool Match(AstNode 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);
        }
Exemple #12
0
        static string CreateJSFromResourceStrings(ResourceStrings resourceStrings)
        {
            var sb = StringBuilderPool.Acquire();

            try
            {
                // start the var statement using the requested name and open the initializer object literal
                sb.Append("var ");
                sb.Append(resourceStrings.Name);
                sb.Append("={");

                // we're going to need to insert commas between each pair, so we'll use a boolean
                // flag to indicate that we're on the first pair. When we output the first pair, we'll
                // set the flag to false. When the flag is false, we're about to insert another pair, so
                // we'll add the comma just before.
                bool firstItem = true;

                // loop through all items in the collection
                foreach (var keyPair in resourceStrings.NameValuePairs)
                {
                    // if this isn't the first item, we need to add a comma separator
                    if (!firstItem)
                    {
                        sb.Append(',');
                    }
                    else
                    {
                        // next loop is no longer the first item
                        firstItem = false;
                    }

                    // append the key as the name, a colon to separate the name and value,
                    // and then the value
                    // must quote if not valid JS identifier format, or if it is, but it's a keyword
                    // (use strict mode just to be safe)
                    string propertyName = keyPair.Key;
                    if (!JSScanner.IsValidIdentifier(propertyName) || JSScanner.IsKeyword(propertyName, true))
                    {
                        sb.Append("\"");
                        // because we are using quotes for the delimiters, replace any instances
                        // of a quote character (") with an escaped quote character (\")
                        sb.Append(propertyName.Replace("\"", "\\\""));
                        sb.Append("\"");
                    }
                    else
                    {
                        sb.Append(propertyName);
                    }
                    sb.Append(':');

                    // make sure the Value is properly escaped, quoted, and whatever we
                    // need to do to make sure it's a proper JS string.
                    // pass false for whether this string is an argument to a RegExp constructor.
                    // pass false for whether to use W3Strict formatting for character escapes (use maximum browser compatibility)
                    // pass true for ecma strict mode
                    string stringValue = ConstantWrapper.EscapeString(
                        keyPair.Value,
                        false,
                        false,
                        true
                        );
                    sb.Append(stringValue);
                }

                // close the object literal and return the string
                sb.AppendLine("};");
                return(sb.ToString());
            }
            finally
            {
                sb.Release();
            }
        }
Exemple #13
0
        internal override Dictionary <string, IAnalysisSet> GetOwnProperties(ProjectEntry accessor)
        {
            if (_descriptors == null || _descriptors.Count == 0)
            {
                return(new Dictionary <string, IAnalysisSet>());
            }

            var res = new Dictionary <string, IAnalysisSet>();

            foreach (var kvp in _descriptors)
            {
                var key = kvp.Key;
                if (!JSScanner.IsValidIdentifier(key))
                {
                    // https://nodejstools.codeplex.com/workitem/987
                    // for intellisense purposes we don't report invalid identifiers, but we can
                    // end up with these from indexing with constant strings.
                    continue;
                }

                if (kvp.Value.Values != null)
                {
                    //kvp.Value.Values.ClearOldValues();
                    if (kvp.Value.Values.VariableStillExists)
                    {
                        var types = kvp.Value.Values.GetTypesNoCopy(accessor, ProjectEntry);
                        if (types.Count != 0 ||
                            kvp.Value.Values.TypesNoCopy.Count == 0)
                        {
                            MergeTypes(res, key, types);
                        }
                    }
                }

                if (kvp.Value.Getter != null)
                {
                    foreach (var value in kvp.Value.Getter.GetTypesNoCopy(accessor, ProjectEntry))
                    {
                        FunctionValue userFunc = value.Value as FunctionValue;
                        if (userFunc != null)
                        {
                            MergeTypes(res, key, userFunc.ReturnTypes);
                        }
                    }
                }

                if (kvp.Value.Setter != null)
                {
                    MergeTypes(res, key, AnalysisSet.Empty);
                }
            }

            if (_linkedValues != null)
            {
                foreach (var linkedValue in _linkedValues.GetTypesNoCopy(accessor, ProjectEntry))
                {
                    if (linkedValue.Value.Push())
                    {
                        try {
                            MergeDictionaries(res, linkedValue.Value.GetAllMembers(accessor));
                        } finally {
                            linkedValue.Value.Pop();
                        }
                    }
                }
            }

            return(res);
        }
        private static void ResolveLookup(ActivationObject scope, Lookup lookup, CodeSettings settings)
        {
            // resolve lookup via the lexical scope
            lookup.VariableField = scope.FindReference(lookup.Name);
            if (lookup.VariableField.FieldType == FieldType.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 UnaryOperator;
                    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 CallNode;
                        var isFunction = callNode != null && callNode.Function == lookup;
                        lookup.Context.HandleError((isFunction ? JSError.UndeclaredFunction : JSError.UndeclaredVariable), false);
                    }
                }
            }
            else if (lookup.VariableField.FieldType == FieldType.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 Member;
                    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 CallNode;
                        if (callNode != null && callNode.InBrackets &&
                            callNode.Arguments.Count == 1 &&
                            callNode.Arguments[0] is ConstantWrapper &&
                            callNode.Arguments[0].FindPrimitiveType() == PrimitiveType.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 != EvalTreatment.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 CallNode;
                    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;
        }