in AjaxMinDll/JavaScript/jsparser.cs [270:509]
private Block InternalParse()
{
// 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;
// assume ES5 unless we find ES6-specific constructs
ParsedVersion = ScriptVersion.EcmaScript5;
// 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();
// get the first token.
GetNextToken();
Block scriptBlock = null;
Block returnBlock = null;
switch (m_settings.SourceMode)
{
case JavaScriptSourceMode.Program:
// simply parse a block of statements.
// however, when parsing this block, we mght determine it's really part of
// a larger structure, and we could return a different block that we would need
// to continue processing.
scriptBlock = returnBlock = ParseStatements(new Block(CurrentPositionContext)
{
EnclosingScope = this.GlobalScope
});
break;
case JavaScriptSourceMode.Module:
// an implicit module as referenced by an import statement.
// create a root block with the global scope, add a module with its module body,
// then parse the input as statements into the module body.
returnBlock = scriptBlock = new Block(CurrentPositionContext)
{
EnclosingScope = this.GlobalScope
};
var module = new ModuleDeclaration(CurrentPositionContext)
{
IsImplicit = true,
Body = new Block(CurrentPositionContext)
{
IsModule = true
}
};
scriptBlock.Append(module);
// we just created an implicit ES6 module, so we are already parsing as ES6
ParsedVersion = ScriptVersion.EcmaScript6;
// we don't need to worry about this function returning a different block, because
// we've already created the module structure.
ParseStatements(module.Body);
break;
case JavaScriptSourceMode.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 Block(CurrentPositionContext)
{
EnclosingScope = this.GlobalScope
};
try
{
var expr = ParseExpression();
if (expr != null)
{
scriptBlock.Append(expr);
scriptBlock.UpdateWith(expr.Context);
}
}
catch (EndOfStreamException)
{
Debug.WriteLine("EOF");
}
break;
case JavaScriptSourceMode.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 Block(CurrentPositionContext)
{
EnclosingScope = this.GlobalScope
};
var parameters = new AstNodeList(CurrentPositionContext);
parameters.Append(new ParameterDeclaration(CurrentPositionContext)
{
Binding = new BindingIdentifier(CurrentPositionContext)
{
Name = "event",
RenameNotAllowed = true
}
});
var funcExpression = new FunctionObject(CurrentPositionContext)
{
FunctionType = FunctionType.Expression,
ParameterDeclarations = parameters,
Body = new Block(CurrentPositionContext)
};
scriptBlock.Append(funcExpression);
ParseFunctionBody(funcExpression.Body);
// but we ONLY want to return the body
returnBlock = funcExpression.Body;
break;
default:
Debug.Fail("Unexpected source mode enumeration");
return null;
}
timePoints[--timeIndex] = stopWatch.ElapsedTicks;
// resolve everything
ResolutionVisitor.Apply(scriptBlock, GlobalScope, this);
timePoints[--timeIndex] = stopWatch.ElapsedTicks;
if (Settings.AmdSupport)
{
// we're doing some AMD support. At this time, walk through the top-level
// statements and if there are any duplicate define(name... calls, remove all
// but the last one.
RemoveDuplicateDefines(scriptBlock);
}
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.
ReorderScopeVisitor.Apply(scriptBlock, this);
timePoints[--timeIndex] = stopWatch.ElapsedTicks;
// analyze the entire node tree (needed for hypercrunch)
// root to leaf (top down)
var analyzeVisitor = new AnalyzeNodeVisitor(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 != LocalRenaming.KeepAll
&& m_settings.IsModificationAllowed(TreeModifications.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 EvaluateLiteralVisitor(this);
scriptBlock.Accept(visitor);
}
timePoints[--timeIndex] = stopWatch.ElapsedTicks;
// make the final cleanup pass
FinalPassVisitor.Apply(scriptBlock, m_settings);
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;
stopWatch.Stop();
}
// mark all child scopes under the global scope as existing so we don't go and re-optimize
// them again should we parse another piece of source code using the same parser
foreach (var childScope in GlobalScope.ChildScopes)
{
childScope.Existing = true;
}
// if the return block is not the entire script block, then we don't want to include
// any thing we must've generated in the process of building our code. Take the scope
// from the parent, but break the tree relationship.
if (returnBlock != scriptBlock)
{
returnBlock.EnclosingScope = returnBlock.Parent.EnclosingScope;
returnBlock.Parent = null;
}
return returnBlock;
}