private string GenerateMappings(IList <string> fileList, IList <string> nameList) { var sb = StringBuilderPool.Acquire(); try { var currentLine = 1; foreach (var segment in m_segments) { if (currentLine < segment.DestinationLine) { // we've jumped forward at least one line in the minified file. // add a semicolon for each line we've advanced do { sb.Append(';'); }while (++currentLine < segment.DestinationLine); } else if (sb.Length > 0) { // same line; separate segments by comma. But only // if we've already output something sb.Append(','); } EncodeNumbers(sb, segment, fileList, nameList); } return(sb.ToString()); } finally { sb.Release(); } }
private void WriteBlockHeader(BlockScope blockScope, string blockType) { string scopeFlags = null; var sb = StringBuilderPool.Acquire(); try { if (!blockScope.IsKnownAtCompileTime) { sb.Append('['); sb.Append(AjaxMin.NotKnown); sb.Append(']'); } if (blockScope.UseStrict) { sb.Append(AjaxMin.ScopeIsStrictFlag); } scopeFlags = sb.ToString(); } finally { sb.Release(); } WriteProgress(); WriteProgress(AjaxMin.BlockScopeHeader.FormatInvariant( blockType, blockScope.Owner.Context.StartLineNumber, blockScope.Owner.Context.StartColumn + 1, scopeFlags)); }
private static void AddEscape(string unescapedRun, string escapedText, ref StringBuilder sb) { // if we haven't yet created the string builder, do it now if (sb == null) { sb = StringBuilderPool.Acquire(); } // add the run of unescaped text (if any), followed by the escaped text sb.Append(unescapedRun); sb.Append(escapedText); }
/// <summary> /// get the algorithmically-generated minified variable name based on the given number /// zero is the first name, 1 is the next, etc. This method needs to be tuned to /// get better gzip results. /// </summary> /// <param name="index">integer position of the name to retrieve</param> /// <returns>minified variable name</returns> public static string GenerateNameFromNumber(int index) { if ((s_smallNames != null) && (index >= 0) && (index < PrecalculateLength)) { return(s_smallNames[index]); } var sb = StringBuilderPool.Acquire(); try { // this REALLY needs some 'splainin. // first off, we want to use a different set of characters for the first digit // than we use for the second digit (they could be the same, but if we want the shortest // possible names, there are more characters available for the second digit than // the first. We could use the same strings if we wanted to limit the available characters // and therefore increase the length of the strings. // But let's think digits for a sec. normal base-10 would be: // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 ... 9 0 1 // 1 1 1 1 1 1 1 1 1 1 2 2 2 2 ... 9 0 0 // 1 1 // but we want to go: // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 ... ... ... 9 0 1 // 0 0 0 0 0 0 0 0 0 0 1 1 1 1 ... ... ... 9 0 0 // ... ... 0 0 // this is because for base-10, the leading zeros are blanks, but in our strings, // the leading blanks are NOT zeros. In essence what we do it START OVER FROM ZERO // but with explicit leading zeros every time we add another character to the length // of the string. // so after we peel off the last character, we divide by the number of possibilities to get // the next number in base-10. But WE want to divide by possibilities AND THEN SUBSTRACT ONE. // that not only gets us starting at 0, it also makes us push out the the number of iterations // we can go through before we need to increase the number of digits again. if (index >= 0) { sb.Append(s_varFirstLetters[index % s_varFirstLetters.Length]); index /= s_varFirstLetters.Length; // this is where we substract the one after our division to get the next character (if any) while (--index >= 0) { sb.Append(s_varPartLetters[index % s_varPartLetters.Length]); index /= s_varPartLetters.Length; } } return(sb.ToString()); } finally { sb.Release(); } }
private void OutputTimingPoints(JSParser parser, int groupIndex, int groupCount) { // frequency is ticks per second, so if we divide by 1000.0, then we will have a // double-precision value indicating the ticks per millisecond. Divide this into the // number of ticks we measure, and we'll get the milliseconds in double-precision. var frequency = Stopwatch.Frequency / 1000.0; // step names var stepNames = new[] { AjaxMin.StepParse, AjaxMin.StepResolve, AjaxMin.StepReorder, AjaxMin.StepAnalyzeNode, AjaxMin.StepAnalyzeScope, AjaxMin.StepAutoRename, AjaxMin.StepEvaluateLiterals, AjaxMin.StepFinalPass, AjaxMin.StepValidateNames }; // and output other steps to debug var stepCount = parser.TimingPoints.Count; var latestTimingPoint = 0L; var previousTimingPoint = 0L; var message = string.Empty; var sb = StringBuilderPool.Acquire(); try { for (var ndx = stepCount - 1; ndx >= 0; --ndx) { if (parser.TimingPoints[ndx] != 0) { // 1-based step index var stepIndex = stepCount - ndx; latestTimingPoint = parser.TimingPoints[ndx]; var deltaMS = (latestTimingPoint - previousTimingPoint) / frequency; previousTimingPoint = latestTimingPoint; sb.AppendFormat(AjaxMin.Culture, AjaxMin.TimerStepFormat, stepIndex, deltaMS, stepNames[stepIndex - 1]); sb.AppendLine(); } } message = sb.ToString(); } finally { sb.Release(); } var timerFormat = groupCount > 1 ? AjaxMin.TimerMultiFormat : AjaxMin.TimerFormat; var timerMessage = string.Format(CultureInfo.CurrentUICulture, timerFormat, groupIndex + 1, latestTimingPoint / frequency); Debug.WriteLine(timerMessage); Debug.Write(message); WriteProgress(timerMessage); WriteProgress(message); }
private void WriteModuleHeader(ModuleScope moduleScope) { var blockType = AjaxMin.BlockTypeModule.FormatInvariant(moduleScope.ScopeName.IfNullOrWhiteSpace(AjaxMin.ModuleNameImplicit)); string scopeFlags = null; var sb = StringBuilderPool.Acquire(); try { if (!moduleScope.IsKnownAtCompileTime) { sb.Append('['); sb.Append(AjaxMin.NotKnown); sb.Append(']'); } if (moduleScope.UseStrict) { sb.Append(AjaxMin.ScopeIsStrictFlag); } if (moduleScope.IsNotComplete) { sb.Append(AjaxMin.ModuleIncompleteFlag); } scopeFlags = sb.ToString(); } finally { sb.Release(); } WriteProgress(); WriteProgress(AjaxMin.BlockScopeHeader.FormatInvariant( blockType, moduleScope.Owner.Context.StartLineNumber, moduleScope.Owner.Context.StartColumn + 1, scopeFlags)); if (moduleScope.HasDefaultExport) { // when there's a default export, we want to flag a line under the module // header that indicates that it's okay to bind to the default export. WriteProgress(AjaxMin.ModuleHasDefaultExport); } }
public override string ToString() { var sb = StringBuilderPool.Acquire(); try { if (m_list.Count > 0) { // output the first one; then all subsequent, each prefaced with a comma sb.Append(m_list[0].ToString()); for (var ndx = 1; ndx < m_list.Count; ++ndx) { sb.Append(" , "); sb.Append(m_list[ndx].ToString()); } } return(sb.ToString()); } finally { sb.Release(); } }
/// <summary> /// Convert the exception to a VisualStudio format error message /// file(startline[-endline]?,startcol[-endcol]?):[ subcategory] category [errorcode]: message /// </summary> /// <returns></returns> public override string ToString() { var sb = StringBuilderPool.Acquire(); try { if (!string.IsNullOrEmpty(File)) { sb.Append(File); } // if there is a startline, then there must be a location. // no start line, then no location if (StartLine > 0) { // we will always at least start with the start line sb.AppendFormat("({0}", StartLine); if (EndLine > StartLine) { if (StartColumn > 0 && EndColumn > 0) { // all four values were specified sb.AppendFormat(",{0},{1},{2}", StartColumn, EndLine, EndColumn); } else { // one or both of the columns wasn't specified, so ignore them both sb.AppendFormat("-{0}", EndLine); } } else if (StartColumn > 0) { sb.AppendFormat(",{0}", StartColumn); if (EndColumn > StartColumn) { sb.AppendFormat("-{0}", EndColumn); } } sb.Append(')'); } // seaprate the location from the error description sb.Append(':'); // if there is a subcategory, add it prefaced with a space if (!string.IsNullOrEmpty(Subcategory)) { sb.Append(' '); sb.Append(Subcategory); } // not localizable sb.Append(IsError ? " error " : " warning "); // if there is an error code if (!string.IsNullOrEmpty(ErrorCode)) { sb.Append(ErrorCode); } // separate description from the message sb.Append(": "); if (!string.IsNullOrEmpty(Message)) { sb.Append(Message); } return(sb.ToString()); } finally { sb.Release(); } }
private 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(); } }
/// <summary> /// Crunched JS string passed to it, returning crunched string. /// The ErrorList property will be set with any errors found during the minification process. /// </summary> /// <param name="source">source Javascript</param> /// <param name="codeSettings">code minification settings</param> /// <returns>minified Javascript</returns> public string MinifyJavaScript(string source, CodeSettings codeSettings) { // default is an empty string var crunched = string.Empty; // reset the errors builder m_errorList = new List <ContextError>(); // create the parser and hook the engine error event var parser = new JSParser(); parser.CompilerError += OnJavaScriptError; var sb = StringBuilderPool.Acquire(); try { var preprocessOnly = codeSettings != null && codeSettings.PreprocessOnly; using (var stringWriter = new StringWriter(sb, CultureInfo.InvariantCulture)) { if (preprocessOnly) { parser.EchoWriter = stringWriter; } // parse the input var scriptBlock = parser.Parse(new DocumentContext(source) { FileContext = this.FileName }, codeSettings); if (scriptBlock != null && !preprocessOnly) { // we'll return the crunched code if (codeSettings != null && codeSettings.Format == JavaScriptFormat.JSON) { // we're going to use a different output visitor -- one // that specifically returns valid JSON. if (!JSONOutputVisitor.Apply(stringWriter, scriptBlock, codeSettings)) { m_errorList.Add(new ContextError() { Severity = 0, File = this.FileName, Message = CommonStrings.InvalidJSONOutput, }); } } else { // just use the normal output visitor OutputVisitor.Apply(stringWriter, scriptBlock, codeSettings); // if we are asking for a symbols map, give it a chance to output a little something // to the minified file. if (codeSettings != null && codeSettings.SymbolsMap != null) { codeSettings.SymbolsMap.EndFile(stringWriter, codeSettings.LineTerminator); } } } } crunched = sb.ToString(); } catch (Exception e) { m_errorList.Add(new ContextError() { Severity = 0, File = this.FileName, Message = e.Message, }); throw; } finally { sb.Release(); } return(crunched); }
//TYPE "NAME" - Starts at line LINE, col COLUMN STATUS [crunched to CRUNCH] // //TYPE: Function, Function getter, Function setter //STATUS: '', Unknown, Unreachable private void WriteFunctionHeader(FunctionObject funcObj, bool isKnown, bool useStrict) { // get the crunched value (if any) string crunched = string.Empty; var functionField = funcObj.Binding.IfNotNull(b => b.VariableField); if (functionField != null && functionField.CrunchedName != null) { crunched = AjaxMin.CrunchedTo.FormatInvariant(functionField.CrunchedName, functionField.RefCount); } // get the status if the function string status = null; var statusBuilder = StringBuilderPool.Acquire(); try { if (!isKnown) { statusBuilder.Append('['); statusBuilder.Append(AjaxMin.NotKnown); } if (funcObj.EnclosingScope.Parent is GlobalScope) { // global function. // if this is a named function expression, we still want to know if it's // referenced by anyone if (funcObj.FunctionType == FunctionType.Expression && funcObj.Binding != null && !funcObj.Binding.Name.IsNullOrWhiteSpace()) { // output a comma separator if not the first item, otherwise // open the square bracket if (statusBuilder.Length > 0) { statusBuilder.Append(", "); } else { statusBuilder.Append('['); } statusBuilder.Append(AjaxMin.FunctionInfoReferences.FormatInvariant( funcObj.Binding.VariableField.IfNotNull(v => v.RefCount) )); } } else if (!funcObj.IsReferenced && m_useReferenceCounts) { // local function that isn't referenced -- unreachable! // output a comma separator if not the first item, otherwise // open the square bracket if (statusBuilder.Length > 0) { statusBuilder.Append(", "); } else { statusBuilder.Append('['); } statusBuilder.Append(AjaxMin.Unreachable); } if (statusBuilder.Length > 0) { statusBuilder.Append(']'); } if (useStrict) { statusBuilder.Append(AjaxMin.ScopeIsStrictFlag); } status = statusBuilder.ToString(); } finally { statusBuilder.Release(); } string functionType; switch (funcObj.FunctionType) { case FunctionType.Getter: functionType = AjaxMin.FunctionTypePropGet; break; case FunctionType.Setter: functionType = AjaxMin.FunctionTypePropSet; break; case FunctionType.Expression: functionType = AjaxMin.FunctionTypeExpression; break; case FunctionType.ArrowFunction: functionType = AjaxMin.FunctionTypeArrow; break; case FunctionType.Method: functionType = AjaxMin.FunctionTypeMethod; break; default: functionType = AjaxMin.FunctionTypeFunction; break; } var functionName = funcObj.Binding.IfNotNull(b => b.Name); if (functionName.IsNullOrWhiteSpace()) { functionName = !funcObj.NameGuess.IsNullOrWhiteSpace() ? '"' + funcObj.NameGuess + '"' : AjaxMin.AnonymousName; } // output WriteProgress(); WriteProgress(AjaxMin.FunctionHeader.FormatInvariant( functionType, functionName, funcObj.Context.StartLineNumber, funcObj.Context.StartColumn + 1, status, crunched, funcObj.IsGenerator ? AjaxMin.FunctionTypeGenerator : string.Empty)); }