internal void SetOuterLocalField(ActivationObject parentScope) { // if we're trying to set the outer local field using a global scope, // then ignore this request. This should only do something for scopes with // local variables if (!(parentScope is GlobalScope)) { // get the field reference for this lookup value JSVariableField variableField = parentScope.FindReference(m_name); if (variableField != null) { // see if this scope already points to this name if (parentScope[m_name] == null) { // create an inner reference so we don't keep walking up the scope chain for this name variableField = parentScope.CreateInnerField(variableField); } // save the local field VariableField = variableField as JSLocalField; // add a reference if (VariableField != null) { VariableField.AddReference(parentScope); } } } }
internal FunctionScope(ActivationObject parent, bool isExpression, JSParser parser) : base(parent, parser) { if (isExpression) { // parent scopes automatically reference enclosed function expressions AddReference(Parent); } }
public ModuleScope(ModuleDeclaration module, ActivationObject parent, CodeSettings settings) : base(parent, settings) { Owner = module; UseStrict = true; ScopeType = ScopeType.Module; m_knownExports = new Dictionary <string, JSVariableField>(); }
internal CatchScope(ActivationObject parent, Context argContext, JSParser parser) : base(parent, argContext, parser) { // get the name of the catch variable m_name = Context.Code; // add it to the catch-scope's name table JSVariableField field = new JSArgumentField(m_name, null); NameTable[m_name] = field; FieldTable.Add(field); }
internal override void HyperCrunch() { // the block scope is used for catch blocks. // we don't want to introduce possible cross-browser problems, so // we need to make sure we don't rename our catch parameter to anything // already existing in our parent function scope. // // so walk through the parents (up to the first function or the global scope) // and add all existing crunched variable names to our verboten list. ActivationObject parentScope = Parent; while (parentScope != null) { // take all the variable names (crunched if they're crunched) // and add them to this block's verboten list if they aren't already. if (parentScope.NameTable.Count > 0) { foreach (var variableField in parentScope.NameTable.Values) { // add it to our verboten list if (!Verboten.ContainsKey(variableField)) { Verboten.Add(variableField, variableField); } } } // also add everything in the parent's verboten list if (parentScope.Verboten.Count > 0) { foreach (var variableField in parentScope.Verboten.Keys) { // add it to our verboten list if (!Verboten.ContainsKey(variableField)) { Verboten.Add(variableField, variableField); } } } // stop as soon as we've processed a funciton or global scope if (!(parentScope is BlockScope)) { break; } // next parent parentScope = parentScope.Parent; } // then just perform as usual base.HyperCrunch(); }
internal override void AnalyzeNode() { // check to see if this node is an argument to a RegExp constructor. // if it is, we'll want to not use certain string escapes AstNode previousNode = null; AstNode parentNode = Parent; while (parentNode != null) { // is this a call node and he previous node was one of the parameters? CallNode callNode = parentNode as CallNode; if (callNode != null && previousNode == callNode.Arguments) { // are we calling a simple lookup for "RegExp"? Lookup lookup = callNode.Function as Lookup; if (lookup != null && lookup.Name == "RegExp") { // we are -- so all string literals passed within this constructor should not use // standard string escape sequences m_isParameterToRegExp = true; // we can stop looking break; } } // next up the chain, keeping track of this current node as next iteration's "previous" node previousNode = parentNode; parentNode = parentNode.Parent; } // we only need to process the literals IF we are actually going to do // anything with them (combine duplicates). So if we aren't, don't call // AddLiteral because it hugely inflates the processing time of the application. if (Parser.Settings.CombineDuplicateLiterals) { // add this literal to the scope's literal collection. // HOWEVER, we do NOT want to add it for consideration of literal combination // if any scope above us is a with-scope -- otherwise the // variable we use to combine the literals might be confused with a // property on the with-object. // AND we don't want to do it if the scope is unknown, for the same reason. // we won't really know if the variable we create will interfere with the // scope resolution of any variables that me in the eval string. ActivationObject thisScope = Parser.ScopeStack.Peek(); if (thisScope.IsKnownAtCompileTime && !thisScope.IsInWithScope) { thisScope.AddLiteral(this, thisScope); } } // this node has no children, so don't bother calling the base }
internal FunctionScope(ActivationObject parent, bool isExpression, CodeSettings settings, FunctionObject funcObj) : base(parent, settings) { ScopeType = ScopeType.Function; m_refScopes = new HashSet <ActivationObject>(); if (isExpression) { // parent scopes automatically reference enclosed function expressions AddReference(Parent); } Owner = funcObj; }
// NAME [SCOPE TYPE] [crunched to CRUNCH] // // SCOPE: global, local, outer, '' // TYPE: var, function, argument, arguments array, possibly undefined private void WriteMemberReport(JSVariableField variableField, ActivationObject immediateScope) { // skip any *unreferenced* named-function-expression fields JSNamedFunctionExpressionField namedFuncExpr = variableField as JSNamedFunctionExpressionField; if (namedFuncExpr == null || namedFuncExpr.RefCount > 0 || !m_removeFunctionExpressionNames) { string scope = string.Empty; string type = string.Empty; string crunched = string.Empty; string name = variableField.Name; if (variableField.IsLiteral) { name = variableField.FieldValue.ToString(); } // calculate the crunched label JSLocalField localField = variableField as JSLocalField; if (localField != null) { if (localField.CrunchedName != null) { crunched = StringMgr.GetString("CrunchedTo", localField.CrunchedName, localField.RefCount); } } // get the field's default scope and type GetFieldScopeType(variableField, immediateScope, out scope, out type); if (variableField is JSWithField) { // if the field is a with field, we won't be using the crunched field (since // those fields can't be crunched), so let's overload it with what the field // could POSSIBLY be if the with object doesn't have a property of that name string outerScope; string outerType; GetFieldScopeType(variableField.OuterField, immediateScope, out outerScope, out outerType); crunched = StringMgr.GetString("MemberInfoWithPossibly", outerScope, outerType); } // format the entire string WriteProgress(StringMgr.GetString( "MemberInfoFormat", name, scope, type, crunched )); } }
public LiteralReference(ConstantWrapper constantWrapper, ActivationObject childScope, List <ConstantWrapper> sharedList) { m_count = 1; m_childScope = childScope; // use the shared list passed to us, or create a new one m_constantWrappers = sharedList ?? new List <ConstantWrapper>(); // only add the constant wrapper passed to us IF it isn't ALREADY in the list // (will only happen with a shared list) if (!m_constantWrappers.Contains(constantWrapper)) { m_constantWrappers.Add(constantWrapper); } }
internal void AddReference(ActivationObject scope) { // we don't want to include block scopes or with scopes -- they are really // contained within their parents while (scope != null && scope is BlockScope) { scope = scope.Parent; } if (scope != null) { // add the scope to the hash m_refScopes.Add(scope); } }
private void AddScopes(List <ActivationObject> list, ActivationObject parentScope) { // for each child scope... foreach (ActivationObject scope in parentScope.ChildScopes) { // add the scope to the list if it's not a globalscopes // which leaves function scopes and block scopes (from catch blocks) if (!(scope is GlobalScope)) { list.Add(scope); } // recurse... AddScopes(list, scope); } }
private void AddScopes(List <ActivationObject> list, ActivationObject parentScope) { // for each child scope... foreach (ActivationObject scope in parentScope.ChildScopes) { // add the scope to the list if it's not a globalscopes if (!(scope is GlobalScope)) { list.Add(scope); } // recurse... AddScopes(list, scope); } }
private void WriteScopeReport(FunctionObject funcObj, ActivationObject scope) { // output the function header if (scope is GlobalScope) { WriteProgress(StringMgr.GetString("GlobalObjectsHeader")); } else { FunctionScope functionScope = scope as FunctionScope; if (functionScope != null && funcObj != null) { WriteFunctionHeader(funcObj, scope.IsKnownAtCompileTime); } else { BlockScope blockScope = scope as BlockScope; if (blockScope is CatchScope) { WriteBlockHeader(blockScope, StringMgr.GetString("BlockTypeCatch")); } else if (blockScope is WithScope) { WriteBlockHeader(blockScope, StringMgr.GetString("BlockTypeWith")); } else { WriteProgress(); WriteProgress(StringMgr.GetString("UnknownScopeType", scope.GetType().ToString())); } } } // get all the fields in the scope JSVariableField[] scopeFields = scope.GetFields(); // sort the fields Array.Sort(scopeFields, FieldComparer.Instance); // iterate over all the fields foreach (JSVariableField variableField in scopeFields) { // don't report placeholder fields if (!variableField.IsPlaceholder) { WriteMemberReport(variableField, scope); } } }
internal override void AnalyzeNode() { // if the developer hasn't explicitly flagged eval statements as safe... if (Parser.Settings.EvalTreatment != EvalTreatment.Ignore) { // mark this scope as unknown so we don't // crunch out locals we might reference in the eval at runtime ActivationObject enclosingScope = ScopeStack.Peek(); if (enclosingScope != null) { enclosingScope.IsKnownAtCompileTime = false; } } // then just do the default analysis base.AnalyzeNode(); }
protected ActivationObject(ActivationObject parent, JSParser parser) { m_parent = parent; m_nameTable = new Dictionary <string, JSVariableField>(); m_fieldTable = new List <JSVariableField>(); m_childScopes = new List <ActivationObject>(); Verboten = new Dictionary <JSVariableField, JSVariableField>(32); m_isKnownAtCompileTime = true; m_parser = parser; // if our parent is a scope.... if (parent != null) { // add us to the parent's list of child scopes parent.m_childScopes.Add(this); } }
internal override void AnalyzeNode() { // first we want to make sure that we are indeed within a function scope. // it makes no sense to have a return outside of a function ActivationObject scope = ScopeStack.Peek(); while (scope != null && !(scope is FunctionScope)) { scope = scope.Parent; } if (scope == null) { Context.HandleError(JSError.BadReturn); } // now just do the default analyze base.AnalyzeNode(); }
public override JSVariableField DeclareField(string name, object value, FieldAttributes attributes) { JSVariableField variableField; if (!NameTable.TryGetValue(name, out variableField)) { // find the owning scope where variables are defined ActivationObject owningScope = Parent; while (owningScope is BlockScope) { owningScope = owningScope.Parent; } // create the variable in that scope variableField = owningScope.DeclareField(name, value, attributes); // and create an inner-reference in our scope variableField = CreateInnerField(variableField); } return(variableField); }
internal void AddReference(ActivationObject scope) { // make sure the hash is created if (m_refScopes == null) { m_refScopes = new Dictionary <ActivationObject, ActivationObject>(); } // we don't want to include block scopes or with scopes -- they are really // contained within their parents while (scope != null && scope is BlockScope) { scope = scope.Parent; } if (scope != null && !m_refScopes.ContainsKey(scope)) { // add the scope to the hash m_refScopes.Add(scope, scope); } }
internal override void AnalyzeScope() { if (Context != null) { // get the parent global/function scope where variables defined within our // scope are REALLY defined ActivationObject definingScope = this; do { definingScope = definingScope.Parent; } while (definingScope is BlockScope); // see if there is a variable already defined in that scope with the same name JSVariableField outerField = definingScope[m_name]; if (outerField != null) { // there is one defined already!!! // but if it isn't referenced, it's safe to just use it // as the outer field for our catch variable so our names // stay in sync when we rename stuff if (outerField.IsReferenced) { // but the outer field IS referenced somewhere! We have a possible ambiguous // catch variable problem that behaves differently in IE and non-IE browsers. Context.HandleError(JSError.AmbiguousCatchVar); } } else { // there isn't one defined -- add one and hook it to our argument // field as the outer reference so the name doesn't collide with any // other fields in that scope if we are renaming fields outerField = definingScope.CreateField(m_name, null, 0); outerField.IsPlaceholder = true; definingScope.AddField(outerField); } // point our inner catch variable to the outer variable this[m_name].OuterField = outerField; } base.AnalyzeScope(); }
private bool m_useStrict; //= false; #endregion Fields #region Constructors protected ActivationObject(ActivationObject parent, JSParser parser) { m_parent = parent; m_nameTable = new Dictionary<string, JSVariableField>(); m_fieldTable = new List<JSVariableField>(); m_childScopes = new List<ActivationObject>(); Verboten = new Dictionary<JSVariableField, JSVariableField>(32); m_isKnownAtCompileTime = true; m_parser = parser; // if our parent is a scope.... if (parent != null) { // add us to the parent's list of child scopes parent.m_childScopes.Add(this); // if the parent is strict, so are we UseStrict = parent.UseStrict; } }
public virtual void AddReference(ActivationObject scope) { // if we have an outer field, add the reference to it if (m_outerField != null) { m_outerField.AddReference(scope); } ++m_refCount; if (m_value is FunctionObject) { // add the reference to the scope ((FunctionObject)FieldValue).FunctionScope.AddReference(scope); } // no longer a placeholder if we are referenced if (m_isPlaceholder) { m_isPlaceholder = false; } }
// called during ConstantWrapper.AnalyzeNode internal virtual List <ConstantWrapper> AddLiteral(ConstantWrapper constantWrapper, ActivationObject refScope) { List <ConstantWrapper> nodeList = null; // numeric constants that are NaN or Infinity need not apply if (!constantWrapper.IsSpecialNumeric) { // if the constant is only one character long, it's never a good idea to // try and replace it string constantValue = constantWrapper.ToCode(); if (constantValue.Length > 1) { // go up the chain recursively. return the highest shared // constant node list so we can share it if we need to nodeList = ((ActivationObject)Parent).AddLiteral(constantWrapper, this); // if we haven't created our literal map yet, do so now if (m_literalMap == null) { m_literalMap = new Dictionary <string, LiteralReference>(); } // now handle our scope LiteralReference literalReference; // see if this constant is in our map if (m_literalMap.ContainsKey(constantValue)) { literalReference = m_literalMap[constantValue]; // increment the counter literalReference.Increment(); // add this constant wrapper to the list if (!literalReference.ConstantWrapperList.Contains(constantWrapper)) { literalReference.ConstantWrapperList.Add(constantWrapper); } // if this is the ref scope, or if the ref scope is not the child scope, // set the child scope to null if (literalReference.ChildScope != null && (refScope == this || refScope != literalReference.ChildScope)) { literalReference.ChildScope = null; } } else { // add to the table with count = 1 and our given constant wrapper // if this is the ref scope, child scope is null; otherwise use refScope // if nodelist is null, create a new list; otherwise use the shared list literalReference = new LiteralReference( constantWrapper, (refScope != this ? refScope : null), nodeList ); m_literalMap.Add(constantValue, literalReference); } // return whatever list it is we used for our node. // it might be shared, or it might be new if we didn't find a shared list nodeList = literalReference.ConstantWrapperList; } } return(nodeList); }
protected virtual void CreateLiteralShortcuts() { if (m_literalMap != null) { // get a reference to the first function scope in the chain // might be this, might be a parent FunctionScope functionScope = null; ActivationObject scope = this; while (scope != null && (functionScope = scope as FunctionScope) == null) { scope = scope.Parent; } // if we didn't find a parent function scope, then don't do any combining // because the literals are globals if (functionScope != null) { // for each value in our literal map foreach (string constantString in m_literalMap.Keys) { LiteralReference literalReference = m_literalMap[constantString]; // if the child scope isn't null, then we don't reference the literal // and only one of our child scopes does, so we don't want to add the // shortcut here. // OR if there are no constant wrappers left in the list, then we've already // replaced them all and there's nothing left to do. // BUT if the child scope is null, either we reference it, or more than // one child references it. So if there are any constant wrappers in the list, // then we want to add the shortcut and replace all the constants if (literalReference.ChildScope == null && literalReference.ConstantWrapperList.Count > 0) { // AND we only want to do it if it will be worthwhile. // (and a constant of length 1 is never worthwhile) int constantLength = constantString.Length; if (constantLength > 1) { int minCount = (constantLength + 7) / (constantLength - 1); if (literalReference.Count > minCount) { // create a special name that won't collide with any other variable names string specialName = string.Format(CultureInfo.InvariantCulture, "[literal:{0}]", ++s_literalCounter); // add a generated var statement at the top of the function block that // is equal to the literal value (just use the first constant wrapper as a model) ConstantWrapper modelConstant = literalReference.ConstantWrapperList[0]; // by default we will use the value of the first instance as the generated variable's value object generatedValue = modelConstant.Value; // BUT.... // if this is a numeric value, then we need to determine whether we should use a // positive or negative version of this value to minimize the number of minus operators in the results if (modelConstant.IsNumericLiteral) { // first we need to go through the existing references and count how many negative values there are var numberOfNegatives = 0; foreach (ConstantWrapper constantWrapper in literalReference.ConstantWrapperList) { // since the model us numeric, we shouldn't have any problems calling the // ToNumber method on the others (which should all also be numeric) if (constantWrapper.ToNumber() < 0) { ++numberOfNegatives; } } // now if more than half of the references are negative, we will want the generated value // to also be negative! Otherwise we want to force it to Positive. var absoluteValue = Math.Abs((double)generatedValue); if (numberOfNegatives > literalReference.ConstantWrapperList.Count / 2) { // force it to negative generatedValue = -absoluteValue; } else { // force it to positive generatedValue = absoluteValue; } } // add the generated variable to the function scope functionScope.FunctionObject.AddGeneratedVar( specialName, new ConstantWrapper( generatedValue, modelConstant.PrimitiveType, modelConstant.Context, Parser), true); // walk the list of constant wrappers backwards (because we'll be removing them // as we go along) and replace each one with a lookup for the generated variable. // Don't forget to analyze the lookup. for (int ndx = literalReference.ConstantWrapperList.Count - 1; ndx >= 0; --ndx) { ConstantWrapper constantWrapper = literalReference.ConstantWrapperList[ndx]; // create the lookup based on the thisliteral context Lookup lookup = new Lookup(specialName, constantWrapper.Context, Parser); // indicate this is generated by our code, not the user lookup.IsGenerated = true; // by default, we're just going to replace the constant with the lookup AstNode replacement = lookup; // if the constant wrapper is a numeric value that is the NEGATIVE of the // combined numeric value (which would happen if the literal was subsequently // combined with a unary minus operator), then we need to change this to a unary-minus // operator on the lookup, not just the lookup. if (constantWrapper.IsNumericLiteral) { // since the constant wrapper is numeric, we shouldn't have any problems // calling ToNumber if ((double)generatedValue == -constantWrapper.ToNumber()) { // it has been negated! Change the replacement to a unary minus operator // with the lookup as its operand replacement = new NumericUnary( constantWrapper.Context, Parser, lookup, JSToken.Minus); } } // replace the this literal with the appropriate node constantWrapper.Parent.ReplaceChild(constantWrapper, replacement); // set up the lookup's outer local field using the scope of the // original constant wrapper lookup.SetOuterLocalField(constantWrapper.EnclosingScope); // and remove it from the list. This is so child scopes don't also try to // add a shortcut -- the list will be empty. literalReference.ConstantWrapperList.RemoveAt(ndx); } } } } } } } }
internal virtual void ReserveFields() { // traverse through our children first to get depth-first foreach (ActivationObject scope in m_childScopes) { scope.ReserveFields(); } // then reserve all our fields that need reserving // check for unused local fields or arguments foreach (JSVariableField variableField in m_nameTable.Values) { JSLocalField localField = variableField as JSLocalField; if (localField != null) { // if this is a named-function-expression name, then we want to use the name of the // outer field so we don't collide in IE JSNamedFunctionExpressionField namedExprField = localField as JSNamedFunctionExpressionField; if (namedExprField != null) { // make sure the field is in this scope's verboten list so we don't accidentally reuse // an outer scope variable name if (!Verboten.ContainsKey(localField)) { Verboten.Add(localField, localField); } // we don't need to reserve up the scope because the named function expression's // "outer" field is always in the very next scope } else if (localField.OuterField != null) { // if the outer field is not null, then this field (not the name) needs to be // reserved up the scope chain until the scope where it's defined. // make sure the field is in this scope's verboten list so we don't accidentally reuse // the outer scope's variable name if (!Verboten.ContainsKey(localField)) { Verboten.Add(localField, localField); } for (ActivationObject scope = this; scope != null; scope = scope.Parent) { // get the local field by this name (if any) JSLocalField scopeField = scope.GetLocalField(variableField.Name); if (scopeField == null) { // it's not referenced in this scope -- if the field isn't in the verboten // list, add it now if (!scope.Verboten.ContainsKey(localField)) { scope.Verboten.Add(localField, localField); } } else if (scopeField.OuterField == null) { // found the original field -- stop looking break; } } } else if (m_parser.Settings.LocalRenaming == LocalRenaming.KeepLocalizationVars && localField.Name.StartsWith("L_", StringComparison.Ordinal)) { // localization variable. don't crunch it. // add it to this scope's verboten list in the extremely off-hand chance // that a crunched variable might be the same pattern if (!Verboten.ContainsKey(localField)) { Verboten.Add(localField, localField); } } else if (!localField.CanCrunch) { // this local field cannot be crunched for whatever reason // (we probably already have a name picked out for it that we want to keep). // add it to the verboten list, too. if (!Verboten.ContainsKey(localField)) { Verboten.Add(localField, localField); } } } else { // must be a global of some sort // reserve the name in this scope and all the way up the chain for (ActivationObject scope = this; scope != null; scope = scope.Parent) { if (!scope.Verboten.ContainsKey(variableField)) { scope.Verboten.Add(variableField, variableField); } } } } // finally, if this scope is not known at compile time, // AND we know we want to make all affected scopes safe // for the eval statement // AND we are actually referenced by the enclosing scope, // then our parent scope is also not known at compile time if (!m_isKnownAtCompileTime && Parser.Settings.EvalTreatment == EvalTreatment.MakeAllSafe) { ActivationObject parentScope = (ActivationObject)Parent; FunctionScope funcScope = this as FunctionScope; if (funcScope == null) { // we're not a function -- parent is unknown too parentScope.IsKnownAtCompileTime = false; } else { JSLocalField localField = parentScope.GetLocalField(funcScope.FunctionObject.Name); if (localField == null || localField.IsReferenced) { parentScope.IsKnownAtCompileTime = false; } } } }
internal virtual void AnalyzeScope() { // check for unused local fields or arguments foreach (JSVariableField variableField in m_nameTable.Values) { JSLocalField locField = variableField as JSLocalField; if (locField != null && !locField.IsReferenced && locField.OriginalContext != null) { if (locField.FieldValue is FunctionObject) { Context ctx = ((FunctionObject)locField.FieldValue).IdContext; if (ctx == null) { ctx = locField.OriginalContext; } ctx.HandleError(JSError.FunctionNotReferenced, false); } else if (!locField.IsGenerated) { JSArgumentField argumentField = locField as JSArgumentField; if (argumentField != null) { // we only want to throw this error if it's possible to remove it // from the argument list. And that will only happen if there are // no REFERENCED arguments after this one in the formal parameter list. // Assertion: because this is a JSArgumentField, this should be a function scope, // let's walk up to the first function scope we find, just in case. FunctionScope functionScope = this as FunctionScope; if (functionScope == null) { ActivationObject scope = this.Parent; while (scope != null) { functionScope = scope as FunctionScope; if (scope != null) { break; } } } if (functionScope == null || functionScope.IsArgumentTrimmable(argumentField)) { locField.OriginalContext.HandleError( JSError.ArgumentNotReferenced, false ); } } else if (locField.OuterField == null || !locField.OuterField.IsReferenced) { locField.OriginalContext.HandleError( JSError.VariableDefinedNotReferenced, false ); } } } } // rename fields if we need to RenameFields(); // recurse foreach (ActivationObject activationObject in m_childScopes) { try { Parser.ScopeStack.Push(activationObject); activationObject.AnalyzeScope(); } finally { Parser.ScopeStack.Pop(); } } }
// called during AnalyzeNodeVisitor(ConstantWrapper) internal virtual List<ConstantWrapper> AddLiteral(ConstantWrapper constantWrapper, ActivationObject refScope) { List<ConstantWrapper> nodeList = null; // numeric constants that are NaN or Infinity need not apply if (!constantWrapper.IsSpecialNumeric) { // if the constant is only one character long, it's never a good idea to // try and replace it string constantValue = constantWrapper.ToCode(); if (constantValue.Length > 1) { // go up the chain recursively. return the highest shared // constant node list so we can share it if we need to nodeList = ((ActivationObject)Parent).AddLiteral(constantWrapper, this); // if we haven't created our literal map yet, do so now if (m_literalMap == null) { m_literalMap = new Dictionary<string, LiteralReference>(); } // now handle our scope LiteralReference literalReference; // see if this constant is in our map if (m_literalMap.ContainsKey(constantValue)) { literalReference = m_literalMap[constantValue]; // increment the counter literalReference.Increment(); // add this constant wrapper to the list if (!literalReference.ConstantWrapperList.Contains(constantWrapper)) { literalReference.ConstantWrapperList.Add(constantWrapper); } // if this is the ref scope, or if the ref scope is not the child scope, // set the child scope to null if (literalReference.ChildScope != null && (refScope == this || refScope != literalReference.ChildScope)) { literalReference.ChildScope = null; } } else { // add to the table with count = 1 and our given constant wrapper // if this is the ref scope, child scope is null; otherwise use refScope // if nodelist is null, create a new list; otherwise use the shared list literalReference = new LiteralReference( constantWrapper, (refScope != this ? refScope : null), nodeList ); m_literalMap.Add(constantValue, literalReference); } // return whatever list it is we used for our node. // it might be shared, or it might be new if we didn't find a shared list nodeList = literalReference.ConstantWrapperList; } } return nodeList; }
public WithScope(ActivationObject parent, Context context, JSParser parser) : base(parent, context, parser) { // with statements are unknown by default //IsKnownAtCompileTime = false; }
public LiteralReference(ConstantWrapper constantWrapper, ActivationObject childScope, List<ConstantWrapper> sharedList) { m_count = 1; m_childScope = childScope; // use the shared list passed to us, or create a new one m_constantWrappers = sharedList ?? new List<ConstantWrapper>(); // only add the constant wrapper passed to us IF it isn't ALREADY in the list // (will only happen with a shared list) if (!m_constantWrappers.Contains(constantWrapper)) { m_constantWrappers.Add(constantWrapper); } }
// not instantiated directly, only through derived classes protected BlockScope(ActivationObject parent, Context context, JSParser parser) : base(parent, parser) { m_context = (context == null ? new Context(parser) : context.Clone()); }
private void ProcessFields(ActivationObject scope) { // split fields into defined and referenced lists var definedFields = new List <JSVariableField>(); var referencedFields = new List <JSVariableField>(); foreach (var field in scope.NameTable.Values) { // if the field has no outer field reference, it is defined in this scope. // otherwise we're just referencing a field defined elsewhere if (!field.IsOuterReference) { switch (field.FieldType) { case FieldType.Global: if (scope is GlobalScope) { definedFields.Add(field); } else { referencedFields.Add(field); } break; case FieldType.Local: // defined within this scope definedFields.Add(field); break; case FieldType.Argument: // ignore the scope's arguments because we handle them separately break; case FieldType.CatchError: // ignore the catch-scope's error parameter because we handle it separately break; case FieldType.Arguments: if (field.RefCount > 0) { referencedFields.Add(field); } break; case FieldType.Super: referencedFields.Add(field); break; case FieldType.UndefinedGlobal: case FieldType.Predefined: case FieldType.WithField: referencedFields.Add(field); break; case FieldType.GhostFunction: case FieldType.GhostCatch: // ignore the ghost fields when reporting break; } } else if (!field.IsPlaceholder) { // we are an outer reference and we are not a placeholder, // so this scope actually references the outer field. referencedFields.Add(field); } } if (definedFields.Count > 0) { m_writer.WriteStartElement("defines"); foreach (var field in definedFields) { ProcessField(field, true); } m_writer.WriteEndElement(); } if (referencedFields.Count > 0) { m_writer.WriteStartElement("references"); foreach (var field in referencedFields) { ProcessField(field, false); } m_writer.WriteEndElement(); } }
// the global scope does nothing when told to add literals -- just returns null internal override List<ConstantWrapper> AddLiteral(ConstantWrapper constantWrapper, ActivationObject refScope) { return null; }
internal CatchScope(ActivationObject parent, CodeSettings settings) : base(parent, settings, ScopeType.Catch) { }
private Context m_context; // = null; #endregion Fields #region Constructors // not instantiated directly, only through derived classes protected BlockScope(ActivationObject parent, Context context, JSParser parser) : base(parent, parser) { m_context = (context == null ? new Context(parser) : context.Clone()); }
internal void AddReference(ActivationObject scope) { // make sure the hash is created if (m_refScopes == null) { m_refScopes = new Dictionary<ActivationObject, ActivationObject>(); } // we don't want to include block scopes or with scopes -- they are really // contained within their parents while (scope != null && scope is BlockScope) { scope = scope.Parent; } if (scope != null && !m_refScopes.ContainsKey(scope)) { // add the scope to the hash m_refScopes.Add(scope, scope); } }
public FunctionObject(Lookup identifier, JSParser parser, FunctionType functionType, ParameterDeclaration[] parameterDeclarations, Block bodyBlock, Context functionContext, FunctionScope functionScope) : base(functionContext, parser) { FunctionType = functionType; m_functionScope = functionScope; if (functionScope != null) { functionScope.FunctionObject = this; } m_name = string.Empty; m_identifier = identifier; if (m_identifier != null) { m_identifier.Parent = this; } // make sure the parameter array is never null so we don't have to keep checking it m_parameterDeclarations = parameterDeclarations ?? new ParameterDeclaration[0]; Body = bodyBlock; if (bodyBlock != null) { bodyBlock.Parent = this; } // now we need to make sure that the enclosing scope has the name of this function defined // so that any references get properly resolved once we start analyzing the parent scope // see if this is not anonymnous AND not a getter/setter bool isGetterSetter = (FunctionType == FunctionType.Getter || FunctionType == FunctionType.Setter); if (m_identifier != null && !isGetterSetter) { // yes -- add the function name to the current enclosing // check whether the function name is in use already // shouldn't be any duplicate names ActivationObject enclosingScope = m_functionScope.Parent; // functions aren't owned by block scopes while (enclosingScope is BlockScope) { enclosingScope = enclosingScope.Parent; } // if the enclosing scope already contains this name, then we know we have a dup string functionName = m_identifier.Name; m_variableField = enclosingScope[functionName]; if (m_variableField != null) { // it's pointing to a function m_variableField.IsFunction = true; if (FunctionType == FunctionType.Expression) { // if the containing scope is itself a named function expression, then just // continue on as if everything is fine. It will chain and be good. if (!(m_variableField is JSNamedFunctionExpressionField)) { if (m_variableField.NamedFunctionExpression != null) { // we have a second named function expression in the same scope // with the same name. Not an error unless someone actually references // it. // we are now ambiguous. m_variableField.IsAmbiguous = true; // BUT because this field now points to multiple function object, we // need to break the connection. We'll leave the inner NFEs pointing // to this field as the outer field so the names all align, however. DetachFromOuterField(true); // create a new NFE pointing to the existing field as the outer so // the names stay in sync, and with a value of our function object. JSNamedFunctionExpressionField namedExpressionField = new JSNamedFunctionExpressionField(m_variableField); namedExpressionField.FieldValue = this; m_functionScope.AddField(namedExpressionField); // hook our function object up to the named field m_variableField = namedExpressionField; m_identifier.VariableField = namedExpressionField; // we're done; quit. return; } else if (m_variableField.IsAmbiguous) { // we're pointing to a field that is already marked as ambiguous. // just create our own NFE pointing to this one, and hook us up. JSNamedFunctionExpressionField namedExpressionField = new JSNamedFunctionExpressionField(m_variableField); namedExpressionField.FieldValue = this; m_functionScope.AddField(namedExpressionField); // hook our function object up to the named field m_variableField = namedExpressionField; m_identifier.VariableField = namedExpressionField; // we're done; quit. return; } else { // we are a named function expression in a scope that has already // defined a local variable of the same name. Not good. Throw the // error but keep them attached because the names have to be synced // to keep the same meaning in all browsers. m_identifier.Context.HandleError(JSError.AmbiguousNamedFunctionExpression, true); // if we are preserving function names, then we need to mark this field // as not crunchable if (Parser.Settings.PreserveFunctionNames) { m_variableField.CanCrunch = false; } } } /*else * { * // it's okay; just chain the NFEs as normal and everything will work out * // and the names will be properly synced. * }*/ } else { // function declaration -- duplicate name m_identifier.Context.HandleError(JSError.DuplicateName, false); } } else { // doesn't exist -- create it now m_variableField = enclosingScope.DeclareField(functionName, this, 0); // and it's a pointing to a function object m_variableField.IsFunction = true; } // set the identifier variable field now. We *know* what the field is now, and during // Analyze mode we aren't going to recurse into the identifier because that would add // a reference to it. m_identifier.VariableField = m_variableField; // if we're here, we have a name. if this is a function expression, then we have // a named function expression and we need to do a little more work to prepare for // the ambiguities of named function expressions in various browsers. if (FunctionType == FunctionType.Expression) { // now add a field within the function scope that indicates that it's okay to reference // this named function expression from WITHIN the function itself. // the inner field points to the outer field since we're going to want to catch ambiguous // references in the future JSNamedFunctionExpressionField namedExpressionField = new JSNamedFunctionExpressionField(m_variableField); m_functionScope.AddField(namedExpressionField); m_variableField.NamedFunctionExpression = namedExpressionField; } else { // function declarations are declared by definition m_variableField.IsDeclared = true; } } }
private void ProcessScope(ActivationObject scope) { switch (scope.ScopeType) { case ScopeType.Block: case ScopeType.Lexical: case ScopeType.None: // must be generic block scope m_writer.WriteStartElement("block"); if (scope.UseStrict) { m_writer.WriteAttributeString("strict", "true"); } break; case ScopeType.Class: m_writer.WriteStartElement("class"); if (!scope.ScopeName.IsNullOrWhiteSpace()) { m_writer.WriteAttributeString("src", scope.ScopeName); } if (scope.UseStrict) { m_writer.WriteAttributeString("strict", "true"); } break; case ScopeType.Catch: var catchScope = (CatchScope)scope; m_writer.WriteStartElement("catch"); if (scope.UseStrict) { m_writer.WriteAttributeString("strict", "true"); } foreach (var bindingIdentifier in BindingsVisitor.Bindings(catchScope.CatchParameter)) { m_writer.WriteStartElement("catchvar"); m_writer.WriteAttributeString("src", bindingIdentifier.Name); OutputContextPosition(bindingIdentifier.Context); var catchVariable = bindingIdentifier.VariableField; if (catchVariable != null) { if (catchVariable.CrunchedName != null) { m_writer.WriteAttributeString("min", catchVariable.CrunchedName); } if (m_useReferenceCounts) { m_writer.WriteAttributeString("refcount", catchVariable.RefCount.ToStringInvariant()); } } m_writer.WriteEndElement(); } break; case ScopeType.Module: m_writer.WriteStartElement("module"); if (!scope.ScopeName.IsNullOrWhiteSpace()) { m_writer.WriteAttributeString("name", scope.ScopeName); } if (scope.UseStrict) { m_writer.WriteAttributeString("strict", "true"); } (scope as ModuleScope).IfNotNull(m => { m_writer.WriteAttributeString("default", m.HasDefaultExport ? "true" : "false"); if (m.IsNotComplete) { m_writer.WriteAttributeString("incomplete", "true"); } }); break; case ScopeType.Function: var functionScope = (FunctionScope)scope; m_writer.WriteStartElement("function"); // for source name, use the scope name if (!scope.ScopeName.IsNullOrWhiteSpace()) { m_writer.WriteAttributeString("src", scope.ScopeName); } var functionObject = functionScope.Owner as FunctionObject; if (functionObject != null) { if (functionObject.Binding == null || functionObject.Binding.Name.IsNullOrWhiteSpace()) { if (!functionObject.NameGuess.IsNullOrWhiteSpace()) { // strip enclosing quotes m_writer.WriteAttributeString("guess", functionObject.NameGuess.Trim('\"')); } } else { if (functionObject.Binding.VariableField != null && functionObject.Binding.VariableField.CrunchedName != null) { m_writer.WriteAttributeString("min", functionObject.Binding.VariableField.CrunchedName); } } m_writer.WriteAttributeString("type", functionObject.FunctionType.ToString().ToLowerInvariant()); OutputContextPosition(functionObject.Context); if (m_useReferenceCounts && functionObject.Binding != null && functionObject.Binding.VariableField != null) { var refCount = functionObject.Binding.VariableField.RefCount; m_writer.WriteAttributeString("refcount", refCount.ToStringInvariant()); if (refCount == 0 && functionObject.FunctionType == FunctionType.Declaration && functionObject.Binding.VariableField.FieldType == FieldType.Local) { // local function declaration with zero references? unreachable code! m_writer.WriteAttributeString("unreachable", "true"); } } if (scope.UseStrict) { m_writer.WriteAttributeString("strict", "true"); } // add the arguments m_writer.WriteStartElement("arguments"); if (functionObject.ParameterDeclarations != null) { foreach (var bindingIdentifier in BindingsVisitor.Bindings(functionObject.ParameterDeclarations)) { m_writer.WriteStartElement("argument"); m_writer.WriteAttributeString("src", bindingIdentifier.Name); if (bindingIdentifier.VariableField.IfNotNull(v => v.CrunchedName != null)) { m_writer.WriteAttributeString("min", bindingIdentifier.VariableField.CrunchedName); } OutputContextPosition(bindingIdentifier.Context); if (m_useReferenceCounts) { bindingIdentifier.VariableField.IfNotNull(v => m_writer.WriteAttributeString("refcount", v.RefCount.ToStringInvariant())); } m_writer.WriteEndElement(); } } m_writer.WriteEndElement(); } break; case ScopeType.Global: Debug.Assert(scope is GlobalScope); Debug.Fail("shouldn't get here!"); m_writer.WriteStartElement("global"); break; case ScopeType.With: Debug.Assert(scope is WithScope); m_writer.WriteStartElement("with"); // with-scopes should never be strict because the with-statement is not allowed in strict code if (scope.UseStrict) { m_writer.WriteAttributeString("strict", "true"); } break; } // process the defined and referenced fields ProcessFields(scope); // recursively process each child scope foreach (var childScope in scope.ChildScopes) { ProcessScope(childScope); } // close the element m_writer.WriteEndElement(); }