public static bool IsOperatorSimple(BinaryOperatorExpression binaryOperatorExpression, IEmitter emitter) { var leftResolverResult = emitter.Resolver.ResolveNode(binaryOperatorExpression.Left, emitter); var rightResolverResult = emitter.Resolver.ResolveNode(binaryOperatorExpression.Right, emitter); bool leftIsSimple = binaryOperatorExpression.Left is PrimitiveExpression || leftResolverResult.Type.IsKnownType(KnownTypeCode.String) || leftResolverResult.Type.IsReferenceType != null && !leftResolverResult.Type.IsReferenceType.Value; bool rightIsSimple = binaryOperatorExpression.Right is PrimitiveExpression || rightResolverResult.Type.IsKnownType(KnownTypeCode.String) || rightResolverResult.Type.IsReferenceType != null && !rightResolverResult.Type.IsReferenceType.Value; bool isSimpleConcat = leftIsSimple && rightIsSimple; if (!isSimpleConcat) { var be = binaryOperatorExpression.Left as BinaryOperatorExpression; leftIsSimple = be != null?BinaryOperatorBlock.IsOperatorSimple(be, emitter) : leftIsSimple; be = binaryOperatorExpression.Right as BinaryOperatorExpression; rightIsSimple = be != null?BinaryOperatorBlock.IsOperatorSimple(be, emitter) : rightIsSimple; isSimpleConcat = leftIsSimple && rightIsSimple; } return(isSimpleConcat); }
protected void VisitBinaryOperatorExpression() { BinaryOperatorExpression binaryOperatorExpression = this.BinaryOperatorExpression; if (this.Emitter.IsAsync && ( binaryOperatorExpression.Operator == BinaryOperatorType.BitwiseAnd || binaryOperatorExpression.Operator == BinaryOperatorType.BitwiseOr || binaryOperatorExpression.Operator == BinaryOperatorType.ConditionalOr || binaryOperatorExpression.Operator == BinaryOperatorType.ConditionalAnd ) && this.GetAwaiters(binaryOperatorExpression).Length > 0) { if (this.Emitter.AsyncBlock.WrittenAwaitExpressions.Contains(binaryOperatorExpression)) { var index = System.Array.IndexOf(this.Emitter.AsyncBlock.AwaitExpressions, binaryOperatorExpression) + 1; this.Write(JS.Vars.ASYNC_TASK_RESULT + index); } else { var index = System.Array.IndexOf(this.Emitter.AsyncBlock.AwaitExpressions, binaryOperatorExpression) + 1; this.WriteAsyncBinaryExpression(index); } return; } var resolveOperator = this.Emitter.Resolver.ResolveNode(binaryOperatorExpression, this.Emitter); var expectedType = this.Emitter.Resolver.Resolver.GetExpectedType(binaryOperatorExpression); bool isDecimalExpected = Helpers.IsDecimalType(expectedType, this.Emitter.Resolver); bool isDecimal = Helpers.IsDecimalType(resolveOperator.Type, this.Emitter.Resolver); bool isLongExpected = Helpers.Is64Type(expectedType, this.Emitter.Resolver); bool isLong = Helpers.Is64Type(resolveOperator.Type, this.Emitter.Resolver); OperatorResolveResult orr = resolveOperator as OperatorResolveResult; var leftResolverResult = this.Emitter.Resolver.ResolveNode(binaryOperatorExpression.Left, this.Emitter); var rightResolverResult = this.Emitter.Resolver.ResolveNode(binaryOperatorExpression.Right, this.Emitter); var charToString = -1; string variable = null; bool leftIsNull = this.BinaryOperatorExpression.Left is NullReferenceExpression; bool rightIsNull = this.BinaryOperatorExpression.Right is NullReferenceExpression; bool isUint = resolveOperator.Type.IsKnownType(KnownTypeCode.UInt16) || resolveOperator.Type.IsKnownType(KnownTypeCode.UInt32) || resolveOperator.Type.IsKnownType(KnownTypeCode.UInt64); var isFloatResult = Helpers.IsFloatType(resolveOperator.Type, this.Emitter.Resolver); var leftExpected = this.Emitter.Resolver.Resolver.GetExpectedType(binaryOperatorExpression.Left); var rightExpected = this.Emitter.Resolver.Resolver.GetExpectedType(binaryOperatorExpression.Right); var strictNullChecks = this.Emitter.AssemblyInfo.StrictNullChecks; if (orr != null && orr.Type.IsKnownType(KnownTypeCode.String)) { for (int i = 0; i < orr.Operands.Count; i++) { var crr = orr.Operands[i] as ConversionResolveResult; if (crr != null && crr.Input.Type.IsKnownType(KnownTypeCode.Char)) { charToString = i; } } } if (resolveOperator is ConstantResolveResult) { this.WriteScript(((ConstantResolveResult)resolveOperator).ConstantValue); return; } var resultIsString = expectedType.IsKnownType(KnownTypeCode.String) || resolveOperator.Type.IsKnownType(KnownTypeCode.String); var isStringConcat = resultIsString && binaryOperatorExpression.Operator == BinaryOperatorType.Add; var toStringForLeft = false; var toStringForRight = false; var parentBinary = binaryOperatorExpression.Parent as BinaryOperatorExpression; bool parentIsString = resultIsString && parentBinary != null && parentBinary.Operator == BinaryOperatorType.Add; if (parentIsString) { var parentResolveOperator = this.Emitter.Resolver.ResolveNode(binaryOperatorExpression.Parent, this.Emitter) as OperatorResolveResult; if (parentResolveOperator != null && parentResolveOperator.UserDefinedOperatorMethod != null || BinaryOperatorBlock.IsOperatorSimple(parentBinary, this.Emitter)) { parentIsString = false; } } bool isSimpleConcat = isStringConcat && BinaryOperatorBlock.IsOperatorSimple(binaryOperatorExpression, this.Emitter); if (charToString == -1 && isStringConcat && !leftResolverResult.Type.IsKnownType(KnownTypeCode.String)) { toStringForLeft = true; } if (charToString == -1 && isStringConcat && !rightResolverResult.Type.IsKnownType(KnownTypeCode.String)) { toStringForRight = true; } if (!isStringConcat && (Helpers.IsDecimalType(leftResolverResult.Type, this.Emitter.Resolver) || Helpers.IsDecimalType(rightResolverResult.Type, this.Emitter.Resolver))) { isDecimal = true; isDecimalExpected = true; } if (isDecimal && isDecimalExpected && binaryOperatorExpression.Operator != BinaryOperatorType.NullCoalescing) { this.HandleDecimal(resolveOperator); return; } var isLeftLong = Helpers.Is64Type(leftExpected, this.Emitter.Resolver); var isRightLong = Helpers.Is64Type(rightExpected, this.Emitter.Resolver); if (!isLeftLong && !isRightLong) { if (leftExpected.Kind == TypeKind.Enum && Helpers.Is64Type(leftExpected.GetDefinition().EnumUnderlyingType, this.Emitter.Resolver)) { isLeftLong = true; } if (rightExpected.Kind == TypeKind.Enum && Helpers.Is64Type(rightExpected.GetDefinition().EnumUnderlyingType, this.Emitter.Resolver)) { isRightLong = true; } } if (!(resultIsString && binaryOperatorExpression.Operator == BinaryOperatorType.Add) && (isLeftLong || isRightLong)) { isLong = true; isLongExpected = true; } if (isLong && isLongExpected && binaryOperatorExpression.Operator != BinaryOperatorType.NullCoalescing) { if (!isFloatResult || binaryOperatorExpression.Operator == BinaryOperatorType.Divide && isLeftLong) { this.HandleLong(resolveOperator, isUint); return; } } var delegateOperator = false; if (this.ResolveOperator(binaryOperatorExpression, orr)) { return; } if (binaryOperatorExpression.Operator == BinaryOperatorType.Equality || binaryOperatorExpression.Operator == BinaryOperatorType.InEquality) { if (leftIsNull || rightIsNull) { this.WritePart(binaryOperatorExpression.Left, toStringForLeft, leftResolverResult); if (binaryOperatorExpression.Operator == BinaryOperatorType.Equality) { this.Write(strictNullChecks ? " === " : " == "); } else { this.Write(strictNullChecks ? " !== " : " != "); } this.WritePart(binaryOperatorExpression.Right, toStringForRight, rightResolverResult); return; } } var insideOverflowContext = ConversionBlock.InsideOverflowContext(this.Emitter, binaryOperatorExpression); if (binaryOperatorExpression.Operator == BinaryOperatorType.Divide && this.Emitter.Rules.Integer == IntegerRule.Managed && !(this.Emitter.IsJavaScriptOverflowMode && !insideOverflowContext) && ( (Helpers.IsIntegerType(leftResolverResult.Type, this.Emitter.Resolver) && Helpers.IsIntegerType(rightResolverResult.Type, this.Emitter.Resolver)) || (Helpers.IsIntegerType(this.Emitter.Resolver.Resolver.GetExpectedType(binaryOperatorExpression.Left), this.Emitter.Resolver) && Helpers.IsIntegerType(this.Emitter.Resolver.Resolver.GetExpectedType(binaryOperatorExpression.Right), this.Emitter.Resolver)) )) { this.Write(JS.Types.BRIDGE_INT + "." + JS.Funcs.Math.DIV + "("); this.WritePart(binaryOperatorExpression.Left, toStringForLeft, leftResolverResult); this.Write(", "); this.WritePart(binaryOperatorExpression.Right, toStringForRight, rightResolverResult); this.Write(")"); return; } if (binaryOperatorExpression.Operator == BinaryOperatorType.Multiply && this.Emitter.Rules.Integer == IntegerRule.Managed && !(this.Emitter.IsJavaScriptOverflowMode && !insideOverflowContext) && ( (Helpers.IsInteger32Type(leftResolverResult.Type, this.Emitter.Resolver) && Helpers.IsInteger32Type(rightResolverResult.Type, this.Emitter.Resolver) && Helpers.IsInteger32Type(resolveOperator.Type, this.Emitter.Resolver)) || (Helpers.IsInteger32Type(this.Emitter.Resolver.Resolver.GetExpectedType(binaryOperatorExpression.Left), this.Emitter.Resolver) && Helpers.IsInteger32Type(this.Emitter.Resolver.Resolver.GetExpectedType(binaryOperatorExpression.Right), this.Emitter.Resolver) && Helpers.IsInteger32Type(resolveOperator.Type, this.Emitter.Resolver)) )) { isUint = NullableType.GetUnderlyingType(resolveOperator.Type).IsKnownType(KnownTypeCode.UInt32); this.Write(JS.Types.BRIDGE_INT + "." + (isUint ? JS.Funcs.Math.UMUL : JS.Funcs.Math.MUL) + "("); this.WritePart(binaryOperatorExpression.Left, toStringForLeft, leftResolverResult); this.Write(", "); this.WritePart(binaryOperatorExpression.Right, toStringForRight, rightResolverResult); if (ConversionBlock.IsInCheckedContext(this.Emitter, this.BinaryOperatorExpression)) { this.Write(", 1"); } this.Write(")"); return; } if (binaryOperatorExpression.Operator == BinaryOperatorType.Add || binaryOperatorExpression.Operator == BinaryOperatorType.Subtract) { var add = binaryOperatorExpression.Operator == BinaryOperatorType.Add; if (expectedType.Kind == TypeKind.Delegate || this.Emitter.Validator.IsDelegateOrLambda(leftResolverResult) && this.Emitter.Validator.IsDelegateOrLambda(rightResolverResult)) { delegateOperator = true; this.Write(add ? JS.Funcs.BRIDGE_COMBINE : JS.Funcs.BRIDGE_REMOVE); this.WriteOpenParentheses(); } } this.NullStringCheck = isStringConcat && !parentIsString && isSimpleConcat; if (isStringConcat && !parentIsString && !isSimpleConcat) { this.Write(JS.Types.System.String.CONCAT); this.WriteOpenParentheses(); } bool nullable = orr != null && orr.IsLiftedOperator; bool isCoalescing = (this.Emitter.AssemblyInfo.StrictNullChecks || NullableType.IsNullable(leftResolverResult.Type) || leftResolverResult.Type.IsKnownType(KnownTypeCode.String) || leftResolverResult.Type.IsKnownType(KnownTypeCode.Object) ) && binaryOperatorExpression.Operator == BinaryOperatorType.NullCoalescing; string root = JS.Types.SYSTEM_NULLABLE + "."; bool special = nullable; bool rootSpecial = nullable; bool isBool = NullableType.IsNullable(resolveOperator.Type) ? NullableType.GetUnderlyingType(resolveOperator.Type).IsKnownType(KnownTypeCode.Boolean) : resolveOperator.Type.IsKnownType(KnownTypeCode.Boolean); bool toBool = isBool && !rootSpecial && !delegateOperator && (binaryOperatorExpression.Operator == BinaryOperatorType.BitwiseAnd || binaryOperatorExpression.Operator == BinaryOperatorType.BitwiseOr); bool isRefEquals = !isCoalescing && !strictNullChecks && (binaryOperatorExpression.Operator == BinaryOperatorType.InEquality || binaryOperatorExpression.Operator == BinaryOperatorType.Equality) && leftExpected.IsReferenceType.HasValue && leftExpected.IsReferenceType.Value && rightExpected.IsReferenceType.HasValue && rightExpected.IsReferenceType.Value; if (rootSpecial) { this.Write(root); } else if (!isRefEquals) { if (isCoalescing) { this.Write("("); variable = this.GetTempVarName(); this.Write(variable); this.Write(" = "); } else if (charToString == 0) { this.Write(JS.Funcs.STRING_FROMCHARCODE + "("); } if (toBool) { this.Write("!!("); } this.WritePart(binaryOperatorExpression.Left, toStringForLeft, leftResolverResult, isCoalescing); if (isCoalescing) { this.Write(", "); this.Write(variable); this.Write(strictNullChecks ? " !== null" : " != null"); this.Write(" ? "); ConversionBlock.expressionMap.Add(binaryOperatorExpression.Left, variable); //this.Write(variable); binaryOperatorExpression.Left.AcceptVisitor(this.Emitter); ConversionBlock.expressionMap.Remove(binaryOperatorExpression.Left); } else if (charToString == 0) { this.Write(")"); } } if (isRefEquals) { if (binaryOperatorExpression.Operator == BinaryOperatorType.InEquality) { this.Write("!"); } this.Write(JS.Funcs.BRIDGE_REFERENCEEQUALS); special = true; } if (!delegateOperator && (!isStringConcat || isSimpleConcat)) { if (!special) { this.WriteSpace(); } switch (binaryOperatorExpression.Operator) { case BinaryOperatorType.Add: this.Write(rootSpecial ? JS.Funcs.Math.ADD : "+"); break; case BinaryOperatorType.BitwiseAnd: if (isBool) { this.Write(rootSpecial ? JS.Funcs.Math.AND : "&"); } else { this.Write(rootSpecial ? JS.Funcs.Math.BAND : "&"); } break; case BinaryOperatorType.BitwiseOr: if (isBool) { this.Write(rootSpecial ? JS.Funcs.Math.OR : "|"); } else { this.Write(rootSpecial ? JS.Funcs.Math.BOR : "|"); } break; case BinaryOperatorType.ConditionalAnd: this.Write(rootSpecial ? JS.Funcs.Math.AND : "&&"); break; case BinaryOperatorType.NullCoalescing: this.Write(isCoalescing ? ":" : "||"); break; case BinaryOperatorType.ConditionalOr: this.Write(rootSpecial ? JS.Funcs.Math.OR : "||"); break; case BinaryOperatorType.Divide: this.Write(rootSpecial ? JS.Funcs.Math.DIV : "/"); break; case BinaryOperatorType.Equality: if (!isRefEquals) { this.Write(rootSpecial ? "eq" : "==="); } break; case BinaryOperatorType.ExclusiveOr: this.Write(rootSpecial ? JS.Funcs.Math.XOR : "^"); break; case BinaryOperatorType.GreaterThan: this.Write(rootSpecial ? JS.Funcs.Math.GT : ">"); break; case BinaryOperatorType.GreaterThanOrEqual: this.Write(rootSpecial ? JS.Funcs.Math.GTE : ">="); break; case BinaryOperatorType.InEquality: if (!isRefEquals) { this.Write(rootSpecial ? "neq" : "!=="); } break; case BinaryOperatorType.LessThan: this.Write(rootSpecial ? JS.Funcs.Math.LT : "<"); break; case BinaryOperatorType.LessThanOrEqual: this.Write(rootSpecial ? JS.Funcs.Math.LTE : "<="); break; case BinaryOperatorType.Modulus: this.Write(rootSpecial ? JS.Funcs.Math.MOD : "%"); break; case BinaryOperatorType.Multiply: this.Write(rootSpecial ? JS.Funcs.Math.MUL : "*"); break; case BinaryOperatorType.ShiftLeft: this.Write(rootSpecial ? JS.Funcs.Math.SL : "<<"); break; case BinaryOperatorType.ShiftRight: if (isUint) { this.Write(rootSpecial ? JS.Funcs.Math.SRR : ">>>"); } else { this.Write(rootSpecial ? JS.Funcs.Math.SR : ">>"); } break; case BinaryOperatorType.Subtract: this.Write(rootSpecial ? JS.Funcs.Math.SUB : "-"); break; default: throw new EmitterException(binaryOperatorExpression, "Unsupported binary operator: " + binaryOperatorExpression.Operator.ToString()); } } else { this.WriteComma(); } if (special) { this.WriteOpenParentheses(); if (charToString == 0) { this.Write(JS.Funcs.STRING_FROMCHARCODE + "("); } this.WritePart(binaryOperatorExpression.Left, toStringForLeft, leftResolverResult); if (charToString == 0) { this.Write(")"); } this.WriteComma(); } else if (!delegateOperator && (!isStringConcat || isSimpleConcat)) { this.WriteSpace(); } if (charToString == 1) { this.Write(JS.Funcs.STRING_FROMCHARCODE + "("); } this.WritePart(binaryOperatorExpression.Right, toStringForRight, rightResolverResult); if (toBool) { this.WriteCloseParentheses(); } if (charToString == 1 || isCoalescing) { this.WriteCloseParentheses(); } if (delegateOperator || special || isStringConcat && !parentIsString && !isSimpleConcat) { this.WriteCloseParentheses(); } }