public void DetachFromOuterField(bool leaveInnerPointingToOuter) { // we're going to change the reference from the outer variable to the inner variable // save the inner variable field JSNamedFunctionExpressionField nfeField = m_variableField.NamedFunctionExpression; // break the connection from the outer to the inner m_variableField.NamedFunctionExpression = null; if (!leaveInnerPointingToOuter) { // detach the inner from the outer nfeField.Detach(); } // the outer field no longer points to the function object m_variableField.FieldValue = null; // but the inner field should nfeField.FieldValue = this; // our variable field is now the inner field m_variableField = nfeField; // and so is out identifier m_identifier.VariableField = nfeField; }
// 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 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; } } }
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; } } } }
private static void GetFieldScopeType(JSVariableField variableField, ActivationObject immediateScope, out string scope, out string type) { JSLocalField localField = variableField as JSLocalField; JSPredefinedField predefinedField = variableField as JSPredefinedField; JSNamedFunctionExpressionField namedFuncExpr = variableField as JSNamedFunctionExpressionField; // default scope is blank scope = string.Empty; if (variableField is JSArgumentField) { type = StringMgr.GetString("MemberInfoTypeArgument"); } else if (variableField is JSArgumentsField) { type = StringMgr.GetString("MemberInfoTypeArguments"); } else if (predefinedField != null) { switch (predefinedField.GlobalObject) { case GlobalObjectInstance.GlobalObject: scope = StringMgr.GetString("MemberInfoScopeGlobalObject"); break; case GlobalObjectInstance.WindowObject: scope = StringMgr.GetString("MemberInfoScopeWindowObject"); break; case GlobalObjectInstance.Other: scope = StringMgr.GetString("MemberInfoScopeOtherObject"); break; } switch (predefinedField.MemberType) { case MemberTypes.Method: type = StringMgr.GetString("MemberInfoBuiltInMethod"); break; case MemberTypes.Property: type = StringMgr.GetString("MemberInfoBuiltInProperty"); break; default: type = StringMgr.GetString("MemberInfoBuiltInObject"); break; } } else if (variableField is JSGlobalField) { if ((variableField.Attributes & FieldAttributes.RTSpecialName) == FieldAttributes.RTSpecialName) { // this is a special "global." It might not be a global, but something referenced // in a with scope somewhere down the line. type = StringMgr.GetString("MemberInfoPossiblyUndefined"); } else if (variableField.FieldValue is FunctionObject) { if (variableField.NamedFunctionExpression == null) { type = StringMgr.GetString("MemberInfoGlobalFunction"); } else { type = StringMgr.GetString("MemberInfoFunctionExpression"); } } else { type = StringMgr.GetString("MemberInfoGlobalVar"); } } else if (variableField is JSWithField) { type = StringMgr.GetString("MemberInfoWithField"); } else if (namedFuncExpr != null) { type = StringMgr.GetString("MemberInfoSelfFuncExpr"); } else if (localField != null) { // type string if (localField.FieldValue is FunctionObject) { if (localField.NamedFunctionExpression == null) { type = StringMgr.GetString("MemberInfoLocalFunction"); } else { type = StringMgr.GetString("MemberInfoFunctionExpression"); } } else if (localField.IsLiteral) { type = StringMgr.GetString("MemberInfoLocalLiteral"); } else { type = StringMgr.GetString("MemberInfoLocalVar"); } // scope string // this is a local variable, so there MUST be a non-null function scope passed // to us. That function scope will be the scope we are expecting local variables // to be defined in. If the field is defined in that scope, it's local -- otherwise // it must be an outer variable. JSVariableField scopeField = immediateScope[variableField.Name]; if (scopeField == null || scopeField.OuterField != null) { scope = StringMgr.GetString("MemberInfoScopeOuter"); } else { scope = StringMgr.GetString("MemberInfoScopeLocal"); } } else { type = StringMgr.GetString("MemberInfoBuiltInObject"); } }
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; Identifier = identifier; if (Identifier != null) { Identifier.Parent = this; } m_parameterDeclarations = parameterDeclarations; 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 (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 = 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; 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; 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. Identifier.Context.HandleError(JSError.AmbiguousNamedFunctionExpression, false); // 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 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. 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; } } }