Ejemplo n.º 1
0
        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;
        }
Ejemplo n.º 2
0
        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;
        }
Ejemplo n.º 3
0
        private Expression ProcessBinaryExpressionNode(BinaryExpressionNode node) {
            if ((node.Operator == TokenType.PlusPlus) ||
                (node.Operator == TokenType.MinusMinus)) {
                Expression childExpression = BuildExpression(node.LeftChild);
                if (childExpression is MemberExpression) {
                    childExpression = TransformMemberExpression((MemberExpression)childExpression);
                }

                return new UnaryExpression((node.Operator == TokenType.PlusPlus) ? Operator.PreIncrement : Operator.PreDecrement,
                                           childExpression);
            }

            if (node.Operator == TokenType.Dot) {
                return ProcessDotExpressionNode(node);
            }

            if (node.Operator == TokenType.OpenParen) {
                return ProcessOpenParenExpressionNode(node);
            }

            if (node.Operator == TokenType.OpenSquare) {
                return ProcessOpenBracketsExpressionNode(node);
            }

            Expression leftExpression = BuildExpression(node.LeftChild);
            Expression rightExpression = BuildExpression(node.RightChild);

            if ((node.Operator == TokenType.PlusEqual) ||
                (node.Operator == TokenType.MinusEqual)) {
                if (rightExpression.Type == ExpressionType.Member) {
                    rightExpression = TransformMemberExpression((MemberExpression)rightExpression);
                }

                if ((rightExpression.Type == ExpressionType.Delegate) ||
                    (rightExpression.EvaluatedType.Type == SymbolType.Delegate)) {
                    Debug.Assert(leftExpression.Type == ExpressionType.Member);
                    Debug.Assert(((MemberExpression)leftExpression).Member.Type == SymbolType.Event);

                    bool add = (node.Operator == TokenType.PlusEqual);
                    EventExpression eventExpression =
                        (EventExpression)TransformMemberExpression((MemberExpression)leftExpression,
                                                                   add, /* isEventAddOrRemove */ true);

                    eventExpression.SetHandler(rightExpression);
                    return eventExpression;
                }

                if (leftExpression.Type == ExpressionType.Member) {
                    MemberExpression leftMemberExpression = (MemberExpression)leftExpression;
                    if (leftMemberExpression.Member.Type == SymbolType.Property) {
                        // For properties, we need to expand out the += and -= into a get, followed by
                        // the + or - operation and then a set with the resulting value

                        leftExpression = TransformMemberExpression(leftMemberExpression, /* getter */ false);

                        Expression initialValueExpression = TransformMemberExpression(leftMemberExpression);
                        Operator substitutedOperator;

                        if (node.Operator == TokenType.PlusEqual) {
                            substitutedOperator = Operator.Plus;
                        }
                        else {
                            substitutedOperator = Operator.Minus;
                        }

                        Expression newValueExpression =
                            new BinaryExpression(substitutedOperator, initialValueExpression, rightExpression);
                        return new BinaryExpression(Operator.Equals, leftExpression, newValueExpression);
                    }
                }
            }

            if (leftExpression.Type == ExpressionType.Member) {
                leftExpression = TransformMemberExpression((MemberExpression)leftExpression,
                    /* getOrAdd */ (node.Operator != TokenType.Equal));
            }

            if (rightExpression.Type == ExpressionType.Member) {
                rightExpression = TransformMemberExpression((MemberExpression)rightExpression);
            }

            TypeSymbol resultType = null;
            Operator operatorType = OperatorConverter.OperatorFromToken(node.Operator);

            switch (node.Operator) {
                case TokenType.EqualEqual:
                case TokenType.NotEqual:
                case TokenType.Less:
                case TokenType.LessEqual:
                case TokenType.Greater:
                case TokenType.GreaterEqual:
                case TokenType.Is:
                    resultType = _symbolSet.ResolveIntrinsicType(IntrinsicType.Boolean);
                    break;
                case TokenType.As:
                    resultType = rightExpression.EvaluatedType;
                    break;
                case TokenType.Plus:
                    if (rightExpression.EvaluatedType == _symbolSet.ResolveIntrinsicType(IntrinsicType.String)) {
                        resultType = rightExpression.EvaluatedType;
                    }
                    break;
                case TokenType.Slash:
                    resultType = _symbolSet.ResolveIntrinsicType(IntrinsicType.Double);
                    break;
            }

            if (operatorType != Operator.Invalid) {
                if ((operatorType == Operator.BitwiseAnd) ||
                    (operatorType == Operator.BitwiseAndEquals) ||
                    (operatorType == Operator.BitwiseOr) ||
                    (operatorType == Operator.BitwiseOrEquals) ||
                    (operatorType == Operator.BitwiseXor) ||
                    (operatorType == Operator.BitwiseXorEquals)) {
                    TypeSymbol leftExpressionType = leftExpression.EvaluatedType;

                    if (leftExpressionType == _symbolSet.ResolveIntrinsicType(IntrinsicType.Boolean)) {
                        // For bitwise operators involving boolean expressions, we perform
                        // a type coercion due to behavioral differences between C# and JavaScript.
                        // Example:
                        //     var result = true & true;
                        //     actual: result === 1
                        //     expected: result === true
                        Operator baseOperatorType = operatorType;

                        switch (operatorType) {
                            case Operator.BitwiseAndEquals:
                                baseOperatorType = Operator.BitwiseAnd;
                                break;
                            case Operator.BitwiseOrEquals:
                                baseOperatorType = Operator.BitwiseOr;
                                break;
                            case Operator.BitwiseXorEquals:
                                baseOperatorType = Operator.BitwiseXor;
                                break;
                        }

                        // Map "x op y" to "(x op y) === 1"
                        // Map "x op= y" to "x = (x op y) === 1"
                        Expression bitwiseExpression =
                            new BinaryExpression(baseOperatorType, leftExpression, rightExpression);
                        bitwiseExpression.AddParenthesisHint();

                        Expression coerceExpression =
                            new BinaryExpression(Operator.EqualEqualEqual, bitwiseExpression,
                                new LiteralExpression(_symbolSet.ResolveIntrinsicType(IntrinsicType.Integer), 1));

                        if (operatorType == baseOperatorType) {
                            return coerceExpression;
                        }
                        else {
                            // Since y above can be a complex expression, add parentheses around it to ensure
                            // proper operator precedence.
                            rightExpression.AddParenthesisHint();

                            return new BinaryExpression(Operator.Equals, TransformGetPropertyExpression(leftExpression), coerceExpression);
                        }
                    }
                }

                if ((operatorType == Operator.EqualEqualEqual) ||
                    (operatorType == Operator.NotEqualEqual)) {
                    LiteralExpression literalExpression = rightExpression as LiteralExpression;
                    if (literalExpression != null) {
                        // Comparisons with null are mapped to the less-strict comparison operator
                        // to handle undefined as well.
                        if (literalExpression.Value == null) {
                            if (operatorType == Operator.EqualEqualEqual) {
                                return new BinaryExpression(Operator.EqualEqual, leftExpression, rightExpression, resultType);
                            }
                            else {
                                return new BinaryExpression(Operator.NotEqual, leftExpression, rightExpression, resultType);
                            }
                        }
                    }
                }

                if ((operatorType == Operator.ShiftRight) ||
                    (operatorType == Operator.ShiftRightEquals)) {
                    TypeSymbol leftExpressionType = leftExpression.EvaluatedType;

                    if ((leftExpressionType == _symbolSet.ResolveIntrinsicType(IntrinsicType.Byte)) ||
                        (leftExpressionType == _symbolSet.ResolveIntrinsicType(IntrinsicType.UnsignedShort)) ||
                        (leftExpressionType == _symbolSet.ResolveIntrinsicType(IntrinsicType.UnsignedInteger)) ||
                        (leftExpressionType == _symbolSet.ResolveIntrinsicType(IntrinsicType.UnsignedLong))) {
                        // Switch to unsigned shift operator for unsigned types (which happens
                        // to be set up to follow the signed operator in the enumeration offset by 1)
                        Debug.Assert(((int)Operator.UnsignedShiftRight - (int)Operator.ShiftRight) == 2);
                        Debug.Assert(((int)Operator.UnsignedShiftRightEquals - (int)Operator.ShiftRightEquals) == 2);

                        operatorType = (Operator)((int)(operatorType) + 2);
                    }
                }

                if (resultType == null) {
                    return new BinaryExpression(operatorType, leftExpression, rightExpression);
                }
                else {
                    return new BinaryExpression(operatorType, leftExpression, rightExpression, resultType);
                }
            }
            return null;
        }