private Expression ProcessDotExpressionNode(BinaryExpressionNode node) { SymbolFilter filter = SymbolFilter.All; MemberSymbol memberSymbol = null; Expression objectExpression = ProcessDotExpressionNode(node, filter, out memberSymbol); if (objectExpression == null) { // We didn't successfully create an expression. The first pass attempted to // process the right child as an instance member of the left child expression. // We need to process the left child again as a type so we can process the // right child as a static member this time around. filter &= ~SymbolFilter.Members; objectExpression = ProcessDotExpressionNode(node, filter, out memberSymbol); } Debug.Assert(objectExpression != null); TypeSymbol dictionaryType = _symbolSet.ResolveIntrinsicType(IntrinsicType.Dictionary); TypeSymbol genericDictionaryType = _symbolSet.ResolveIntrinsicType(IntrinsicType.GenericDictionary); TypeSymbol nullableType = _symbolSet.ResolveIntrinsicType(IntrinsicType.Nullable); if (memberSymbol.Type == SymbolType.Property) { if ((memberSymbol.Parent == dictionaryType) || (memberSymbol.Parent == genericDictionaryType)) { MethodSymbol methodSymbol = null; if (String.CompareOrdinal(memberSymbol.Name, "Count") == 0) { methodSymbol = (MethodSymbol)dictionaryType.GetMember("GetKeyCount"); Debug.Assert(methodSymbol != null); } else if (String.CompareOrdinal(memberSymbol.Name, "Keys") == 0) { methodSymbol = (MethodSymbol)dictionaryType.GetMember("GetKeys"); Debug.Assert(methodSymbol != null); } if (methodSymbol != null) { MethodExpression methodExpression = new MethodExpression(new TypeExpression(dictionaryType, SymbolFilter.Public | SymbolFilter.StaticMembers), methodSymbol); methodExpression.AddParameterValue(objectExpression); return methodExpression; } } else if (memberSymbol.Parent == nullableType) { if (String.CompareOrdinal(memberSymbol.Name, "Value") == 0) { // Nullable<T>.Value becomes Nullable<T> TypeSymbol underlyingType = objectExpression.EvaluatedType.GenericArguments.First(); objectExpression.Reevaluate(underlyingType); return objectExpression; } else if (String.CompareOrdinal(memberSymbol.Name, "HasValue") == 0) { // Nullable<T>.Value becomes Script.IsValue(Nullable<T>) TypeSymbol scriptType = _symbolSet.ResolveIntrinsicType(IntrinsicType.Script); MethodSymbol isValueMethod = (MethodSymbol)scriptType.GetMember("IsValue"); MethodExpression methodExpression = new MethodExpression(new TypeExpression(scriptType, SymbolFilter.Public | SymbolFilter.StaticMembers), isValueMethod); methodExpression.AddParameterValue(objectExpression); return methodExpression; } } } else if (memberSymbol.Type == SymbolType.Method) { if (memberSymbol.Parent == nullableType) { // Nullable<T>.GetValueOrDefault() becomes Nullable<T> || 0|false TypeSymbol underlyingType = objectExpression.EvaluatedType.GenericArguments.First(); object defaultValue = 0; if (underlyingType == _symbolSet.ResolveIntrinsicType(IntrinsicType.Boolean)) { defaultValue = false; } else if (underlyingType == _symbolSet.ResolveIntrinsicType(IntrinsicType.String)) { defaultValue = String.Empty; } LiteralExpression literalExpression = new LiteralExpression(underlyingType, defaultValue); BinaryExpression logicalOrExpression = new BinaryExpression(Operator.LogicalOr, objectExpression, literalExpression); logicalOrExpression.Reevaluate(underlyingType); logicalOrExpression.AddParenthesisHint(); return logicalOrExpression; } } string dependency = ((TypeSymbol)memberSymbol.Parent).DependencyName; if (String.IsNullOrEmpty(dependency) == false) { _options.AddExecutionDependency(dependency); } MemberExpression expression = new MemberExpression(objectExpression, memberSymbol); if ((memberSymbol.Type == SymbolType.Method) && memberSymbol.AssociatedType.IsGeneric && (memberSymbol.AssociatedType.GenericArguments == null)) { Debug.Assert(node.RightChild.NodeType == ParseNodeType.GenericName); Debug.Assert(((GenericNameNode)node.RightChild).TypeArguments != null); List<TypeSymbol> typeArgs = new List<TypeSymbol>(); foreach (ParseNode typeArgNode in ((GenericNameNode)node.RightChild).TypeArguments) { typeArgs.Add(_symbolSet.ResolveType(typeArgNode, _symbolTable, _symbolContext)); } TypeSymbol returnType = _symbolSet.CreateGenericTypeSymbol(memberSymbol.AssociatedType, typeArgs); if (returnType != null) { MethodSymbol genericMethod = (MethodSymbol)memberSymbol; MethodSymbol instanceMethod = new MethodSymbol(genericMethod.Name, (TypeSymbol)genericMethod.Parent, returnType); if (genericMethod.IsTransformed) { instanceMethod.SetTransformedName(genericMethod.GeneratedName); } instanceMethod.SetNameCasing(genericMethod.IsCasePreserved); expression = new MemberExpression(objectExpression, instanceMethod); } } return expression; }
private Expression ProcessOpenParenExpressionNode(BinaryExpressionNode node) { bool isDelegateInvoke = false; Expression leftExpression = BuildExpression(node.LeftChild); if (leftExpression is LocalExpression) { Debug.Assert(leftExpression.EvaluatedType.Type == SymbolType.Delegate); // Handle the implicit delegate invoke scenario by turning the expression // into an explicit call into the delegate's Invoke method MemberSymbol invokeMethodSymbol = leftExpression.EvaluatedType.GetMember("Invoke"); Debug.Assert(invokeMethodSymbol != null); leftExpression = new MemberExpression(leftExpression, invokeMethodSymbol); isDelegateInvoke = true; } if (leftExpression.Type != ExpressionType.Member) { // A method call was evaluated into a non-member expression as part of building // the left node. For example, Nullable<T>.GetValueOrDefault() becomes a logical or expression. return leftExpression; } MemberExpression memberExpression = (MemberExpression)leftExpression; ExpressionListNode argNodes = null; List<Expression> args = null; if (node.RightChild != null) { Debug.Assert(node.RightChild is ExpressionListNode); argNodes = (ExpressionListNode)node.RightChild; args = BuildExpressionList(argNodes); } MethodExpression methodExpression = null; if (memberExpression.Member.Type != SymbolType.Method) { // A non-method member is being used in a method call; the member must be // a delegate... Debug.Assert(memberExpression.EvaluatedType.Type == SymbolType.Delegate); Expression instanceExpression = TransformMemberExpression(memberExpression); MethodSymbol invokeMethod = (MethodSymbol)memberExpression.EvaluatedType.GetMember("Invoke"); Debug.Assert(invokeMethod != null); methodExpression = new MethodExpression(instanceExpression, invokeMethod); isDelegateInvoke = true; } else { // The member being accessed is a method... // REVIEW: Uggh... this has become too complex over time with all the transformations // added over time. Refactoring needed... MethodSymbol method = (MethodSymbol)memberExpression.Member; TypeSymbol objectType = _symbolSet.ResolveIntrinsicType(IntrinsicType.Object); TypeSymbol typeType = _symbolSet.ResolveIntrinsicType(IntrinsicType.Type); TypeSymbol dictionaryType = _symbolSet.ResolveIntrinsicType(IntrinsicType.Dictionary); TypeSymbol genericDictionaryType = _symbolSet.ResolveIntrinsicType(IntrinsicType.GenericDictionary); TypeSymbol numberType = _symbolSet.ResolveIntrinsicType(IntrinsicType.Number); TypeSymbol stringType = _symbolSet.ResolveIntrinsicType(IntrinsicType.String); TypeSymbol scriptType = _symbolSet.ResolveIntrinsicType(IntrinsicType.Script); TypeSymbol argsType = _symbolSet.ResolveIntrinsicType(IntrinsicType.Arguments); if ((method.Parent == objectType) && method.Name.Equals("GetType", StringComparison.Ordinal)) { // Since we can't extend object's prototype, we need to transform the // natural c# syntax into a static method. // Object.GetType instance method becomes Type.GetInstanceType static method TypeSymbol typeSymbol = typeType; method = (MethodSymbol)typeSymbol.GetMember("GetInstanceType"); Debug.Assert(method != null); methodExpression = new MethodExpression(new TypeExpression(typeSymbol, SymbolFilter.Public | SymbolFilter.StaticMembers), method); methodExpression.AddParameterValue(memberExpression.ObjectReference); return methodExpression; } else if ((method.Parent == objectType) && (method.Name.Equals("ToString", StringComparison.Ordinal) || method.Name.Equals("ToLocaleString", StringComparison.Ordinal)) && (memberExpression.ObjectReference.EvaluatedType == stringType)) { // No-op ToString calls on strings (this happens when performing a ToString // on a named enum value. return memberExpression.ObjectReference; } else if ((method.Parent == objectType) && (method.Name.Equals("ToString", StringComparison.Ordinal) || method.Name.Equals("ToLocaleString", StringComparison.Ordinal)) && (memberExpression.ObjectReference.EvaluatedType is EnumerationSymbol)) { EnumerationSymbol enumSymbol = (EnumerationSymbol)memberExpression.ObjectReference.EvaluatedType; if (enumSymbol.UseNamedValues) { // If the enum value is a named enum, then it is already a string. return memberExpression.ObjectReference; } return new MethodExpression(memberExpression.ObjectReference, method); } else if (((method.Parent == dictionaryType) || (method.Parent == genericDictionaryType)) && method.Name.Equals("ContainsKey", StringComparison.Ordinal)) { // Switch the instance ContainsKey method on Dictionary to // calls to the static KeyExists method. Debug.Assert(args.Count == 1); MethodSymbol keyExistsMethod = (MethodSymbol)dictionaryType.GetMember("KeyExists"); Debug.Assert(keyExistsMethod != null); methodExpression = new MethodExpression(new TypeExpression(dictionaryType, SymbolFilter.Public | SymbolFilter.StaticMembers), keyExistsMethod); methodExpression.AddParameterValue(memberExpression.ObjectReference); methodExpression.AddParameterValue(args[0]); return methodExpression; } else if (((method.Parent == dictionaryType) || (method.Parent == genericDictionaryType)) && method.Name.Equals("Remove", StringComparison.Ordinal)) { // Switch the instance Remove method on Dictionary to // calls to the static DeleteKey method. Debug.Assert(args.Count == 1); return new LateBoundExpression(memberExpression.ObjectReference, args[0], LateBoundOperation.DeleteField, objectType); } else if (((method.Parent == dictionaryType) || (method.Parent == genericDictionaryType)) && method.Name.Equals("Clear")) { // Switch the instance Clear method on Dictionary to // calls to the static ClearKeys method. MethodSymbol clearKeysMethod = (MethodSymbol)dictionaryType.GetMember("ClearKeys"); Debug.Assert(clearKeysMethod != null); methodExpression = new MethodExpression(new TypeExpression(dictionaryType, SymbolFilter.Public | SymbolFilter.StaticMembers), clearKeysMethod); methodExpression.AddParameterValue(memberExpression.ObjectReference); return methodExpression; } else if (((method.Parent == dictionaryType) || (((TypeSymbol)method.Parent).GenericType == genericDictionaryType)) && ((method.Visibility & MemberVisibility.Static) != 0) && method.Name.Equals("GetDictionary", StringComparison.Ordinal)) { // Dictionary.GetDictionary is a no-op method; we're just interested // in the object being passed in. // However we'll re-evaluate the argument to be of dictionary type // so that subsequent use of this expression sees it as a dictionary. Debug.Assert(args.Count == 1); args[0].Reevaluate((TypeSymbol)method.Parent); return args[0]; } else if ((method.Parent == stringType) && (method.Name.Equals("Escape", StringComparison.Ordinal) || method.Name.Equals("Unescape", StringComparison.Ordinal) || method.Name.Equals("EncodeUri", StringComparison.Ordinal) || method.Name.Equals("DecodeUri", StringComparison.Ordinal) || method.Name.Equals("EncodeUriComponent", StringComparison.Ordinal) || method.Name.Equals("DecodeUriComponent", StringComparison.Ordinal))) { // String.<Method> becomes Script.<Method>, as its a global method in script MethodSymbol scriptMethod = (MethodSymbol)scriptType.GetMember(method.Name); methodExpression = new MethodExpression(new TypeExpression(scriptType, SymbolFilter.Public | SymbolFilter.StaticMembers), scriptMethod); methodExpression.AddParameterValue(memberExpression.ObjectReference); return methodExpression; } else if ((method.Parent == scriptType) && method.Name.Equals("Literal", StringComparison.Ordinal)) { // Convert a call to Script.Literal into a literal expression Debug.Assert(args.Count >= 1); string script = null; if (args[0].Type == ExpressionType.Field) { Debug.Assert(args[0].EvaluatedType == stringType); FieldSymbol field = ((FieldExpression)args[0]).Field; if (field.IsConstant) { Debug.Assert(field.Value is string); script = (string)field.Value; } } else if (args[0].Type == ExpressionType.Literal) { Debug.Assert(((LiteralExpression)args[0]).Value is string); script = (string)((LiteralExpression)args[0]).Value; } if (script == null) { // TODO: When we start raising errors at the expression level instead of the statement // level, we should return an ErrorExpression instead of a dummy expression. Token argToken = argNodes.Expressions[0].Token; _errorHandler.ReportError("The argument to Script.Literal must be a constant string.", argToken.Location); return new InlineScriptExpression("", objectType); } InlineScriptExpression scriptExpression = new InlineScriptExpression(script, objectType); for (int i = 1; i < args.Count; i++) { scriptExpression.AddParameterValue(args[i]); } return scriptExpression; } else if ((method.Parent == scriptType) && method.Name.Equals("Boolean", StringComparison.Ordinal)) { Debug.Assert(args.Count == 1); return new UnaryExpression(Operator.LogicalNot, new UnaryExpression(Operator.LogicalNot, args[0])); } else if ((method.Parent == scriptType) && method.Name.Equals("Value", StringComparison.Ordinal)) { Debug.Assert(args.Count >= 2); Expression expr = args[0]; for (int i = 1; i < args.Count; i++) { expr = new BinaryExpression(Operator.LogicalOr, expr, args[i]); } expr.Reevaluate(args[0].EvaluatedType); expr.AddParenthesisHint(); return expr; } else if (method.Parent == argsType) { if (method.Name.Equals("GetArgument", StringComparison.Ordinal)) { // Switch Arguments.GetArgument into Arguments[] Debug.Assert(args.Count == 1); IndexerExpression indexExpression = new IndexerExpression(new LiteralExpression(typeType, method.Parent), ((ClassSymbol)method.Parent).GetIndexer()); indexExpression.AddIndexParameterValue(args[0]); return indexExpression; } else if (method.Name.Equals("ToArray", StringComparison.Ordinal)) { // Switch Arguments.ToArray into Array.ToArray(arguments) TypeSymbol arrayType = _symbolSet.ResolveIntrinsicType(IntrinsicType.Array); MethodSymbol toArrayMethod = (MethodSymbol)arrayType.GetMember("ToArray"); InlineScriptExpression argsExpression = new InlineScriptExpression("arguments", objectType, /* parenthesize */ false); MethodExpression toArrayExpression = new MethodExpression(new TypeExpression(arrayType, SymbolFilter.Public | SymbolFilter.StaticMembers), toArrayMethod); toArrayExpression.AddParameterValue(argsExpression); return toArrayExpression; } } else { bool lateBound = false; if (method.Parent == scriptType) { LateBoundOperation lateBoundOperation = LateBoundOperation.InvokeMethod; if (method.Name.Equals("InvokeMethod", StringComparison.Ordinal)) { lateBound = true; } else if (method.Name.Equals("DeleteField", StringComparison.Ordinal)) { lateBound = true; lateBoundOperation = LateBoundOperation.DeleteField; } else if (method.Name.Equals("GetField", StringComparison.Ordinal)) { lateBound = true; lateBoundOperation = LateBoundOperation.GetField; } else if (method.Name.Equals("SetField", StringComparison.Ordinal)) { lateBound = true; lateBoundOperation = LateBoundOperation.SetField; } else if (method.Name.Equals("GetScriptType", StringComparison.Ordinal)) { lateBound = true; lateBoundOperation = LateBoundOperation.GetScriptType; } else if (method.Name.Equals("HasField", StringComparison.Ordinal)) { lateBound = true; lateBoundOperation = LateBoundOperation.HasField; } else if (method.Name.Equals("HasMethod", StringComparison.Ordinal)) { lateBound = true; lateBoundOperation = LateBoundOperation.HasMethod; } else if (method.Name.Equals("CreateInstance", StringComparison.Ordinal)) { Debug.Assert(args.Count >= 1); if ((args[0].Type == ExpressionType.MethodInvoke) || (args[0].Type == ExpressionType.PropertyGet)) { // When using the result of a method call/property access directly // with Type.CreateInstance, the following script would be generated: // // new method()() // which is invalid. Instead we need to generate the following: // var type = method(); // new type() _errorHandler.ReportError("You must store the type returned from a method or property into a local variable to use with Type.CreateInstance.", node.Token.Location); } NewExpression newExpression = new NewExpression(args[0], objectType); if (args.Count > 1) { bool first = true; foreach (Expression paramExpr in args) { if (first) { first = false; continue; } newExpression.AddParameterValue(paramExpr); } } return newExpression; } if (lateBound) { // Switch explicit late-bound calls into implicit late-bound expressions // in script Debug.Assert((args != null) && (((lateBoundOperation == LateBoundOperation.GetScriptType) && (args.Count == 1)) || (args.Count >= 2))); LateBoundExpression lateBoundExpression = null; Expression instanceExpression = null; Expression nameExpression = null; foreach (Expression paramExpr in args) { if (instanceExpression == null) { instanceExpression = paramExpr; if (lateBoundOperation == LateBoundOperation.GetScriptType) { // GetScriptType only takes an instance return new LateBoundExpression(instanceExpression, null, lateBoundOperation, objectType); } continue; } if (nameExpression == null) { nameExpression = paramExpr; Expression objectExpression = instanceExpression; if (lateBoundOperation == LateBoundOperation.InvokeMethod) { if ((instanceExpression.Type == ExpressionType.Literal) && (((LiteralExpression)instanceExpression).Value == null)) { objectExpression = null; LiteralExpression literalExpression = nameExpression as LiteralExpression; if (literalExpression == null) { _errorHandler.ReportError("The name of a global method must be a constant string known at compile time.", argNodes.Expressions[0].Token.Location); } else if (!Utility.IsValidIdentifier((string)literalExpression.Value)) { _errorHandler.ReportError("The name of a global method must be a valid identifer.", argNodes.Expressions[0].Token.Location); } } } lateBoundExpression = new LateBoundExpression(objectExpression, nameExpression, lateBoundOperation, objectType); continue; } lateBoundExpression.AddParameterValue(paramExpr); } Debug.Assert(lateBoundExpression != null); return lateBoundExpression; } } if (!method.MatchesConditions(_options.Defines)) { return null; } methodExpression = new MethodExpression(memberExpression.ObjectReference, method); } } if (args != null) { foreach (Expression paramExpr in args) { methodExpression.AddParameterValue(paramExpr); } } if (isDelegateInvoke) { return new DelegateInvokeExpression(methodExpression); } return methodExpression; }
private Expression ProcessCastNode(CastNode node) { Expression childExpression = BuildExpression(node.Child); if (childExpression.Type == ExpressionType.Member) { childExpression = TransformMemberExpression((MemberExpression)childExpression); } TypeSymbol typeSymbol = _symbolSet.ResolveType(node.TypeReference, _symbolTable, _symbolContext); Debug.Assert(typeSymbol != null); if ((typeSymbol == _symbolSet.ResolveIntrinsicType(IntrinsicType.Integer))) { if ((childExpression.EvaluatedType == _symbolSet.ResolveIntrinsicType(IntrinsicType.Double)) || (childExpression.EvaluatedType == _symbolSet.ResolveIntrinsicType(IntrinsicType.Single))) { // A float or double is being cast to an int // In regular .net this causes a truncation to happen, and we'd like // to preserve that behavior. TypeSymbol mathType = (TypeSymbol)((ISymbolTable)_symbolSet.SystemNamespace).FindSymbol("Math", null, SymbolFilter.Types); Debug.Assert(mathType != null); MethodSymbol truncateMethod = (MethodSymbol)mathType.GetMember("Truncate"); Debug.Assert(truncateMethod != null); MethodExpression truncateExpression = new MethodExpression(new TypeExpression(mathType, SymbolFilter.Public | SymbolFilter.StaticMembers), truncateMethod); truncateExpression.AddParameterValue(childExpression); return truncateExpression; } } return new UnaryExpression(Operator.Cast, childExpression, typeSymbol, childExpression.MemberMask); }