private bool m_useStrict; //= false; #endregion Fields #region Constructors protected JsActivationObject(JsActivationObject parent, JsSettings codeSettings) { m_isKnownAtCompileTime = true; m_useStrict = false; m_settings = codeSettings; Parent = parent; NameTable = new Dictionary<string, JsVariableField>(); ChildScopes = new List<JsActivationObject>(); // if our parent is a scope.... if (parent != null) { // add us to the parent's list of child scopes parent.ChildScopes.Add(this); // if the parent is strict, so are we UseStrict = parent.UseStrict; } // create the two lists of declared items for this scope ScopeLookups = new HashSet<JsLookup>(); VarDeclaredNames = new HashSet<IJsNameDeclaration>(); LexicallyDeclaredNames = new HashSet<IJsNameDeclaration>(); GhostedCatchParameters = new HashSet<JsParameterDeclaration>(); GhostedFunctions = new HashSet<JsFunctionObject>(); }
public MinifierParser() { // initialize with default values JSSettings = new JsSettings(); CssSettings = new CssSettings(); // see if this is running under the Mono runtime (on UNIX) m_isMono = Type.GetType("Mono.Runtime") != null; }
private JsContext m_context; // = null; #endregion Fields #region Constructors public JsBlockScope(JsActivationObject parent, JsContext context, JsSettings settings) : base(parent, settings) { if (context == null) { throw new ArgumentNullException("context"); } m_context = context.Clone(); }
private JsResolutionVisitor(JsActivationObject rootScope, JsSettings settings) { // create the lexical and variable scope stacks and push the root scope onto them m_lexicalStack = new Stack<JsActivationObject>(); m_lexicalStack.Push(rootScope); m_variableStack = new Stack<JsActivationObject>(); m_variableStack.Push(rootScope); m_settings = settings; }
public static void Apply(TextWriter writer, JsAstNode node, JsSettings settings) { if (node != null) { var outputVisitor = new JsOutputVisitor(writer, settings); node.Accept(outputVisitor); // if there is a symbol map that we are tracking, tell it that we have ended an output run // and pass it offsets to the last line and column positions. settings.IfNotNull(s => s.SymbolsMap.IfNotNull(m => m.EndOutputRun(outputVisitor.m_lineCount, outputVisitor.m_lineLength))); } }
internal JsFunctionScope(JsActivationObject parent, bool isExpression, JsSettings settings, JsFunctionObject funcObj) : base(parent, settings) { m_refScopes = new HashSet<JsActivationObject>(); if (isExpression) { // parent scopes automatically reference enclosed function expressions AddReference(Parent); } FunctionObject = funcObj; }
public ProjectView(UnrealProject uproject, JsSettings s) { SelectedUproject = uproject; settings = s; InitializeComponent(); gr.DataContext = this; if (SelectedUproject.JsUnrealProject.Plugins != null) { Plugins.ItemsSource = SelectedUproject.JsUnrealProject.Plugins; } Modules.ItemsSource = SelectedUproject.JsUnrealProject.Modules; }
internal JsGlobalScope(JsSettings settings) : base(null, settings) { // define the Global object's properties, and methods m_globalProperties = new HashSet<string>(new[] { "Infinity", "NaN", "undefined", "window", "Image", "JSON", "Map", "Math", "Set", "WeakMap", "XMLHttpRequest", "DOMParser", "applicationCache", "clientInformation", "clipboardData", "closed", "console", "document", "event", "external", "frameElement", "frames", "history", "length", "localStorage", "location", "name", "navigator", "opener", "parent", "screen", "self", "sessionStorage", "status", "top"}); m_globalFunctions = new HashSet<string>(new[] { "decodeURI", "decodeURIComponent", "encodeURI", "encodeURIComponent", "escape", "eval", "importScripts", "isNaN", "isFinite", "parseFloat", "parseInt", "unescape", "ActiveXObject", "Array", "Boolean", "Date", "Error", "EvalError", "EventSource", "File", "FileList", "FileReader", "Function", "GeckoActiveXObject", "HTMLElement", "Number", "Object", "Proxy", "RangeError", "ReferenceError", "RegExp", "SharedWorker", "String", "SyntaxError", "TypeError", "URIError", "WebSocket", "Worker", "addEventListener", "alert", "attachEvent", "blur", "clearInterval", "clearTimeout", "close", "confirm", "createPopup", "detachEvent", "dispatchEvent", "execScript", "focus", "getComputedStyle", "getSelection", "moveBy", "moveTo", "navigate", "open", "postMessage", "prompt", "removeEventListener", "resizeBy", "resizeTo", "scroll", "scrollBy", "scrollTo", "setActive", "setInterval", "setTimeout", "showModalDialog", "showModelessDialog" }); }
private void LoadSettings() { if (File.Exists(appdatapath)) { settings = JsonConvert.DeserializeObject <JsSettings>(File.ReadAllText(appdatapath)); return; } settings = new JsSettings { AutomaticallyRecompile = false, DeleteDerivedFiles = true, IncludeSavedFolder = false }; if (!Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "/UnrealHelpers/ModuleManager")) { Directory.CreateDirectory(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "/UnrealHelpers/ModuleManager"); } File.WriteAllText(appdatapath, JsonConvert.SerializeObject(settings)); }
public MinifierParser(JsSettings scriptSettings, CssSettings cssSettings) { // apply the switches to these two settings objects JSSettings = scriptSettings ?? new JsSettings(); CssSettings = cssSettings ?? new CssSettings(); }
internal void SetAssumedGlobals(JsSettings settings) { if (settings != null) { // start off with any known globals m_assumedGlobals = settings.KnownGlobalCollection == null ? new HashSet<string>() : new HashSet<string>(settings.KnownGlobalCollection); // chek to see if there are any debug lookups foreach (var debugLookup in settings.DebugLookupCollection) { m_assumedGlobals.Add(debugLookup.SubstringUpToFirst('.')); } // and the root name of any resource strings is also an assumed global foreach (var resourceStrings in settings.ResourceStrings) { if (!resourceStrings.Name.IsNullOrWhiteSpace()) { m_assumedGlobals.Add(resourceStrings.Name.SubstringUpToFirst('.')); } } } else { // empty set m_assumedGlobals = new HashSet<string>(); } }
/// <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, JsSettings codeSettings) { // default is an empty string var crunched = string.Empty; // reset the errors builder m_errorList = new List<MinifierError>(); // create the parser from the source string. // pass null for the assumed globals array var parser = new JsParser(source); // file context is a property on the parser parser.FileContext = FileName; // hook the engine error event parser.CompilerError += OnJavaScriptError; try { var preprocessOnly = codeSettings != null && codeSettings.PreprocessOnly; var sb = new StringBuilder(); using (var stringWriter = new StringWriter(sb, CultureInfo.InvariantCulture)) { if (preprocessOnly) { parser.EchoWriter = stringWriter; } // parse the input var scriptBlock = parser.Parse(codeSettings); if (scriptBlock != null && !preprocessOnly) { // we'll return the crunched code if (codeSettings != null && codeSettings.Format == JsFormat.JSON) { // we're going to use a different output visitor -- one // that specifically returns valid JSON. if (!JsonOutputVisitor.Apply(stringWriter, scriptBlock)) { m_errorList.Add(new MinifierError( true, 0, null, null, null, this.FileName, 0, 0, 0, 0, JScript.InvalidJSONOutput)); } } else { // just use the normal output visitor JsOutputVisitor.Apply(stringWriter, scriptBlock, codeSettings); } } } crunched = sb.ToString(); } catch (Exception e) { m_errorList.Add(new MinifierError( true, 0, null, null, null, this.FileName, 0, 0, 0, 0, e.Message)); throw; } return crunched; }
/// <summary> /// Minifies the CSS stylesheet passes to it using the given settings, returning the minified results /// The ErrorList property will be set with any errors found during the minification process. /// </summary> /// <param name="source">CSS Source</param> /// <param name="settings">CSS minification settings</param> /// <param name="scriptSettings">JS minification settings to use for expression-minification</param> /// <returns>Minified StyleSheet</returns> public string MinifyStyleSheet(string source, CssSettings settings, JsSettings scriptSettings) { // initialize some values, including the error list (which shoudl start off empty) string minifiedResults = string.Empty; m_errorList = new List<MinifierError>(); // create the parser object and if we specified some settings, // use it to set the Parser's settings object CssParser parser = new CssParser(); parser.FileContext = FileName; if (settings != null) { parser.Settings = settings; } if (scriptSettings != null) { parser.JSSettings = scriptSettings; } // hook the error handler parser.CssError += new EventHandler<CssErrorEventArgs>(OnCssError); // try parsing the source and return the results try { minifiedResults = parser.Parse(source); } catch (Exception e) { m_errorList.Add(new MinifierError( true, 0, null, null, null, this.FileName, 0, 0, 0, 0, e.Message)); throw; } return minifiedResults; }
private static void ResolveLookups(JsActivationObject scope, JsSettings settings) { // resolve each lookup this scope contains foreach (var lookup in scope.ScopeLookups) { ResolveLookup(scope, lookup, settings); } // and recurse foreach (var childScope in scope.ChildScopes) { ResolveLookups(childScope, settings); } // mark any variables defined in this scope that don't have any references // so we can throw warnings later. We can't rely on the reference count because // we might remove references while optimizing code -- if we throw an error when // the count gets to zero, then we would be reporting errors that don't exist. // but we DO know right now what isn't referenced at all. foreach (var field in scope.NameTable.Values) { if (field.RefCount == 0) { field.HasNoReferences = true; } } }
private JsOutputVisitor(TextWriter writer, JsSettings settings) { m_outputStream = writer; m_settings = settings ?? new JsSettings(); m_onNewLine = true; }
/// <summary> /// Parse the source code using the given settings, getting back an abstract syntax tree Block node as the root /// representing the list of statements in the source code. /// </summary> /// <param name="settings">code settings to use to process the source code</param> /// <returns>root Block node representing the top-level statements</returns> public JsBlock Parse(JsSettings settings) { // initialize the scanner with our settings // make sure the RawTokens setting is OFF or we won't be able to create our AST InitializeScanner(settings); // make sure we initialize the global scope's strict mode to our flag, whether or not it // is true. This means if the setting is false, we will RESET the flag to false if we are // reusing the scope and a previous Parse call had code that set it to strict with a // program directive. GlobalScope.UseStrict = m_settings.StrictMode; // make sure the global scope knows about our known global names GlobalScope.SetAssumedGlobals(m_settings); // start of a new module m_newModule = true; var timePoints = m_timingPoints = new long[9]; var timeIndex = timePoints.Length; var stopWatch = new Stopwatch(); stopWatch.Start(); JsBlock scriptBlock = null; JsBlock returnBlock = null; try { switch (m_settings.SourceMode) { case JsSourceMode.Program: // simply parse a block of statements returnBlock = scriptBlock = ParseStatements(); break; case JsSourceMode.Expression: // create a block, get the first token, add in the parse of a single expression, // and we'll go fron there. returnBlock = scriptBlock = new JsBlock(CurrentPositionContext(), this); GetNextToken(); try { var expr = ParseExpression(); if (expr != null) { scriptBlock.Append(expr); scriptBlock.UpdateWith(expr.Context); } } catch (EndOfStreamException) { Debug.WriteLine("EOF"); } break; case JsSourceMode.EventHandler: // we're going to create the global block, add in a function expression with a single // parameter named "event", and then we're going to parse the input as the body of that // function expression. We're going to resolve the global block, but only return the body // of the function. scriptBlock = new JsBlock(null, this); var parameters = new JsAstNodeList(null, this); parameters.Append(new JsParameterDeclaration(null, this) { Name = "event", RenameNotAllowed = true }); var funcExpression = new JsFunctionObject(null, this) { FunctionType = JsFunctionType.Expression, ParameterDeclarations = parameters }; scriptBlock.Append(funcExpression); funcExpression.Body = returnBlock = ParseStatements(); break; default: Debug.Fail("Unexpected source mode enumeration"); return null; } } catch (RecoveryTokenException) { // this should never happen but let's make SURE we don't expose our // private exception object to the outside world m_currentToken.HandleError(JsError.ApplicationError, true); } timePoints[--timeIndex] = stopWatch.ElapsedTicks; if (scriptBlock != null) { // resolve everything JsResolutionVisitor.Apply(scriptBlock, GlobalScope, m_settings); } timePoints[--timeIndex] = stopWatch.ElapsedTicks; if (scriptBlock != null && Settings.MinifyCode && !Settings.PreprocessOnly) { // this visitor doesn't just reorder scopes. It also combines the adjacent var variables, // unnests blocks, identifies prologue directives, and sets the strict mode on scopes. JsReorderScopeVisitor.Apply(scriptBlock, this); timePoints[--timeIndex] = stopWatch.ElapsedTicks; // analyze the entire node tree (needed for hypercrunch) // root to leaf (top down) var analyzeVisitor = new JsAnalyzeNodeVisitor(this); scriptBlock.Accept(analyzeVisitor); timePoints[--timeIndex] = stopWatch.ElapsedTicks; // analyze the scope chain (also needed for hypercrunch) // root to leaf (top down) GlobalScope.AnalyzeScope(); timePoints[--timeIndex] = stopWatch.ElapsedTicks; // if we want to crunch any names.... if (m_settings.LocalRenaming != JsLocalRenaming.KeepAll && m_settings.IsModificationAllowed(JsTreeModifications.LocalRenaming)) { // then do a top-down traversal of the scope tree. For each field that had not // already been crunched (globals and outers will already be crunched), crunch // the name with a crunch iterator that does not use any names in the verboten set. GlobalScope.AutoRenameFields(); } timePoints[--timeIndex] = stopWatch.ElapsedTicks; // if we want to evaluate literal expressions, do so now if (m_settings.EvalLiteralExpressions) { var visitor = new JsEvaluateLiteralVisitor(this); scriptBlock.Accept(visitor); } timePoints[--timeIndex] = stopWatch.ElapsedTicks; // make the final cleanup pass JsFinalPassVisitor.Apply(scriptBlock, this); timePoints[--timeIndex] = stopWatch.ElapsedTicks; // we want to walk all the scopes to make sure that any generated // variables that haven't been crunched have been assigned valid // variable names that don't collide with any existing variables. GlobalScope.ValidateGeneratedNames(); timePoints[--timeIndex] = stopWatch.ElapsedTicks; } if (returnBlock != null && returnBlock.Parent != null) { returnBlock.Parent = null; } return returnBlock; }
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; }
public JsWithScope(JsActivationObject parent, JsContext context, JsSettings settings) : base(parent, context, settings) { IsInWithScope = true; }
private void InitializeScanner(JsSettings settings) { // save the settings // if we are passed null, just create a default settings object m_settings = settings = settings ?? new JsSettings(); // if the settings list is not null, use it to initialize a new list // with the same settings. If it is null, initialize an empty list // because we already determined that we want to strip debug statements, // and the scanner might add items to the list as it scans the source. DebugLookups = new HashSet<string>(m_settings.DebugLookupCollection); // pass our list to the scanner -- it might add more as we encounter special comments m_scanner.DebugLookupCollection = DebugLookups; m_scanner.AllowEmbeddedAspNetBlocks = m_settings.AllowEmbeddedAspNetBlocks; m_scanner.IgnoreConditionalCompilation = m_settings.IgnoreConditionalCompilation; // set any defines m_scanner.UsePreprocessorDefines = !m_settings.IgnorePreprocessorDefines; if (m_scanner.UsePreprocessorDefines) { m_scanner.SetPreprocessorDefines(m_settings.PreprocessorValues); } // if we want to strip debug statements, let's also strip ///#DEBUG comment // blocks for legacy reasons. ///#DEBUG will get stripped ONLY is this // flag is true AND the name "DEBUG" is not in the preprocessor defines. // Alternately, we will keep those blocks in the output is this flag is // set to false OR we define "DEBUG" in the preprocessor defines. m_scanner.StripDebugCommentBlocks = m_settings.StripDebugStatements; }
public void PreprocessOnly(JsSettings settings, TextWriter outputStream) { if (outputStream != null) { EchoWriter = outputStream; Parse(settings); EchoWriter = null; if (m_settings.TermSemicolons) { // if we want to make sure this file has a terminating semicolon, start a new line // (to make sure any single-line comments are terminated) and output a semicolon // followed by another line break. outputStream.WriteLine(); outputStream.WriteLine(';'); } } }
public string PreprocessOnly(JsSettings settings) { // create an empty string builder using (var outputStream = new StringWriter(CultureInfo.InvariantCulture)) { // output to the string builder PreprocessOnly(settings, outputStream); // return the resulting text return outputStream.ToString(); } }
public JsBlock ParseExpression(JsSettings settings) { // we need to make sure the settings object has the expression source mode property set, // but let's not modify the settings object passed in. So clone it, set the property on the // clone, and use that object for parsing. settings = settings == null ? new JsSettings() : settings.Clone(); settings.SourceMode = JsSourceMode.Expression; return Parse(settings); }
internal JsCatchScope(JsActivationObject parent, JsContext catchContext, JsSettings settings, JsParameterDeclaration catchParameter) : base(parent, catchContext, settings) { CatchParameter = catchParameter; }
public static void Apply(JsAstNode node, JsActivationObject scope, JsSettings settings) { if (node != null && scope != null) { // create the visitor and run it. This will create all the child // scopes and populate all the scopes with the var-decl, lex-decl, // and lookup references within them. var visitor = new JsResolutionVisitor(scope, settings); node.Accept(visitor); // now that all the scopes are created and they all know what decls // they contains, create all the fields CreateFields(scope); // now that all the fields have been created in all the scopes, // let's go through and resolve all the references ResolveLookups(scope, settings); // now that everything is declared and resolved as per the language specs, // we need to go back and add ghosted fields for older versions of IE that // incorrectly implement catch-variables and named function expressions. AddGhostedFields(scope); } }