/// <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(); }
/// <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); }
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); }
/// <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); }
/// <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); }
public bool AddNoAutoRename(string noRename) { if (!JSScanner.IsValidIdentifier(noRename)) { return(false); } m_noRenameSet.Add(noRename); return(true); }
/// <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); }
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(); } }
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; }