internal JSLocalField(JSVariableField outerField) : base(outerField) { JSLocalField outerLocalField = outerField as JSLocalField; if (outerLocalField != null) { // copy some properties m_isDefined = outerLocalField.m_isDefined; m_isGenerated = outerLocalField.m_isGenerated; } }
// 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 )); } }
private static FieldOrder GetOrderIndex(JSVariableField obj) { if (obj is JSArgumentField) { return(FieldOrder.Argument); } if (obj is JSArgumentsField) { return(FieldOrder.ArgumentsArray); } JSGlobalField globalField = obj as JSGlobalField; if (globalField != null) { return( globalField.FieldValue is FunctionObject ? FieldOrder.GlobalFunctionReferenced : FieldOrder.GlobalFieldReferenced ); } JSLocalField localField = obj as JSLocalField; if (localField != null) { if (localField.OuterField != null) { return( localField.FieldValue is FunctionObject ? FieldOrder.OuterFunctionReferenced : FieldOrder.OuterFieldReferenced ); } else { return( localField.FieldValue is FunctionObject ? FieldOrder.FunctionDefined : FieldOrder.FieldDefined ); } } return(FieldOrder.Other); }
internal virtual void HyperCrunch() { // if we're not known at compile time, then we can't crunch // the local variables in this scope, because we can't know if // something will reference any of it at runtime. // eval is something that will make the scope unknown because we // don't know what eval will evaluate to until runtime if (m_isKnownAtCompileTime) { // get an array of all the uncrunched local variables defined in this scope JSLocalField[] localFields = GetUncrunchedLocals(); if (localFields.Length > 0) { // create a crunch-name enumerator, taking into account our verboten set CrunchEnumerator crunchEnum = new CrunchEnumerator(Verboten); for (int ndx = 0; ndx < localFields.Length; ++ndx) { JSLocalField localField = localFields[ndx]; // if we are an unambiguous reference to a named function expression and we are not // referenced by anyone else, then we can just skip this variable because the // name will be stripped from the output anyway. // we also always want to crunch "placeholder" fields. if (localField.CanCrunch && (localField.RefCount > 0 || localField.IsDeclared || localField.IsPlaceholder || !(Parser.Settings.RemoveFunctionExpressionNames && Parser.Settings.IsModificationAllowed(TreeModifications.RemoveFunctionExpressionNames)))) { localFields[ndx].CrunchedName = crunchEnum.NextName(); } } } } // then traverse through our children foreach (ActivationObject scope in m_childScopes) { scope.HyperCrunch(); } }
internal JSLocalField[] GetUncrunchedLocals() { // there can't be more uncrunched fields than total fields List <JSLocalField> list = new List <JSLocalField>(m_nameTable.Count); foreach (JSVariableField variableField in m_nameTable.Values) { // we're only interested in local fields JSLocalField localField = variableField as JSLocalField; // if the local field is defined in this scope and hasn't been crunched // AND can still be crunched if (localField != null && localField.OuterField == null && localField.CrunchedName == null && localField.CanCrunch) { // if local renaming is not crunch all, then it must be crunch all but localization // (we don't get called if we aren't crunching anything). // SO for the first clause: // IF we are crunch all, we're good; but if we aren't crunch all, then we're only good if // the name doesn't start with "L_". // The second clause is only computed IF we already think we're good to go. // IF we aren't preserving function names, then we're good. BUT if we are, we're // only good to go if this field doesn't represent a function object. if ((m_parser.Settings.LocalRenaming == LocalRenaming.CrunchAll || !localField.Name.StartsWith("L_", StringComparison.Ordinal)) && !(m_parser.Settings.PreserveFunctionNames && localField.IsFunction)) { // add to our list list.Add(localField); } } } // sort the array by reference count, descending list.Sort(ReferenceComparer.Instance); // return as an array return(list.ToArray()); }
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(); } } }
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"); } }
//TYPE "NAME" - Starts at line LINE, col COLUMN STATUS [crunched to CRUNCH] // //TYPE: Function, Function getter, Function setter //STATUS: '', Unknown, Unreachable private void WriteFunctionHeader(FunctionObject funcObj, bool isKnown) { // get the crunched value (if any) string crunched = string.Empty; JSLocalField localField = funcObj.LocalField as JSLocalField; if (localField != null && localField.CrunchedName != null) { crunched = StringMgr.GetString("CrunchedTo", localField.CrunchedName, localField.RefCount); } // get the status if the function StringBuilder statusBuilder = new StringBuilder(); if (!isKnown) { statusBuilder.Append('['); statusBuilder.Append(StringMgr.GetString("NotKnown")); } if (funcObj.FunctionScope.Parent is GlobalScope) { // global function. // if this is a named function expression, we still want to know if it's // referenced by anyone if (funcObj.FunctionType == FunctionType.Expression && !string.IsNullOrEmpty(funcObj.Name)) { // output a comma separator if not the first item, otherwise // open the square bracket if (statusBuilder.Length > 0) { statusBuilder.Append(", "); } else { statusBuilder.Append('['); } statusBuilder.Append(StringMgr.GetString( "FunctionInfoReferences", funcObj.RefCount )); } } else if (!funcObj.FunctionScope.IsReferenced(null)) { // local function that isn't referenced -- unreachable! // output a comma separator if not the first item, otherwise // open the square bracket if (statusBuilder.Length > 0) { statusBuilder.Append(", "); } else { statusBuilder.Append('['); } statusBuilder.Append(StringMgr.GetString("Unreachable")); } if (statusBuilder.Length > 0) { statusBuilder.Append(']'); } string status = statusBuilder.ToString(); string functionType; switch (funcObj.FunctionType) { case FunctionType.Getter: functionType = "FunctionTypePropGet"; break; case FunctionType.Setter: functionType = "FunctionTypePropSet"; break; case FunctionType.Expression: functionType = "FunctionTypeExpression"; break; default: functionType = "FunctionTypeFunction"; break; } // output WriteProgress(); WriteProgress(StringMgr.GetString( "FunctionHeader", StringMgr.GetString(functionType), funcObj.Name, funcObj.Context.StartLineNumber, funcObj.Context.StartColumn, status, crunched )); }
internal void Remove(JSLocalField localField) { NameTable.Remove(localField.Name); FieldTable.Remove(localField); }