/// <summary> /// Emits code for comparing a nullable /// </summary> private void compileNullable(Context ctx, NodeBase nullValue, NodeBase otherValue) { var gen = ctx.CurrentILGenerator; var nullType = nullValue.GetExpressionType(ctx); var otherType = otherValue.GetExpressionType(ctx); var otherNull = otherType.IsNullableType(); var getValOrDefault = nullType.GetMethod("GetValueOrDefault", Type.EmptyTypes); var hasValueGetter = nullType.GetProperty("HasValue").GetGetMethod(); var falseLabel = gen.DefineLabel(); var endLabel = gen.DefineLabel(); LocalName nullVar, otherVar = null; nullVar = ctx.CurrentScope.DeclareImplicitName(ctx, nullType, true); if (otherNull) otherVar = ctx.CurrentScope.DeclareImplicitName(ctx, otherType, true); // if (otherNull) // { // otherVar = ctx.CurrentScope.DeclareImplicitName(ctx, otherType, true); // // var code = Expr.Block( // Expr.Let(nullVar, nullValue), // Expr.Let(otherVar, otherValue), // Expr.Binary( // Kind == ComparisonOperatorKind.Equals ? BooleanOperatorKind.And : BooleanOperatorKind.Or, // Expr.Compare( // Kind, // Expr.Invoke(Expr.GetIdentifier(nullVar), "GetValueOrDefault"), // Expr.Invoke(Expr.GetIdentifier(otherVar), "GetValueOrDefault") // ), // Expr.Compare( // Kind, // Expr.Invoke(Expr.GetIdentifier(nullVar), "get_HasValue"), // Expr.Invoke(Expr.GetIdentifier(otherVar), "get_HasValue") // ) // ) // ); // // code.Compile(ctx, true); // } // else // { // var code = Expr.Block( // Expr.Let(nullVar, nullValue), // Expr.Binary( // Kind == ComparisonOperatorKind.Equals ? BooleanOperatorKind.And : BooleanOperatorKind.Or, // Expr.Compare( // Kind, // Expr.Invoke(Expr.GetIdentifier(nullVar), "GetValueOrDefault"), // Expr.Cast(otherValue, Nullable.GetUnderlyingType(nullType)) // ), // Expr.Invoke(Expr.GetIdentifier(nullVar), "get_HasValue") // ) // ); // // code.Compile(ctx, true); // } // $tmp = nullValue nullValue.Compile(ctx, true); gen.EmitSaveLocal(nullVar); if (otherNull) { // $tmp2 = otherValue otherValue.Compile(ctx, true); gen.EmitSaveLocal(otherVar); } // $tmp == $tmp2 gen.EmitLoadLocal(nullVar, true); gen.EmitCall(getValOrDefault); if (otherNull) { gen.EmitLoadLocal(otherVar, true); gen.EmitCall(getValOrDefault); } else { otherValue.Compile(ctx, true); } gen.EmitBranchNotEquals(falseLabel); // otherwise, compare HasValues gen.EmitLoadLocal(nullVar, true); gen.EmitCall(hasValueGetter); if (otherNull) { gen.EmitLoadLocal(otherVar, true); gen.EmitCall(hasValueGetter); gen.EmitCompareEqual(); } if(Kind == ComparisonOperatorKind.NotEquals) emitInversion(gen); gen.EmitJump(endLabel); gen.MarkLabel(falseLabel); gen.EmitConstant(false); gen.MarkLabel(endLabel); gen.EmitNop(); }
private void resolveGetMember(Context ctx, GetMemberNode node) { m_InvocationSource = node.Expression; var type = m_InvocationSource != null ? m_InvocationSource.GetExpressionType(ctx) : ctx.ResolveType(node.StaticType); SafeModeCheckType(ctx, type); if (node.TypeHints.Any()) m_TypeHints = node.TypeHints.Select(x => x.FullSignature == "_" ? null : ctx.ResolveType(x)).ToArray(); try { // resolve a normal method try { m_Method = ctx.ResolveMethod(type, node.MemberName, m_ArgTypes, m_TypeHints); if (m_Method.IsStatic) m_InvocationSource = null; return; } catch (KeyNotFoundException) { if (m_InvocationSource == null) throw; } // resolve a callable field try { ctx.ResolveField(type, node.MemberName); resolveExpression(ctx, node); return; } catch (KeyNotFoundException) { } // resolve a callable field try { ctx.ResolveProperty(type, node.MemberName); resolveExpression(ctx, node); return; } catch (KeyNotFoundException) { } // resolve a local function that is implicitly used as an extension method // move invocation source to arguments if (Arguments[0] is UnitNode) Arguments[0] = m_InvocationSource; else Arguments.Insert(0, m_InvocationSource); var oldArgTypes = m_ArgTypes; m_ArgTypes = Arguments.Select(a => a.GetExpressionType(ctx)).ToArray(); m_InvocationSource = null; try { m_Method = ctx.ResolveMethod(ctx.MainType.TypeInfo, node.MemberName, m_ArgTypes); } catch (KeyNotFoundException) { // resolve a declared extension method // most time-consuming operation, therefore is last checked if(ctx.Options.AllowExtensionMethods) m_Method = ctx.ResolveExtensionMethod(type, node.MemberName, oldArgTypes, m_TypeHints); } } catch (AmbiguousMatchException) { Error(CompilerMessages.TypeMethodInvocationAmbiguous, type, node.MemberName); } catch (KeyNotFoundException) { var msg = node.StaticType != null ? CompilerMessages.TypeStaticMethodNotFound : CompilerMessages.TypeMethodNotFound; Error(msg, type, node.MemberName); } }
/// <summary> /// Checks if the nullable expression is null. /// </summary> private void compileHasValue(Context ctx, NodeBase nullValue) { var gen = ctx.CurrentILGenerator; var nullType = nullValue.GetExpressionType(ctx); var nullVar = ctx.CurrentScope.DeclareImplicitName(ctx, nullType, true); var hasValueGetter = nullType.GetProperty("HasValue").GetGetMethod(); nullValue.Compile(ctx, true); gen.EmitSaveLocal(nullVar); gen.EmitLoadLocal(nullVar, true); gen.EmitCall(hasValueGetter); // sic! get_HasValue == true when value != null if(Kind == ComparisonOperatorKind.Equals) emitInversion(gen); }
private void resolveExpression(Context ctx, NodeBase node) { var exprType = node.GetExpressionType(ctx); if (!exprType.IsCallableType()) Error(CompilerMessages.TypeNotCallable, exprType); m_Method = ctx.ResolveMethod(exprType, "Invoke"); var argTypes = m_Method.ArgumentTypes; if (argTypes.Length != m_ArgTypes.Length) Error(CompilerMessages.DelegateArgumentsCountMismatch, exprType, argTypes.Length, m_ArgTypes.Length); for (var idx = 0; idx < argTypes.Length; idx++) { var fromType = m_ArgTypes[idx]; var toType = argTypes[idx]; if (!toType.IsExtendablyAssignableFrom(fromType)) Error(Arguments[idx], CompilerMessages.ArgumentTypeMismatch, fromType, toType); } m_InvocationSource = node; }