protected Expression[] FinishTestForCandidate(IList <Type> testTypes, Type[] explicitArgTypes) { Expression[] exprArgs = MakeArgumentExpressions(); Debug.Assert(exprArgs.Length == (explicitArgTypes.Length + ((_instance == null) ? 0 : 1))); Debug.Assert(testTypes == null || exprArgs.Length == testTypes.Count); MakeSplatTests(); if (_reversedOperator) { ArrayUtils.SwapLastTwo(exprArgs); } if (explicitArgTypes.Length > 0 && testTypes != null) { // We've already tested the instance, no need to test it again. So remove it before adding // rules for the arguments Expression[] exprArgsWithoutInstance = exprArgs; List <Type> testTypesWithoutInstance = new List <Type>(testTypes); for (int i = 0; i < exprArgs.Length; i++) { if (exprArgs[i] == _instance) { // We found the instance, so remove it exprArgsWithoutInstance = ArrayUtils.RemoveAt(exprArgs, i); testTypesWithoutInstance.RemoveAt(i); break; } } _test = Ast.AndAlso(_test, MakeNecessaryTests(_rule, testTypesWithoutInstance.ToArray(), exprArgsWithoutInstance)); } return(exprArgs); }
private void MakeParamsArrayTest() { int listIndex = Action.Signature.IndexOf(ArgumentType.List); Debug.Assert(listIndex != -1); _test = Ast.AndAlso(_test, MakeParamsTest(_args[listIndex + 1], _rule.Parameters[listIndex + 1])); }
private void MakeParamsDictionaryTest() { IDictionary dict = (IDictionary)_args[_args.Length - 1]; IDictionaryEnumerator dictEnum = dict.GetEnumerator(); // verify the dictionary has the same count and arguments. string[] names = new string[dict.Count]; int index = 0; while (dictEnum.MoveNext()) { names[index++] = (string)dictEnum.Entry.Key; } _test = Ast.AndAlso( _test, Ast.AndAlso( Ast.TypeIs(_rule.Parameters[_rule.Parameters.Length - 1], typeof(IDictionary)), Ast.Call( typeof(BinderOps).GetMethod("CheckDictionaryMembers"), Ast.Convert(_rule.Parameters[_rule.Parameters.Length - 1], typeof(IDictionary)), _rule.AddTemplatedConstant(typeof(string[]), names) ) ) ); }
internal static ValidationInfo /*!*/ GetValidationInfo(params DynamicMetaObject /*!*/[] /*!*/ args) { Expression typeTest = null; for (int i = 0; i < args.Length; i++) { if (args[i].HasValue) { IPythonObject val = args[i].Value as IPythonObject; if (val != null) { Expression test = BindingHelpers.CheckTypeVersion( AstUtils.Convert(args[i].Expression, val.GetType()), val.PythonType.Version ); test = Ast.AndAlso( Ast.TypeEqual(args[i].Expression, val.GetType()), test ); if (typeTest != null) { typeTest = Ast.AndAlso(typeTest, test); } else { typeTest = test; } } } } return(new ValidationInfo(typeTest)); }
private Expression[] FinishTestForCandidate(Type[] testTypes, Type[] argTypes) { Expression[] exprargs = MakeArgumentExpressions(); MakeSplatTests(); if (_reversedOperator) { ArrayUtils.SwapLastTwo(exprargs); } if (argTypes.Length > 0 && testTypes != null) { // we've already tested the instance, no need to test it again... Expression[] testArgs = exprargs; Type[] types = testTypes; for (int i = 0; i < testArgs.Length; i++) { if (testArgs[i] == _instance) { testArgs = ArrayUtils.RemoveAt(testArgs, i); types = ArrayUtils.RemoveAt(types, i); break; } } _test = Ast.AndAlso(_test, MakeNecessaryTests(_rule, new Type[][] { types }, testArgs)); } return(exprargs); }
public Expression MakeTestForTypes(Type[] types, int index) { Expression test = MakeTypeTest(types[index], index); if (index < types.Length - 1) { Expression nextTests = MakeTestForTypes(types, index + 1); if (ConstantCheck.Check(test, true)) { return(nextTests); } else if (ConstantCheck.Check(nextTests, true)) { return(test); } else { return(Ast.AndAlso(test, nextTests)); } } else { return(test); } }
public static Expression MakeTypeTestExpression(Type t, Expression expr) { // we must always check for non-sealed types explicitly - otherwise we end up // doing fast-path behavior on a subtype which overrides behavior that wasn't // present for the base type. //TODO there's a question about nulls here if (CompilerHelpers.IsSealed(t) && t == expr.Type) { if (t.IsValueType) { return(Ast.True()); } return(Ast.NotEqual(expr, Ast.Null())); } return(Ast.AndAlso( Ast.NotEqual( expr, Ast.Null()), Ast.Equal( Ast.Call( Ast.ConvertHelper(expr, typeof(object)), typeof(object).GetMethod("GetType") ), Ast.Constant(t) ) )); }
private void MakeParamsDictionaryTest() { IDictionary dict = (IDictionary)_args[_args.Length - 1]; IDictionaryEnumerator dictEnum = dict.GetEnumerator(); // verify the dictionary has the same count and arguments. string[] names = new string[dict.Count]; int index = 0; while (dictEnum.MoveNext()) { string name = dictEnum.Entry.Key as string; if (name == null) { throw new ArgumentTypeException(String.Format("expected string for dictionary argument got {0}", dictEnum.Entry.Key)); } names[index++] = name; } _test = Ast.AndAlso( _test, Ast.AndAlso( Ast.TypeIs(_rule.Parameters[_rule.Parameters.Count - 1], typeof(IDictionary)), Ast.Call( typeof(ScriptingRuntimeHelpers).GetMethod("CheckDictionaryMembers"), Ast.Convert(_rule.Parameters[_rule.Parameters.Count - 1], typeof(IDictionary)), Ast.Constant(names) ) ) ); }
protected virtual MethodBase[] GetTargetMethods() { if (_targets != null) { return(_targets); } object target = Callable; MethodBase[] targets; Delegate d; MemberGroup mg; MethodGroup mthgrp; BoundMemberTracker bmt; if ((d = target as Delegate) != null) { targets = GetDelegateTargets(d); } else if ((mg = target as MemberGroup) != null) { List <MethodInfo> foundTargets = new List <MethodInfo>(); foreach (MemberTracker mt in mg) { if (mt.MemberType == TrackerTypes.Method) { foundTargets.Add(((MethodTracker)mt).Method); } } targets = foundTargets.ToArray(); } else if ((mthgrp = target as MethodGroup) != null) { _test = Ast.AndAlso(_test, Ast.Equal(Ast.Convert(Rule.Parameters[0], typeof(object)), Ast.Constant(target))); List <MethodBase> foundTargets = new List <MethodBase>(); foreach (MethodTracker mt in mthgrp.Methods) { foundTargets.Add(mt.Method); } targets = foundTargets.ToArray(); } else if ((bmt = target as BoundMemberTracker) != null) { targets = GetBoundMemberTargets(bmt); } else { targets = GetOperatorTargets(target); } return(targets); }
public static MSA.Expression /*!*/ Logical(MSA.Expression /*!*/ left, MSA.Expression /*!*/ right, bool isConjunction) { if (isConjunction) { return(Ast.AndAlso(left, right)); } else { return(Ast.OrElse(left, right)); } }
public void AddTest(Expression expression) { Assert.NotNull(expression); if (_test == null) { _test = expression; } else { _test = Ast.AndAlso(_test, expression); } }
internal static ValidationInfo /*!*/ GetValidationInfo(DynamicMetaObject /*!*/ tested, PythonType type) { return(new ValidationInfo( Ast.AndAlso( Ast.TypeEqual(tested.Expression, type.UnderlyingSystemType), CheckTypeVersion( AstUtils.Convert(tested.Expression, type.UnderlyingSystemType), type.Version ) ) )); }
public static Expression MakeParamsTest(StandardRule <T> rule, object paramArg, Expression listArg) { return(Ast.AndAlso( Ast.TypeIs(listArg, typeof(ICollection <object>)), Ast.Equal( Ast.ReadProperty( Ast.Convert(listArg, typeof(ICollection <object>)), typeof(ICollection <object>).GetProperty("Count") ), rule.AddTemplatedConstant(typeof(int), ((IList <object>)paramArg).Count) ) )); }
/// <summary> /// Builds the restrictions for calling with a splatted argument array. Ensures that the /// argument is still an ICollection of object and that it has the same number of arguments. /// </summary> private static BindingRestrictions MakeParamsTest(DynamicMetaObject splattee, bool testTypes) { IList <object> list = splattee.Value as IList <object>; if (list == null) { if (splattee.Value == null) { return(BindingRestrictions.GetExpressionRestriction(Ast.Equal(splattee.Expression, AstUtils.Constant(null)))); } else { return(BindingRestrictions.GetTypeRestriction(splattee.Expression, splattee.Value.GetType())); } } BindingRestrictions res = BindingRestrictions.GetExpressionRestriction( Ast.AndAlso( Ast.TypeIs(splattee.Expression, typeof(IList <object>)), Ast.Equal( Ast.Property( Ast.Convert(splattee.Expression, typeof(IList <object>)), typeof(ICollection <object>).GetDeclaredProperty("Count") ), AstUtils.Constant(list.Count) ) ) ); if (testTypes) { for (int i = 0; i < list.Count; i++) { res = res.Merge( BindingRestrictionsHelpers.GetRuntimeTypeRestriction( Ast.Call( AstUtils.Convert( splattee.Expression, typeof(IList <object>) ), typeof(IList <object>).GetMethod("get_Item"), AstUtils.Constant(i) ), CompilerHelpers.GetType(list[i]) ) ); } } return(res); }
protected override MethodBase[] GetTargetMethods() { object target = Arguments[0]; Type t = GetTargetType(target); if (t != null) { Test = Ast.AndAlso(Test, Ast.Equal(Rule.Parameters[0], Ast.Constant(target))); return(CompilerHelpers.GetConstructors(t, Binder.PrivateBinding)); } return(null); }
/// <summary> /// If present, converts the finish condition body be a normal conditional body. /// The builder instance will become unfinished again. /// /// If no finish condition body is available, this extends the last condition check /// with the new condition. /// </summary> public void ExtendLastCondition(Expression condition) { if (_body != null) { AddCondition(condition, _body); _body = null; } else { _conditions[_conditions.Count - 1] = Ast.AndAlso( _conditions[_conditions.Count - 1], condition); } }
public void AddTest(Expression expression) { ContractUtils.RequiresNotNull(expression, "expression"); ContractUtils.Requires(TypeUtils.IsBool(expression.Type), "expression", Strings.TypeOfExpressionMustBeBool); if (_test == null) { _test = expression; } else { _test = Ast.AndAlso(_test, expression); } }
internal override Expression CheckExpression(MethodBinderContext context, Expression[] parameters) { if (_count == 0) { return(null); } Expression res = context.CheckExpression(parameters[_start], _elementType); for (int i = 1; i < _count; i++) { res = Ast.AndAlso(res, context.CheckExpression(parameters[_start + i], _elementType)); } return(res); }
public static Expression BooleanAnd(Expression[] args) { int len = args.Length; switch (len) { case 0: return(Ast.Constant(true)); default: var rargs = new Expression[len - 1]; Array.Copy(args, 1, rargs, 0, rargs.Length); return(Ast.AndAlso(Ast.ConvertHelper(args[0], typeof(bool)), BooleanAnd(rargs))); } }
private MethodBase[] GetBoundMemberTargets(BoundMemberTracker bmt) { Debug.Assert(bmt.Instance == null); // should be null for trackers that leak to user code MethodBase[] targets; _instance = Ast.Convert( Ast.Property( Ast.Convert(Rule.Parameters[0], typeof(BoundMemberTracker)), typeof(BoundMemberTracker).GetProperty("ObjectInstance") ), bmt.BoundTo.DeclaringType ); _test = Ast.AndAlso( _test, Ast.Equal( Ast.Property( Ast.Convert(Rule.Parameters[0], typeof(BoundMemberTracker)), typeof(BoundMemberTracker).GetProperty("BoundTo") ), Ast.Constant(bmt.BoundTo) ) ); _test = Ast.AndAlso( _test, Rule.MakeTypeTest( CompilerHelpers.GetType(bmt.ObjectInstance), Ast.Property( Ast.Convert(Rule.Parameters[0], typeof(BoundMemberTracker)), typeof(BoundMemberTracker).GetProperty("ObjectInstance") ) ) ); switch (bmt.BoundTo.MemberType) { case TrackerTypes.MethodGroup: targets = ((MethodGroup)bmt.BoundTo).GetMethodBases(); break; case TrackerTypes.Method: targets = new MethodBase[] { ((MethodTracker)bmt.BoundTo).Method }; break; default: throw new InvalidOperationException(); // nothing else binds yet } return(targets); }
private Expression /*!*/ MakeCheckSelf(DynamicMetaObjectBinder /*!*/ binder, CallSignature signature, DynamicMetaObject /*!*/[] /*!*/ args) { ArgumentType firstArgKind = signature.GetArgumentKind(0); Expression res; if (firstArgKind == ArgumentType.Simple || firstArgKind == ArgumentType.Instance) { res = CheckSelf(binder, AstUtils.Convert(Expression, typeof(Method)), args[0].Expression); } else if (firstArgKind != ArgumentType.List) { res = CheckSelf(binder, AstUtils.Convert(Expression, typeof(Method)), AstUtils.Constant(null)); } else { // list, check arg[0] and then return original list. If not a list, // or we have no items, then check against null & throw. res = CheckSelf( binder, AstUtils.Convert(Expression, typeof(Method)), Ast.Condition( Ast.AndAlso( Ast.TypeIs(args[0].Expression, typeof(IList <object>)), Ast.NotEqual( Ast.Property( Ast.Convert(args[0].Expression, typeof(ICollection)), typeof(ICollection).GetProperty("Count") ), AstUtils.Constant(0) ) ), Ast.Call( Ast.Convert(args[0].Expression, typeof(IList <object>)), typeof(IList <object>).GetMethod("get_Item"), AstUtils.Constant(0) ), AstUtils.Constant(null) ) ); } return(res); }
public static Expression MakeNecessaryTests(StandardRule <T> rule, IList <Type[]> necessaryTests, Expression [] arguments) { Expression typeTest = Ast.Constant(true); if (necessaryTests.Count > 0) { Type[] testTypes = null; for (int i = 0; i < necessaryTests.Count; i++) { if (necessaryTests[i] == null) { continue; } if (testTypes == null) { testTypes = new Type[necessaryTests[i].Length]; } for (int j = 0; j < necessaryTests[i].Length; j++) { if (testTypes[j] == null || testTypes[j].IsAssignableFrom(necessaryTests[i][j])) { // no test yet or more specific test testTypes[j] = necessaryTests[i][j]; } } } if (testTypes != null) { for (int i = 0; i < testTypes.Length; i++) { if (testTypes[i] != null) { Debug.Assert(i < arguments.Length); typeTest = Ast.AndAlso(typeTest, rule.MakeTypeTest(testTypes[i], arguments[i])); } } } } return(typeTest); }
/// <summary> /// Builds the restrictions for calling with a splatted argument array. Ensures that the /// argument is still an ICollection of object and that it has the same number of arguments. /// </summary> private static Restrictions MakeParamsTest(object paramArg, Expression listArg, bool testTypes) { IList <object> coll = (IList <object>)paramArg; Restrictions res = Restrictions.GetExpressionRestriction( Ast.AndAlso( Ast.TypeIs(listArg, typeof(IList <object>)), Ast.Equal( Ast.Property( Ast.Convert(listArg, typeof(IList <object>)), typeof(ICollection <object>).GetProperty("Count") ), Ast.Constant(coll.Count) ) ) ); if (testTypes) { for (int i = 0; i < coll.Count; i++) { res = res.Merge( Restrictions.GetTypeRestriction( Ast.Call( AstUtils.Convert( listArg, typeof(IList <object>) ), typeof(IList <object>).GetMethod("get_Item"), Ast.Constant(i) ), CompilerHelpers.GetType(coll[i]) ) ); } } return(res); }
private void MakeInvalidParametersRule(MethodBinder binder, CallType callType, MethodBase[] targets) { MakeSplatTests(); if (_args.Length > 1) { // we do an exact type check on all of the arguments types for a failed call. Expression[] argExpr = MakeArgumentExpressions(); SymbolId[] names; Type[] vals; GetArgumentNamesAndTypes(out names, out vals); if (_instance != null) { // target type was added to test already argExpr = ArrayUtils.RemoveFirst(argExpr); } _test = Ast.AndAlso(_test, MakeNecessaryTests(_rule, new Type[][] { vals }, argExpr)); } _rule.SetTarget(Binder.MakeInvalidParametersError(binder, Action, callType, targets, _rule, _args)); }
/// <summary> /// Builds the restrictions for calling with keyword arguments. The restrictions include /// tests on the individual keys of the dictionary to ensure they have the same names. /// </summary> private static BindingRestrictions MakeParamsDictionaryTest(IList <DynamicMetaObject> args, bool testTypes) { IDictionary dict = (IDictionary)args[args.Count - 1].Value; IDictionaryEnumerator dictEnum = dict.GetEnumerator(); // verify the dictionary has the same count and arguments. string[] names = new string[dict.Count]; Type[] types = testTypes ? new Type[dict.Count] : null; int index = 0; while (dictEnum.MoveNext()) { string name = dictEnum.Entry.Key as string; if (name == null) { throw ScriptingRuntimeHelpers.SimpleTypeError( $"expected string for dictionary argument got {dictEnum.Entry.Key}"); } names[index] = name; if (types != null) { types[index] = CompilerHelpers.GetType(dictEnum.Entry.Value); } index++; } return(BindingRestrictions.GetExpressionRestriction( Ast.AndAlso( Ast.TypeIs(args[args.Count - 1].Expression, typeof(IDictionary)), Ast.Call( typeof(BinderOps).GetMethod("CheckDictionaryMembers"), Ast.Convert(args[args.Count - 1].Expression, typeof(IDictionary)), AstUtils.Constant(names), testTypes ? AstUtils.Constant(types) : AstUtils.Constant(null, typeof(Type[])) ) ) )); }
private void MakeInvalidParametersRule(BindingTarget bt) { MakeSplatTests(); if (_args.Length > 1) { // we do an exact type check on all of the arguments types for a failed call. Expression[] argExpr = MakeArgumentExpressions(); SymbolId[] names; Type[] vals; GetArgumentNamesAndTypes(out names, out vals); if (_instance != null) { // target type was added to test already argExpr = ArrayUtils.RemoveFirst(argExpr); } _test = Ast.AndAlso(_test, MakeNecessaryTests(_rule, vals, argExpr)); } _rule.Target = Binder.MakeInvalidParametersError(bt).MakeErrorForRule(_rule, Binder); }
public void AddTargetTypeTest(object target, RubyClass /*!*/ targetClass, Expression /*!*/ targetParameter, DynamicMetaObject /*!*/ metaContext, IEnumerable <string> /*!*/ resolvedNames) { // no changes to the module's class hierarchy while building the test: targetClass.Context.RequiresClassHierarchyLock(); // initialization changes the version number, so ensure that the module is initialized: targetClass.InitializeMethodsNoLock(); var context = (RubyContext)metaContext.Value; if (target is IRubyObject) { Type type = target.GetType(); AddTypeRestriction(type, targetParameter); // Ruby objects (get the method directly to prevent interface dispatch): MethodInfo classGetter = type.GetMethod(Methods.IRubyObject_get_ImmediateClass.Name, BindingFlags.Public | BindingFlags.Instance); if (type.IsVisible() && classGetter != null && classGetter.ReturnType == typeof(RubyClass)) { AddCondition( // (#{type})target.ImmediateClass.Version.Method == #{immediateClass.Version.Method} Ast.Equal( Ast.Field( Ast.Field( Ast.Call(Ast.Convert(targetParameter, type), classGetter), Fields.RubyModule_Version ), Fields.VersionHandle_Method ), AstUtils.Constant(targetClass.Version.Method) ) ); return; } // TODO: explicit iface-implementation throw new NotSupportedException("Type implementing IRubyObject should be visible and have ImmediateClass getter"); } AddRuntimeTest(metaContext); // singleton nil: if (target == null) { AddRestriction(Ast.Equal(targetParameter, AstUtils.Constant(null))); AddVersionTest(context.NilClass); return; } // singletons true, false: if (target is bool) { AddRestriction(Ast.AndAlso( Ast.TypeIs(targetParameter, typeof(bool)), Ast.Equal(Ast.Convert(targetParameter, typeof(bool)), AstUtils.Constant(target)) )); AddVersionTest((bool)target ? context.TrueClass : context.FalseClass); return; } var nominalClass = targetClass.NominalClass; Debug.Assert(!nominalClass.IsSingletonClass); Debug.Assert(!nominalClass.IsRubyClass); // Do we need a singleton check? if (nominalClass.ClrSingletonMethods == null || CollectionUtils.TrueForAll(resolvedNames, (methodName) => !nominalClass.ClrSingletonMethods.ContainsKey(methodName))) { // no: there is no singleton subclass of target class that defines any method being called: AddTypeRestriction(target.GetType(), targetParameter); AddVersionTest(targetClass); } else if (targetClass.IsSingletonClass) { // yes: check whether the incoming object is a singleton and the singleton has the right version: AddTypeRestriction(target.GetType(), targetParameter); AddCondition(Methods.IsClrSingletonRuleValid.OpCall( metaContext.Expression, targetParameter, AstUtils.Constant(targetClass.Version.Method) )); } else { // yes: check whether the incoming object is NOT a singleton and the class has the right version: AddTypeRestriction(target.GetType(), targetParameter); AddCondition(Methods.IsClrNonSingletonRuleValid.OpCall( metaContext.Expression, targetParameter, Ast.Constant(targetClass.Version), AstUtils.Constant(targetClass.Version.Method) )); } }
public void AddCondition(Expression /*!*/ condition) { Assert.NotNull(condition); _condition = (_condition != null) ? Ast.AndAlso(_condition, condition) : condition; }
private MSAst.LambdaExpression CreateOuterLambda(Type lambdaType, MSAst.Expression debuggableBody) { List <MSAst.Expression> bodyExpressions = new List <MSAst.Expression>(); List <MSAst.Expression> tryExpressions = new List <MSAst.Expression>(); List <MSAst.Expression> finallyExpressions = new List <MSAst.Expression>(); Type returnType = lambdaType.GetMethod("Invoke").ReturnType; MSAst.LabelTarget returnLabelTarget = Ast.Label(returnType); // Init $funcInfo tryExpressions.Add( Ast.Assign( _funcInfo, Ast.Convert(_functionInfo, typeof(FunctionInfo)) ) ); // Init $traceLocations // $TODO: only do this if we're in TracePoints mode tryExpressions.Add( Ast.Assign( _traceLocations, Ast.Call(typeof(RuntimeOps).GetMethod("GetTraceLocations"), _funcInfo) ) ); // Init sourceFile locals foreach (var entry in _sourceFilesMap) { tryExpressions.Add( Ast.Assign( entry.Value, Ast.Constant(entry.Key, typeof(DebugSourceFile)) ) ); } if (_noPushFrameOptimization) { tryExpressions.Add(_pushFrame); } tryExpressions.Add(Ast.Call( typeof(RuntimeOps).GetMethod("OnFrameEnterTraceEvent"), _thread )); var frameExit = AstUtils.If( Ast.Equal( _debugMarkerLocationMap.Length > 0 ? Ast.Property(_sourceFilesMap[_debugMarkerLocationMap[0].SourceFile], "Mode") : _globalDebugMode, AstUtils.Constant((int)DebugMode.FullyEnabled) ), Ast.Call( typeof(RuntimeOps).GetMethod("OnFrameExitTraceEvent"), _thread, _debugMarker, _retVal != null ? (MSAst.Expression)Ast.Convert(_retVal, typeof(object)) : Ast.Constant(null) ) ); // normal exit tryExpressions.Add( Ast.Block( _retVal != null ? Ast.Assign(_retVal, debuggableBody) : debuggableBody, Ast.Assign(_frameExitException, Ast.Constant(true)), frameExit) ); tryExpressions.Add( _retVal != null ? (MSAst.Expression)Ast.Return(returnLabelTarget, _retVal) : Ast.Empty() ); MSAst.Expression[] popFrame = new MSAst.Expression[] { AstUtils.If( // Fire thead-exit event if PopFrame returns true Ast.AndAlso( Ast.Equal(Ast.Call(typeof(RuntimeOps).GetMethod("PopFrame"), _thread), Ast.Constant(true)), Ast.Equal(_globalDebugMode, AstUtils.Constant((int)DebugMode.FullyEnabled)) ), Ast.Call( typeof(RuntimeOps).GetMethod("OnThreadExitEvent"), _thread ) ) }; if (_noPushFrameOptimization) { finallyExpressions.AddRange(popFrame); } else { finallyExpressions.Add( AstUtils.If( Ast.Equal(_framePushed, Ast.Constant(true)), popFrame ) ); } MSAst.ParameterExpression caughtException; // Run the function body bodyExpressions.Add(Ast.TryCatchFinally( Ast.TryCatch( Ast.Block( ArrayUtils.Append(tryExpressions.ToArray(), Ast.Default(returnType)) ), Ast.Catch( caughtException = Ast.Variable(typeof(Exception), "$caughtException"), Ast.Block( // The expressions below will always throw. // If the exception needs to be cancelled then OnTraceEvent will throw ForceToGeneratorLoopException. // If the exception is not being cancelled then we'll just rethrow at the end of the catch block. AstUtils.If( Ast.Not( Ast.TypeIs( caughtException, typeof(ForceToGeneratorLoopException) ) ), AstUtils.If( Ast.NotEqual(_globalDebugMode, AstUtils.Constant((int)DebugMode.Disabled)), _noPushFrameOptimization ? Ast.Empty() : _conditionalPushFrame, Ast.Call( typeof(RuntimeOps).GetMethod("OnTraceEventUnwind"), _thread, _debugMarker, caughtException ) ), // exception exit AstUtils.If( Ast.Not(_frameExitException), frameExit ) ), Ast.Rethrow(), // Ensuring that the catch block is of the same type as the try block Ast.Default(returnType) ) ) ), Ast.Block(finallyExpressions), Ast.Catch( typeof(ForceToGeneratorLoopException), Ast.TryFinally( // Handle ForceToGeneratorLoopException Ast.Block( returnType != typeof(void) ? Ast.Block( Ast.Assign( _retValFromGeneratorLoop, Ast.Call( typeof(RuntimeOps).GetMethod("GeneratorLoopProc"), _thread ) ), AstUtils.If( Ast.NotEqual( _retValFromGeneratorLoop, Ast.Constant(null) ), Ast.Assign(_retVal, Ast.Convert(_retValFromGeneratorLoop, returnType)), Ast.Return( returnLabelTarget, Ast.Convert(_retValFromGeneratorLoop, returnType) ) ).Else( Ast.Assign(_retVal, Ast.Default(returnType)), Ast.Return( returnLabelTarget, Ast.Default(returnType) ) ) ) : Ast.Block( Ast.Call( typeof(RuntimeOps).GetMethod("GeneratorLoopProc"), _thread ), Ast.Return(returnLabelTarget) ) , // Ensuring that the catch block is of the same type as the try block Ast.Default(returnType) ), // Make sure that the debugMarker is up-to-date after the generator loop Ast.Assign( _debugMarker, Ast.Call( typeof(RuntimeOps).GetMethod("GetCurrentSequencePointForLeafGeneratorFrame"), _thread ) ) ) ) )); MSAst.Expression body = Ast.Block(bodyExpressions); if (body.Type == typeof(void) && returnType != typeof(void)) { body = Ast.Block(body, Ast.Default(returnType)); } return(Ast.Lambda( lambdaType, Ast.Block( _lambdaVars, Ast.Label(returnLabelTarget, body) ), _alias, _lambdaParams)); }
private MSA.Expression /*!*/ TransformExceptionHandling(AstGenerator /*!*/ gen, ResultOperation resultOperation) { Assert.NotNull(gen); MSA.Expression exceptionThrownVariable = gen.CurrentScope.DefineHiddenVariable("#exception-thrown", typeof(bool)); MSA.Expression exceptionRethrowVariable = gen.CurrentScope.DefineHiddenVariable("#exception-rethrow", typeof(bool)); MSA.Expression retryingVariable = gen.CurrentScope.DefineHiddenVariable("#retrying", typeof(bool)); MSA.Expression oldExceptionVariable = gen.CurrentScope.DefineHiddenVariable("#old-exception", typeof(Exception)); MSA.ParameterExpression unwinder, exceptionVariable; MSA.Expression transformedBody; MSA.Expression transformedEnsure; MSA.Expression transformedElse; if (_ensureStatements != null) { transformedEnsure = Ast.Block( // ensure: Ast.Assign(oldExceptionVariable, Methods.GetCurrentException.OpCall(gen.CurrentScopeVariable)), gen.TransformStatements(_ensureStatements, ResultOperation.Ignore), Methods.SetCurrentException.OpCall(gen.CurrentScopeVariable, oldExceptionVariable), // rethrow: AstUtils.IfThen( Ast.AndAlso( exceptionRethrowVariable, Ast.NotEqual(oldExceptionVariable, AstUtils.Constant(null)) ), Ast.Throw(oldExceptionVariable) ) ); } else { // rethrow: transformedEnsure = AstUtils.IfThen( Ast.AndAlso( exceptionRethrowVariable, Ast.NotEqual( Ast.Assign(oldExceptionVariable, Methods.GetCurrentException.OpCall(gen.CurrentScopeVariable)), AstUtils.Constant(null, typeof(Exception))) ), Ast.Throw(oldExceptionVariable) ); } if (_elseStatements != null) { transformedElse = gen.TransformStatements(_elseStatements, resultOperation); } else { transformedElse = AstUtils.Empty(); } // body should do return, but else-clause is present => we cannot do return from the guarded statements: // (the value of the last expression in the body cannot be the last executed expression statement => we can ignore it): transformedBody = gen.TransformStatements(_statements, (_elseStatements != null) ? ResultOperation.Ignore : resultOperation); MSA.Expression enterRescue = null, leaveRescue = null; var retryLabel = Ast.Label("retry"); // make rescue clause: MSA.Expression transformedRescue; if (_rescueClauses != null) { // outer-most EH blocks sets and clears runtime flag RuntimeFlowControl.InTryRescue: if (gen.CurrentRescue == null) { enterRescue = Methods.EnterRescue.OpCall(gen.CurrentScopeVariable); leaveRescue = Methods.LeaveRescue.OpCall(gen.CurrentScopeVariable); } else { enterRescue = leaveRescue = AstUtils.Empty(); } gen.EnterRescueClause(retryingVariable, retryLabel); var handlers = new IfStatementTest[_rescueClauses.Count]; for (int i = 0; i < handlers.Length; i++) { handlers[i] = _rescueClauses[i].Transform(gen, resultOperation); } transformedRescue = AstUtils.Try( enterRescue, AstUtils.If(handlers, Ast.Assign(exceptionRethrowVariable, AstUtils.Constant(true))) ).Filter(unwinder = Ast.Parameter(typeof(EvalUnwinder), "#u"), Ast.Equal(Ast.Field(unwinder, EvalUnwinder.ReasonField), AstUtils.Constant(BlockReturnReason.Retry)), Ast.Block( Ast.Assign(retryingVariable, AstUtils.Constant(true)), Ast.Continue(retryLabel), AstUtils.Empty() ) ); gen.LeaveRescueClause(); } else { transformedRescue = Ast.Assign(exceptionRethrowVariable, AstUtils.Constant(true)); } if (_elseStatements != null) { transformedElse = AstUtils.Unless(exceptionThrownVariable, transformedElse); } var result = Ast.Block( Ast.Label(retryLabel), AstUtils.Try( Ast.Assign(exceptionThrownVariable, AstUtils.Constant(false)), Ast.Assign(exceptionRethrowVariable, AstUtils.Constant(false)), Ast.Assign(retryingVariable, AstUtils.Constant(false)), // save exception (old_$! is not used unless there is a rescue clause): (_rescueClauses == null) ? (MSA.Expression)AstUtils.Empty() : Ast.Assign(oldExceptionVariable, Methods.GetCurrentException.OpCall(gen.CurrentScopeVariable)), AstUtils.Try( Ast.Block(transformedBody, AstUtils.Empty()) ).Filter(exceptionVariable = Ast.Parameter(typeof(Exception), "#e"), Methods.CanRescue.OpCall(gen.CurrentScopeVariable, exceptionVariable), Ast.Assign(exceptionThrownVariable, AstUtils.Constant(true)), transformedRescue, AstUtils.Empty() ).FinallyIf((_rescueClauses != null), // restore previous exception if the current one has been handled: AstUtils.Unless(exceptionRethrowVariable, Methods.SetCurrentException.OpCall(gen.CurrentScopeVariable, oldExceptionVariable) ), leaveRescue ), // unless (exception_thrown) do <else-statements> end transformedElse, AstUtils.Empty() ).FilterIf((_rescueClauses != null || _elseStatements != null), exceptionVariable = Ast.Parameter(typeof(Exception), "#e"), Methods.CanRescue.OpCall(gen.CurrentScopeVariable, exceptionVariable), Ast.Assign(exceptionRethrowVariable, AstUtils.Constant(true)), AstUtils.Empty() ).FinallyWithJumps( AstUtils.Unless(retryingVariable, transformedEnsure) ) ); return(result); }