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 ProcessOpenBracketsExpressionNode(BinaryExpressionNode node) { Expression leftExpression = BuildExpression(node.LeftChild); if (leftExpression is MemberExpression) { leftExpression = TransformMemberExpression((MemberExpression)leftExpression); } Debug.Assert((leftExpression.EvaluatedType.Type == SymbolType.Class) || (leftExpression.EvaluatedType.Type == SymbolType.Record) || (leftExpression.EvaluatedType.Type == SymbolType.Interface)); IndexerSymbol indexer = null; if (leftExpression.EvaluatedType.Type != SymbolType.Interface) { indexer = ((ClassSymbol)leftExpression.EvaluatedType).GetIndexer(); } else { indexer = ((InterfaceSymbol)leftExpression.EvaluatedType).Indexer; } Debug.Assert(indexer != null); Debug.Assert(indexer.MatchFilter(leftExpression.MemberMask)); IndexerExpression indexerExpression = new IndexerExpression(leftExpression, indexer); Debug.Assert(node.RightChild is ExpressionListNode); ICollection<Expression> args = BuildExpressionList((ExpressionListNode)node.RightChild); foreach (Expression paramExpr in args) { indexerExpression.AddIndexParameterValue(paramExpr); } return indexerExpression; }