private static void GenerateBinaryExpression(ScriptGenerator generator, MemberSymbol symbol, BinaryExpression expression) { ScriptTextWriter writer = generator.Writer; if (expression.Operator == Operator.Equals) { PropertyExpression propExpression = expression.LeftOperand as PropertyExpression; if (propExpression != null) { Debug.Assert(propExpression.Type == ExpressionType.PropertySet); if (propExpression.ObjectReference is BaseExpression) { Debug.Assert(symbol.Parent is ClassSymbol); writer.Write(((ClassSymbol)symbol.Parent).FullGeneratedName); writer.Write(".callBaseMethod(this, 'set_"); writer.Write(propExpression.Property.GeneratedName); writer.Write("',"); writer.WriteTrimmed(" [ "); GenerateExpression(generator, symbol, expression.RightOperand); writer.WriteTrimmed(" ])"); } else { GenerateExpression(generator, symbol, propExpression.ObjectReference); writer.Write(".set_"); writer.Write(propExpression.Property.GeneratedName); writer.Write("("); GenerateExpression(generator, symbol, expression.RightOperand); writer.Write(")"); } return; } IndexerExpression indexExpression = expression.LeftOperand as IndexerExpression; if ((indexExpression != null) && !indexExpression.Indexer.IsIntrinsic) { Debug.Assert(indexExpression.Type == ExpressionType.Indexer); if (indexExpression.ObjectReference is BaseExpression) { Debug.Assert(symbol.Parent is ClassSymbol); writer.Write(((ClassSymbol)symbol.Parent).FullGeneratedName); writer.Write(".callBaseMethod(this, 'set_"); writer.Write(indexExpression.Indexer.GeneratedName); writer.Write("',"); writer.WriteTrimmed(" [ "); GenerateExpressionList(generator, symbol, indexExpression.Indices); writer.WriteTrimmed(", "); GenerateExpression(generator, symbol, expression.RightOperand); writer.WriteTrimmed(" ])"); } else { IndexerSymbol indexerSymbol = indexExpression.Indexer; GenerateExpression(generator, symbol, indexExpression.ObjectReference); writer.Write(".set_"); writer.Write(indexerSymbol.GeneratedName); writer.Write("("); GenerateExpressionList(generator, symbol, indexExpression.Indices); writer.WriteTrimmed(", "); GenerateExpression(generator, symbol, expression.RightOperand); writer.Write(")"); } return; } } else if ((expression.Operator == Operator.PlusEquals) || (expression.Operator == Operator.MinusEquals) || (expression.Operator == Operator.MultiplyEquals) || (expression.Operator == Operator.DivideEquals) || (expression.Operator == Operator.ModEquals) || (expression.Operator == Operator.BitwiseOrEquals) || (expression.Operator == Operator.BitwiseAndEquals) || (expression.Operator == Operator.BitwiseXorEquals) || (expression.Operator == Operator.ShiftLeftEquals) || (expression.Operator == Operator.ShiftRightEquals) || (expression.Operator == Operator.UnsignedShiftRightEquals)) { PropertyExpression propExpression = expression.LeftOperand as PropertyExpression; if (propExpression != null) { Debug.Assert(propExpression.Type == ExpressionType.PropertyGet); GenerateExpression(generator, symbol, propExpression.ObjectReference); writer.Write(".set_"); writer.Write(propExpression.Property.GeneratedName); writer.Write("("); GenerateExpression(generator, symbol, propExpression.ObjectReference); writer.Write(".get_"); writer.Write(propExpression.Property.GeneratedName); writer.WriteTrimmed("()"); writer.WriteTrimmed(OperatorConverter.OperatorToString(expression.Operator - 1)); GenerateExpression(generator, symbol, expression.RightOperand); writer.Write(")"); return; } } else if ((expression.Operator == Operator.Is) || (expression.Operator == Operator.As)) { TypeExpression typeExpression = expression.RightOperand as TypeExpression; Debug.Assert(typeExpression != null); writer.Write("Type."); if (expression.Operator == Operator.Is) { writer.Write("canCast("); } else { writer.Write("safeCast("); } GenerateExpression(generator, symbol, expression.LeftOperand); writer.WriteTrimmed(", "); writer.Write(typeExpression.AssociatedType.FullGeneratedName); writer.Write(")"); return; } else if ((expression.Operator == Operator.EqualEqualEqual) || (expression.Operator == Operator.NotEqualEqual)) { LiteralExpression literalExpression = expression.RightOperand as LiteralExpression; if (literalExpression != null) { // Optimize generated script to perform loose equality checks for false-y values // (null, false, 0, empty string) // TODO: This should really be happening at compilation time, rather than generation // time. Because this is happening at generation time, we get something like // if (!!x) when if(x) would suffice just so we can get // foo(!!x) where foo is a method expecting a boolean. // Doing this at compilation time would allow handling if scenarios specially. bool optimizable = false; bool checkForFalse = false; if (literalExpression.Value is bool) { optimizable = true; bool compareValue = (bool)literalExpression.Value; if ((compareValue && (expression.Operator == Operator.NotEqualEqual)) || (!compareValue && (expression.Operator == Operator.EqualEqualEqual))) { checkForFalse = true; } } else if ((literalExpression.Value is int) && (((int)literalExpression.Value) == 0)) { optimizable = true; checkForFalse = (expression.Operator == Operator.EqualEqualEqual); } else if ((literalExpression.Value is string) && ((string)literalExpression.Value == String.Empty)) { optimizable = true; checkForFalse = (expression.Operator == Operator.EqualEqualEqual); } if (optimizable) { bool parenthesize = false; writer.Write(checkForFalse ? "!" : "!!"); if ((expression.LeftOperand.Parenthesized == false) && ((expression.LeftOperand.Type == ExpressionType.Binary) || (expression.LeftOperand.Type == ExpressionType.Conditional) || (expression.LeftOperand.Type == ExpressionType.InlineScript))) { parenthesize = true; writer.Write("("); } GenerateExpression(generator, symbol, expression.LeftOperand); if (parenthesize) { writer.Write(")"); } return; } } } GenerateExpression(generator, symbol, expression.LeftOperand); writer.WriteTrimmed(OperatorConverter.OperatorToString(expression.Operator)); GenerateExpression(generator, symbol, expression.RightOperand); }
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 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; }